summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Dufresne <nicolas.dufresne@collabora.com>2012-02-08 11:21:33 +0100
committerNicolas Dufresne <nicolas.dufresne@collabora.com>2012-02-08 11:31:00 +0100
commit00a76e3668c2c10ab2b3cceaf6cc4e1a3eb70c97 (patch)
tree4e84aa38efe7dc509bdd884f66a9297dee3642b4
parent415706b45a63915dcd442fab7364a6d9ade1d79c (diff)
Add CallTest base class
This base class can be use to greatly reduce Call test code size
-rw-r--r--tests/twisted/jingle/call_helper.py796
1 files changed, 796 insertions, 0 deletions
diff --git a/tests/twisted/jingle/call_helper.py b/tests/twisted/jingle/call_helper.py
new file mode 100644
index 000000000..704e289d4
--- /dev/null
+++ b/tests/twisted/jingle/call_helper.py
@@ -0,0 +1,796 @@
+"""
+Base classes for Call tests
+"""
+
+import config
+
+if not config.CHANNEL_TYPE_CALL_ENABLED:
+ print "NOTE: built with --disable-channel-type-call"
+ raise SystemExit(77)
+
+import dbus
+from dbus.exceptions import DBusException
+
+from functools import partial
+from servicetest import (
+ EventPattern, call_async, wrap_channel, wrap_content,
+ assertEquals, assertDoesNotContain, assertContains, assertLength,
+ assertNotEquals, DictionarySupersetOf)
+from gabbletest import sync_stream, make_result_iq
+from jingletest2 import JingleTest2, test_all_dialects
+import constants as cs
+import ns
+
+class CallTest(object):
+
+ SELF_JID = 'test@localhost'
+ PEER_JID = 'foo@bar.com/Foo'
+
+ # These can be changed as needed by base class
+ initial_audio = True
+ initial_video = False
+
+ # The following will be set after initiate_call()
+ chan = None
+
+ audio_content = None
+ audio_content_name = None
+ audio_stream = None
+
+ video_content = None
+ video_content_name = None
+ video_stream = None
+
+
+ def __init__(self, jp, q, bus, conn, stream, incoming, params):
+ self.jp = jp
+ self.q = q
+ self.bus = bus
+ self.conn = conn
+ self.stream = stream
+ self.incoming = incoming
+ self.params = params
+ self.jt2 = JingleTest2(jp, conn, q, stream, self.SELF_JID,
+ self.PEER_JID)
+ self.can_change_direction = (jp.dialect not in ['gtalk-v0.3',
+ 'gtalk-v0.4'])
+ self.self_handle = conn.GetSelfHandle()
+ self.peer_handle = conn.RequestHandles(1, ["foo@bar.com/Foo"])[0]
+
+
+ def check_channel_state(self, state, wait = False):
+ """Optionnally wait for channel state to be reached and check that the
+ property has the right value"""
+ if wait:
+ self.q.expect('dbus-signal', signal='CallStateChanged',
+ interface = cs.CHANNEL_TYPE_CALL,
+ predicate = lambda e: e.args[0] == state)
+
+ assertEquals(state,
+ self.chan.Get(cs.CHANNEL_TYPE_CALL, 'CallState',
+ dbus_interface=dbus.PROPERTIES_IFACE))
+
+
+ def check_stream_recv_state(self, stream, state):
+ assertEquals(state,
+ stream.Get(cs.CALL_STREAM_IFACE_MEDIA, 'ReceivingState',
+ dbus_interface=dbus.PROPERTIES_IFACE))
+
+
+ def check_stream_send_state(self, stream, state):
+ assertEquals(state,
+ stream.Get(cs.CALL_STREAM_IFACE_MEDIA, 'SendingState',
+ dbus_interface=dbus.PROPERTIES_IFACE))
+
+
+ def complete_receiving_state(self, stream):
+ if stream is None:
+ return
+ self.check_stream_recv_state(stream,
+ cs.CALL_STREAM_FLOW_STATE_PENDING_START)
+ stream.CompleteReceivingStateChange(cs.CALL_STREAM_FLOW_STATE_STARTED,
+ dbus_interface = cs.CALL_STREAM_IFACE_MEDIA)
+ self.q.expect('dbus-signal', signal='ReceivingStateChanged',
+ args = [cs.CALL_STREAM_FLOW_STATE_STARTED],
+ interface = cs.CALL_STREAM_IFACE_MEDIA)
+
+
+ def check_and_accept_offer(self, content, md, md_changed = True,
+ offer_path = None):
+ [path, remote_md] = content.Get(cs.CALL_CONTENT_IFACE_MEDIA,
+ "MediaDescriptionOffer", dbus_interface=dbus.PROPERTIES_IFACE)
+
+ if offer_path != None:
+ assertEquals(offer_path, path)
+
+ assertNotEquals("/", path)
+
+ offer = self.bus.get_object(self.conn.bus_name, path)
+ codecmap_property = offer.Get(cs.CALL_CONTENT_MEDIADESCRIPTION,
+ "Codecs", dbus_interface=dbus.PROPERTIES_IFACE)
+
+ assertEquals(remote_md[cs.CALL_CONTENT_MEDIADESCRIPTION + '.Codecs'],
+ codecmap_property)
+
+ offer.Accept(md, dbus_interface=cs.CALL_CONTENT_MEDIADESCRIPTION)
+
+ current_md = content.Get(cs.CALL_CONTENT_IFACE_MEDIA,
+ "LocalMediaDescriptions", dbus_interface=dbus.PROPERTIES_IFACE)
+ assertEquals(md, current_md[self.peer_handle])
+
+ if md_changed:
+ o = self.q.expect('dbus-signal',
+ signal='LocalMediaDescriptionChanged')
+ assertEquals([md], o.args)
+
+
+ def store_content(self, content_path, initial = True):
+ content = wrap_content(self.bus.get_object(self.conn.bus_name,
+ content_path), ['DTMF', 'Media'])
+ content_props = content.GetAll(cs.CALL_CONTENT,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+
+ # Has one stream
+ assertLength(1, content_props["Streams"])
+ if initial:
+ assertEquals(cs.CALL_DISPOSITION_INITIAL,
+ content_props["Disposition"])
+ else:
+ assertEquals(cs.CALL_DISPOSITION_NONE, content_props["Disposition"])
+
+
+ # Implements Content.Interface.Media
+ assertContains(cs.CALL_CONTENT_IFACE_MEDIA, content_props["Interfaces"])
+
+ if content_props['Type'] == cs.CALL_MEDIA_TYPE_AUDIO:
+ # Implements Content.Interface.DTMF
+ assertContains(cs.CALL_CONTENT_IFACE_DTMF,
+ content_props["Interfaces"])
+
+ assertContains("Name", content_props.keys())
+ content_name = content_props["Name"]
+
+ stream = self.bus.get_object(self.conn.bus_name,
+ content_props["Streams"][0])
+
+ stream_props = stream.GetAll(cs.CALL_STREAM,
+ dbus_interface = dbus.PROPERTIES_IFACE)
+
+ assertDoesNotContain(self.self_handle,
+ stream_props["RemoteMembers"].keys())
+ assertContains(self.peer_handle, stream_props["RemoteMembers"].keys())
+ assertEquals([cs.CALL_STREAM_IFACE_MEDIA], stream_props["Interfaces"])
+ assertEquals(self.can_change_direction,
+ stream_props["CanRequestReceiving"])
+
+ # We only check direction for initial content Telepathy does not
+ # expose enough information to know which state we should have when
+ # the stream was created locally or remotly.
+ if initial:
+ if self.incoming:
+ assertEquals(cs.CALL_SENDING_STATE_PENDING_SEND,
+ stream_props["LocalSendingState"])
+ assertEquals(cs.CALL_SENDING_STATE_SENDING,
+ stream_props["RemoteMembers"][self.peer_handle])
+ else:
+ assertEquals(cs.CALL_SENDING_STATE_PENDING_SEND,
+ stream_props["RemoteMembers"][self.peer_handle])
+ assertEquals(cs.CALL_SENDING_STATE_SENDING,
+ stream_props["LocalSendingState"])
+
+ # Packetization should be RTP
+ content_media_props = content.GetAll(cs.CALL_CONTENT_IFACE_MEDIA,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ assertEquals(cs.CALL_CONTENT_PACKETIZATION_RTP,
+ content_media_props["Packetization"])
+
+ # Check the directions
+ stream_media_props = stream.GetAll(cs.CALL_STREAM_IFACE_MEDIA,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ assertEquals(cs.CALL_STREAM_FLOW_STATE_STOPPED,
+ stream_media_props["SendingState"])
+ assertEquals(cs.CALL_STREAM_FLOW_STATE_STOPPED,
+ stream_media_props["ReceivingState"])
+ assertEquals(False, stream_media_props["ICERestartPending"])
+
+ # Store the content and stream
+ if content_props['Type'] == cs.CALL_MEDIA_TYPE_AUDIO:
+ assert self.initial_audio == initial
+ assert self.audio_content == None
+ assert self.audio_stream == None
+ self.audio_content = content
+ self.audio_content_name = content_name
+ self.audio_stream = stream
+ elif content_props['Type'] == cs.CALL_MEDIA_TYPE_VIDEO:
+ assert self.initial_video == initial
+ assert self.video_content == None
+ assert self.video_stream == None
+ self.video_content = content
+ self.video_content_name = content_name
+ self.video_stream = stream
+ else:
+ assert not 'Bad content type value'
+
+
+ def enable_endpoint(self, endpoint):
+ endpoint.SetEndpointState(cs.CALL_STREAM_COMPONENT_DATA,
+ cs.CALL_STREAM_ENDPOINT_STATE_FULLY_CONNECTED,
+ dbus_interface=cs.CALL_STREAM_ENDPOINT)
+ self.q.expect('dbus-signal', signal='EndpointStateChanged',
+ interface=cs.CALL_STREAM_ENDPOINT)
+
+ endpoint.SetEndpointState(cs.CALL_STREAM_COMPONENT_CONTROL,
+ cs.CALL_STREAM_ENDPOINT_STATE_FULLY_CONNECTED,
+ dbus_interface=cs.CALL_STREAM_ENDPOINT)
+ self.q.expect('dbus-signal', signal='EndpointStateChanged',
+ interface=cs.CALL_STREAM_ENDPOINT)
+
+ state = endpoint.Get(cs.CALL_STREAM_ENDPOINT,
+ "EndpointState", dbus_interface=dbus.PROPERTIES_IFACE)
+ assertEquals(cs.CALL_STREAM_ENDPOINT_STATE_FULLY_CONNECTED, state[1])
+ assertEquals(cs.CALL_STREAM_ENDPOINT_STATE_FULLY_CONNECTED, state[2])
+
+
+ def advertise(self, initial_audio = True, initial_video = True):
+ """Advertise that Call is supported"""
+ self.conn.ContactCapabilities.UpdateCapabilities([
+ (cs.CLIENT + ".CallHandler", [
+ { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL,
+ cs.CALL_INITIAL_AUDIO: initial_audio},
+ { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL,
+ cs.CALL_INITIAL_VIDEO: initial_video},
+ ], [
+ cs.CHANNEL_TYPE_CALL + '/gtalk-p2p',
+ cs.CHANNEL_TYPE_CALL + '/ice-udp',
+ cs.CHANNEL_TYPE_CALL + '/video/h264',
+ ]),
+ ])
+
+
+ def prepare(self, events=None):
+ """Prepare the JingleTest2 object. This method can be override to trap
+ special event linke jingleinfo."""
+ self.jt2.prepare(events=events)
+
+
+ def initiate(self):
+ """Brind the call to INITIALISING state. This method will fill the
+ channel, contents and streams members."""
+ # Ensure a channel that doesn't exist yet.
+ if self.incoming:
+ if self.initial_audio and self.initial_video:
+ self.jt2.incoming_call(audio='audio1', video='video1')
+ elif self.initial_audio:
+ self.jt2.incoming_call(audio='audio1', video=None)
+ else:
+ self.jt2.incoming_call(audio=None, video='video1')
+ else:
+ ret = self.conn.Requests.CreateChannel(
+ { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL,
+ cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
+ cs.TARGET_HANDLE: self.peer_handle,
+ cs.CALL_INITIAL_AUDIO: self.initial_audio,
+ cs.CALL_INITIAL_VIDEO: self.initial_video,
+ })
+
+ signal = self.q.expect('dbus-signal', signal='NewChannels',
+ predicate=lambda e:
+ cs.CHANNEL_TYPE_CONTACT_LIST not in e.args[0][0][1].values())
+
+ assertLength(1, signal.args)
+ assertLength(1, signal.args[0]) # one channel
+ assertLength(2, signal.args[0][0]) # two struct members
+ emitted_props = signal.args[0][0][1]
+
+ assertEquals(
+ cs.CHANNEL_TYPE_CALL, emitted_props[cs.CHANNEL_TYPE])
+
+ peer_bare_jid = self.PEER_JID.split('/')[0]
+ assertEquals(self.peer_handle, emitted_props[cs.TARGET_HANDLE])
+ assertEquals(cs.HT_CONTACT, emitted_props[cs.TARGET_HANDLE_TYPE])
+ assertEquals(peer_bare_jid, emitted_props[cs.TARGET_ID])
+
+ assertEquals(not self.incoming, emitted_props[cs.REQUESTED])
+ if self.incoming:
+ assertEquals(self.peer_handle, emitted_props[cs.INITIATOR_HANDLE])
+ assertEquals(peer_bare_jid, emitted_props[cs.INITIATOR_ID])
+ else:
+ assertEquals(self.self_handle, emitted_props[cs.INITIATOR_HANDLE])
+ assertEquals(self.SELF_JID, emitted_props[cs.INITIATOR_ID])
+
+ assertEquals(self.initial_audio, emitted_props[cs.CALL_INITIAL_AUDIO])
+ assertEquals(self.initial_video, emitted_props[cs.CALL_INITIAL_VIDEO])
+
+ chan_path = signal.args[0][0][0]
+ self.chan = wrap_channel(
+ self.bus.get_object(self.conn.bus_name, chan_path),
+ 'Call', ['Hold'])
+
+ properties = self.chan.GetAll(cs.CHANNEL_TYPE_CALL,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+
+ # Check if all the properties are there
+ assertEquals(sorted([ "Contents", "CallMembers",
+ "CallState", "CallFlags", "CallStateReason", "CallStateDetails",
+ "HardwareStreaming", "InitialAudio", "InitialAudioName",
+ "InitialVideo", "InitialVideoName", "MutableContents",
+ "InitialTransport", "MemberIdentifiers" ]),
+ sorted(properties.keys()))
+
+ # Remote member is the target
+ assertEquals([self.peer_handle], properties["CallMembers"].keys())
+ assertEquals(0, properties["CallMembers"][self.peer_handle])
+
+ # No Hardware Streaming for you
+ assertEquals(False, properties["HardwareStreaming"])
+
+ # Store content and stream
+ nb_contents = self.initial_audio + self.initial_video
+ assertLength(nb_contents, properties["Contents"])
+
+ for content_path in properties["Contents"]:
+ self.store_content(content_path)
+
+ if self.initial_audio:
+ assert self.audio_content
+ if self.initial_video:
+ assert self.video_content
+
+
+ def accept_outgoing(self):
+ """If call is incoming, accept the channel and complete the receiving
+ state change. Then do state check. This method shall be called even
+ if receiving a call to execute the state sanity checks."""
+ # Check if the channel is in the right pending state
+ if not self.incoming:
+ self.check_channel_state(cs.CALL_STATE_PENDING_INITIATOR)
+ self.chan.Accept(dbus_interface=cs.CHANNEL_TYPE_CALL)
+
+ if self.initial_audio:
+ self.complete_receiving_state(self.audio_stream)
+ # Don't start sending before the call is accepted locally or
+ # remotely
+ self.check_stream_send_state(self.audio_stream,
+ cs.CALL_STREAM_FLOW_STATE_STOPPED)
+
+ if self.initial_video:
+ self.complete_receiving_state(self.video_stream)
+ self.check_stream_send_state(self.video_stream,
+ cs.CALL_STREAM_FLOW_STATE_STOPPED)
+
+ # All Direction should be both now for outgoing
+ if self.initial_audio:
+ stream_props = self.audio_stream.GetAll(cs.CALL_STREAM,
+ dbus_interface = dbus.PROPERTIES_IFACE)
+
+ if self.incoming:
+ assertEquals({self.peer_handle: cs.CALL_SENDING_STATE_SENDING},
+ stream_props["RemoteMembers"])
+ assertEquals(cs.CALL_SENDING_STATE_PENDING_SEND,
+ stream_props["LocalSendingState"])
+ else:
+ assertEquals(
+ {self.peer_handle: cs.CALL_SENDING_STATE_PENDING_SEND},
+ stream_props["RemoteMembers"])
+ assertEquals(cs.CALL_SENDING_STATE_SENDING,
+ stream_props["LocalSendingState"])
+
+ if self.initial_video:
+ stream_props = self.video_stream.GetAll(cs.CALL_STREAM,
+ dbus_interface = dbus.PROPERTIES_IFACE)
+
+ if self.incoming:
+ assertEquals({self.peer_handle: cs.CALL_SENDING_STATE_SENDING},
+ stream_props["RemoteMembers"])
+ assertEquals(cs.CALL_SENDING_STATE_PENDING_SEND,
+ stream_props["LocalSendingState"])
+ else:
+ assertEquals(
+ {self.peer_handle: cs.CALL_SENDING_STATE_PENDING_SEND},
+ stream_props["RemoteMembers"])
+ assertEquals(cs.CALL_SENDING_STATE_SENDING,
+ stream_props["LocalSendingState"])
+
+ self.check_channel_state(cs.CALL_STATE_INITIALISING)
+
+ def connect_streams(self, contents, streams, mds, expect_after_si=None):
+ # Expected to fail since we did not said we are controlling side
+ try:
+ contents[0].UpdateLocalMediaDescription(mds[0],
+ dbus_interface=cs.CALL_CONTENT_IFACE_MEDIA)
+ except DBusException, e:
+ if e.get_dbus_name() != cs.NOT_AVAILABLE:
+ raise e
+ else:
+ assert False
+
+ expected = []
+ candidates = self.jt2.get_call_remote_transports_dbus()
+
+ for i in range(len(contents)):
+ self.check_and_accept_offer(contents[i], mds[i], md_changed=False)
+ expected.append(EventPattern('dbus-signal',
+ signal='LocalMediaDescriptionChanged', args=[mds[i]]))
+
+ current_md = contents[i].Get(cs.CALL_CONTENT_IFACE_MEDIA,
+ "LocalMediaDescriptions",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ assertEquals(mds[i], current_md[self.peer_handle])
+
+ streams[i].SetCredentials(self.jt2.ufrag, self.jt2.pwd,
+ dbus_interface=cs.CALL_STREAM_IFACE_MEDIA)
+
+ expected.append(EventPattern('dbus-signal',
+ signal='LocalCredentialsChanged',
+ args=[self.jt2.ufrag, self.jt2.pwd]))
+
+ credentials = streams[i].GetAll(cs.CALL_STREAM_IFACE_MEDIA,
+ dbus_interface=dbus.PROPERTIES_IFACE)["LocalCredentials"]
+ assertEquals((self.jt2.ufrag, self.jt2.pwd), credentials)
+
+ # Add candidates
+ streams[i].AddCandidates(candidates,
+ dbus_interface=cs.CALL_STREAM_IFACE_MEDIA)
+
+ expected.append(EventPattern('dbus-signal',
+ signal='LocalCandidatesAdded'))
+
+ if not self.incoming:
+ expected.append(EventPattern('stream-iq',
+ predicate=self.jp.action_predicate('session-initiate')))
+
+ ret = self.q.expect_many(*expected)
+ # Check the first LocalCandidatesAdded signal (third in the array)
+ assertEquals(candidates, ret[2].args[0])
+
+ if not self.incoming:
+ if expect_after_si is not None:
+ sync_stream(self.q, self.stream)
+ self.q.unforbid_events(expect_after_si)
+
+ self.stream.send(make_result_iq(self.stream, ret[-1].stanza))
+
+ if expect_after_si is not None:
+ self.q.expect_many(*expect_after_si)
+
+ self.jt2.parse_session_initiate(ret[-1].query)
+
+ endpoints = []
+
+ for stream in streams:
+ stream.FinishInitialCandidates(
+ dbus_interface=cs.CALL_STREAM_IFACE_MEDIA)
+
+ local_candidates = stream.Get(cs.CALL_STREAM_IFACE_MEDIA,
+ "LocalCandidates", dbus_interface=dbus.PROPERTIES_IFACE)
+ assertEquals(candidates, local_candidates)
+
+ endpoint_paths = stream.Get(cs.CALL_STREAM_IFACE_MEDIA,
+ "Endpoints", dbus_interface=dbus.PROPERTIES_IFACE)
+ assertLength(1, endpoint_paths)
+
+ # There doesn't seem to be a good way to get the transport type from
+ # the JP used, for now assume we prefer gtalk p2p and always pick
+ # that..
+ transport = stream.Get(cs.CALL_STREAM_IFACE_MEDIA,
+ "Transport", dbus_interface=dbus.PROPERTIES_IFACE)
+ assertEquals(cs.CALL_STREAM_TRANSPORT_GTALK_P2P, transport)
+
+ endpoint = self.bus.get_object(self.conn.bus_name,
+ endpoint_paths[0])
+ endpoints.append(endpoint)
+
+ endpoint_props = endpoint.GetAll(cs.CALL_STREAM_ENDPOINT,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ transport = endpoint_props["Transport"]
+ assertEquals(cs.CALL_STREAM_TRANSPORT_GTALK_P2P, transport)
+
+ remote_candidates = endpoint.Get(cs.CALL_STREAM_ENDPOINT,
+ "RemoteCandidates", dbus_interface=dbus.PROPERTIES_IFACE)
+
+ assertEquals([], remote_candidates)
+
+ selected_candidate = endpoint.Get(cs.CALL_STREAM_ENDPOINT,
+ "SelectedCandidatePairs",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ assertEquals([], selected_candidate)
+
+ state = endpoint.Get(cs.CALL_STREAM_ENDPOINT,
+ "EndpointState", dbus_interface=dbus.PROPERTIES_IFACE)
+ assertEquals({}, state)
+
+ names = []
+ for content in contents:
+ if content is self.audio_content:
+ names.append(self.jt2.audio_names[0])
+ else:
+ names.append(self.jt2.video_names[0])
+
+ for name in names:
+ if self.jp.dialect == 'gtalk-v0.3':
+ # Candidates must be sent one at a time.
+ for candidate in self.jt2.get_call_remote_transports_dbus():
+ component, addr, port, props = candidate
+ self.jt2.send_remote_candidates_call_xmpp(
+ name, "initiator", [candidate])
+ self.q.expect('dbus-signal',
+ signal='RemoteCandidatesAdded',
+ interface=cs.CALL_STREAM_ENDPOINT,
+ args=[[(component, addr, port,
+ DictionarySupersetOf(props))]])
+ elif self.jp.dialect == 'gtalk-v0.4' and not self.incoming:
+ # Don't test this case at all.
+ pass
+ else:
+ self.jt2.send_remote_candidates_call_xmpp(name, "initiator")
+
+ candidates = []
+ for component, addr, port, props in \
+ self.jt2.get_call_remote_transports_dbus():
+ candidates.append((component, addr, port,
+ DictionarySupersetOf(props)))
+
+ self.q.expect('dbus-signal',
+ signal='RemoteCandidatesAdded',
+ interface=cs.CALL_STREAM_ENDPOINT,
+ args=[candidates])
+
+ # FIXME: makes sense to have same local and remote candidate?
+ candidate1 = self.jt2.get_call_remote_transports_dbus()[0]
+ candidate2 = self.jt2.get_call_remote_transports_dbus()[1]
+
+ for endpoint in endpoints:
+ # Expected to fail since we did not said we are controlling side
+ try:
+ endpoint.SetSelectedCandidatePair(candidate1, candidate1,
+ dbus_interface=cs.CALL_STREAM_ENDPOINT)
+ except DBusException, e:
+ if e.get_dbus_name() != cs.INVALID_ARGUMENT:
+ raise e
+ else:
+ assert false
+
+ endpoint.SetControlling(True,
+ dbus_interface=cs.CALL_STREAM_ENDPOINT)
+ endpoint.SetSelectedCandidatePair(candidate1, candidate1,
+ dbus_interface=cs.CALL_STREAM_ENDPOINT)
+
+ pair = self.q.expect('dbus-signal',
+ signal='CandidatePairSelected',
+ interface=cs.CALL_STREAM_ENDPOINT)
+ assertEquals(candidate1, pair.args[0])
+ assertEquals(candidate1, pair.args[1])
+
+ endpoint.SetSelectedCandidatePair(candidate2, candidate2,
+ dbus_interface=cs.CALL_STREAM_ENDPOINT)
+
+ # We have an RTCP candidate as well, so we should set this as
+ # selected too.
+ pair = self.q.expect('dbus-signal', signal='CandidatePairSelected',
+ interface=cs.CALL_STREAM_ENDPOINT)
+ assertEquals(candidate2, pair.args[0])
+ assertEquals(candidate2, pair.args[1])
+
+ pairs = endpoint.Get(cs.CALL_STREAM_ENDPOINT,
+ "SelectedCandidatePairs",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ assertEquals(len(pairs), 2)
+ assertEquals(pairs[0][0], pairs[0][1])
+ assertEquals(pairs[1][0], pairs[1][1])
+ if pairs[0][0] == candidate1:
+ assertEquals(pairs[1][0], candidate2)
+ else:
+ assertEquals(pairs[0][0], candidate2)
+ assertEquals(pairs[1][0], candidate1)
+
+ # setting endpoints to CONNECTED should make the call state move
+ # from INITIALISING to INITIALISED
+ self.enable_endpoint(endpoint)
+
+ self.check_channel_state(cs.CALL_STATE_INITIALISED)
+
+ def connect(self, expect_after_si=None):
+ """Negotiate all the codecs, bringing the channel to INITIALISED
+ state"""
+
+ contents = []
+ streams = []
+ mds = []
+
+ if self.initial_audio:
+ # Setup media description
+ contents.append(self.audio_content)
+ streams.append(self.audio_stream)
+ mds.append(self.jt2.get_call_audio_md_dbus())
+
+ if self.initial_video:
+ contents.append(self.video_content)
+ streams.append(self.video_stream)
+ mds.append(self.jt2.get_call_video_md_dbus())
+
+ self.connect_streams(contents, streams, mds,
+ expect_after_si=expect_after_si)
+
+
+ def pickup(self, held=False):
+ if self.initial_audio:
+ self.check_stream_send_state(self.audio_stream,
+ cs.CALL_STREAM_FLOW_STATE_STOPPED)
+ if self.initial_video:
+ self.check_stream_send_state(self.video_stream,
+ cs.CALL_STREAM_FLOW_STATE_STOPPED)
+
+ if self.incoming:
+ # Act as if we're ringing
+ self.chan.SetRinging(dbus_interface=cs.CHANNEL_TYPE_CALL)
+ signal = self.q.expect('dbus-signal', signal='CallStateChanged')
+ assertEquals(cs.CALL_FLAG_LOCALLY_RINGING,
+ signal.args[1] & cs.CALL_FLAG_LOCALLY_RINGING)
+
+ # And now pickup the call
+ self.chan.Accept(dbus_interface=cs.CHANNEL_TYPE_CALL)
+
+ expected = [
+ EventPattern('dbus-signal', signal='CallStateChanged'),
+ EventPattern('stream-iq',
+ predicate=self.jp.action_predicate('session-accept'))]
+ if self.initial_audio:
+ # SendingStateChanged is caused by chan.Accept
+ expected.append(EventPattern('dbus-signal',
+ signal='SendingStateChanged'))
+ recv_state = self.audio_stream.GetAll(
+ cs.CALL_STREAM_IFACE_MEDIA,
+ dbus_interface=dbus.PROPERTIES_IFACE)["ReceivingState"]
+ assertEquals(cs.CALL_STREAM_FLOW_STATE_PENDING_START,
+ recv_state)
+ self.audio_stream.CompleteReceivingStateChange(
+ cs.CALL_STREAM_FLOW_STATE_STARTED,
+ dbus_interface = cs.CALL_STREAM_IFACE_MEDIA)
+ expected.append(EventPattern('dbus-signal',
+ signal='ReceivingStateChanged'))
+
+ if self.initial_video:
+ # SendingStateChanged is caused by chan.Accept
+ expected.append(EventPattern('dbus-signal',
+ signal='SendingStateChanged'))
+ recv_state = self.video_stream.GetAll(
+ cs.CALL_STREAM_IFACE_MEDIA,
+ dbus_interface=dbus.PROPERTIES_IFACE)["ReceivingState"]
+ assertEquals(cs.CALL_STREAM_FLOW_STATE_PENDING_START,
+ recv_state)
+ self.video_stream.CompleteReceivingStateChange(
+ cs.CALL_STREAM_FLOW_STATE_STARTED,
+ dbus_interface = cs.CALL_STREAM_IFACE_MEDIA)
+ expected.append(EventPattern('dbus-signal',
+ signal='ReceivingStateChanged'))
+
+ ret = self.q.expect_many(*expected)
+
+ assertEquals(0, ret[0].args[1] & cs.CALL_FLAG_LOCALLY_RINGING)
+ if self.initial_audio and self.initial_video:
+ assertEquals(cs.CALL_STREAM_FLOW_STATE_PENDING_START,
+ ret[2].args[0])
+ assertEquals(cs.CALL_STREAM_FLOW_STATE_PENDING_START,
+ ret[3].args[0])
+ else:
+ assertEquals(cs.CALL_STREAM_FLOW_STATE_PENDING_START,
+ ret[2].args[0])
+
+ self.jt2.result_iq(ret[1])
+ else:
+ if self.jp.is_modern_jingle():
+ # The other person's client starts ringing, and tells us so!
+ node = self.jp.SetIq(self.jt2.peer, self.jt2.jid, [
+ self.jp.Jingle(self.jt2.sid, self.jt2.jid, 'session-info', [
+ ('ringing', ns.JINGLE_RTP_INFO_1, {}, []) ]) ])
+ self.stream.send(self.jp.xml(node))
+
+ o = self.q.expect('dbus-signal', signal="CallMembersChanged")
+ assertEquals(cs.CALL_MEMBER_FLAG_RINGING,
+ o.args[0][self.peer_handle])
+
+ self.jt2.accept()
+
+ expected = [EventPattern('dbus-signal',
+ signal='NewMediaDescriptionOffer')]
+
+ if not held:
+ if self.initial_audio:
+ expected.append(EventPattern('dbus-signal',
+ signal='SendingStateChanged'))
+ if self.initial_video:
+ expected.append(EventPattern('dbus-signal',
+ signal='SendingStateChanged'))
+
+ ret = self.q.expect_many(*expected)
+
+ if not held:
+ # Checking one of sending states
+ assertEquals(cs.CALL_STREAM_FLOW_STATE_PENDING_START,
+ ret[1].args[0])
+
+ if self.initial_audio:
+ md = self.jt2.get_call_audio_md_dbus()
+ self.check_and_accept_offer(self.audio_content, md,
+ md_changed = False)
+ if self.initial_video:
+ md = self.jt2.get_call_video_md_dbus()
+ self.check_and_accept_offer(self.video_content, md,
+ md_changed = False)
+
+ self.check_channel_state(cs.CALL_STATE_ACTIVE)
+
+ # All Direction should be both sending now
+
+ if self.initial_audio and not held:
+ stream_props = self.audio_stream.GetAll(cs.CALL_STREAM,
+ dbus_interface = dbus.PROPERTIES_IFACE)
+ assertEquals({self.peer_handle: cs.CALL_SENDING_STATE_SENDING},
+ stream_props["RemoteMembers"])
+ assertEquals(cs.CALL_SENDING_STATE_SENDING,
+ stream_props["LocalSendingState"])
+ assertEquals(cs.CALL_STREAM_FLOW_STATE_PENDING_START,
+ self.audio_stream.Get(cs.CALL_STREAM_IFACE_MEDIA,
+ "SendingState",
+ dbus_interface = dbus.PROPERTIES_IFACE))
+
+ self.audio_stream.CompleteSendingStateChange(
+ cs.CALL_STREAM_FLOW_STATE_STARTED,
+ dbus_interface = cs.CALL_STREAM_IFACE_MEDIA)
+ o = self.q.expect('dbus-signal', signal='SendingStateChanged',
+ interface = cs.CALL_STREAM_IFACE_MEDIA)
+ assertEquals(cs.CALL_STREAM_FLOW_STATE_STARTED, o.args[0])
+
+ if self.initial_video and not held:
+ stream_props = self.video_stream.GetAll(cs.CALL_STREAM,
+ dbus_interface = dbus.PROPERTIES_IFACE)
+ assertEquals({self.peer_handle: cs.CALL_SENDING_STATE_SENDING},
+ stream_props["RemoteMembers"])
+ assertEquals(cs.CALL_SENDING_STATE_SENDING,
+ stream_props["LocalSendingState"])
+ assertEquals(cs.CALL_STREAM_FLOW_STATE_PENDING_START,
+ self.video_stream.Get(cs.CALL_STREAM_IFACE_MEDIA,
+ "SendingState",
+ dbus_interface = dbus.PROPERTIES_IFACE))
+
+ self.video_stream.CompleteSendingStateChange(
+ cs.CALL_STREAM_FLOW_STATE_STARTED,
+ dbus_interface = cs.CALL_STREAM_IFACE_MEDIA)
+ o = self.q.expect('dbus-signal', signal='SendingStateChanged',
+ interface = cs.CALL_STREAM_IFACE_MEDIA)
+ assertEquals(cs.CALL_STREAM_FLOW_STATE_STARTED, o.args[0])
+
+ def hangup(self):
+ if self.incoming:
+ self.jt2.terminate()
+ else:
+ self.chan.Hangup(0, "", "",
+ dbus_interface=cs.CHANNEL_TYPE_CALL)
+
+ self.check_channel_state(cs.CALL_STATE_ENDED, wait = True)
+
+
+ def run(self):
+ if self.initial_video:
+ if not self.initial_audio and not self.jp.can_do_video_only():
+ return
+ elif not self.jp.can_do_video():
+ return
+ self.advertise()
+ self.prepare()
+ self.initiate()
+ self.accept_outgoing()
+ self.connect()
+ self.pickup()
+ self.hangup()
+
+
+def run_call_test(jp, q, bus, conn, stream, klass=CallTest, incoming=False,
+ params={}):
+ test = klass(jp, q, bus, conn, stream, incoming, params)
+ test.run()
+
+if __name__ == '__main__':
+ test_all_dialects(partial(run_call_test, incoming=False))
+ test_all_dialects(partial(run_call_test, incoming=True))
+