def check_and_accept_offer (q, bus, conn, self_handle, remote_handle, content, codecs, offer_path = None, check_codecs_changed = True ): (path, handle, remote_codecs ) = content.Get(cs.CALL_CONTENT_IFACE_MEDIA, "CodecOffer", dbus_interface=dbus.PROPERTIES_IFACE) if offer_path != None: assertEquals (offer_path, path) assertNotEquals ("/", path) offer = bus.get_object (conn.bus_name, path) remote_codecs_property = offer.Get (cs.CALL_CONTENT_CODECOFFER, "RemoteContactCodecs", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals (remote_codecs, remote_codecs_property) offer.Accept (codecs, dbus_interface=cs.CALL_CONTENT_CODECOFFER) current_codecs = content.Get(cs.CALL_CONTENT_IFACE_MEDIA, "ContactCodecMap", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals (codecs, current_codecs[self_handle]) if check_codecs_changed: o = q.expect ('dbus-signal', signal='CodecsChanged')
def run_cancel_test(q, bus, conn, stream): jp = JingleProtocol031 () jt = JingleTest2(jp, conn, q, stream, 'test@localhost', muc + '/bob') jt.prepare() for x in xrange (0, 10): (path, props) = create_muji_channel (q, conn, stream, muc, x > 0) channel = bus.get_object (conn.bus_name, path) contents = channel.Get (cs.CHANNEL_TYPE_CALL, "Contents", dbus_interface = dbus.PROPERTIES_IFACE) content = bus.get_object (conn.bus_name, contents[0]) md = jt.get_call_audio_md_dbus() check_and_accept_offer (q, bus, conn, content, md) # Accept the channel channel.Accept() e = q.expect('stream-presence', to = muc + "/test") mujinode = xpath.queryForNodes("/presence/muji/preparing", e.stanza) assertNotEquals(None, mujinode) channel.Hangup(0, "", "", dbus_interface=cs.CHANNEL_TYPE_CALL) e = q.expect('stream-presence', to = muc + "/test") mujinode = xpath.queryForNodes("/presence/muji/preparing", e.stanza) assertEquals(None, mujinode) if x % 2 == 0: channel.Close()
def test_invisible_on_connect_fail(q, bus, conn, stream): props = conn.Properties.GetAll(cs.CONN_IFACE_SIMPLE_PRESENCE) assertNotEquals({}, props['Statuses']) presence_event_pattern = EventPattern('stream-presence') q.forbid_events([presence_event_pattern]) conn.SimplePresence.SetPresence("hidden", "") conn.Connect() stream.handle_get_all_privacy_lists(q, bus, conn) create_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') # Check its name assertNotEquals([], xpath.queryForNodes('/query/list/item/presence-out', create_list.query)) acknowledge_iq(stream, create_list.stanza) set_active = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') active = xpath.queryForNodes('//active', set_active.query)[0] assertEquals('invisible', active['name']) send_error_reply(stream, set_active.stanza) q.unforbid_events([presence_event_pattern]) # Darn! At least we should have our presence set to DND. q.expect_many( EventPattern('dbus-signal', signal='PresencesChanged', interface=cs.CONN_IFACE_SIMPLE_PRESENCE, args=[{1: (6, 'dnd', '')}]), EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]))
def check_and_accept_offer( q, bus, conn, self_handle, content, codecs, remote_handle=None, offer_path=None, codecs_changed=True ): [path, handle, remote_codecs] = content.Get( cs.CALL_CONTENT_IFACE_MEDIA, "CodecOffer", dbus_interface=dbus.PROPERTIES_IFACE ) if offer_path != None: assertEquals(offer_path, path) assertNotEquals("/", path) offer = bus.get_object(conn.bus_name, path) codecmap_property = offer.Get( cs.CALL_CONTENT_CODECOFFER, "RemoteContactCodecs", dbus_interface=dbus.PROPERTIES_IFACE ) assertEquals(remote_codecs, codecmap_property) offer.Accept(codecs, dbus_interface=cs.CALL_CONTENT_CODECOFFER) current_codecs = content.Get(cs.CALL_CONTENT_IFACE_MEDIA, "ContactCodecMap", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals(codecs, current_codecs[self_handle]) if codecs_changed: o = q.expect("dbus-signal", signal="CodecsChanged") codecs_should_be = {self_handle: codecs} if remote_handle is not None: codecs_should_be[remote_handle] = codecs assertEquals([codecs_should_be, []], o.args)
def test_create_invisible_list(q, bus, conn, stream): conn.SimplePresence.SetPresence("away", "") conn.Connect() stream.handle_get_all_privacy_lists(q, bus, conn) get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='get') list_node = xpath.queryForNodes('//list', get_list.query)[0] assertEquals('invisible', list_node['name']) error = domish.Element((None, 'error')) error['type'] = 'cancel' error.addElement((ns.STANZA, 'item-not-found')) send_error_reply (stream, get_list.stanza, error) create_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') list_node = xpath.queryForNodes('//list', create_list.query)[0] assertEquals('invisible', list_node['name']) assertNotEquals([], xpath.queryForNodes('/query/list/item/presence-out', create_list.query)) acknowledge_iq(stream, create_list.stanza) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) assertContains("hidden", conn.Properties.Get(cs.CONN_IFACE_SIMPLE_PRESENCE, "Statuses"))
def test_invisible_on_connect(q, bus, conn, stream): props = conn.Properties.GetAll(cs.CONN_IFACE_SIMPLE_PRESENCE) assertNotEquals({}, props['Statuses']) presence_event_pattern = EventPattern('stream-presence') q.forbid_events([presence_event_pattern]) conn.SimplePresence.SetPresence("hidden", "") conn.Connect() stream.handle_get_all_privacy_lists(q, bus, conn, ['invisible']) get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='get') list_node = xpath.queryForNodes('//list', get_list.query)[0] assertEquals('invisible', list_node['name']) stream.send_privacy_list(get_list.stanza, [elem('item', action='deny', order='1')(elem('presence-out'))]) set_active = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') active = xpath.queryForNodes('//active', set_active.query)[0] assertEquals('invisible', active['name']) acknowledge_iq(stream, set_active.stanza) q.unforbid_events([presence_event_pattern]) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])
def test_invisible_on_connect_fail_no_list(q, bus, conn, stream): props = conn.Properties.GetAll(cs.CONN_IFACE_SIMPLE_PRESENCE) assertNotEquals({}, props['Statuses']) presence_event_pattern = EventPattern('stream-presence') q.forbid_events([presence_event_pattern]) conn.SimplePresence.SetPresence("hidden", "") conn.Connect() stream.handle_get_all_privacy_lists(q, bus, conn) get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='get') list_node = xpath.queryForNodes('//list', get_list.query)[0] assertEquals('invisible', list_node['name']) send_error_reply(stream, get_list.stanza) q.unforbid_events([presence_event_pattern]) # Darn! At least we should have our presence set to DND. q.expect_many( EventPattern('dbus-signal', signal='PresencesChanged', interface=cs.CONN_IFACE_SIMPLE_PRESENCE, args=[{1: (6, 'dnd', '')}]), EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])) # 'hidden' should not be an available status. assertDoesNotContain("hidden", conn.Properties.Get(cs.CONN_IFACE_SIMPLE_PRESENCE, "Statuses"))
def test_invisible_on_connect(q, bus, conn, stream): props = conn.Properties.GetAll(cs.CONN_IFACE_SIMPLE_PRESENCE) assertNotEquals({}, props['Statuses']) presence_event_pattern = EventPattern('stream-presence') q.forbid_events([presence_event_pattern]) conn.SimplePresence.SetPresence("hidden", "") conn.Connect() stream.handle_get_all_privacy_lists(q, bus, conn, ['invisible']) get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='get') list_node = xpath.queryForNodes('//list', get_list.query)[0] assertEquals('invisible', list_node['name']) stream.send_privacy_list( get_list.stanza, [elem('item', action='deny', order='1')(elem('presence-out'))]) set_active = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') active = xpath.queryForNodes('//active', set_active.query)[0] assertEquals('invisible', active['name']) acknowledge_iq(stream, set_active.stanza) q.unforbid_events([presence_event_pattern]) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])
def test_create_invisible_list_failed(q, bus, conn, stream): conn.SimplePresence.SetPresence("away", "") conn.Connect() stream.handle_get_all_privacy_lists(q, bus, conn) get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='get') list_node = xpath.queryForNodes('//list', get_list.query)[0] assertEquals('invisible', list_node['name']) error = domish.Element((None, 'error')) error['type'] = 'cancel' error.addElement((ns.STANZA, 'item-not-found')) send_error_reply(stream, get_list.stanza, error) create_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') list_node = xpath.queryForNodes('//list', create_list.query)[0] assertEquals('invisible', list_node['name']) assertNotEquals([], xpath.queryForNodes('/query/list/item/presence-out', create_list.query)) send_error_reply(stream, create_list.stanza) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) assertDoesNotContain( "hidden", conn.Properties.Get(cs.CONN_IFACE_SIMPLE_PRESENCE, "Statuses"))
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_MEDIA_DESCRIPTION, "Codecs", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals(remote_md[cs.CALL_CONTENT_MEDIA_DESCRIPTION + '.Codecs'], codecmap_property) offer.Accept(md, dbus_interface=cs.CALL_CONTENT_MEDIA_DESCRIPTION) 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 check_and_accept_offer (q, bus, conn, content, md, in_remote_handle = 0, offer_path = None, md_changed = True): [path, remote_md] = content.Get(cs.CALL_CONTENT_IFACE_MEDIA, "MediaDescriptionOffer", dbus_interface=dbus.PROPERTIES_IFACE) remote_handle = remote_md[cs.CALL_CONTENT_MEDIADESCRIPTION + '.RemoteContact'] if in_remote_handle != 0: assertEquals(remote_handle, in_remote_handle) if offer_path != None: assertEquals (offer_path, path) assertNotEquals ("/", path) offer = bus.get_object (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[remote_handle]) if md_changed: o = q.expect ('dbus-signal', signal='LocalMediaDescriptionChanged') assertEquals ([md], o.args)
def add_content(self, content_path, initial=False, incoming=None): if initial: incoming = self.incoming else: assert incoming is not None content = self.bus.get_object(self.conn.bus_name, content_path) content_props = content.GetAll(cs.CALL_CONTENT) if initial: assertEquals(cs.CALL_DISPOSITION_INITIAL, content_props['Disposition']) if content_props['Type'] == cs.MEDIA_STREAM_TYPE_AUDIO: assertEquals(self.initial_audio_content_name, content_props['Name']) elif content_props['Type'] == cs.MEDIA_STREAM_TYPE_VIDEO: assertEquals(self.initial_video_content_name, content_props['Name']) else: assert Fale else: assertEquals(cs.CALL_DISPOSITION_NONE, content_props['Disposition']) content.media_type = content_props['Type'] cmedia_props = content.GetAll(cs.CALL_CONTENT_IFACE_MEDIA) assertLength(0, cmedia_props['RemoteMediaDescriptions']) assertLength(0, cmedia_props['LocalMediaDescriptions']) if incoming: assertNotEquals('/', cmedia_props['MediaDescriptionOffer'][0]) else: assertNotEquals('/', cmedia_props['MediaDescriptionOffer'][0]) assertEquals(cs.CALL_CONTENT_PACKETIZATION_RTP, cmedia_props['Packetization']) assertEquals(cs.CALL_SENDING_STATE_NONE, cmedia_props['CurrentDTMFState']) self.contents.append(content) self.__add_stream(content, content_props['Streams'][0], initial, incoming) if incoming: md = self.bus.get_object(self.conn.bus_name, cmedia_props['MediaDescriptionOffer'][0]) md.Accept(self.context.get_audio_md_dbus(self.remote_handle)) o = self.q.expect_many( EventPattern('dbus-signal', signal='MediaDescriptionOfferDone'), EventPattern('dbus-signal', signal='LocalMediaDescriptionChanged'), EventPattern('dbus-signal', signal='RemoteMediaDescriptionsChanged')) return content
def test(q, bus, conn, stream): statuses = conn.Properties.Get(cs.CONN_IFACE_SIMPLE_PRESENCE, 'Statuses') # testbusy and testaway are provided by test plugin assertContains('testbusy', statuses) assertContains('testaway', statuses) assertEquals(statuses['testbusy'][0], cs.PRESENCE_BUSY) assertEquals(statuses['testaway'][0], cs.PRESENCE_AWAY) conn.SimplePresence.SetPresence('testbusy', '') conn.Connect() # ... gabble asks for all the available lists on the server ... stream.handle_get_all_privacy_lists( q, bus, conn, lists=["foo-list", "test-busy-list", "bar-list"]) # ... gabble checks whether there's usable invisible list on the server ... get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='get') list_node = xpath.queryForNodes('//list', get_list.query)[0] assertEquals('invisible', list_node['name']) error = domish.Element((None, 'error')) error['type'] = 'cancel' error.addElement((ns.STANZA, 'item-not-found')) send_error_reply(stream, get_list.stanza, error) # ... since there is none, Gabble creates it ... create_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') list_node = xpath.queryForNodes('//list', create_list.query)[0] assertEquals('invisible', list_node['name']) assertNotEquals([], xpath.queryForNodes('/query/list/item/presence-out', create_list.query)) acknowledge_iq(stream, create_list.stanza) get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') list_node = xpath.queryForNodes('//active', get_list.query)[0] # ... and then activates the one linked with the requested status # Note: testbusy status is linked to test-busy-list by test plugin assertEquals('test-busy-list', list_node['name']) acknowledge_iq(stream, get_list.stanza) q.expect('dbus-signal', signal='PresencesChanged', args=[{ 1: (cs.PRESENCE_BUSY, u'testbusy', '') }]) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) # ... testaway is not supposed to be settable on us call_async(q, conn.SimplePresence, 'SetPresence', 'testaway', '') q.expect('dbus-error', method='SetPresence', name=cs.INVALID_ARGUMENT)
def test(q, bus, conn, stream): path, _ = conn.Future.EnsureSidecar(CONSOLE_PLUGIN_IFACE) console = ProxyWrapper(bus.get_object(conn.bus_name, path), CONSOLE_PLUGIN_IFACE) assert not console.Properties.Get(CONSOLE_PLUGIN_IFACE, 'SpewStanzas') es = [ EventPattern('dbus-signal', signal='StanzaReceived'), EventPattern('dbus-signal', signal='StanzaSent'), ] q.forbid_events(es) call_async(q, console, 'SendIQ', 'get', STACY, '<coffee xmlns="urn:unimaginative"/>') e = q.expect('stream-iq', iq_type='get', query_ns='urn:unimaginative', query_name='coffee') acknowledge_iq(stream, e.stanza) e = q.expect('dbus-return', method='SendIQ') type_, body = e.value assertEquals('result', type_) # We just assume the body works. # Turn on signalling incoming and outgoing stanzas console.Properties.Set(CONSOLE_PLUGIN_IFACE, 'SpewStanzas', True) sync_dbus(bus, q, conn) q.unforbid_events(es) send_unrecognised_get(q, stream) e = q.expect('dbus-signal', signal='StanzaReceived') xml, = e.args assertContains('<iq', xml) assertContains('<dont-handle-me-bro', xml) signal = q.expect('dbus-signal', signal='StanzaSent') assertContains('service-unavailable', signal.args[0]) # Turn off spewing out stanzas; check it works. console.Properties.Set(CONSOLE_PLUGIN_IFACE, 'SpewStanzas', False) q.forbid_events(es) send_unrecognised_get(q, stream) sync_dbus(bus, q, conn) # Try sending just any old stanza console.SendStanza(''' <message to='%(stacy)s' type='headline'> <body> Hi sis. </body> </message>''' % { 'stacy': STACY }) e = q.expect('stream-message', to=STACY, message_type='headline') # Wocky fills in xmlns='' for us if we don't specify a namespace... great. # So this means <message/> gets sent as <message xmlns=''/> and the server # kicks us off. assertNotEquals('', e.stanza.uri)
def test(q, bus, conn, stream): self_presence = q.expect('stream-presence') c = xpath.queryForNodes('/presence/c', self_presence.stanza)[0] jid = '[email protected]/omg' # Gabble shouldn't send any disco requests to our contact during this test. q.forbid_events([ EventPattern('stream-iq', to=jid, iq_type='get', query_ns=ns.DISCO_INFO), ]) # Check that Gabble doesn't disco other clients with the same caps hash. p = make_presence(jid, caps={'node': c['node'], 'hash': c['hash'], 'ver': c['ver'], }) stream.send(p) sync_stream(q, stream) # Check that Gabble doesn't disco its own ext='' bundles (well, its own # bundles as advertised by Gabbles that don't do hashed caps) p = make_presence(jid, caps={'node': c['node'], 'ver': c['ver'], # omitting hash='' so Gabble doesn't ignore ext='' 'ext': 'voice-v1 video-v1', }) stream.send(p) sync_stream(q, stream) # Advertise some different capabilities, to change our own caps hash. add = [(cs.CHANNEL_TYPE_STREAMED_MEDIA, 2L**32-1), (cs.CHANNEL_TYPE_STREAM_TUBE, 2L**32-1), (cs.CHANNEL_TYPE_STREAM_TUBE, 2L**32-1)] remove = [] caps = conn.Capabilities.AdvertiseCapabilities(add, remove) self_presence = q.expect('stream-presence') c_ = xpath.queryForNodes('/presence/c', self_presence.stanza)[0] assertNotEquals(c['ver'], c_['ver']) # But then someone asks us for our old caps iq = IQ(stream, 'get') iq['from'] = jid query = iq.addElement((ns.DISCO_INFO, 'query')) query['node'] = c['node'] + '#' + c['ver'] stream.send(iq) # Gabble should still know what they are, and reply. This is actually quite # important: there's a bug in iChat where if you return an error to a disco # query, it just asks again, and again, and again... reply = q.expect('stream-iq', to=jid) assertEquals('result', reply.iq_type)
def check_offer (bus, conn, content): [path, md] = content.Get(cs.CALL_CONTENT_IFACE_MEDIA, "MediaDescriptionOffer", dbus_interface=dbus.PROPERTIES_IFACE) assertNotEquals ("/", path) offer = bus.get_object (conn.bus_name, path) md_property = offer.Get (cs.CALL_CONTENT_MEDIA_DESCRIPTION, "Codecs", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals (md[cs.CALL_CONTENT_MEDIA_DESCRIPTION + ".Codecs"], md_property)
def check_offer (bus, conn, content): [path, handle, codecs] = content.Get(cs.CALL_CONTENT_IFACE_MEDIA, "CodecOffer", dbus_interface=dbus.PROPERTIES_IFACE) assertNotEquals ("/", path) offer = bus.get_object (conn.bus_name, path) codecs_property = offer.Get (cs.CALL_CONTENT_CODECOFFER, "RemoteContactCodecs", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals (codecs, codecs_property)
def add_content(self, content_path, initial = False, incoming = None): if initial: incoming = self.incoming else: assert incoming is not None content = self.bus.get_object (self.conn.bus_name, content_path) content_props = content.GetAll(cs.CALL_CONTENT) if initial: assertEquals(cs.CALL_DISPOSITION_INITIAL, content_props['Disposition']) if content_props['Type'] == cs.MEDIA_STREAM_TYPE_AUDIO: assertEquals(self.initial_audio_content_name, content_props['Name']) elif content_props['Type'] == cs.MEDIA_STREAM_TYPE_VIDEO: assertEquals(self.initial_video_content_name, content_props['Name']) else: assert Fale else: assertEquals(cs.CALL_DISPOSITION_NONE, content_props['Disposition']) content.media_type = content_props['Type'] cmedia_props = content.GetAll(cs.CALL_CONTENT_IFACE_MEDIA) assertLength(0, cmedia_props['RemoteMediaDescriptions']) assertLength(0, cmedia_props['LocalMediaDescriptions']) if incoming: assertNotEquals('/', cmedia_props['MediaDescriptionOffer'][0]) else: assertNotEquals('/', cmedia_props['MediaDescriptionOffer'][0]) assertEquals(cs.CALL_CONTENT_PACKETIZATION_RTP, cmedia_props['Packetization']) assertEquals(cs.CALL_SENDING_STATE_NONE, cmedia_props['CurrentDTMFState']) self.contents.append(content) self.__add_stream(content, content_props['Streams'][0], initial, incoming) if incoming: md = self.bus.get_object (self.conn.bus_name, cmedia_props['MediaDescriptionOffer'][0]) md.Accept(self.context.get_audio_md_dbus(self.remote_handle)) o = self.q.expect_many( EventPattern('dbus-signal', signal='MediaDescriptionOfferDone'), EventPattern('dbus-signal', signal='LocalMediaDescriptionChanged'), EventPattern('dbus-signal', signal='RemoteMediaDescriptionsChanged')) return content
def test_invisible_on_connect_fail_invalid_list(q, bus, conn, stream, really_invalid=False): props = conn.Properties.GetAll(cs.CONN_IFACE_SIMPLE_PRESENCE) assertNotEquals({}, props['Statuses']) presence_event_pattern = EventPattern('stream-presence') q.forbid_events([presence_event_pattern]) conn.SimplePresence.SetPresence("hidden", "") conn.Connect() stream.handle_get_all_privacy_lists(q, bus, conn, ['invisible']) get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='get') list_node = xpath.queryForNodes('//list', get_list.query)[0] assertEquals('invisible', list_node['name']) if really_invalid: # At one point Gabble would crash if the reply was of type 'result' but # wasn't well-formed. acknowledge_iq(stream, get_list.stanza) else: stream.send_privacy_list(get_list.stanza, [elem('item', type='jid', value='*****@*****.**', action='allow', order='1')(elem('presence-out')), elem('item', action='deny', order='2')(elem('presence-out'))]) create_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') created = xpath.queryForNodes('//list', create_list.stanza)[0] assertEquals(created["name"], 'invisible-gabble') acknowledge_iq (stream, create_list.stanza) q.unforbid_events([presence_event_pattern]) activate_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') active = xpath.queryForNodes('//active', activate_list.stanza)[0] assertEquals(active["name"], 'invisible-gabble') acknowledge_iq (stream, activate_list.stanza) q.expect_many( EventPattern('dbus-signal', signal='PresencesChanged', interface=cs.CONN_IFACE_SIMPLE_PRESENCE, args=[{1: (5, 'hidden', '')}]), EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])) # 'hidden' should not be an available status. assertContains("hidden", conn.Properties.Get(cs.CONN_IFACE_SIMPLE_PRESENCE, "Statuses"))
def test(q, bus, conn, stream): statuses = conn.Properties.Get(cs.CONN_IFACE_SIMPLE_PRESENCE, 'Statuses') # testbusy and testaway are provided by test plugin assertContains('testbusy', statuses) assertContains('testaway', statuses) assertEquals(statuses['testbusy'][0], cs.PRESENCE_BUSY) assertEquals(statuses['testaway'][0], cs.PRESENCE_AWAY) conn.SimplePresence.SetPresence('testbusy', '') conn.Connect() # ... gabble asks for all the available lists on the server ... stream.handle_get_all_privacy_lists(q, bus, conn, lists=["foo-list", "test-busy-list", "bar-list"]) # ... gabble checks whether there's usable invisible list on the server ... get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='get') list_node = xpath.queryForNodes('//list', get_list.query)[0] assertEquals('invisible', list_node['name']) error = domish.Element((None, 'error')) error['type'] = 'cancel' error.addElement((ns.STANZA, 'item-not-found')) send_error_reply (stream, get_list.stanza, error) # ... since there is none, Gabble creates it ... create_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') list_node = xpath.queryForNodes('//list', create_list.query)[0] assertEquals('invisible', list_node['name']) assertNotEquals([], xpath.queryForNodes('/query/list/item/presence-out', create_list.query)) acknowledge_iq(stream, create_list.stanza) get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') list_node = xpath.queryForNodes('//active', get_list.query)[0] # ... and then activates the one linked with the requested status # Note: testbusy status is linked to test-busy-list by test plugin assertEquals('test-busy-list', list_node['name']) acknowledge_iq(stream, get_list.stanza) q.expect('dbus-signal', signal='PresencesChanged', args=[{1L: (cs.PRESENCE_BUSY, u'testbusy', '')}])
def test(q, bus, conn, stream): props = conn.Properties.GetAll(cs.CONN_IFACE_SIMPLE_PRESENCE) assertNotEquals({}, props['Statuses']) conn.SimplePresence.SetPresence("away", "watching bees") conn.Connect() _, presence = q.expect_many( EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]), EventPattern('stream-presence'), ) children = list(presence.stanza.elements()) assertEquals('show', children[0].name) assertEquals('away', children[0].children[0]) assertEquals('status', children[1].name) assertEquals('watching bees', children[1].children[0])
def test_invisible_on_connect(q, bus, conn, stream): props = conn.Properties.GetAll(cs.CONN_IFACE_SIMPLE_PRESENCE) assertNotEquals({}, props['Statuses']) presence_event_pattern = EventPattern('stream-presence') q.forbid_events([presence_event_pattern]) conn.SimplePresence.SetPresence("hidden", "") conn.Connect() event = q.expect('stream-iq', query_name='invisible') acknowledge_iq(stream, event.stanza) q.unforbid_events([presence_event_pattern]) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])
def test_invisible_on_connect_fail(q, bus, conn, stream): props = conn.Properties.GetAll(cs.CONN_IFACE_SIMPLE_PRESENCE) assertNotEquals({}, props['Statuses']) presence_event_pattern = EventPattern('stream-presence') q.forbid_events([presence_event_pattern]) conn.SimplePresence.SetPresence("hidden", "") conn.Connect() stream.handle_get_all_privacy_lists(q, bus, conn) create_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') # Check its name assertNotEquals([], xpath.queryForNodes('/query/list/item/presence-out', create_list.query)) acknowledge_iq(stream, create_list.stanza) set_active = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') active = xpath.queryForNodes('//active', set_active.query)[0] assertEquals('invisible', active['name']) send_error_reply(stream, set_active.stanza) q.unforbid_events([presence_event_pattern]) # Darn! At least we should have our presence set to DND. q.expect_many( EventPattern('dbus-signal', signal='PresencesChanged', interface=cs.CONN_IFACE_SIMPLE_PRESENCE, args=[{ 1: (6, 'dnd', '') }]), EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]))
def test_invisible_on_connect_fail_no_list(q, bus, conn, stream): props = conn.Properties.GetAll(cs.CONN_IFACE_SIMPLE_PRESENCE) assertNotEquals({}, props['Statuses']) presence_event_pattern = EventPattern('stream-presence') q.forbid_events([presence_event_pattern]) conn.SimplePresence.SetPresence("hidden", "") conn.Connect() stream.handle_get_all_privacy_lists(q, bus, conn) get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='get') list_node = xpath.queryForNodes('//list', get_list.query)[0] assertEquals('invisible', list_node['name']) send_error_reply(stream, get_list.stanza) q.unforbid_events([presence_event_pattern]) # Darn! At least we should have our presence set to DND. q.expect_many( EventPattern('dbus-signal', signal='PresencesChanged', interface=cs.CONN_IFACE_SIMPLE_PRESENCE, args=[{ 1: (6, 'dnd', '') }]), EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])) # 'hidden' should not be an available status. assertDoesNotContain( "hidden", conn.Properties.Get(cs.CONN_IFACE_SIMPLE_PRESENCE, "Statuses"))
def test(q, bus, conn, stream): self_presence = q.expect('stream-presence') c = xpath.queryForNodes('/presence/c', self_presence.stanza)[0] jid = '[email protected]/omg' # Gabble shouldn't send any disco requests to our contact during this test. q.forbid_events([ EventPattern('stream-iq', to=jid, iq_type='get', query_ns=ns.DISCO_INFO), ]) # Check that Gabble doesn't disco other clients with the same caps hash. p = make_presence(jid, caps={'node': c['node'], 'hash': c['hash'], 'ver': c['ver'], }) stream.send(p) sync_stream(q, stream) # Check that Gabble doesn't disco its own ext='' bundles (well, its own # bundles as advertised by Gabbles that don't do hashed caps) p = make_presence(jid, caps={'node': c['node'], 'ver': c['ver'], # omitting hash='' so Gabble doesn't ignore ext='' 'ext': 'voice-v1 video-v1', }) stream.send(p) sync_stream(q, stream) conn.ContactCapabilities.UpdateCapabilities([ (cs.CLIENT + '.AbiWord', [ { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.STREAM_TUBE_SERVICE: 'x-abiword' }, { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.STREAM_TUBE_SERVICE: 'x-abiword' }, ], []), (cs.CLIENT + '.KCall', [ { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL }, { 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-udp', cs.CHANNEL_TYPE_CALL + '/video/h264', ]), ]) self_presence = q.expect('stream-presence') c_ = xpath.queryForNodes('/presence/c', self_presence.stanza)[0] assertNotEquals(c['ver'], c_['ver']) for suffix in [c['ver'], 'voice-v1', 'video-v1', 'camera-v1', 'share-v1', 'pmuc-v1'] + list(c_['ext'].split()): # But then someone asks us for our old caps iq = IQ(stream, 'get') iq['from'] = jid query = iq.addElement((ns.DISCO_INFO, 'query')) query['node'] = c['node'] + '#' + suffix stream.send(iq) # Gabble should still know what they are, and reply. This is # actually quite important: there's a bug in iChat where if you # return an error to a disco query, it just asks again, and again, # and again... reply = q.expect('stream-iq', to=jid) assertEquals('result', reply.iq_type)
def test(q, bus, conn, stream): rccs = conn.Properties.Get(cs.CONN_IFACE_REQUESTS, 'RequestableChannelClasses') fixed = { cs.CHANNEL_TYPE: CONSOLE_PLUGIN_IFACE, cs.TARGET_HANDLE_TYPE: cs.HT_NONE, } allowed = [] assertContains((fixed, allowed), rccs) path, _ = conn.Requests.CreateChannel( {cs.CHANNEL_TYPE: CONSOLE_PLUGIN_IFACE}) other_path, _ = conn.Requests.CreateChannel( {cs.CHANNEL_TYPE: CONSOLE_PLUGIN_IFACE}) assertNotEquals(path, other_path) # leave the other one open, to test we don't crash on disconnect console = ProxyWrapper(bus.get_object(conn.bus_name, path), CONSOLE_PLUGIN_IFACE, {'Channel': cs.CHANNEL}) assert not console.Properties.Get(CONSOLE_PLUGIN_IFACE, 'SpewStanzas') es = [ EventPattern('dbus-signal', signal='StanzaReceived'), EventPattern('dbus-signal', signal='StanzaSent'), ] q.forbid_events(es) call_async(q, console, 'SendIQ', 'get', STACY, '<coffee xmlns="urn:unimaginative"/>') e = q.expect('stream-iq', iq_type='get', query_ns='urn:unimaginative', query_name='coffee') acknowledge_iq(stream, e.stanza) e = q.expect('dbus-return', method='SendIQ') type_, body = e.value assertEquals('result', type_) # We just assume the body works. # Turn on signalling incoming and outgoing stanzas console.Properties.Set(CONSOLE_PLUGIN_IFACE, 'SpewStanzas', True) sync_dbus(bus, q, conn) q.unforbid_events(es) send_unrecognised_get(q, stream) e = q.expect('dbus-signal', signal='StanzaReceived') xml, = e.args assertContains('<iq', xml) assertContains('<dont-handle-me-bro', xml) signal = q.expect('dbus-signal', signal='StanzaSent') assertContains('service-unavailable', signal.args[0]) # Turn off spewing out stanzas; check it works. console.Properties.Set(CONSOLE_PLUGIN_IFACE, 'SpewStanzas', False) q.forbid_events(es) send_unrecognised_get(q, stream) sync_dbus(bus, q, conn) # Try sending just any old stanza console.SendStanza(''' <message to='%(stacy)s' type='headline'> <body> Hi sis. </body> </message>''' % {'stacy': STACY}) e = q.expect('stream-message', to=STACY, message_type='headline') # Make sure that Wocky has filled in the jabber:client namespace we # carelessly omitted. message = e.stanza assertEquals('message', message.name) assertEquals(ns.CLIENT, message.uri) body = message.firstChildElement() assertEquals('body', body.name) assertEquals(ns.CLIENT, body.uri) console.Channel.Close()
def test(q, bus, conn, stream): self_presence = q.expect('stream-presence') c = xpath.queryForNodes('/presence/c', self_presence.stanza)[0] jid = '[email protected]/omg' # Gabble shouldn't send any disco requests to our contact during this test. q.forbid_events([ EventPattern('stream-iq', to=jid, iq_type='get', query_ns=ns.DISCO_INFO), ]) # Check that Gabble doesn't disco other clients with the same caps hash. p = make_presence(jid, caps={ 'node': c['node'], 'hash': c['hash'], 'ver': c['ver'], }) stream.send(p) sync_stream(q, stream) # Check that Gabble doesn't disco its own ext='' bundles (well, its own # bundles as advertised by Gabbles that don't do hashed caps) p = make_presence( jid, caps={ 'node': c['node'], 'ver': c['ver'], # omitting hash='' so Gabble doesn't ignore ext='' 'ext': 'voice-v1 video-v1', }) stream.send(p) sync_stream(q, stream) conn.ContactCapabilities.UpdateCapabilities([ (cs.CLIENT + '.AbiWord', [ { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.STREAM_TUBE_SERVICE: 'x-abiword' }, { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.STREAM_TUBE_SERVICE: 'x-abiword' }, ], []), (cs.CLIENT + '.KCall', [ { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_CALL }, { 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-udp', cs.CHANNEL_TYPE_CALL + '/video/h264', ]), ]) self_presence = q.expect('stream-presence') c_ = xpath.queryForNodes('/presence/c', self_presence.stanza)[0] assertNotEquals(c['ver'], c_['ver']) for suffix in [ c['ver'], 'voice-v1', 'video-v1', 'camera-v1', 'share-v1', 'pmuc-v1' ] + list(c_['ext'].split()): # But then someone asks us for our old caps iq = IQ(stream, 'get') iq['from'] = jid query = iq.addElement((ns.DISCO_INFO, 'query')) query['node'] = c['node'] + '#' + suffix stream.send(iq) # Gabble should still know what they are, and reply. This is # actually quite important: there's a bug in iChat where if you # return an error to a disco query, it just asks again, and again, # and again... reply = q.expect('stream-iq', to=jid) assertEquals('result', reply.iq_type)
def test(q, bus, conn, stream): self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") jid = '*****@*****.**' full_jid = '[email protected]/Foo' foo_handle = conn.get_contact_handle_sync(jid) path = conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: foo_handle, })[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text') presence = make_presence(full_jid, status='hello', caps={ 'node': 'http://telepathy.freedesktop.org/homeopathy', 'ver': '0.1', }) stream.send(presence) version_event = q.expect( 'stream-iq', to=full_jid, query_ns=ns.DISCO_INFO, query_node='http://telepathy.freedesktop.org/homeopathy#0.1') result = make_result_iq(stream, version_event.stanza) query = result.firstChildElement() feature = query.addElement('feature') feature['var'] = ns.CHAT_STATES stream.send(result) sync_stream(q, stream) states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_INACTIVE, states.get(self_handle, cs.CHAT_STATE_INACTIVE)) assertEquals(cs.CHAT_STATE_INACTIVE, states.get(foo_handle, cs.CHAT_STATE_INACTIVE)) # Receiving chat states: # Composing... stream.send(make_message(full_jid, state='composing')) changed = q.expect('dbus-signal', signal='ChatStateChanged', path=chan.object_path) handle, state = changed.args assertEquals(foo_handle, handle) assertEquals(cs.CHAT_STATE_COMPOSING, state) states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_INACTIVE, states.get(self_handle, cs.CHAT_STATE_INACTIVE)) assertEquals(cs.CHAT_STATE_COMPOSING, states.get(foo_handle, cs.CHAT_STATE_INACTIVE)) # Message! stream.send(make_message(full_jid, body='hello', state='active')) changed = q.expect('dbus-signal', signal='ChatStateChanged', path=chan.object_path) handle, state = changed.args assertEquals(foo_handle, handle) assertEquals(cs.CHAT_STATE_ACTIVE, state) states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_INACTIVE, states.get(self_handle, cs.CHAT_STATE_INACTIVE)) assertEquals(cs.CHAT_STATE_ACTIVE, states.get(foo_handle, cs.CHAT_STATE_INACTIVE)) # Assert that a redundant chat-state change doesn't emit a signal forbidden = [ EventPattern('dbus-signal', signal='ChatStateChanged', args=[foo_handle, cs.CHAT_STATE_ACTIVE]) ] q.forbid_events(forbidden) m = domish.Element((None, 'message')) m['from'] = '[email protected]/Foo' m['type'] = 'chat' m.addElement((ns.CHAT_STATES, 'active')) m.addElement('body', content='hello') stream.send(m) sync_dbus(bus, q, conn) sync_stream(q, stream) q.unforbid_events(forbidden) # Sending chat states: # Composing... chan.ChatState.SetChatState(cs.CHAT_STATE_COMPOSING) stream_message = q.expect('stream-message') check_state_notification(stream_message.stanza, 'composing') states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_COMPOSING, states.get(self_handle, cs.CHAT_STATE_INACTIVE)) assertEquals(cs.CHAT_STATE_ACTIVE, states.get(foo_handle, cs.CHAT_STATE_INACTIVE)) # XEP 0085: # every content message SHOULD contain an <active/> notification. chan.send_msg_sync('hi.') stream_message = q.expect('stream-message') elem = stream_message.stanza assertEquals('chat', elem['type']) check_state_notification(elem, 'active', allow_body=True) states = chan.Properties.Get(cs.CHANNEL_IFACE_CHAT_STATE, 'ChatStates') assertEquals(cs.CHAT_STATE_ACTIVE, states.get(self_handle, cs.CHAT_STATE_INACTIVE)) assertEquals(cs.CHAT_STATE_ACTIVE, states.get(foo_handle, cs.CHAT_STATE_INACTIVE)) def is_body(e): if e.name == 'body': assert e.children[0] == u'hi.', e.toXml() return True return False assert len([x for x in elem.elements() if is_body(x)]) == 1, elem.toXml() # Close the channel without acking the received message. The peer should # get a <gone/> notification, and the channel should respawn. chan.Close() gone, _ = q.expect_many( EventPattern('stream-message'), EventPattern('dbus-signal', signal='Closed'), ) check_state_notification(gone.stanza, 'gone') # Reusing the proxy object because we happen to know it'll be at the same # path... # Destroy the channel. The peer shouldn't get a <gone/> notification, since # we already said we were gone and haven't sent them any messages to the # contrary. es = [EventPattern('stream-message')] q.forbid_events(es) chan.Destroyable.Destroy() sync_stream(q, stream) # Make the channel anew. path = conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: foo_handle, })[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text') # Close it immediately; the peer should again not get a <gone/> # notification, since we haven't sent any notifications on that channel. chan.Close() sync_stream(q, stream) q.unforbid_events(es) # XEP-0085 §5.1 defines how to negotiate support for chat states with a # contact in the absence of capabilities. This is useful when talking to # invisible contacts, for example. # First, if we receive a message from a contact, containing an <active/> # notification, they support chat states, so we should send them. jid = '*****@*****.**' full_jid = jid + '/GTalk' path = conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: jid, })[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text') stream.send(make_message(full_jid, body='i am invisible', state='active')) changed = q.expect('dbus-signal', signal='ChatStateChanged', path=chan.object_path) assertEquals(cs.CHAT_STATE_ACTIVE, changed.args[1]) # We've seen them send a chat state notification, so we should send them # notifications when the UI tells us to. chan.ChatState.SetChatState(cs.CHAT_STATE_COMPOSING) stream_message = q.expect('stream-message', to=full_jid) check_state_notification(stream_message.stanza, 'composing') changed = q.expect('dbus-signal', signal='ChatStateChanged', path=chan.object_path) handle, state = changed.args assertEquals(cs.CHAT_STATE_COMPOSING, state) assertEquals(self_handle, handle) chan.send_msg_sync('very convincing') stream_message = q.expect('stream-message', to=full_jid) check_state_notification(stream_message.stanza, 'active', allow_body=True) # Now, test the case where we start the negotiation, and the contact # turns out to support chat state notifications. jid = '*****@*****.**' full_jid = jid + '/GTalk' path = conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: jid, })[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text') # We shouldn't send any notifications until we actually send a message. # But ChatStateChanged is still emitted locally e = EventPattern('stream-message', to=jid) q.forbid_events([e]) for i in [ cs.CHAT_STATE_ACTIVE, cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_INACTIVE ]: chan.ChatState.SetChatState(i) changed = q.expect('dbus-signal', signal='ChatStateChanged', path=chan.object_path) handle, state = changed.args assertEquals(i, state) assertEquals(self_handle, handle) sync_stream(q, stream) q.unforbid_events([e]) # When we send a message, say we're active. chan.send_msg_sync('is anyone there?') stream_message = q.expect('stream-message', to=jid) check_state_notification(stream_message.stanza, 'active', allow_body=True) # The D-Bus property changes, too changed = q.expect('dbus-signal', signal='ChatStateChanged', path=chan.object_path) handle, state = changed.args assertEquals(cs.CHAT_STATE_ACTIVE, state) assertEquals(self_handle, handle) # We get a notification back from our contact. stream.send(make_message(full_jid, state='composing')) # Wait until gabble tells us the chat-state of the remote party has # changed so we know gabble knows chat state notification are supported changed = q.expect('dbus-signal', signal='ChatStateChanged', path=chan.object_path) handle, state = changed.args assertEquals(cs.CHAT_STATE_COMPOSING, state) assertNotEquals(foo_handle, handle) # So now we know they support notification, so should send notifications. chan.ChatState.SetChatState(cs.CHAT_STATE_COMPOSING) # This doesn't check whether we're sending to the bare jid, or the # jid+resource. In fact, the notification is sent to the bare jid, because # we only update which jid we send to when we actually receive a message, # not when we receive a notification. wjt thinks this is less surprising # than the alternative: # # • I'm talking to you on my N900, and signed in on my laptop; # • I enter one character in a tab to you on my laptop, and then delete # it; # • Now your messages to me appear on my laptop (until I send you another # one from my N900)! stream_message = q.expect('stream-message') check_state_notification(stream_message.stanza, 'composing') # The D-Bus property changes, too changed = q.expect('dbus-signal', signal='ChatStateChanged', path=chan.object_path) handle, state = changed.args assertEquals(cs.CHAT_STATE_COMPOSING, state) assertEquals(self_handle, handle) # But! Now they start messaging us from a different client, which *doesn't* # support notifications. other_jid = jid + '/Library' stream.send(make_message(other_jid, body='grr, library computers')) q.expect('dbus-signal', signal='MessageReceived') # Okay, we should stop sending typing notifications. e = EventPattern('stream-message', to=other_jid) q.forbid_events([e]) for i in [ cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_INACTIVE, cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_ACTIVE ]: chan.ChatState.SetChatState(i) sync_stream(q, stream) q.unforbid_events([e]) # Now, test the case where we start the negotiation, and the contact # does not support chat state notifications jid = '*****@*****.**' full_jid = jid + '/Nonsense' path = conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: jid, })[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text') # We shouldn't send any notifications until we actually send a message. e = EventPattern('stream-message', to=jid) q.forbid_events([e]) for i in [ cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_INACTIVE, cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_ACTIVE ]: chan.ChatState.SetChatState(i) sync_stream(q, stream) q.unforbid_events([e]) # When we send a message, say we're active. chan.send_msg_sync('#n900 #maemo #zomg #woo #yay http://bit.ly/n900') stream_message = q.expect('stream-message', to=jid) check_state_notification(stream_message.stanza, 'active', allow_body=True) # They reply without a chat state. stream.send(make_message(full_jid, body="posted.")) q.expect('dbus-signal', signal='MessageReceived') # Okay, we shouldn't send any more. e = EventPattern('stream-message', to=other_jid) q.forbid_events([e]) for i in [ cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_INACTIVE, cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_ACTIVE ]: chan.ChatState.SetChatState(i) sync_stream(q, stream) q.unforbid_events([e]) chan.send_msg_sync('@stephenfry simmer down') message = q.expect('stream-message') states = [x for x in message.stanza.elements() if x.uri == ns.CHAT_STATES] assertLength(0, states)
def test(q, bus, conn, stream): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) id = '1845a1a9-f7bc-4a2e-a885-633aadc81e1b' # <message type="chat"><body>hello</body</message> m = domish.Element((None, 'message')) m['from'] = '[email protected]/Pidgin' m['id'] = id m['type'] = 'chat' m.addElement('body', content='hello') stream.send(m) event = q.expect('dbus-signal', signal='NewChannel') text_chan = wrap_channel( bus.get_object(conn.bus_name, event.args[0]), 'Text', ['Messages']) assert event.args[1] == cs.CHANNEL_TYPE_TEXT assert event.args[2] == cs.HT_CONTACT foo_at_bar_dot_com_handle = event.args[3] jid = conn.InspectHandles(1, [foo_at_bar_dot_com_handle])[0] assert jid == '*****@*****.**' assert event.args[4] == False # suppress handler # Exercise basic Channel Properties from spec 0.17.7 channel_props = text_chan.Properties.GetAll(cs.CHANNEL) assert channel_props.get('TargetHandle') == event.args[3],\ (channel_props.get('TargetHandle'), event.args[3]) assert channel_props.get('TargetHandleType') == cs.HT_CONTACT,\ channel_props.get('TargetHandleType') assert channel_props.get('ChannelType') == \ cs.CHANNEL_TYPE_TEXT,\ channel_props.get('ChannelType') assert cs.CHANNEL_IFACE_CHAT_STATE in \ channel_props.get('Interfaces', ()), \ channel_props.get('Interfaces') assert cs.CHANNEL_IFACE_MESSAGES in \ channel_props.get('Interfaces', ()), \ channel_props.get('Interfaces') assert channel_props['TargetID'] == jid,\ (channel_props['TargetID'], jid) assert channel_props['Requested'] == False assert channel_props['InitiatorHandle'] == event.args[3],\ (channel_props['InitiatorHandle'], event.args[3]) assert channel_props['InitiatorID'] == jid,\ (channel_props['InitiatorID'], jid) received, message_received = q.expect_many( EventPattern('dbus-signal', signal='Received'), EventPattern('dbus-signal', signal='MessageReceived'), ) # Check that C.T.Text.Received looks right # message type: normal assert received.args[3] == 0 # flags: none assert received.args[4] == 0 # body assert received.args[5] == 'hello' # Check that C.I.Messages.MessageReceived looks right. message = message_received.args[0] # message should have two parts: the header and one content part assert len(message) == 2, message header, body = message assert header['message-sender'] == foo_at_bar_dot_com_handle, header # the spec says that message-type "MAY be omitted for normal chat # messages." assert 'message-type' not in header or header['message-type'] == 0, header # This looks wrong, but is correct. We don't know if our contacts generate # message id='' attributes which are unique enough for our requirements, so # we should not use them as the message-token for incoming messages. assertNotEquals(id, header['message-token']) assert body['content-type'] == 'text/plain', body assert body['content'] == 'hello', body # Remove the message from the pending message queue, and check that # PendingMessagesRemoved fires. message_id = header['pending-message-id'] text_chan.Text.AcknowledgePendingMessages([message_id]) removed = q.expect('dbus-signal', signal='PendingMessagesRemoved') removed_ids = removed.args[0] assert len(removed_ids) == 1, removed_ids assert removed_ids[0] == message_id, (removed_ids, message_id) # Send a Notice using the Messages API greeting = [ dbus.Dictionary({ 'message-type': 2, # Notice }, signature='sv'), { 'content-type': 'text/plain', 'content': u"what up", } ] sent_token = text_chan.Messages.SendMessage(greeting, dbus.UInt32(0)) stream_message, sent, message_sent = q.expect_many( EventPattern('stream-message'), EventPattern('dbus-signal', signal='Sent'), EventPattern('dbus-signal', signal='MessageSent'), ) elem = stream_message.stanza assert elem.name == 'message' assert elem['type'] == 'normal' body = list(stream_message.stanza.elements())[0] assert body.name == 'body' assert body.children[0] == u'what up' sent_message = message_sent.args[0] assert len(sent_message) == 2, sent_message header = sent_message[0] assert header['message-type'] == 2, header # Notice assert header['message-token'] == sent_token, header body = sent_message[1] assert body['content-type'] == 'text/plain', body assert body['content'] == u'what up', body assert message_sent.args[2] == sent_token assert sent.args[1] == 2, sent.args # Notice assert sent.args[2] == u'what up', sent.args # Send a message using Channel.Type.Text API text_chan.Text.Send(0, 'goodbye') stream_message, sent, message_sent = q.expect_many( EventPattern('stream-message'), EventPattern('dbus-signal', signal='Sent'), EventPattern('dbus-signal', signal='MessageSent'), ) elem = stream_message.stanza assert elem.name == 'message' assert elem['type'] == 'chat' body = list(stream_message.stanza.elements())[0] assert body.name == 'body' assert body.children[0] == u'goodbye' sent_message = message_sent.args[0] assert len(sent_message) == 2, sent_message header = sent_message[0] # the spec says that message-type "MAY be omitted for normal chat # messages." assert 'message-type' not in header or header['message-type'] == 0, header body = sent_message[1] assert body['content-type'] == 'text/plain', body assert body['content'] == u'goodbye', body assert sent.args[1] == 0, sent.args # message type normal assert sent.args[2] == u'goodbye', sent.args
def test_invisible_on_connect_fail_invalid_list(q, bus, conn, stream, really_invalid=False): props = conn.Properties.GetAll(cs.CONN_IFACE_SIMPLE_PRESENCE) assertNotEquals({}, props['Statuses']) presence_event_pattern = EventPattern('stream-presence') q.forbid_events([presence_event_pattern]) conn.SimplePresence.SetPresence("hidden", "") conn.Connect() stream.handle_get_all_privacy_lists(q, bus, conn, ['invisible']) get_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='get') list_node = xpath.queryForNodes('//list', get_list.query)[0] assertEquals('invisible', list_node['name']) if really_invalid: # At one point Gabble would crash if the reply was of type 'result' but # wasn't well-formed. acknowledge_iq(stream, get_list.stanza) else: stream.send_privacy_list(get_list.stanza, [ elem('item', type='jid', value='*****@*****.**', action='allow', order='1')(elem('presence-out')), elem('item', action='deny', order='2')(elem('presence-out')) ]) create_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') created = xpath.queryForNodes('//list', create_list.stanza)[0] assertEquals(created["name"], 'invisible-gabble') acknowledge_iq(stream, create_list.stanza) q.unforbid_events([presence_event_pattern]) activate_list = q.expect('stream-iq', query_ns=ns.PRIVACY, iq_type='set') active = xpath.queryForNodes('//active', activate_list.stanza)[0] assertEquals(active["name"], 'invisible-gabble') acknowledge_iq(stream, activate_list.stanza) q.expect_many( EventPattern('dbus-signal', signal='PresencesChanged', interface=cs.CONN_IFACE_SIMPLE_PRESENCE, args=[{ 1: (5, 'hidden', '') }]), EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED])) # 'hidden' should not be an available status. assertContains( "hidden", conn.Properties.Get(cs.CONN_IFACE_SIMPLE_PRESENCE, "Statuses"))
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 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 test(q, bus, conn, stream, access_control): iq_event = q.expect('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard') acknowledge_iq(stream, iq_event.stanza) muc = '*****@*****.**' _, test_handle, bob_handle = \ join_muc_and_check(q, bus, conn, stream, muc) # Bob offers a stream tube bob_bus_name = ':2.Ym9i' presence = make_muc_presence('owner', 'moderator', '*****@*****.**', 'bob') tubes = presence.addElement((ns.TUBES, 'tubes')) tube = tubes.addElement((None, 'tube')) tube['type'] = 'dbus' tube['initiator'] = '[email protected]/bob' tube['stream-id'] = '10' tube['id'] = '1' tube['service'] = 'com.example.Test' tube['dbus-name'] = bob_bus_name parameters = tube.addElement((None, 'parameters')) parameter = parameters.addElement((None, 'parameter')) parameter['type'] = 'str' parameter['name'] = 'foo' parameter.addContent('bar') stream.send(presence) # tube channel is created def new_chan_predicate(e): path, props = e.args[0][0] return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_DBUS_TUBE event = q.expect('dbus-signal', signal='NewChannels', predicate=new_chan_predicate) channels = event.args[0] path, props = channels[0] assertEquals(cs.CHANNEL_TYPE_DBUS_TUBE, props[cs.CHANNEL_TYPE]) assertEquals('[email protected]/bob', props[cs.INITIATOR_ID]) bob_handle = props[cs.INITIATOR_HANDLE] assertSameSets([cs.CHANNEL_IFACE_GROUP, cs.CHANNEL_IFACE_TUBE], props[cs.INTERFACES]) assertEquals(False, props[cs.REQUESTED]) assertEquals('*****@*****.**', props[cs.TARGET_ID]) assertEquals('com.example.Test', props[cs.DBUS_TUBE_SERVICE_NAME]) assertEquals({'foo': 'bar'}, props[cs.TUBE_PARAMETERS]) assertEquals([ cs.SOCKET_ACCESS_CONTROL_CREDENTIALS, cs.SOCKET_ACCESS_CONTROL_LOCALHOST ], props[cs.DBUS_TUBE_SUPPORTED_ACCESS_CONTROLS]) tube_chan = wrap_channel(bus.get_object(conn.bus_name, path), 'DBusTube') # only Bob is in DBusNames dbus_names = tube_chan.Get(cs.CHANNEL_TYPE_DBUS_TUBE, 'DBusNames', dbus_interface=cs.PROPERTIES_IFACE) assertEquals({bob_handle: bob_bus_name}, dbus_names) call_async(q, tube_chan.DBusTube, 'Accept', access_control) return_event, names_changed1, names_changed2, presence_event = q.expect_many( EventPattern('dbus-return', method='Accept'), EventPattern('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_DBUS_TUBE), EventPattern('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_DBUS_TUBE), EventPattern('stream-presence', to='[email protected]/test', predicate=lambda e: t.presence_contains_tube(e))) tube_addr = return_event.value[0] assert len(tube_addr) > 0 # check presence stanza tube_node = xpath.queryForNodes('/presence/tubes/tube', presence_event.stanza)[0] assertEquals('[email protected]/bob', tube_node['initiator']) assertEquals('com.example.Test', tube_node['service']) assertEquals('10', tube_node['stream-id']) assertEquals('dbus', tube_node['type']) assertEquals('1', tube_node['id']) self_bus_name = tube_node['dbus-name'] tubes_self_handle = tube_chan.Properties.Get(cs.CHANNEL_IFACE_GROUP, 'SelfHandle') assertNotEquals(0, tubes_self_handle) # both of us are in DBusNames now dbus_names = tube_chan.Get(cs.CHANNEL_TYPE_DBUS_TUBE, 'DBusNames', dbus_interface=cs.PROPERTIES_IFACE) assertEquals({ bob_handle: bob_bus_name, tubes_self_handle: self_bus_name }, dbus_names) added, removed = names_changed1.args assertEquals({bob_handle: bob_bus_name}, added) assertEquals([], removed) added, removed = names_changed2.args assertEquals({tubes_self_handle: self_bus_name}, added) assertEquals([], removed) tube_chan.Channel.Close() q.expect_many(EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed'))
def test(q, bus, conn, stream, access_control): conn.Connect() _, iq_event = q.expect_many( EventPattern('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]), EventPattern('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard')) acknowledge_iq(stream, iq_event.stanza) muc = '*****@*****.**' _, _, test_handle, bob_handle = \ join_muc_and_check(q, bus, conn, stream, muc) # Bob offers a stream tube bob_bus_name = ':2.Ym9i' presence = make_muc_presence('owner', 'moderator', '*****@*****.**', 'bob') tubes = presence.addElement((ns.TUBES, 'tubes')) tube = tubes.addElement((None, 'tube')) tube['type'] = 'dbus' tube['initiator'] = '[email protected]/bob' tube['stream-id'] = '10' tube['id'] = '1' tube['service'] = 'com.example.Test' tube['dbus-name'] = bob_bus_name parameters = tube.addElement((None, 'parameters')) parameter = parameters.addElement((None, 'parameter')) parameter['type'] = 'str' parameter['name'] = 'foo' parameter.addContent('bar') stream.send(presence) # tubes channel is created event = q.expect('dbus-signal', signal='NewChannels') channels = event.args[0] path, props = channels[0] # tube channel is created event = q.expect('dbus-signal', signal='NewChannels') channels = event.args[0] path, props = channels[0] assertEquals(cs.CHANNEL_TYPE_DBUS_TUBE, props[cs.CHANNEL_TYPE]) assertEquals('[email protected]/bob', props[cs.INITIATOR_ID]) bob_handle = props[cs.INITIATOR_HANDLE] assertEquals([cs.CHANNEL_IFACE_GROUP, cs.CHANNEL_IFACE_TUBE], props[cs.INTERFACES]) assertEquals(False, props[cs.REQUESTED]) assertEquals('*****@*****.**', props[cs.TARGET_ID]) assertEquals('com.example.Test', props[cs.DBUS_TUBE_SERVICE_NAME]) assertEquals({'foo': 'bar'}, props[cs.TUBE_PARAMETERS]) assertEquals([cs.SOCKET_ACCESS_CONTROL_CREDENTIALS, cs.SOCKET_ACCESS_CONTROL_LOCALHOST], props[cs.DBUS_TUBE_SUPPORTED_ACCESS_CONTROLS]) tube_chan = bus.get_object(conn.bus_name, path) tube_iface = dbus.Interface(tube_chan, cs.CHANNEL_IFACE_TUBE) dbus_tube_iface = dbus.Interface(tube_chan, cs.CHANNEL_TYPE_DBUS_TUBE) tube_chan_iface = dbus.Interface(tube_chan, cs.CHANNEL) # only Bob is in DBusNames dbus_names = tube_chan.Get(cs.CHANNEL_TYPE_DBUS_TUBE, 'DBusNames', dbus_interface=cs.PROPERTIES_IFACE) assertEquals({bob_handle: bob_bus_name}, dbus_names) call_async(q, dbus_tube_iface, 'Accept', access_control) return_event, names_changed1, names_changed2, presence_event = q.expect_many( EventPattern('dbus-return', method='Accept'), EventPattern('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_DBUS_TUBE), EventPattern('dbus-signal', signal='DBusNamesChanged', interface=cs.CHANNEL_TYPE_DBUS_TUBE), EventPattern('stream-presence', to='[email protected]/test')) tube_addr = return_event.value[0] assert len(tube_addr) > 0 # check presence stanza tube_node = xpath.queryForNodes('/presence/tubes/tube', presence_event.stanza)[0] assertEquals('[email protected]/bob', tube_node['initiator']) assertEquals('com.example.Test', tube_node['service']) assertEquals('10', tube_node['stream-id']) assertEquals('dbus', tube_node['type']) assertEquals('1', tube_node['id']) self_bus_name = tube_node['dbus-name'] tubes_self_handle = tube_chan.GetSelfHandle(dbus_interface=cs.CHANNEL_IFACE_GROUP) assertNotEquals(0, tubes_self_handle) # both of us are in DBusNames now dbus_names = tube_chan.Get(cs.CHANNEL_TYPE_DBUS_TUBE, 'DBusNames', dbus_interface=cs.PROPERTIES_IFACE) assertEquals({bob_handle: bob_bus_name, tubes_self_handle: self_bus_name}, dbus_names) added, removed = names_changed1.args assertEquals({bob_handle: bob_bus_name}, added) assertEquals([], removed) added, removed = names_changed2.args assertEquals({tubes_self_handle: self_bus_name}, added) assertEquals([], removed) tube_chan_iface.Close() q.expect_many( EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed'))
def test(q, bus, conn, stream, peer='[email protected]/Foo'): jp = JingleProtocol031() jt = JingleTest2(jp, conn, q, stream, 'test@localhost', peer) jt.prepare() # Remote end calls us jt.incoming_call(audio = "Audio", video = "Video") e = q.expect ('dbus-signal', signal='NewSessionHandler') handler = make_channel_proxy(conn, e.args[0], 'Media.SessionHandler') handler.Ready() events = q.expect_many ( EventPattern('dbus-signal', signal='NewStreamHandler'), EventPattern('dbus-signal', signal='NewStreamHandler') ) for e in events: handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler') handler.Ready([]) candidate0 = ( "1.2.3.4", # host 666, # port 0, # protocol = TP_MEDIA_STREAM_BASE_PROTO_UDP "RTP", # protocol subtype "AVP", # profile 1.0, # preference 0, # transport type = TP_MEDIA_STREAM_TRANSPORT_TYPE_LOCAL, "username", "password" ) candidate1 = ( "5.6.7.8", # host 999, # port 0, # protocol = TP_MEDIA_STREAM_BASE_PROTO_UDP "RTP", # protocol subtype "AVP", # profile 1.0, # preference 0, # transport type = TP_MEDIA_STREAM_TRANSPORT_TYPE_LOCAL, "username", "password" ) node = jp.SetIq(jt.peer, jt.jid, [ jp.Jingle(jt.sid, jt.peer, 'transport-info', [ jp.Content('Audio', 'initiator', 'both', transport = jp.TransportGoogleP2P([candidate0])), jp.Content('Video', 'initiator', 'both', transport = jp.TransportGoogleP2P([candidate1])), ] ) ]) stream.send(jp.xml(node)) q.expect ('stream-iq', iq_type='result') (c0, c1) = q.expect_many( EventPattern('dbus-signal', signal='AddRemoteCandidate'), EventPattern('dbus-signal', signal='AddRemoteCandidate')) assertNotEquals(c0.path, c1.path) mapping = { 666: candidate0, 999: candidate1} # Candidate without component number to compare candidate = c0.args[1][0][1:] assertEquals(mapping[candidate[1]], candidate) candidate = c1.args[1][0][1:] assertEquals(mapping[candidate[1]], candidate)
def test(q, bus, conn, stream): self_handle = conn.GetSelfHandle() jid = '*****@*****.**' full_jid = '[email protected]/Foo' foo_handle = conn.RequestHandles(cs.HT_CONTACT, [jid])[0] path = conn.Requests.CreateChannel( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: foo_handle, })[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['ChatState', 'Destroyable']) presence = make_presence(full_jid, status='hello', caps={ 'node': 'http://telepathy.freedesktop.org/homeopathy', 'ver' : '0.1', }) stream.send(presence) version_event = q.expect('stream-iq', to=full_jid, query_ns=ns.DISCO_INFO, query_node='http://telepathy.freedesktop.org/homeopathy#0.1') result = make_result_iq(stream, version_event.stanza) query = result.firstChildElement() feature = query.addElement('feature') feature['var'] = ns.CHAT_STATES stream.send(result) sync_stream(q, stream) # Receiving chat states: # Composing... stream.send(make_message(full_jid, state='composing')) changed = q.expect('dbus-signal', signal='ChatStateChanged') handle, state = changed.args assertEquals(foo_handle, handle) assertEquals(cs.CHAT_STATE_COMPOSING, state) # Message! stream.send(make_message(full_jid, body='hello', state='active')) changed = q.expect('dbus-signal', signal='ChatStateChanged') handle, state = changed.args assertEquals(foo_handle, handle) assertEquals(cs.CHAT_STATE_ACTIVE, state) # Sending chat states: # Composing... chan.ChatState.SetChatState(cs.CHAT_STATE_COMPOSING) stream_message = q.expect('stream-message') check_state_notification(stream_message.stanza, 'composing') # XEP 0085: # every content message SHOULD contain an <active/> notification. chan.Text.Send(0, 'hi.') stream_message = q.expect('stream-message') elem = stream_message.stanza assertEquals('chat', elem['type']) check_state_notification(elem, 'active', allow_body=True) def is_body(e): if e.name == 'body': assert e.children[0] == u'hi.', e.toXml() return True return False assert len([x for x in elem.elements() if is_body(x)]) == 1, elem.toXml() # Close the channel without acking the received message. The peer should # get a <gone/> notification, and the channel should respawn. chan.Close() gone, _, _ = q.expect_many( EventPattern('stream-message'), EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='NewChannel'), ) check_state_notification(gone.stanza, 'gone') # Reusing the proxy object because we happen to know it'll be at the same # path... # Destroy the channel. The peer shouldn't get a <gone/> notification, since # we already said we were gone and haven't sent them any messages to the # contrary. es = [EventPattern('stream-message')] q.forbid_events(es) chan.Destroyable.Destroy() sync_stream(q, stream) # Make the channel anew. path = conn.Requests.CreateChannel( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: foo_handle, })[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['ChatState', 'Destroyable']) # Close it immediately; the peer should again not get a <gone/> # notification, since we haven't sent any notifications on that channel. chan.Close() sync_stream(q, stream) q.unforbid_events(es) # XEP-0085 §5.1 defines how to negotiate support for chat states with a # contact in the absence of capabilities. This is useful when talking to # invisible contacts, for example. # First, if we receive a message from a contact, containing an <active/> # notification, they support chat states, so we should send them. jid = '*****@*****.**' full_jid = jid + '/GTalk' path = conn.Requests.CreateChannel( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: jid, })[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['ChatState']) stream.send(make_message(full_jid, body='i am invisible', state='active')) changed = q.expect('dbus-signal', signal='ChatStateChanged') assertEquals(cs.CHAT_STATE_ACTIVE, changed.args[1]) # We've seen them send a chat state notification, so we should send them # notifications when the UI tells us to. chan.ChatState.SetChatState(cs.CHAT_STATE_COMPOSING) stream_message = q.expect('stream-message', to=full_jid) check_state_notification(stream_message.stanza, 'composing') changed = q.expect('dbus-signal', signal='ChatStateChanged') handle, state = changed.args assertEquals(cs.CHAT_STATE_COMPOSING, state) assertEquals(self_handle, handle) chan.Text.Send(0, 'very convincing') stream_message = q.expect('stream-message', to=full_jid) check_state_notification(stream_message.stanza, 'active', allow_body=True) # Now, test the case where we start the negotiation, and the contact # turns out to support chat state notifications. jid = '*****@*****.**' full_jid = jid + '/GTalk' path = conn.Requests.CreateChannel( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: jid, })[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['ChatState']) # We shouldn't send any notifications until we actually send a message. e = EventPattern('stream-message', to=jid) q.forbid_events([e]) for i in [cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_INACTIVE, cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_ACTIVE]: chan.ChatState.SetChatState(i) sync_stream(q, stream) q.unforbid_events([e]) # When we send a message, say we're active. chan.Text.Send(0, 'is anyone there?') stream_message = q.expect('stream-message', to=jid) check_state_notification(stream_message.stanza, 'active', allow_body=True) # We get a notification back from our contact. stream.send(make_message(full_jid, state='composing')) # Wait until gabble tells us the chat-state of the remote party has # changed so we know gabble knows chat state notification are supported changed = q.expect('dbus-signal', signal='ChatStateChanged') handle, state = changed.args assertEquals(cs.CHAT_STATE_COMPOSING, state) assertNotEquals(self_handle, handle) # So now we know they support notification, so should send notifications. chan.ChatState.SetChatState(cs.CHAT_STATE_COMPOSING) # This doesn't check whether we're sending to the bare jid, or the # jid+resource. In fact, the notification is sent to the bare jid, because # we only update which jid we send to when we actually receive a message, # not when we receive a notification. wjt thinks this is less surprising # than the alternative: # # • I'm talking to you on my N900, and signed in on my laptop; # • I enter one character in a tab to you on my laptop, and then delete # it; # • Now your messages to me appear on my laptop (until I send you another # one from my N900)! stream_message = q.expect('stream-message') check_state_notification(stream_message.stanza, 'composing') # But! Now they start messaging us from a different client, which *doesn't* # support notifications. other_jid = jid + '/Library' stream.send(make_message(other_jid, body='grr, library computers')) q.expect('dbus-signal', signal='Received') # Okay, we should stop sending typing notifications. e = EventPattern('stream-message', to=other_jid) q.forbid_events([e]) for i in [cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_INACTIVE, cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_ACTIVE]: chan.ChatState.SetChatState(i) sync_stream(q, stream) q.unforbid_events([e]) # Now, test the case where we start the negotiation, and the contact # does not support chat state notifications jid = '*****@*****.**' full_jid = jid + '/Nonsense' path = conn.Requests.CreateChannel( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_ID: jid, })[0] chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['ChatState']) # We shouldn't send any notifications until we actually send a message. e = EventPattern('stream-message', to=jid) q.forbid_events([e]) for i in [cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_INACTIVE, cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_ACTIVE]: chan.ChatState.SetChatState(i) sync_stream(q, stream) q.unforbid_events([e]) # When we send a message, say we're active. chan.Text.Send(0, '#n900 #maemo #zomg #woo #yay http://bit.ly/n900') stream_message = q.expect('stream-message', to=jid) check_state_notification(stream_message.stanza, 'active', allow_body=True) # They reply without a chat state. stream.send(make_message(full_jid, body="posted.")) q.expect('dbus-signal', signal='Received') # Okay, we shouldn't send any more. e = EventPattern('stream-message', to=other_jid) q.forbid_events([e]) for i in [cs.CHAT_STATE_COMPOSING, cs.CHAT_STATE_INACTIVE, cs.CHAT_STATE_PAUSED, cs.CHAT_STATE_ACTIVE]: chan.ChatState.SetChatState(i) sync_stream(q, stream) q.unforbid_events([e]) chan.Text.Send(0, '@stephenfry simmer down') message = q.expect('stream-message') states = [x for x in message.stanza.elements() if x.uri == ns.CHAT_STATES] assertLength(0, states)
def test(q, bus, conn, stream): rccs = conn.Properties.Get(cs.CONN_IFACE_REQUESTS, 'RequestableChannelClasses') fixed = { cs.CHANNEL_TYPE: CONSOLE_PLUGIN_IFACE, cs.TARGET_HANDLE_TYPE: cs.HT_NONE, } allowed = [] assertContains((fixed, allowed), rccs) path, _ = conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: CONSOLE_PLUGIN_IFACE }) other_path, _ = conn.Requests.CreateChannel({ cs.CHANNEL_TYPE: CONSOLE_PLUGIN_IFACE }) assertNotEquals(path, other_path) # leave the other one open, to test we don't crash on disconnect console = ProxyWrapper(bus.get_object(conn.bus_name, path), CONSOLE_PLUGIN_IFACE, {'Channel': cs.CHANNEL}) assert not console.Properties.Get(CONSOLE_PLUGIN_IFACE, 'SpewStanzas') es = [ EventPattern('dbus-signal', signal='StanzaReceived'), EventPattern('dbus-signal', signal='StanzaSent'), ] q.forbid_events(es) call_async(q, console, 'SendIQ', 'get', STACY, '<coffee xmlns="urn:unimaginative"/>') e = q.expect('stream-iq', iq_type='get', query_ns='urn:unimaginative', query_name='coffee') acknowledge_iq(stream, e.stanza) e = q.expect('dbus-return', method='SendIQ') type_, body = e.value assertEquals('result', type_) # We just assume the body works. # Turn on signalling incoming and outgoing stanzas console.Properties.Set(CONSOLE_PLUGIN_IFACE, 'SpewStanzas', True) sync_dbus(bus, q, conn) q.unforbid_events(es) send_unrecognised_get(q, stream) e = q.expect('dbus-signal', signal='StanzaReceived') xml, = e.args assertContains('<iq', xml) assertContains('<dont-handle-me-bro', xml) signal = q.expect('dbus-signal', signal='StanzaSent') assertContains('service-unavailable', signal.args[0]) # Turn off spewing out stanzas; check it works. console.Properties.Set(CONSOLE_PLUGIN_IFACE, 'SpewStanzas', False) q.forbid_events(es) send_unrecognised_get(q, stream) sync_dbus(bus, q, conn) # Try sending just any old stanza console.SendStanza(''' <message to='%(stacy)s' type='headline'> <body> Hi sis. </body> </message>''' % { 'stacy': STACY }) e = q.expect('stream-message', to=STACY, message_type='headline') # Make sure that Wocky has filled in the jabber:client namespace we # carelessly omitted. message = e.stanza assertEquals('message', message.name) assertEquals(ns.CLIENT, message.uri) body = message.firstChildElement() assertEquals('body', body.name) assertEquals(ns.CLIENT, body.uri) console.Channel.Close()