def _test(jp, q, bus, conn, stream, jingle_reason, group_change_reason, stream_error): jt = JingleTest2(jp, conn, q, stream, 'test@localhost', '[email protected]/Foo') jt.prepare() self_handle = conn.GetSelfHandle() remote_handle = conn.RequestHandles(cs.HT_CONTACT, ["[email protected]/Foo"])[0] # Ring ring! jt.incoming_call() new_channel, new_session_handler = q.expect_many( EventPattern('dbus-signal', signal='NewChannel', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args), EventPattern('dbus-signal', signal='NewSessionHandler')) assertEquals(cs.CHANNEL_TYPE_STREAMED_MEDIA, new_channel.args[1]) assertEquals(cs.HT_CONTACT, new_channel.args[2]) assertEquals(remote_handle, new_channel.args[3]) assertEquals('rtp', new_session_handler.args[1]) channel_path = new_channel.args[0] # Client calls Ready on new session handler. session_handler = make_channel_proxy( conn, new_session_handler.args[0], 'Media.SessionHandler') session_handler.Ready() # Client gets notified about a newly created stream... new_stream_handler = q.expect('dbus-signal', signal='NewStreamHandler') stream_id = new_stream_handler.args[1] stream_handler = make_channel_proxy( conn, new_stream_handler.args[0], 'Media.StreamHandler') stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) stream_handler.Ready(jt.dbusify_codecs([("FOO", 5, 8000, {})])) q.expect('dbus-signal', signal='SetRemoteCodecs') msg = u"o noes" # ...but something goes wrong. stream_handler.Error(stream_error, msg) q.expect("stream-iq", iq_type="set", predicate=lambda x: _session_terminate_predicate(x, jingle_reason, msg, jp)) # Bye bye members. mc = q.expect('dbus-signal', signal='MembersChanged', interface=cs.CHANNEL_IFACE_GROUP, path=channel_path, args=[msg, [], [self_handle, remote_handle], [], [], self_handle, group_change_reason]) q.expect('dbus-signal', signal='StreamError', interface=cs.CHANNEL_TYPE_STREAMED_MEDIA, args=[stream_id, stream_error, msg]) # Bye bye stream q.expect('dbus-signal', signal='Close') q.expect('dbus-signal', signal='StreamRemoved') # Bye bye channel. q.expect('dbus-signal', signal='Closed') q.expect('dbus-signal', signal='ChannelClosed')
def make_call(expected_recipient): jp = JingleProtocol031() jt = JingleTest2(jp, conn, q, stream, 'test@localhost', 'dummy') conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: cat, cs.INITIAL_AUDIO: True, }) e = q.expect('dbus-signal', signal='NewSessionHandler') session = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session.Ready() e = q.expect('dbus-signal', signal='NewStreamHandler') stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) stream_handler.Ready(jt.get_audio_codecs_dbus()) stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) e = q.expect('stream-iq', predicate=jp.action_predicate('session-initiate')) assertEquals(expected_recipient, e.to)
def create_ft_channel(self): self.channel = make_channel_proxy(self.conn, self.ft_path, 'Channel') self.ft_channel = make_channel_proxy(self.conn, self.ft_path, 'Channel.Type.FileTransfer') self.ft_props = dbus.Interface( self.bus.get_object(self.conn.object.bus_name, self.ft_path), PROPERTIES_IFACE)
def test(jp, q, bus, conn, stream): if not jp.can_do_video_only(): return remote_jid = '[email protected]/Foo' jt = JingleTest2(jp, conn, q, stream, 'test@localhost', remote_jid) jt.prepare() self_handle = conn.GetSelfHandle() handle = conn.RequestHandles(cs.HT_CONTACT, [remote_jid])[0] chan_path = conn.RequestChannel(cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.HT_CONTACT, handle, True) chan = wrap_channel(bus.get_object(conn.bus_name, chan_path), 'StreamedMedia', ['MediaSignalling', 'Group', 'CallState', 'DTMF']) chan_props = chan.Properties.GetAll(cs.CHANNEL) assert cs.CHANNEL_IFACE_DTMF in chan_props['Interfaces'], \ chan_props['Interfaces'] chan.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_VIDEO]) # S-E gets notified about new session handler, and calls Ready on it e = q.expect('dbus-signal', signal='NewSessionHandler') assert e.args[1] == 'rtp' session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() e = q.expect('dbus-signal', signal='NewStreamHandler') video_path = e.args[0] stream_handler = make_channel_proxy(conn, video_path, 'Media.StreamHandler') stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) stream_handler.Ready(jt.get_video_codecs_dbus()) stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) e = q.expect('stream-iq', predicate=jp.action_predicate('session-initiate')) stream.send(make_result_iq(stream, e.stanza)) jt.parse_session_initiate(e.query) jt.accept() # Gabble tells s-e to start sending q.expect('dbus-signal', signal='SetStreamSending', args=[True], path=video_path) # We don't actually have an audio stream, so this is a non-starter. call_async(q, chan.DTMF, 'StartTone', 666, 3) q.expect('dbus-error', method='StartTone', name=cs.NOT_AVAILABLE) call_async(q, chan.DTMF, 'MultipleTones', '**666##') q.expect('dbus-error', method='MultipleTones', name=cs.NOT_AVAILABLE) # We can still stop all the tones that are playing (a no-op). call_async(q, chan.DTMF, 'StopTone', 666) q.expect('dbus-return', method='StopTone') chan.Group.RemoveMembers([self_handle], 'closed') e = q.expect('dbus-signal', signal='Closed', path=chan_path)
def _test(jp, q, bus, conn, stream, jingle_reason, group_change_reason, stream_error): remote_jid = '[email protected]/Foo' jt = JingleTest2(jp, conn, q, stream, 'test@localhost', remote_jid) jt.prepare() self_handle = conn.GetSelfHandle() remote_handle = conn.RequestHandles(cs.HT_CONTACT, [remote_jid])[0] path = conn.RequestChannel(cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.HT_CONTACT, remote_handle, True) signalling_iface = make_channel_proxy(conn, path, 'Channel.Interface.MediaSignalling') media_iface = make_channel_proxy(conn, path, 'Channel.Type.StreamedMedia') media_iface.RequestStreams(remote_handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) # S-E gets notified about new session handler, and calls Ready on it e = q.expect('dbus-signal', signal='NewSessionHandler') assert e.args[1] == 'rtp' session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() e = q.expect('dbus-signal', signal='NewStreamHandler') stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) stream_handler.Ready(jt.get_audio_codecs_dbus()) stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) e = q.expect('stream-iq', predicate=jp.action_predicate('session-initiate')) stream.send(make_result_iq(stream, e.stanza)) text = u"begone!" jt.parse_session_initiate(e.query) jt.terminate(reason=jingle_reason, text=text) mc = q.expect('dbus-signal', signal='MembersChanged') message, added, removed, lp, rp, actor, reason = mc.args assert added == [], added assert set(removed) == set([self_handle, remote_handle]), \ (removed, self_handle, remote_handle) assert lp == [], lp assert rp == [], rp assert actor == remote_handle, (actor, remote_handle) if jp.is_modern_jingle(): assertEquals(text, message) assertEquals(group_change_reason, reason) if jp.is_modern_jingle() and stream_error: se = q.expect('dbus-signal', signal='StreamError') assertEquals(stream_error, se.args[1]) q.expect('dbus-signal', signal='Close') #XXX - match against the path
def accept_file(self): # decline FT self. channel.Close() e = self.q.expect('dbus-signal', signal='FileTransferStateChanged') state, reason = e.args assert state == cs.FT_STATE_CANCELLED assert reason == cs.FT_STATE_CHANGE_REASON_LOCAL_STOPPED self.q.expect('dbus-signal', signal='Closed') # Re send offer (this is a regression test as Salut used to crash at this # point) self.send_ft_offer_iq() e = self.q.expect('dbus-signal', signal='NewChannels') channels = e.args[0] assert len(channels) == 1 path, props = channels[0] channel = make_channel_proxy(self.conn, path, 'Channel') # decline FT channel.Close() e = self.q.expect('dbus-signal', signal='FileTransferStateChanged') state, reason = e.args assert state == cs.FT_STATE_CANCELLED assert reason == cs.FT_STATE_CHANGE_REASON_LOCAL_STOPPED self.q.expect('dbus-signal', signal='Closed') # stop test return True
def _local_content_add(jp, q, bus, conn, stream, initiate_call_func): jt, chan = initiate_call_func(jp, q, bus, conn, stream) remote_handle = conn.RequestHandles(cs.HT_CONTACT, [jt.peer])[0] chan.RequestStreams(remote_handle, [cs.MEDIA_STREAM_TYPE_VIDEO]) nsh = q.expect('dbus-signal', signal='NewStreamHandler') stream_handler_path, stream_id, media_type, direction = nsh.args video_handler = make_channel_proxy(conn, stream_handler_path, 'Media.StreamHandler') video_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) video_handler.Ready(jt.get_audio_codecs_dbus()) video_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) e = q.expect('stream-iq', predicate=jp.action_predicate('content-add')) c = e.query.firstChildElement() stream.send(make_result_iq(stream, e.stanza)) node = jp.SetIq(jt.peer, jt.jid, [ jp.Jingle(jt.sid, jt.peer, 'content-reject', [ ('reason', None, {}, [ ('failed-application', None, {}, [])]), jp.Content(c['name'], c['creator'], c['senders']) ]) ]) stream.send(jp.xml(node)) q.expect('dbus-signal', signal='StreamError', args=[stream_id, cs.MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED, ""]),
def test(q, bus, conn, stream): jp = JingleProtocol031() remote_jid = '[email protected]/misc' jt = JingleTest2(jp, conn, q, stream, 'test@localhost', remote_jid) jt.prepare() self_handle = conn.GetSelfHandle() remote_handle = conn.RequestHandles(cs.HT_CONTACT, [remote_jid])[0] path, _ = conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: remote_handle}) chan = wrap_channel(bus.get_object(conn.bus_name, path), 'StreamedMedia') # In Gabble, the StreamedMedia channel is secretly also the SessionHandler. # Let's make up a proxy and call some methods on it. They should fail # gracefully, rather than crashing Gabble. session_handler = make_channel_proxy(conn, path, 'Media.SessionHandler') try: session_handler.Ready() except DBusException, e: assertEquals(cs.NOT_AVAILABLE, e.get_dbus_name())
def test(q, bus, conn, stream): conn.Connect() q.expect_many( EventPattern('dbus-signal', signal='StatusChanged', args=[1, 1]), EventPattern('irc-connected')) q.expect('dbus-signal', signal='SelfHandleChanged', args=[1L]) q.expect('dbus-signal', signal='StatusChanged', args=[0, 1]) CHANNEL_NAME = "#idletest" self_handle = conn.Get(CONN, 'SelfHandle', dbus_interface=PROPERTIES_IFACE) # The bouncer initiates a JOIN. path = test_join_bouncer(q, conn, stream, CHANNEL_NAME) # We PART. chan = make_channel_proxy(conn, path, 'Channel') chan.RemoveMembers([self_handle], "bye bye cruel world", dbus_interface=CHANNEL_IFACE_GROUP) q.expect('dbus-signal', signal='MembersChanged') # The bouncer initiates a JOIN to force the issue. test_join_bouncer(q, conn, stream, CHANNEL_NAME) call_async(q, conn, 'Disconnect') q.expect_many( EventPattern('dbus-return', method='Disconnect'), EventPattern('dbus-signal', signal='StatusChanged', args=[2, 1])) return True
def invite_to_muc(q, group1, conn2, invited_handle, inviter_handle): # first connection: invite contact group1.AddMembers([invited_handle], "Let's tube!") # channel is created on conn2 e = q.expect('dbus-signal', signal='NewChannel', path=conn2.object_path) path = e.args[0] group2 = make_channel_proxy(conn2, path, "Channel.Interface.Group") # we are invited to the muc # added as local pending conn2_self_handle = conn2.Properties.Get(cs.CONN, "SelfHandle") q.expect('dbus-signal', signal='MembersChanged', path=path, args=["Let's tube!", [], [], [conn2_self_handle], [], inviter_handle, 4]) # second connection: accept the invite group2.AddMembers([conn2_self_handle], "") # added as remote pending q.expect('dbus-signal', signal='MembersChanged', path=path, args=['', [], [], [], [conn2_self_handle], conn2_self_handle, 0]) # added as member q.expect('dbus-signal', signal='MembersChanged', path=path, args=['', [conn2_self_handle], [], [], [], conn2_self_handle, 0]) return group2
def test(q, bus, conn, stream): conn.Connect() q.expect_many( EventPattern('dbus-signal', signal='StatusChanged', args=[1, 1]), EventPattern('irc-connected')) q.expect('dbus-signal', signal='SelfHandleChanged', args=[1]) q.expect('dbus-signal', signal='StatusChanged', args=[0, 1]) CHANNEL_NAME = "#idletest" self_handle = conn.Get(CONN, 'SelfHandle', dbus_interface=PROPERTIES_IFACE) # The bouncer initiates a JOIN. path = test_join_bouncer(q, conn, stream, CHANNEL_NAME) # We PART. chan = make_channel_proxy(conn, path, 'Channel') chan.RemoveMembers([self_handle], "bye bye cruel world", dbus_interface=CHANNEL_IFACE_GROUP) q.expect('dbus-signal', signal='MembersChanged') # The bouncer initiates a JOIN to force the issue. test_join_bouncer(q, conn, stream, CHANNEL_NAME) call_async(q, conn, 'Disconnect') q.expect_many( EventPattern('dbus-return', method='Disconnect'), EventPattern('dbus-signal', signal='StatusChanged', args=[2, 1])) return True
def test(q, bus, conn, stream, call_error_on): jt = jingletest.JingleTest(stream, 'test@localhost', '[email protected]/Foo') conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) remote_handle = conn.RequestHandles(1, ["[email protected]/Foo"])[0] # Remote end calls us jt.incoming_call() # FIXME: these signals are not observable by real clients, since they # happen before NewChannels. # The caller is in members e = q.expect('dbus-signal', signal='MembersChanged', args=[u'', [remote_handle], [], [], [], 0, 0]) # We're pending because of remote_handle e = q.expect('dbus-signal', signal='MembersChanged', args=[u'', [], [], [1L], [], remote_handle, cs.GC_REASON_INVITED]) media_chan_suffix = e.path e = q.expect('dbus-signal', signal='NewSessionHandler') session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') if call_error_on == 'session': session_handler.Error(0, "this has been deprecated for years") else: session_handler.Ready() e = q.expect('dbus-signal', signal='NewStreamHandler') # S-E gets notified about a newly-created stream stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') # Something goes wrong immediately! stream_handler.Error(0, "i'll have the eggs tostada please") # Gabble doesn't fall over, and the channel closes nicely. e = q.expect('dbus-signal', signal='Closed', path=media_chan_suffix)
def make_stream_request(stream_type): media_iface.RequestStreams(remote_handle, [stream_type]) e = q.expect("dbus-signal", signal="NewStreamHandler") stream_id = e.args[1] stream_handler = make_channel_proxy(conn, e.args[0], "Media.StreamHandler") stream_handler.NewNativeCandidate("fake", jt2.get_remote_transports_dbus()) stream_handler.Ready(jt2.get_audio_codecs_dbus()) stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) return (stream_handler, stream_id)
def run_test(q, bus, conn, stream, jt, request_before_presence): """ Requests streams on a media channel to jt.remote_jid, either before their presence is received (if request_before_presence is True) or after their presence is received but before we've got a disco response for their capabilities (otherwise). """ # We intentionally DON'T set remote presence yet. Since Gabble is still # unsure whether to treat contact as offline for this purpose, it # will tentatively allow channel creation and contact handle addition request = dbus.Dictionary( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: jt.remote_jid, }, signature="sv", ) path, props = conn.CreateChannel(request, dbus_interface=cs.CONN_IFACE_REQUESTS) media_iface = make_channel_proxy(conn, path, "Channel.Type.StreamedMedia") handle = props[cs.TARGET_HANDLE] sync_dbus(bus, q, conn) def call_request_streams(): call_async(q, media_iface, "RequestStreams", handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) def send_presence(): jt.send_remote_presence() return q.expect("stream-iq", query_ns=ns.DISCO_INFO, to=jt.remote_jid) if request_before_presence: # Request streams before either <presence> or caps have arrived. Gabble # should wait for both to arrive before returning from RequestStreams. call_request_streams() # Ensure Gabble's received the method call. sync_dbus(bus, q, conn) # Now send the presence. info_event = send_presence() else: info_event = send_presence() # Now call RequestStreams; it should wait for the disco reply. call_request_streams() jt.send_remote_disco_reply(info_event.stanza) # RequestStreams should now happily complete q.expect("dbus-return", method="RequestStreams")
def join_muc(q, conn, muc_name): self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") muc_handle = conn.RequestHandles(cs.HT_ROOM, [muc_name])[0] path = conn.RequestChannel(cs.CHANNEL_TYPE_TEXT, cs.HT_ROOM, muc_handle, True) # added as remote pending q.expect('dbus-signal', signal='MembersChanged', path=path, args=['', [], [], [], [self_handle], self_handle, 0]) # added as member q.expect('dbus-signal', signal='MembersChanged', path=path, args=['', [self_handle], [], [], [], self_handle, 0]) group = make_channel_proxy(conn, path, "Channel.Interface.Group") return muc_handle, group
def do_one_search(q, bus, conn, stream, fields, expected_search_keys, terms, results): call_create(q, conn, server) ret, nc_sig = answer_extended_field_query(q, stream, server, fields) path, props = ret.value props = unwrap(props) assert props[cs.CONTACT_SEARCH_SERVER] == server, pformat(props) assert sorted(props[cs.CONTACT_SEARCH_ASK]) == expected_search_keys, \ sorted(props[cs.CONTACT_SEARCH_ASK]) assert cs.CONTACT_SEARCH_STATE not in props, pformat(props) c = make_channel_proxy(conn, path, 'Channel') c_props = dbus.Interface(c, cs.PROPERTIES_IFACE) c_search = dbus.Interface(c, cs.CHANNEL_TYPE_CONTACT_SEARCH) state = c_props.Get(cs.CHANNEL_TYPE_CONTACT_SEARCH, 'SearchState') assert state == cs.SEARCH_NOT_STARTED, state # We make a search. iq = make_search(q, c_search, c_props, server, terms) query = iq.firstChildElement() fields_sent = xpath.queryForNodes( '/iq/query[@xmlns="%s"]/x[@xmlns="%s"][@type="submit"]/field' % (ns.SEARCH, ns.X_DATA), iq) assert fields_sent is not None # check FORM_TYPE f = fields_sent[0] assert f['type'] == 'hidden' assert f['var'] == 'FORM_TYPE' value = f.firstChildElement() assert value.name == 'value' assert value.children[0] == ns.SEARCH # extract search fields search_fields = [] for f in fields_sent[1:]: value = f.firstChildElement() assert value.name == 'value' search_fields.append((f['var'], value.children[0])) # Server sends the results of the search. send_results_extended(stream, iq, results, fields) return search_fields, c, c_search, c_props
def test(q, bus, conn, stream, channel_type): jt = jingletest.JingleTest(stream, 'test@localhost', '[email protected]/Foo') # We intentionally DON'T set remote presence yet. Since Gabble is still # unsure whether to treat contact as offline for this purpose, it # will tentatively allow channel creation and contact handle addition handle = conn.RequestHandles(cs.HT_CONTACT, [jt.remote_jid])[0] if channel_type == cs.CHANNEL_TYPE_STREAMED_MEDIA: path = conn.RequestChannel(cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.HT_CONTACT, handle, True) media_iface = make_channel_proxy(conn, path, 'Channel.Type.StreamedMedia') # So it turns out that the calls to RequestStreams and Disconnect could be # reordered while the first waits on the result of introspecting the # channel's object which is kicked off by making a proxy object for it, # whereas the connection proxy is long ago introspected. Isn't dbus-python # great? Syncing here forces that introspection to finish so we can rely on # the ordering of RequestStreams and Disconnect. Yay. sync_dbus(bus, q, conn) # Now we request streams before either <presence> or caps have arrived if channel_type == cs.CHANNEL_TYPE_STREAMED_MEDIA: call_async(q, media_iface, 'RequestStreams', handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) before_events, after_events = disconnect_conn(q, conn, stream, [EventPattern('dbus-error', method='RequestStreams')]) # RequestStreams should now return NotAvailable assert before_events[0].error.get_dbus_name() == cs.NOT_AVAILABLE, \ before_events[0].error else: call_async(q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: channel_type, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: jt.remote_jid, cs.CALL_INITIAL_AUDIO: True }) before_events, after_events = disconnect_conn(q, conn, stream, [EventPattern('dbus-error', method='CreateChannel')]) # CreateChannel should now return Disconnected assert before_events[0].error.get_dbus_name() == cs.DISCONNECTED, \ before_events[0].error
def cancelled_while_in_progress(q, bus, conn, stream, server): call_create(q, conn, server) ret, _ = answer_field_query(q, stream, server) path, props = ret.value c = make_channel_proxy(conn, path, 'Channel') c_props = dbus.Interface(c, cs.PROPERTIES_IFACE) c_search = dbus.Interface(c, cs.CHANNEL_TYPE_CONTACT_SEARCH) iq = make_search(q, c_search, c_props, server, { 'x-n-family': 'Threepwood' }) # Before the server sends back the results, the client cancels the search. call_async(q, c_search, 'Stop') ret, ssc = q.expect_many( EventPattern('dbus-return', method='Stop'), EventPattern('dbus-signal', signal='SearchStateChanged'), ) assert ssc.args[0] == cs.SEARCH_FAILED, ssc.args assert ssc.args[1] == cs.CANCELLED, ssc.args state = c_props.Get(cs.CHANNEL_TYPE_CONTACT_SEARCH, 'SearchState') assert state == cs.SEARCH_FAILED, (state, cs.SEARCH_FAILED) # Now the server sends us the results; SearchResultReceived shouldn't fire search_result_received_event = EventPattern('dbus-signal', signal='SearchResultReceived') q.forbid_events([search_result_received_event]) send_results(stream, iq, results.values()) # Make sure Gabble's received the results. sync_stream(q, stream) # Hooray! We survived. Now let's call Stop again; it should succeed but do # nothing. search_state_changed_event = EventPattern('dbus-signal', signal='SearchStateChanged') q.forbid_events([search_state_changed_event]) call_async(q, c_search, 'Stop') ssc = q.expect('dbus-return', method='Stop') c.Close() q.unforbid_events([search_result_received_event, search_state_changed_event])
def test(q, bus, conn, stream): jt = jingletest.JingleTest(stream, 'test@localhost', '[email protected]/Foo') conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTING, cs.CSR_REQUESTED]) q.expect('stream-authenticated') q.expect('dbus-signal', signal='PresenceUpdate', args=[{1L: (0L, {u'available': {}})}]) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) # We intentionally DON'T set remote presence yet. Since Gabble is still # unsure whether to treat contact as offline for this purpose, it # will tentatively allow channel creation and contact handle addition handle = conn.RequestHandles(cs.HT_CONTACT, [jt.remote_jid])[0] path = conn.RequestChannel(cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.HT_CONTACT, handle, True) media_iface = make_channel_proxy(conn, path, 'Channel.Type.StreamedMedia') # So it turns out that the calls to RequestStreams and Disconnect could be # reordered while the first waits on the result of introspecting the # channel's object which is kicked off by making a proxy object for it, # whereas the connection proxy is long ago introspected. Isn't dbus-python # great? Syncing here forces that introspection to finish so we can rely on # the ordering of RequestStreams and Disconnect. Yay. sync_dbus(bus, q, conn) # Now we request streams before either <presence> or caps have arrived call_async(q, media_iface, 'RequestStreams', handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) event = disconnect_conn(q, conn, stream, [EventPattern('dbus-error', method='RequestStreams')])[0] # RequestStreams should now return NotAvailable assert event.error.get_dbus_name() == cs.NOT_AVAILABLE, event.error
def no_x_in_reply(q, bus, conn, stream): fields = [('nickname', 'text-single', 'NickName', []), ('nick', 'text-single', 'Nick', []),] terms = { 'nickname': 'Badger' } call_create(q, conn, server) ret, nc_sig = answer_extended_field_query(q, stream, server, fields) path, _ = ret.value c = make_channel_proxy(conn, path, 'Channel') c_props = dbus.Interface(c, cs.PROPERTIES_IFACE) c_search = dbus.Interface(c, cs.CHANNEL_TYPE_CONTACT_SEARCH) iq = make_search(q, c_search, c_props, server, terms) # The server sends back an IQ with a <query/> but no <x/> inside the query. acknowledge_iq(stream, iq) # Gabble should tell us the query failed, and not crash. event = q.expect('dbus-signal', signal='SearchStateChanged') state = event.args[0] assertEquals(cs.SEARCH_FAILED, state)
def returns_error_from_search(q, stream, conn): server = 'nofunforyou.localhost' iq = call_create(q, conn, server) result = make_result_iq(stream, iq) query = result.firstChildElement() query.addElement("first") stream.send(result) event = q.expect('dbus-return', method='CreateChannel') c = make_channel_proxy(conn, event.value[0], 'Channel') c_search = dbus.Interface(c, cs.CHANNEL_TYPE_CONTACT_SEARCH) call_async(q, c_search, 'Search', {'x-n-given': 'World of Goo'}) iq_event, _ = q.expect_many( EventPattern('stream-iq', to=server, query_ns=ns.SEARCH), EventPattern('dbus-signal', signal='SearchStateChanged'), ) iq = iq_event.stanza error = domish.Element((None, 'error')) error['type'] = 'modify' error.addElement((ns.STANZA, 'not-acceptable')) error.addElement((ns.STANZA, 'text'), content="We don't believe in games here.") send_error_reply(stream, iq, error) ssc = q.expect('dbus-signal', signal='SearchStateChanged') new_state, reason, details = ssc.args assert new_state == cs.SEARCH_FAILED, new_state assert reason == cs.PERMISSION_DENIED, reason # We call stop after the search has failed; it should succeed and do nothing. call_async(q, c_search, 'Stop') event = q.expect('dbus-return', method='Stop') c.Close()
def _remote_content_add(jp, q, bus, conn, stream, initiate_call_func): jt, chan = initiate_call_func(jp, q, bus, conn, stream) video_codecs = [ jp.PayloadType(name, str(rate), str(id), parameters) \ for (name, id, rate, parameters) in jt.video_codecs] node = jp.SetIq(jt.peer, jt.jid, [ jp.Jingle(jt.sid, jt.peer, 'content-add', [ jp.Content( 'videostream', 'initiator', 'both', jp.Description('video', video_codecs), jp.TransportGoogleP2P()) ]) ]) stream.send(jp.xml(node)) _, nsh = q.expect_many( EventPattern('dbus-signal', signal='StreamAdded'), EventPattern('dbus-signal', signal='NewStreamHandler')) stream_handler_path, stream_id, media_type, direction = nsh.args video_handler = make_channel_proxy(conn, stream_handler_path, 'Media.StreamHandler') video_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) video_handler.Ready(jt.dbusify_codecs([("FOO", 5, 8000, {})])) msg = u"None of the codecs are good for us, damn!" video_handler.Error(cs.MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED, msg) q.expect_many( EventPattern('dbus-signal', signal='StreamError', args=[stream_id, cs.MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED, msg]), EventPattern('stream-iq', predicate=_content_reject_predicate))
def returns_bees_from_search(q, stream, conn): server = 'hivemind.localhost' iq = call_create(q, conn, server) result = make_result_iq(stream, iq) query = result.firstChildElement() query.addElement("nick") stream.send(result) event = q.expect('dbus-return', method='CreateChannel') c = make_channel_proxy(conn, event.value[0], 'Channel') c_search = dbus.Interface(c, cs.CHANNEL_TYPE_CONTACT_SEARCH) call_async(q, c_search, 'Search', {'nickname': 'Buzzy'}) iq_event, _ = q.expect_many( EventPattern('stream-iq', to=server, query_ns=ns.SEARCH), EventPattern('dbus-signal', signal='SearchStateChanged'), ) iq = iq_event.stanza result = IQ(stream, 'result') result['id'] = iq['id'] result['from'] = iq['to'] result.addElement((ns.SEARCH, 'bees')).addElement('bzzzzzzz') stream.send(result) ssc = q.expect('dbus-signal', signal='SearchStateChanged') new_state, reason, details = ssc.args assert new_state == cs.SEARCH_FAILED, new_state assert reason == cs.NOT_AVAILABLE, reason # We call stop after the search has failed; it should succeed and do nothing. call_async(q, c_search, 'Stop') event = q.expect('dbus-return', method='Stop') c.Close()
def invite_to_muc(q, group1, conn2, invited_handle, inviter_handle): # first connection: invite contact group1.AddMembers([invited_handle], "Let's tube!") # channel is created on conn2 e = q.expect('dbus-signal', signal='NewChannel', path=conn2.object_path) path = e.args[0] group2 = make_channel_proxy(conn2, path, "Channel.Interface.Group") # we are invited to the muc # added as local pending conn2_self_handle = conn2.Properties.Get(cs.CONN, "SelfHandle") q.expect('dbus-signal', signal='MembersChanged', path=path, args=[ "Let's tube!", [], [], [conn2_self_handle], [], inviter_handle, 4 ]) # second connection: accept the invite group2.AddMembers([conn2_self_handle], "") # added as remote pending q.expect('dbus-signal', signal='MembersChanged', path=path, args=['', [], [], [], [conn2_self_handle], conn2_self_handle, 0]) # added as member q.expect('dbus-signal', signal='MembersChanged', path=path, args=['', [conn2_self_handle], [], [], [], conn2_self_handle, 0]) return group2
def run_test(q, bus, conn, stream, jt, decloak_allowed): """ Requests streams on a media channel to jt.remote_jid without having their presence at all. """ request = dbus.Dictionary({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: jt.remote_jid }, signature='sv') path, props = conn.CreateChannel(request, dbus_interface=cs.CONN_IFACE_REQUESTS) media_iface = make_channel_proxy(conn, path, 'Channel.Type.StreamedMedia') handle = props[cs.TARGET_HANDLE] call_async(q, media_iface, 'RequestStreams', handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) e = q.expect('stream-presence', to=jt.remote_bare_jid, presence_type=None) nodes = xpath.queryForNodes('/presence/temppres[@xmlns="%s"]' % ns.TEMPPRES, e.stanza) assertLength(1, nodes) assertEquals('media', nodes[0].getAttribute('reason')) if decloak_allowed: jt.send_remote_presence() info_event = q.expect('stream-iq', query_ns=ns.DISCO_INFO, to=jt.remote_jid) jt.send_remote_disco_reply(info_event.stanza) # RequestStreams should now happily complete q.expect('dbus-return', method='RequestStreams') else: q.expect('dbus-error', method='RequestStreams', name=cs.OFFLINE)
def test(q, bus, conn): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L]) basic_txt = {"txtvers": "1", "status": "avail"} self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") self_handle_name = conn.Properties.Get(cs.CONN, "SelfID") contact_name = "test-text-channel@" + get_host_name() listener, port = setup_stream_listener(q, contact_name) announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port, basic_txt) handle = wait_for_contact_in_publish(q, bus, conn, contact_name) # create a clique room basic_txt = {"txtvers": "0"} AvahiAnnouncer("myroom", "_clique._udp", 41377, basic_txt) # connect a stream AvahiListener(q).listen_for_service("_presence._tcp") e = q.expect('service-added', name=self_handle_name, protocol=avahi.PROTO_INET) service = e.service service.resolve() e = q.expect('service-resolved', service=service) xmpp_connection = connect_to_stream(q, contact_name, self_handle_name, str(e.pt), e.port) e = q.expect('connection-result') assert e.succeeded, e.reason e = q.expect('stream-opened', connection=xmpp_connection) # send an invitation message = domish.Element(('', 'message')) message['type'] = 'normal' message.addElement('body', content='You got a Clique chatroom invitation') invite = message.addElement((NS_CLIQUE, 'invite')) invite.addElement('roomname', content='myroom') invite.addElement('reason', content='Inviting to this room') invite.addElement('address', content='127.0.0.1') invite.addElement('port', content='41377') xmpp_connection.send(message) # group channel is created e = q.expect('dbus-signal', signal='NewChannel', predicate=lambda e: e.args[1] == cs.CHANNEL_TYPE_TEXT and e. args[2] == cs.HT_ROOM) path = e.args[0] channel = make_channel_proxy(conn, path, 'Channel') props_iface = dbus.Interface(bus.get_object(conn.object.bus_name, path), dbus.PROPERTIES_IFACE) q.expect('dbus-signal', signal='MembersChanged', path=path) lp_members = props_iface.Get( 'org.freedesktop.Telepathy.Channel.Interface.Group', 'LocalPendingMembers') assert len(lp_members) == 1 added, actor, reason, msg = lp_members[0] assert added == self_handle assert actor == handle assert reason == 4 #invited assert msg == 'Inviting to this room' # decline invitation channel.Close() q.expect('dbus-signal', signal='Closed')
def test(q, bus, conn): self_name = 'testsuite' + '@' + avahitest.get_host_name() conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L]) # FIXME: this is a hack to be sure to have all the contact list channels # announced so they won't interfere with the muc ones announces. wait_for_contact_list(q, conn) # check if we can request roomlist channels properties = conn.GetAll( tp_name_prefix + '.Connection.Interface.Requests', dbus_interface='org.freedesktop.DBus.Properties') assert ({tp_name_prefix + '.Channel.ChannelType': cs.CHANNEL_TYPE_TEXT, tp_name_prefix + '.Channel.TargetHandleType': cs.HT_ROOM, }, [tp_name_prefix + '.Channel.TargetHandle', tp_name_prefix + '.Channel.TargetID'], ) in properties.get('RequestableChannelClasses'),\ properties['RequestableChannelClasses'] requestotron = dbus.Interface(conn, tp_name_prefix + '.Connection.Interface.Requests') # create muc channel using new API call_async(q, requestotron, 'CreateChannel', { tp_name_prefix + '.Channel.ChannelType': cs.CHANNEL_TYPE_TEXT, tp_name_prefix + '.Channel.TargetHandleType': cs.HT_ROOM, tp_name_prefix + '.Channel.TargetID': 'my-second-room', }) ret, new_sig = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-signal', signal='NewChannels'), ) path2 = ret.value[0] chan = make_channel_proxy(conn, path2, "Channel") props = ret.value[1] assert props[tp_name_prefix + '.Channel.ChannelType'] ==\ cs.CHANNEL_TYPE_TEXT assert props[tp_name_prefix + '.Channel.TargetHandleType'] == cs.HT_ROOM assert props[tp_name_prefix + '.Channel.TargetID'] == 'my-second-room' assert props[tp_name_prefix + '.Channel.Requested'] == True assert props[tp_name_prefix + '.Channel.InitiatorHandle'] \ == conn.Properties.Get(cs.CONN, "SelfHandle") assert props[tp_name_prefix + '.Channel.InitiatorID'] \ == self_name assert new_sig.args[0][0][0] == path2 assert new_sig.args[0][0][1] == props # ensure roomlist channel handle = props[tp_name_prefix + '.Channel.TargetHandle'] yours, ensured_path, ensured_props = ret.value = requestotron.EnsureChannel( { tp_name_prefix + '.Channel.ChannelType': cs.CHANNEL_TYPE_TEXT, tp_name_prefix + '.Channel.TargetHandleType': cs.HT_ROOM, tp_name_prefix + '.Channel.TargetHandle': handle, }) assert not yours assert ensured_path == path2, (ensured_path, path2) conn.Disconnect() q.expect_many( EventPattern('dbus-signal', signal='Closed', path=path2), EventPattern('dbus-signal', signal='ChannelClosed', args=[path2]), EventPattern('dbus-signal', signal='StatusChanged', args=[2, 1]), )
def test(q, bus, conn, stream, send_early_description_info=False): jp = JingleProtocol031() jt2 = JingleTest2(jp, conn, q, stream, 'test@localhost', '[email protected]/Foo') jt2.prepare() self_handle = conn.GetSelfHandle() remote_handle = conn.RequestHandles(cs.HT_CONTACT, ["[email protected]/Foo"])[0] # Remote end calls us jt2.incoming_call() # FIXME: these signals are not observable by real clients, since they # happen before NewChannels. # The caller is in members e = q.expect('dbus-signal', signal='MembersChanged', args=[u'', [remote_handle], [], [], [], 0, 0]) # We're pending because of remote_handle e = q.expect('dbus-signal', signal='MembersChanged', args=[u'', [], [], [self_handle], [], remote_handle, cs.GC_REASON_INVITED]) chan = wrap_channel(bus.get_object(conn.bus_name, e.path), 'StreamedMedia') # S-E gets notified about new session handler, and calls Ready on it e = q.expect('dbus-signal', signal='NewSessionHandler') assert e.args[1] == 'rtp' if send_early_description_info: """ Regression test for a bug where Gabble would crash if you sent it description-info before calling Ready() on the relevant StreamHandler, and then for a bug where Gabble would never accept the call if a description-info was received before all StreamHandlers were Ready(). """ node = jp.SetIq(jt2.peer, jt2.jid, [ jp.Jingle(jt2.sid, jt2.peer, 'description-info', [ jp.Content('stream1', 'initiator', 'both', jp.Description('audio', [ ])) ]) ]) stream.send(jp.xml(node)) sync_stream(q, stream) session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() chan.Group.AddMembers([self_handle], 'accepted') # S-E gets notified about a newly-created stream e = q.expect('dbus-signal', signal='NewStreamHandler') id1 = e.args[1] stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') # We are now in members too e = q.expect('dbus-signal', signal='MembersChanged', args=[u'', [self_handle], [], [], [], self_handle, cs.GC_REASON_NONE]) # we are now both in members members = chan.Group.GetMembers() assert set(members) == set([self_handle, remote_handle]), members local_codecs = [('GSM', 3, 8000, {}), ('PCMA', 8, 8000, {'helix':'woo yay'}), ('PCMU', 0, 8000, {}) ] local_codecs_dbus = jt2.dbusify_codecs_with_params(local_codecs) stream_handler.NewNativeCandidate("fake", jt2.get_remote_transports_dbus()) stream_handler.Ready(local_codecs_dbus) stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) stream_handler.CodecsUpdated(local_codecs_dbus) local_codecs = [('GSM', 3, 8000, {}), ('PCMA', 8, 8000, {'gstreamer':'rock on'}), ('PCMU', 0, 8000, {}) ] local_codecs_dbus = jt2.dbusify_codecs_with_params(local_codecs) stream_handler.CodecsUpdated(local_codecs_dbus) # First IQ is transport-info; also, we expect to be told what codecs the # other end wants. e, src = q.expect_many( EventPattern('stream-iq', predicate=jp.action_predicate('transport-info')), EventPattern('dbus-signal', signal='SetRemoteCodecs') ) assertEquals('[email protected]/Foo', e.query['initiator']) assert jt2.audio_codecs == [ (name, id, rate, parameters) for id, name, type, rate, channels, parameters in unwrap(src.args[0]) ], \ (jt2.audio_codecs, unwrap(src.args[0])) stream.send(jp.xml(jp.ResultIq('test@localhost', e.stanza, []))) # S-E reports codec intersection, after which gabble can send acceptance stream_handler.SupportedCodecs(local_codecs_dbus) # Second one is session-accept e = q.expect('stream-iq', predicate=jp.action_predicate('session-accept')) # farstream is buggy, and tells tp-fs to tell Gabble to change the third # codec's clockrate. This isn't legal, so Gabble says no. new_codecs = [ ('GSM', 3, 8000, {}), ('PCMA', 8, 8000, {}), ('PCMU', 0, 4000, {}) ] call_async(q, stream_handler, 'CodecsUpdated', jt2.dbusify_codecs(new_codecs)) event = q.expect('dbus-error', method='CodecsUpdated') assert event.error.get_dbus_name() == cs.INVALID_ARGUMENT, \ event.error.get_dbus_name() # With its tail between its legs, tp-fs decides it wants to add some # parameters to the first two codecs, not changing the third. new_codecs = [ ('GSM', 3, 8000, {'type': 'banana'}), ('PCMA', 8, 8000, {'helix': 'BUFFERING'}), ('PCMU', 0, 8000, {}) ] stream_handler.CodecsUpdated(jt2.dbusify_codecs_with_params(new_codecs)) audio_content = jt2.audio_names[0] e = q.expect('stream-iq', iq_type='set', predicate=lambda x: xpath.queryForNodes("/iq/jingle[@action='description-info']", x.stanza)) payload_types = xpath.queryForNodes( "/iq/jingle/content[@name='%s']/description/payload-type" % audio_content, e.stanza) # Gabble SHOULD only include the changed codecs in description-info assert len(payload_types) == 2, payload_types payload_types_tupled = [ (pt['name'], int(pt['id']), int(pt['clockrate']), extract_params(pt)) for pt in payload_types ] assert sorted(payload_types_tupled) == sorted(new_codecs[0:2]), \ (payload_types_tupled, new_codecs[0:2]) # The remote end decides it wants to change the number of channels in the # third codec. This is not meant to happen, so Gabble should send it an IQ # error back. node = jp.SetIq(jt2.peer, jt2.jid, [ jp.Jingle(jt2.sid, jt2.peer, 'description-info', [ jp.Content(audio_content, 'initiator', 'both', jp.Description('audio', [ jp.PayloadType('PCMU', '1600', '0') ])) ]) ]) stream.send(jp.xml(node)) q.expect('stream-iq', iq_type='error', predicate=lambda x: x.stanza['id'] == node[2]['id']) # Instead, the remote end decides to add a parameter to the third codec. new_codecs = [ ('GSM', 3, 8000, {}), ('PCMA', 8, 8000, {}), ('PCMU', 0, 8000, {'choppy': 'false'}), ] # As per the XEP, it only sends the ones which have changed. c = new_codecs[2] node = jp.SetIq(jt2.peer, jt2.jid, [ jp.Jingle(jt2.sid, jt2.peer, 'description-info', [ jp.Content(audio_content, 'initiator', 'both', jp.Description('audio', [ jp.PayloadType(c[0], str(c[2]), str(c[1]), c[3]) ])) ]) ]) stream.send(jp.xml(node)) # Gabble should patch its idea of the remote codecs with the update it just # got, and emit SetRemoteCodecs for them all. e = q.expect('dbus-signal', signal='SetRemoteCodecs') new_codecs_dbus = unwrap(jt2.dbusify_codecs_with_params(new_codecs)) announced = unwrap(e.args[0]) assert new_codecs_dbus == announced, (new_codecs_dbus, announced) # We close the session by removing the stream chan.StreamedMedia.RemoveStreams([id1]) e = q.expect('stream-iq', iq_type='set', predicate=lambda x: xpath.queryForNodes("/iq/jingle[@action='session-terminate']", x.stanza))
def test(q, bus, conn, stream): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[0, 1]) props = conn.GetAll(cs.CONN, dbus_interface=cs.PROPERTIES_IFACE) assertContains(cs.CONN_IFACE_REQUESTS, props['Interfaces']) nick = 'foo' foo_handle = conn.get_contact_handle_sync(nick) properties = conn.GetAll(cs.CONN_IFACE_REQUESTS, dbus_interface=cs.PROPERTIES_IFACE) assert properties.get('Channels') == [], properties['Channels'] assert ({cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, }, [cs.TARGET_HANDLE, cs.TARGET_ID], ) in properties.get('RequestableChannelClasses'),\ properties['RequestableChannelClasses'] requestotron = dbus.Interface(conn, cs.CONN_IFACE_REQUESTS) request = { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: nick, } call_async(q, requestotron, 'CreateChannel', request) ret, new_sig = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-signal', signal='NewChannels'), ) assert len(ret.value) == 2 path, emitted_props = ret.value assert emitted_props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT assert emitted_props[cs.TARGET_HANDLE_TYPE] == cs.HT_CONTACT assert emitted_props[cs.TARGET_HANDLE] == foo_handle assert emitted_props[cs.TARGET_ID] == nick assert emitted_props[cs.REQUESTED] assert emitted_props[cs.INITIATOR_HANDLE] == props['SelfHandle'] assert emitted_props[cs.INITIATOR_ID] == stream.nick assert len(new_sig.args) == 1 assert len(new_sig.args[0]) == 1 # one channel assert len(new_sig.args[0][0]) == 2 # two struct members assert new_sig.args[0][0][0] == ret.value[0] assert new_sig.args[0][0][1] == ret.value[1] properties = conn.GetAll(cs.CONN_IFACE_REQUESTS, dbus_interface=cs.PROPERTIES_IFACE) assert new_sig.args[0][0] in properties['Channels'], \ (new_sig.args[0][0], properties['Channels']) chan = make_channel_proxy(conn, path, 'Channel') stream.sendMessage('PRIVMSG', stream.nick, ":oh hai", prefix=nick) q.expect('dbus-signal', signal='Received') # Without acknowledging the message, we close the channel: chan.Close() # It should close and respawn! q.expect('dbus-signal', signal='ChannelClosed') chans, = q.expect('dbus-signal', signal='NewChannels').args assert len(chans) == 1 new_props = chans[0][1] # It should look pretty similar... assert new_props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT assert new_props[cs.TARGET_HANDLE_TYPE] == cs.HT_CONTACT assert new_props[cs.TARGET_HANDLE] == foo_handle assert new_props[cs.TARGET_ID] == nick # ...but this time they started it... assert not new_props[cs.REQUESTED] assert new_props[cs.INITIATOR_HANDLE] == foo_handle assert new_props[cs.INITIATOR_ID] == nick # ...and it's got some messages in it! ms = chan.ListPendingMessages(False, dbus_interface=cs.CHANNEL_TYPE_TEXT) assert len(ms) == 1 assert ms[0][5] == 'oh hai' # Without acknowledging the message, we destroy the channel: assertContains( cs.CHANNEL_IFACE_DESTROYABLE, chan.Get(cs.CHANNEL, 'Interfaces', dbus_interface=cs.PROPERTIES_IFACE)) chan.Destroy(dbus_interface=cs.CHANNEL_IFACE_DESTROYABLE) q.expect('dbus-signal', signal='ChannelClosed') # It should be gone for good this time. channels = conn.Get(cs.CONN_IFACE_REQUESTS, 'Channels', dbus_interface=cs.PROPERTIES_IFACE) assert channels == [], channels call_async(q, conn, 'Disconnect') q.expect('dbus-signal', signal='StatusChanged', args=[2, 1])
def test(jp, q, bus, conn, stream): # this test uses multiple streams if not jp.is_modern_jingle(): return remote_jid = '[email protected]/Foo' jt = JingleTest2(jp, conn, q, stream, 'test@localhost', remote_jid) jt.prepare() self_handle = conn.GetSelfHandle() handle = conn.RequestHandles(cs.HT_CONTACT, [remote_jid])[0] chan_path = conn.RequestChannel(cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.HT_CONTACT, handle, True) chan = wrap_channel(bus.get_object(conn.bus_name, chan_path), 'StreamedMedia', ['MediaSignalling', 'Group', 'CallState', 'DTMF']) chan_props = chan.Properties.GetAll(cs.CHANNEL) assert cs.CHANNEL_IFACE_DTMF in chan_props['Interfaces'], \ chan_props['Interfaces'] assertEquals('', chan.Properties.Get(cs.CHANNEL_IFACE_DTMF, 'InitialTones')) assertEquals('', chan.Properties.Get(cs.CHANNEL_IFACE_DTMF, 'DeferredTones')) chan.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) # S-E gets notified about new session handler, and calls Ready on it e = q.expect('dbus-signal', signal='NewSessionHandler') assert e.args[1] == 'rtp' session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() e = q.expect('dbus-signal', signal='NewStreamHandler') audio_path = e.args[0] stream_handler = make_channel_proxy(conn, audio_path, 'Media.StreamHandler') stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) stream_handler.Ready(jt.get_audio_codecs_dbus()) stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) e = q.expect('stream-iq', predicate=jp.action_predicate('session-initiate')) stream.send(make_result_iq(stream, e.stanza)) jt.parse_session_initiate(e.query) jt.accept() # Gabble tells s-e to start sending q.expect('dbus-signal', signal='SetStreamSending', args=[True], path=audio_path) chan.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) e = q.expect('dbus-signal', signal='NewStreamHandler') audio2_path = e.args[0] # The Stream_ID is specified to be ignored; we use 666 here. call_async(q, chan.DTMF, 'StartTone', 666, 3) q.expect_many( EventPattern('dbus-signal', signal='StartTelephonyEvent', path=audio2_path), EventPattern('dbus-signal', signal='StartTelephonyEvent', path=audio_path), EventPattern('dbus-signal', signal='SendingTones', args=['3'], path=chan_path), EventPattern('dbus-return', method='StartTone'), ) call_async(q, chan.DTMF, 'StopTone', 666) q.expect_many( EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio_path), EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio2_path), EventPattern('dbus-signal', signal='StoppedTones', args=[True], path=chan_path), EventPattern('dbus-return', method='StopTone'), ) call_async(q, chan.DTMF, 'MultipleTones', '123w*#') q.expect_many( EventPattern('dbus-signal', signal='StartTelephonyEvent', path=audio_path, args=[1]), EventPattern('dbus-signal', signal='StartTelephonyEvent', path=audio2_path, args=[1]), EventPattern('dbus-signal', signal='SendingTones', args=['123w*#'], path=chan_path), EventPattern('dbus-return', method='MultipleTones'), ) q.expect_many( EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio_path), EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio2_path), ) q.expect_many( EventPattern('dbus-signal', signal='StartTelephonyEvent', path=audio_path, args=[2]), EventPattern('dbus-signal', signal='StartTelephonyEvent', path=audio2_path, args=[2]), ) q.expect_many( EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio_path), EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio2_path), ) q.expect_many( EventPattern('dbus-signal', signal='StartTelephonyEvent', path=audio_path, args=[3]), EventPattern('dbus-signal', signal='StartTelephonyEvent', path=audio2_path, args=[3]), ) q.expect_many( EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio_path), EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio2_path), EventPattern('dbus-signal', signal='StoppedTones', args=[False], path=chan_path), EventPattern('dbus-signal', signal='TonesDeferred', args=['*#']), ) assertEquals('*#', chan.Properties.Get(cs.CHANNEL_IFACE_DTMF, 'DeferredTones')) forbidden = [EventPattern('dbus-signal', signal='StartTelephonyEvent', args=[9])] q.forbid_events(forbidden) # This is technically a race condition, but this dialstring is almost # certainly long enough that the Python script will win the race, i.e. # cancel before Gabble processes the whole dialstring. call_async(q, chan.DTMF, 'MultipleTones', '1,1' * 100) q.expect('dbus-return', method='MultipleTones') call_async(q, chan.DTMF, 'MultipleTones', '9') q.expect('dbus-error', method='MultipleTones', name=cs.SERVICE_BUSY) call_async(q, chan.DTMF, 'StartTone', 666, 9) q.expect('dbus-error', method='StartTone', name=cs.SERVICE_BUSY) call_async(q, chan.DTMF, 'StopTone', 666) q.expect_many( EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio_path), EventPattern('dbus-signal', signal='StopTelephonyEvent', path=audio2_path), EventPattern('dbus-signal', signal='StoppedTones', args=[True], path=chan_path), EventPattern('dbus-return', method='StopTone'), ) # emitting any sound resets TonesDeferred assertEquals('', chan.Properties.Get(cs.CHANNEL_IFACE_DTMF, 'DeferredTones')) q.unforbid_events(forbidden) chan.Group.RemoveMembers([self_handle], 'closed') e = q.expect('dbus-signal', signal='Closed', path=chan_path)
def test(q, bus, conn): self_name = 'testsuite' + '@' + get_host_name() conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L]) basic_txt = { "txtvers": "1", "status": "avail" } contact_name = "test-request-im@" + get_host_name() listener, port = setup_stream_listener(q, contact_name) # FIXME: this is a hack to be sure to have all the contact list channels # announced so they won't interfere with the muc ones announces. wait_for_contact_list(q, conn) AvahiAnnouncer(contact_name, "_presence._tcp", port, basic_txt) handle = wait_for_contact_in_publish(q, bus, conn, contact_name) # check if we can request roomlist channels properties = conn.GetAll( tp_name_prefix + '.Connection.Interface.Requests', dbus_interface='org.freedesktop.DBus.Properties') assert ({tp_name_prefix + '.Channel.ChannelType': cs.CHANNEL_TYPE_TEXT, tp_name_prefix + '.Channel.TargetHandleType': cs.HT_CONTACT, }, [tp_name_prefix + '.Channel.TargetHandle', tp_name_prefix + '.Channel.TargetID'], ) in properties.get('RequestableChannelClasses'),\ properties['RequestableChannelClasses'] # create muc channel requestotron = dbus.Interface(conn, tp_name_prefix + '.Connection.Interface.Requests') call_async(q, requestotron, 'CreateChannel', { tp_name_prefix + '.Channel.ChannelType': cs.CHANNEL_TYPE_TEXT, tp_name_prefix + '.Channel.TargetHandleType': cs.HT_CONTACT, tp_name_prefix + '.Channel.TargetID': contact_name, }) ret, new_sig = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-signal', signal='NewChannels'), ) path2 = ret.value[0] chan = make_channel_proxy(conn, path2, "Channel") props = ret.value[1] assert props[tp_name_prefix + '.Channel.ChannelType'] ==\ cs.CHANNEL_TYPE_TEXT assert props[tp_name_prefix + '.Channel.TargetHandleType'] == cs.HT_CONTACT assert props[tp_name_prefix + '.Channel.TargetHandle'] == handle assert props[tp_name_prefix + '.Channel.TargetID'] == contact_name assert props[tp_name_prefix + '.Channel.Requested'] == True assert props[tp_name_prefix + '.Channel.InitiatorHandle'] \ == conn.Properties.Get(cs.CONN, "SelfHandle") assert props[tp_name_prefix + '.Channel.InitiatorID'] \ == self_name assert new_sig.args[0][0][0] == path2 assert new_sig.args[0][0][1] == props # ensure roomlist channel yours, ensured_path, ensured_props = requestotron.EnsureChannel( { tp_name_prefix + '.Channel.ChannelType': cs.CHANNEL_TYPE_TEXT, tp_name_prefix + '.Channel.TargetHandleType': cs.HT_CONTACT, tp_name_prefix + '.Channel.TargetHandle': handle, }) assert not yours assert ensured_path == path2, (ensured_path, path2) conn.Disconnect() q.expect_many( EventPattern('dbus-signal', signal='Closed', path=path2), EventPattern('dbus-signal', signal='ChannelClosed', args=[path2]), EventPattern('dbus-signal', signal='StatusChanged', args=[2, 1]), )
def test(jp, q, bus, conn, stream, peer_removes_final_content): jt = JingleTest2(jp, conn, q, stream, 'test@localhost', '[email protected]/Foo') jt.prepare() handle = conn.RequestHandles(cs.HT_CONTACT, [jt.peer])[0] path = conn.RequestChannel( cs.CHANNEL_TYPE_STREAMED_MEDIA, cs.HT_CONTACT, handle, True) chan = wrap_channel(bus.get_object(conn.bus_name, path), 'StreamedMedia') chan.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_AUDIO]) # S-E gets notified about new session handler, and calls Ready on it e = q.expect('dbus-signal', signal='NewSessionHandler') assert e.args[1] == 'rtp' session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() e = q.expect('dbus-signal', signal='NewStreamHandler') stream_id = e.args[1] stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) # Before sending the initiate, request another stream chan.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_VIDEO]) e = q.expect('dbus-signal', signal='NewStreamHandler') stream_id2 = e.args[1] stream_handler2 = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') stream_handler2.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) # Before the CM can initiate session, we modify a stream direction. This # should result in a no-op since there's no need to inform the peer of # change. chan.StreamedMedia.RequestStreamDirection(stream_id2, cs.MEDIA_STREAM_DIRECTION_RECEIVE) # We set both streams as ready, which will trigger the session initiate stream_handler.Ready(jt.get_audio_codecs_dbus()) stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) stream_handler2.Ready(jt.get_audio_codecs_dbus()) stream_handler2.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) # We changed our mind locally, don't want video chan.StreamedMedia.RemoveStreams([stream_id2]) e = q.expect('stream-iq', predicate=jp.action_predicate('session-initiate')) stream.send(make_result_iq(stream, e.stanza)) jt.parse_session_initiate(e.query) # Gabble sends content-remove for the video stream... e2 = q.expect('stream-iq', predicate=jp.action_predicate('content-remove')) # ...but before the peer notices, they accept the call. jt.accept() # Only now the remote end removes the video stream; if gabble mistakenly # marked it as accepted on session acceptance, it'll crash right about # now. If it's good, stream will be really removed, and # we can proceed. stream.send(make_result_iq(stream, e2.stanza)) q.expect('dbus-signal', signal='StreamRemoved') # Actually, we *do* want video! chan.StreamedMedia.RequestStreams(handle, [cs.MEDIA_STREAM_TYPE_VIDEO]) e = q.expect('dbus-signal', signal='NewStreamHandler') stream2_id = e.args[1] stream_handler2 = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') stream_handler2.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) stream_handler2.Ready(jt.get_audio_codecs_dbus()) stream_handler2.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) e = q.expect('stream-iq', predicate=jp.action_predicate('content-add')) c = e.query.firstChildElement() assertEquals('initiator', c['creator']) stream.send(make_result_iq(stream, e.stanza)) # Peer accepts jt.content_accept(e.query, 'video') # Let's start sending and receiving video! q.expect_many( EventPattern('dbus-signal', signal='SetStreamPlaying', args=[True]), EventPattern('dbus-signal', signal='SetStreamSending', args=[True]), ) # Now, the call draws to a close. # We first remove the original stream chan.StreamedMedia.RemoveStreams([stream_id]) e = q.expect('stream-iq', predicate=jp.action_predicate('content-remove')) content_remove_ack = make_result_iq(stream, e.stanza) if peer_removes_final_content: # The peer removes the final countdo^W content. From a footnote (!) in # XEP 0166: # If the content-remove results in zero content definitions for the # session, the entity that receives the content-remove SHOULD send # a session-terminate action to the other party (since a session # with no content definitions is void). # So, Gabble should respond to the content-remove with a # session-terminate. node = jp.SetIq(jt.peer, jt.jid, [ jp.Jingle(jt.sid, jt.peer, 'content-remove', [ jp.Content(c['name'], c['creator'], c['senders']) ]) ]) stream.send(jp.xml(node)) else: # The Telepathy client removes the second stream; Gabble should # terminate the session rather than sending a content-remove. chan.StreamedMedia.RemoveStreams([stream2_id]) st, closed = q.expect_many( EventPattern('stream-iq', predicate=jp.action_predicate('session-terminate')), # Gabble shouldn't wait for the peer to ack the terminate before # considering the call finished. EventPattern('dbus-signal', signal='Closed', path=path)) # Only now does the peer ack the content-remove. This serves as a # regression test for contents outliving the session; if the content didn't # die properly, this crashed Gabble. stream.send(content_remove_ack) sync_stream(q, stream) # The peer can ack the terminate too, just for completeness. stream.send(make_result_iq(stream, st.stanza))
def test(q, bus, conn): self_name = 'testsuite' + '@' + avahitest.get_host_name() conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L]) # FIXME: this is a hack to be sure to have all the contact list channels # announced so they won't interfere with the muc ones announces. wait_for_contact_list(q, conn) # check if we can request roomlist channels properties = conn.GetAll(tp_name_prefix + '.Connection.Interface.Requests', dbus_interface='org.freedesktop.DBus.Properties') assert ({tp_name_prefix + '.Channel.ChannelType': cs.CHANNEL_TYPE_TEXT, tp_name_prefix + '.Channel.TargetHandleType': cs.HT_ROOM, }, [tp_name_prefix + '.Channel.TargetHandle', tp_name_prefix + '.Channel.TargetID'], ) in properties.get('RequestableChannelClasses'),\ properties['RequestableChannelClasses'] requestotron = dbus.Interface( conn, tp_name_prefix + '.Connection.Interface.Requests') # create muc channel using new API call_async( q, requestotron, 'CreateChannel', { tp_name_prefix + '.Channel.ChannelType': cs.CHANNEL_TYPE_TEXT, tp_name_prefix + '.Channel.TargetHandleType': cs.HT_ROOM, tp_name_prefix + '.Channel.TargetID': 'my-second-room', }) ret, new_sig = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-signal', signal='NewChannels'), ) path2 = ret.value[0] chan = make_channel_proxy(conn, path2, "Channel") props = ret.value[1] assert props[tp_name_prefix + '.Channel.ChannelType'] ==\ cs.CHANNEL_TYPE_TEXT assert props[tp_name_prefix + '.Channel.TargetHandleType'] == cs.HT_ROOM assert props[tp_name_prefix + '.Channel.TargetID'] == 'my-second-room' assert props[tp_name_prefix + '.Channel.Requested'] == True assert props[tp_name_prefix + '.Channel.InitiatorHandle'] \ == conn.Properties.Get(cs.CONN, "SelfHandle") assert props[tp_name_prefix + '.Channel.InitiatorID'] \ == self_name assert new_sig.args[0][0][0] == path2 assert new_sig.args[0][0][1] == props # ensure roomlist channel handle = props[tp_name_prefix + '.Channel.TargetHandle'] yours, ensured_path, ensured_props = ret.value = requestotron.EnsureChannel( { tp_name_prefix + '.Channel.ChannelType': cs.CHANNEL_TYPE_TEXT, tp_name_prefix + '.Channel.TargetHandleType': cs.HT_ROOM, tp_name_prefix + '.Channel.TargetHandle': handle, }) assert not yours assert ensured_path == path2, (ensured_path, path2) conn.Disconnect() q.expect_many( EventPattern('dbus-signal', signal='Closed', path=path2), EventPattern('dbus-signal', signal='ChannelClosed', args=[path2]), EventPattern('dbus-signal', signal='StatusChanged', args=[2, 1]), )
def test(q, bus, conn): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L]) basic_txt = {"txtvers": "1", "status": "avail"} self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") self_handle_name = conn.Properties.Get(cs.CONN, "SelfID") contact_name = "test-muc@" + get_host_name() listener, port = setup_stream_listener(q, contact_name) AvahiAnnouncer(contact_name, "_presence._tcp", port, basic_txt) handle = wait_for_contact_in_publish(q, bus, conn, contact_name) requests_iface = dbus.Interface(conn, cs.CONN_IFACE_REQUESTS) # Create a connection to send the muc invite AvahiListener(q).listen_for_service("_presence._tcp") e = q.expect('service-added', name=self_handle_name, protocol=avahi.PROTO_INET) service = e.service service.resolve() e = q.expect('service-resolved', service=service) outbound = connect_to_stream(q, contact_name, self_handle_name, str(e.pt), e.port) e = q.expect('connection-result') assert e.succeeded, e.reason e = q.expect('stream-opened', connection=outbound) # connected to Salut, now send the muc invite msg = domish.Element((None, 'message')) msg['to'] = self_handle_name msg['from'] = contact_name msg['type'] = 'normal' body = msg.addElement('body', content='You got a Clique chatroom invitation') invite = msg.addElement((NS_CLIQUE, 'invite')) invite.addElement('roomname', content='my-room') invite.addElement('reason', content='Inviting to this room') # FIXME: we should create a real Clique room and use its IP and port. # Hardcode values for now. The IP is a multicast address. invite.addElement('address', content='239.255.71.249') invite.addElement('port', content='62472') outbound.send(msg) e = q.expect('dbus-signal', signal='NewChannels', predicate=lambda e: e.args[0][0][1][cs.CHANNEL_TYPE] == cs. CHANNEL_TYPE_TEXT) channels = e.args[0] assert len(channels) == 1 path, props = channels[0] channel = make_channel_proxy(conn, path, "Channel") channel_group = make_channel_proxy(conn, path, "Channel.Interface.Group") # check channel properties # org.freedesktop.Telepathy.Channel D-Bus properties assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_TEXT assertContains(cs.CHANNEL_IFACE_GROUP, props[cs.INTERFACES]) assertContains(cs.CHANNEL_IFACE_MESSAGES, props[cs.INTERFACES]) assert props[cs.TARGET_ID] == 'my-room' assert props[cs.TARGET_HANDLE_TYPE] == HT_ROOM assert props[cs.REQUESTED] == False assert props[cs.INITIATOR_HANDLE] == handle assert props[cs.INITIATOR_ID] == contact_name # we are added to local pending e = q.expect('dbus-signal', signal='MembersChanged') msg, added, removed, lp, rp, actor, reason = e.args assert msg == 'Inviting to this room' assert added == [] assert removed == [] assert lp == [self_handle] assert rp == [] assert actor == handle assert reason == 4 # invited # TODO: join the muc, check if we are added to remote-pending and then # to members. This would need some tweak in Salut and/or the test framework # as Clique takes too much time to join the room and so the event times # out. # TODO: test sending invitations # FIXME: fd.o #30531: this ought to work, but doesn't #channel_group.RemoveMembersWithReason([self_handle], "bored now", 0) channel.Close() q.expect('dbus-signal', signal='Closed')
def test_call(jp, q, bus, conn, stream, expected_stun_servers=None, google=False, google_push_replacements=None, expected_relays=[]): # Initialize the test values jt, remote_handle = init_test(jp, q, conn, stream, google, google_push_replacements) # Advertise that we can do new style calls conn.ContactCapabilities.UpdateCapabilities([ (cs.CLIENT + ".CallHandler", [ { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL, cs.CALL_INITIAL_AUDIO: True }, { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL, cs.CALL_INITIAL_VIDEO: True }, ], [ cs.CHANNEL_TYPE_CALL + '/gtalk-p2p', cs.CHANNEL_TYPE_CALL + '/ice', cs.CHANNEL_TYPE_CALL + '/video/h264', ]), ]) # Remote end calls us jt.incoming_call() e = q.expect('dbus-signal', signal='ServerInfoRetrieved') assertLength(0, e.args) assertEquals(e.interface, cs.CALL_STREAM_IFACE_MEDIA) e = q.expect( 'dbus-signal', signal='NewChannels', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args) assert e.args[0][0][0] call_chan = make_channel_proxy(conn, e.args[0][0][0], 'Channel') # Exercise channel properties channel_props = call_chan.GetAll(cs.CHANNEL, dbus_interface=dbus.PROPERTIES_IFACE) assertEquals(remote_handle, channel_props['TargetHandle']) assertEquals(1, channel_props['TargetHandleType']) assertEquals('*****@*****.**', channel_props['TargetID']) assertEquals(False, channel_props['Requested']) assertEquals('*****@*****.**', channel_props['InitiatorID']) assertEquals(remote_handle, channel_props['InitiatorHandle']) # Get the call's Content object channel_props = call_chan.Get(cs.CHANNEL_TYPE_CALL, 'Contents', dbus_interface=dbus.PROPERTIES_IFACE) assertLength(1, channel_props) assert len(channel_props[0]) > 0 assertNotEquals('/', channel_props[0]) # Get the call's Stream object call_content = make_channel_proxy(conn, channel_props[0], 'Call.Content.Draft') content_props = call_content.Get(cs.CALL_CONTENT, 'Streams', dbus_interface=dbus.PROPERTIES_IFACE) assertLength(1, content_props) assert len(content_props[0]) > 0 assertNotEquals('/', content_props[0]) # Test the call's Stream's properties call_stream = make_channel_proxy(conn, content_props[0], 'Call.Stream.Interface.Media.Draft') stream_props = call_stream.GetAll(cs.CALL_STREAM_IFACE_MEDIA, dbus_interface=dbus.PROPERTIES_IFACE) assertEquals(cs.CALL_STREAM_TRANSPORT_GTALK_P2P, stream_props['Transport']) test_stun_server(stream_props['STUNServers'], expected_stun_servers) assertEquals(expected_relays, stream_props['RelayInfo']) assertEquals(True, stream_props['HasServerInfo'])
def worker(jp, q, bus, conn, stream): jp.features.append(ns.JINGLE_TRANSPORT_ICEUDP) jt2 = JingleTest2(jp, conn, q, stream, 'test@localhost', '[email protected]/Foo') jt2.prepare() remote_handle = conn.RequestHandles(cs.HT_CONTACT, ["[email protected]/Foo"])[0] # Remote end calls us node = jp.SetIq(jt2.peer, jt2.jid, [ jp.Jingle(jt2.sid, jt2.peer, 'session-initiate', [ jp.Content('stream1', 'initiator', 'both', jp.Description('audio', [ jp.PayloadType(name, str(rate), str(id), parameters) for (name, id, rate, parameters) in jt2.audio_codecs ]), jp.TransportIceUdp()) ]) ]) stream.send(jp.xml(node)) nc, e = q.expect_many( EventPattern('dbus-signal', signal='NewChannel', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args), EventPattern('dbus-signal', signal='NewSessionHandler'), ) path = nc.args[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'StreamedMedia') hrggh = chan.ListProperties(dbus_interface=cs.TP_AWKWARD_PROPERTIES) id = [x for x, name, _, _ in hrggh if name == 'nat-traversal'][0] nrgrg = chan.GetProperties([id], dbus_interface=cs.TP_AWKWARD_PROPERTIES) _, nat_traversal = nrgrg[0] assertEquals('ice-udp', nat_traversal) session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() chan.Group.AddMembers([conn.GetSelfHandle()], 'accepted') # S-E gets notified about a newly-created stream e = q.expect('dbus-signal', signal='NewStreamHandler') stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') stream_handler.NewNativeCandidate("fake", jt2.get_remote_transports_dbus()) stream_handler.Ready(jt2.get_audio_codecs_dbus()) stream_handler.StreamState(2) # First one is transport-info e = q.expect('stream-iq', predicate=jp.action_predicate('transport-info')) transport_stanza = xpath.queryForNodes( "/iq/jingle/content/transport[@xmlns='%s']" % ns.JINGLE_TRANSPORT_ICEUDP, e.stanza)[0] # username assertEquals(transport_stanza['ufrag'], jt2.remote_transports[0][7]) # password assertEquals(transport_stanza['pwd'], jt2.remote_transports[0][8]) children = list(transport_stanza.elements()) assertLength(1, children) stream.send(jp.xml(jp.ResultIq('test@localhost', e.stanza, []))) # Set codec intersection so gabble can accept the session stream_handler.SupportedCodecs(jt2.get_audio_codecs_dbus()) # Second one is session-accept e = q.expect('stream-iq', predicate=jp.action_predicate('session-accept')) assert xpath.queryForNodes("/iq/jingle/content/transport[@xmlns='%s']" % ns.JINGLE_TRANSPORT_ICEUDP, e.stanza) stream.send(jp.xml(jp.ResultIq('test@localhost', e.stanza, []))) # Connected! Blah, blah, ... jt2.terminate() e = q.expect('dbus-signal', signal='Close')
def create_ft_channel(self): self.channel = make_channel_proxy(self.conn, self.ft_path, 'Channel') self.ft_channel = make_channel_proxy(self.conn, self.ft_path, 'Channel.Type.FileTransfer') self.ft_props = dbus.Interface(self.bus.get_object( self.conn.object.bus_name, self.ft_path), PROPERTIES_IFACE)
def test(jp, q, bus, conn, stream, peer='[email protected]/Foo'): jt = JingleTest2(jp, conn, q, stream, 'test@localhost', peer) jt.prepare() self_handle = conn.GetSelfHandle() remote_handle = conn.RequestHandles(cs.HT_CONTACT, [jt.peer])[0] # Remote end calls us jt.incoming_call() # If this is a Jingle dialect that supports it, Gabble should send a # <ringing/> notification when it gets the session-initiate until Telepathy # has a way for the UI to do this. # https://bugs.freedesktop.org/show_bug.cgi?id=21964 ringing_event = jp.rtp_info_event_list("ringing") if jp.dialect == 'gtalk-v0.4': # With gtalk4, apparently we have to send transport-accept immediately, # not even just before we send our transport-info. wjt tested this, and # indeed if we don't send this for incoming calls, the call never # connects. ta_event = [ EventPattern('stream-iq', predicate=lambda x: xpath.queryForNodes("/iq/session[@type='transport-accept']", x.stanza)), ] else: ta_event = [] nc, e = q.expect_many( EventPattern('dbus-signal', signal='NewChannel', predicate=lambda e: cs.CHANNEL_TYPE_CONTACT_LIST not in e.args), EventPattern('dbus-signal', signal='NewSessionHandler'), *(ringing_event + ta_event) )[0:2] path, ct, ht, h, _ = nc.args assert ct == cs.CHANNEL_TYPE_STREAMED_MEDIA, ct assert ht == cs.HT_CONTACT, ht assert h == remote_handle, h media_chan = make_channel_proxy(conn, path, 'Channel.Interface.Group') media_iface = make_channel_proxy(conn, path, 'Channel.Type.StreamedMedia') # S-E was notified about new session handler, and calls Ready on it assert e.args[1] == 'rtp' session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() nsh_event = q.expect('dbus-signal', signal='NewStreamHandler') # S-E gets notified about a newly-created stream stream_handler = make_channel_proxy(conn, nsh_event.args[0], 'Media.StreamHandler') streams = media_iface.ListStreams() assertLength(1, streams) stream_id, stream_handle, stream_type, _, stream_direction, pending_flags =\ streams[0] assertEquals(remote_handle, stream_handle) assertEquals(cs.MEDIA_STREAM_TYPE_AUDIO, stream_type) assertEquals(cs.MEDIA_STREAM_DIRECTION_RECEIVE, stream_direction) assertEquals(cs.MEDIA_STREAM_PENDING_LOCAL_SEND, pending_flags) # Exercise channel properties channel_props = media_chan.GetAll( cs.CHANNEL, dbus_interface=dbus.PROPERTIES_IFACE) assertEquals(remote_handle, channel_props['TargetHandle']) assertEquals(cs.HT_CONTACT, channel_props['TargetHandleType']) assertEquals((cs.HT_CONTACT, remote_handle), media_chan.GetHandle(dbus_interface=cs.CHANNEL)) assertEquals(jt.peer_bare_jid, channel_props['TargetID']) assertEquals(jt.peer_bare_jid, channel_props['InitiatorID']) assertEquals(remote_handle, channel_props['InitiatorHandle']) assertEquals(False, channel_props['Requested']) group_props = media_chan.GetAll( cs.CHANNEL_IFACE_GROUP, dbus_interface=dbus.PROPERTIES_IFACE) assert group_props['SelfHandle'] == self_handle, \ (group_props['SelfHandle'], self_handle) flags = group_props['GroupFlags'] assert flags & cs.GF_PROPERTIES, flags # Changing members in any way other than adding or removing yourself is # meaningless for incoming calls, and the flags need not be sent to change # your own membership. assert not flags & cs.GF_CAN_ADD, flags assert not flags & cs.GF_CAN_REMOVE, flags assert not flags & cs.GF_CAN_RESCIND, flags assert group_props['Members'] == [remote_handle], group_props['Members'] assert group_props['RemotePendingMembers'] == [], \ group_props['RemotePendingMembers'] # We're local pending because remote_handle invited us. assert group_props['LocalPendingMembers'] == \ [(self_handle, remote_handle, cs.GC_REASON_INVITED, '')], \ unwrap(group_props['LocalPendingMembers']) streams = media_chan.ListStreams( dbus_interface=cs.CHANNEL_TYPE_STREAMED_MEDIA) assert len(streams) == 1, streams assert len(streams[0]) == 6, streams[0] # streams[0][0] is the stream identifier, which in principle we can't # make any assertion about (although in practice it's probably 1) assert streams[0][1] == remote_handle, (streams[0], remote_handle) assert streams[0][2] == cs.MEDIA_STREAM_TYPE_AUDIO, streams[0] # We haven't connected yet assert streams[0][3] == cs.MEDIA_STREAM_STATE_DISCONNECTED, streams[0] # In Gabble, incoming streams start off with remote send enabled, and # local send requested assert streams[0][4] == cs.MEDIA_STREAM_DIRECTION_RECEIVE, streams[0] assert streams[0][5] == cs.MEDIA_STREAM_PENDING_LOCAL_SEND, streams[0] # Connectivity checks happen before we have accepted the call stream_handler.NewNativeCandidate("fake", jt.get_remote_transports_dbus()) stream_handler.Ready(jt.get_audio_codecs_dbus()) stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED) stream_handler.SupportedCodecs(jt.get_audio_codecs_dbus()) # peer gets the transport e = q.expect('stream-iq', predicate=jp.action_predicate('transport-info')) assertEquals(jt.peer, e.query['initiator']) if jp.dialect in ['jingle-v0.15', 'jingle-v0.31']: content = xpath.queryForNodes('/iq/jingle/content', e.stanza)[0] assertEquals('initiator', content['creator']) stream.send(make_result_iq(stream, e.stanza)) # At last, accept the call media_chan.AddMembers([self_handle], 'accepted') # Call is accepted, we become a member, and the stream that was pending # local send is now sending. memb, acc, _, _, _ = q.expect_many( EventPattern('dbus-signal', signal='MembersChanged', args=[u'', [self_handle], [], [], [], self_handle, cs.GC_REASON_NONE]), EventPattern('stream-iq', predicate=jp.action_predicate('session-accept')), EventPattern('dbus-signal', signal='SetStreamSending', args=[True]), EventPattern('dbus-signal', signal='SetStreamPlaying', args=[True]), EventPattern('dbus-signal', signal='StreamDirectionChanged', args=[stream_id, cs.MEDIA_STREAM_DIRECTION_BIDIRECTIONAL, 0]), ) stream.send(make_result_iq(stream, acc.stanza)) # Also, if this is a Jingle dialect that supports it, Gabble should send an # <active/> notification when the session-accept is acked (until the # Telepathy spec lets the UI say it's not ringing any more). active_event = jp.rtp_info_event("active") if active_event is not None: q.expect_many(active_event) # we are now both in members members = media_chan.GetMembers() assert set(members) == set([self_handle, remote_handle]), members # Connected! Blah, blah, ... # 'Nuff said jt.terminate() q.expect('dbus-signal', signal='Closed', path=path)
def test(jp, q, bus, conn, stream, busy): remote_jid = '[email protected]/Foo' jt = JingleTest2(jp, conn, q, stream, 'test@localhost', remote_jid) jt.prepare() self_handle = conn.GetSelfHandle() remote_handle = conn.RequestHandles(cs.HT_CONTACT, [remote_jid])[0] # Remote end calls us jt.incoming_call() # FIXME: these signals are not observable by real clients, since they # happen before NewChannels. # The caller is in members e = q.expect('dbus-signal', signal='MembersChanged', args=[u'', [remote_handle], [], [], [], 0, 0]) # We're pending because of remote_handle e = q.expect('dbus-signal', signal='MembersChanged', args=[u'', [], [], [self_handle], [], remote_handle, cs.GC_REASON_INVITED]) # S-E gets notified about new session handler, and calls Ready on it e = q.expect('dbus-signal', signal='NewSessionHandler') assert e.args[1] == 'rtp' session_handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') session_handler.Ready() media_chan = make_channel_proxy(conn, e.path, 'Channel.Interface.Group') # Exercise channel properties channel_props = media_chan.GetAll(cs.CHANNEL, dbus_interface=cs.PROPERTIES_IFACE) assert channel_props['TargetHandle'] == remote_handle assert channel_props['TargetHandleType'] == 1 assert channel_props['TargetID'] == '*****@*****.**' assert channel_props['Requested'] == False assert channel_props['InitiatorID'] == '*****@*****.**' assert channel_props['InitiatorHandle'] == remote_handle if busy: # First, try using a reason that doesn't make any sense call_async(q, media_chan, 'RemoveMembersWithReason', [self_handle], "what kind of a reason is Separated?!", cs.GC_REASON_SEPARATED) e = q.expect('dbus-error', method='RemoveMembersWithReason') assert e.error.get_dbus_name() == cs.INVALID_ARGUMENT # Now try a more sensible reason. media_chan.RemoveMembersWithReason([self_handle], "which part of 'Do Not Disturb' don't you understand?", cs.GC_REASON_BUSY) else: media_chan.RemoveMembers([self_handle], 'rejected') iq, mc, _ = q.expect_many( EventPattern('stream-iq', predicate=jp.action_predicate('session-terminate')), EventPattern('dbus-signal', signal='MembersChanged'), EventPattern('dbus-signal', signal='Closed'), ) _, added, removed, lp, rp, actor, reason = mc.args assert added == [], added assert set(removed) == set([self_handle, remote_handle]), \ (removed, self_handle, remote_handle) assert lp == [], lp assert rp == [], rp assert actor == self_handle, (actor, self_handle) if busy: assert reason == cs.GC_REASON_BUSY, reason else: assert reason == cs.GC_REASON_NONE, reason if jp.is_modern_jingle(): jingle = iq.query if busy: r = "/jingle/reason/busy" else: r = "/jingle/reason/cancel" assert xpath.queryForNodes(r, jingle) is not None, (jingle.toXml(), r)
def test(q, bus, conn): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[0L, 0L]) basic_txt = { "txtvers": "1", "status": "avail" } contact_name = "test-text-channel@" + get_host_name() listener, port = setup_stream_listener(q, contact_name) announcer = AvahiAnnouncer(contact_name, "_presence._tcp", port, basic_txt) handle = wait_for_contact_in_publish(q, bus, conn, contact_name) t = conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: handle})[0] text_channel = make_channel_proxy(conn, t, "Channel.Type.Text") text_channel.Send(cs.MT_NORMAL, INCOMING_MESSAGE) e = q.expect('incoming-connection', listener = listener) incoming = e.connection e = q.expect('stream-message', connection = incoming) assert e.message_type == "chat" body = xpath.queryForNodes("/message/body", e.stanza ) assert map(str, body) == [ INCOMING_MESSAGE ] # drop the connection incoming.transport.loseConnection() # Now send a message to salut self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") self_handle_name = conn.Properties.Get(cs.CONN, "SelfID") AvahiListener(q).listen_for_service("_presence._tcp") e = q.expect('service-added', name = self_handle_name, protocol = avahi.PROTO_INET) service = e.service service.resolve() e = q.expect('service-resolved', service = service) outbound = connect_to_stream(q, contact_name, self_handle_name, str(e.pt), e.port) e = q.expect('connection-result') assert e.succeeded, e.reason e = q.expect('stream-opened', connection = outbound) # connected to salut, now send a message message = domish.Element(('', 'message')) message['type'] = "chat" message.addElement('body', content=OUTGOING_MESSAGE) e.connection.send(message) e = q.expect('dbus-signal', signal='Received') assert e.args[2] == handle assert e.args[3] == cs.MT_NORMAL assert e.args[5] == OUTGOING_MESSAGE