def test(q, bus, mc): # For this test, we should never actually be asked to make a channel. forbidden = [ EventPattern('dbus-method-call', method='CreateChannel'), EventPattern('dbus-method-call', method='EnsureChannel'), EventPattern('dbus-method-call', method='ObserveChannels'), EventPattern('dbus-method-call', method='AddDispatchOperation'), EventPattern('dbus-method-call', method='HandleChannels'), ] q.forbid_events(forbidden) params = dbus.Dictionary( { "account": "*****@*****.**", "password": "******" }, signature='sv') simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) fixed_properties = dbus.Dictionary( { cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT, cs.CHANNEL + '.ChannelType': FORBIDDEN_CTYPE, }, signature='sv') client = SimulatedClient(q, bus, 'Empathy', observe=[fixed_properties], approve=[fixed_properties], handle=[fixed_properties], bypass_approval=False) # wait for MC to download the properties expect_client_setup(q, [client]) user_action_time = dbus.Int64(1238582606) cd = bus.get_object(cs.CD, cs.CD_PATH) cd_props = dbus.Interface(cd, cs.PROPERTIES_IFACE) # UI calls ChannelDispatcher.CreateChannel request = dbus.Dictionary( { cs.CHANNEL + '.ChannelType': FORBIDDEN_CTYPE, cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT, cs.CHANNEL + '.TargetID': 'juliet', }, signature='sv') call_async(q, cd, 'CreateChannel', account.object_path, request, user_action_time, client.bus_name, dbus_interface=cs.CD) ret = q.expect('dbus-return', method='CreateChannel') request_path = ret.value[0] # UI connects to signals and calls ChannelRequest.Proceed() cr = bus.get_object(cs.AM, request_path) request_props = cr.GetAll(cs.CR, dbus_interface=cs.PROPERTIES_IFACE) assert request_props['Account'] == account.object_path assert request_props['Requests'] == [request] assert request_props['UserActionTime'] == user_action_time assert request_props['PreferredHandler'] == client.bus_name assert request_props['Interfaces'] == [] call_async(q, cr, 'Proceed', dbus_interface=cs.CR) q.expect('dbus-return', method='Proceed') q.expect('dbus-signal', path=cr.object_path, interface=cs.CR, signal='Failed', args=[cs.PERMISSION_DENIED, "No, you don't"])
def no_muji_presences(muc): return EventPattern( 'stream-presence', to=muc + "/test", predicate=lambda x: xpath.queryForNodes("/presence/muji", x.stanza))
def test(q, bus, conn, stream, bytestream_cls, address_type, access_control, access_control_param): address1 = t.set_up_echo(q, address_type, True, streamfile='stream') address2 = t.set_up_echo(q, address_type, True, streamfile='stream2') t.check_conn_properties(q, conn) vcard_event, roster_event = q.expect_many( EventPattern('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard'), EventPattern('stream-iq', query_ns=ns.ROSTER)) self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") acknowledge_iq(stream, vcard_event.stanza) roster = roster_event.stanza roster['type'] = 'result' item = roster_event.query.addElement('item') item['jid'] = 'bob@localhost' # Bob can do tubes item['subscription'] = 'both' stream.send(roster) bob_full_jid = 'bob@localhost/Bob' self_full_jid = 'test@localhost/Resource' # Send Bob presence and his tube caps presence = domish.Element(('jabber:client', 'presence')) presence['from'] = bob_full_jid presence['to'] = self_full_jid c = presence.addElement('c') c['xmlns'] = 'http://jabber.org/protocol/caps' c['node'] = 'http://example.com/ICantBelieveItsNotTelepathy' c['ver'] = '1.2.3' stream.send(presence) event = q.expect('stream-iq', iq_type='get', query_ns='http://jabber.org/protocol/disco#info', to=bob_full_jid) assert event.query['node'] == \ 'http://example.com/ICantBelieveItsNotTelepathy#1.2.3' result = make_result_iq(stream, event.stanza) query = result.firstChildElement() feature = query.addElement('feature') feature['var'] = ns.TUBES stream.send(result) # A tube request can be done only if the contact has tube capabilities # Ensure that Bob's caps have been received sync_stream(q, stream) # Also ensure that all the new contact list channels have been announced, # so that the NewChannel(s) signals we look for after calling # RequestChannel are the ones we wanted. sync_dbus(bus, q, conn) # Test tubes with Bob. Bob has tube capabilities. bob_handle = conn.get_contact_handle_sync('bob@localhost') # Try CreateChannel with correct properties # Gabble must succeed call_async( q, conn.Requests, 'CreateChannel', { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAM_TUBE, cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, cs.TARGET_HANDLE: bob_handle, cs.STREAM_TUBE_SERVICE: "newecho", }) def find_stream_tube(channels): for path, props in channels: if props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAM_TUBE: return path, props return None, None def new_chan_predicate(e): path, _ = find_stream_tube(e.args[0]) return path is not None ret, new_sig = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-signal', signal='NewChannels', predicate=new_chan_predicate), ) new_chan_path = ret.value[0] new_chan_prop_asv = ret.value[1] # State and Parameters are mutables so not announced assert cs.TUBE_STATE not in new_chan_prop_asv assert cs.TUBE_PARAMETERS not in new_chan_prop_asv assert new_chan_path.find("StreamTube") != -1, new_chan_path assert new_chan_path.find("SITubesChannel") == -1, new_chan_path new_tube_chan = bus.get_object(conn.bus_name, new_chan_path) new_tube_iface = dbus.Interface(new_tube_chan, cs.CHANNEL_TYPE_STREAM_TUBE) # check State and Parameters new_tube_props = new_tube_chan.GetAll(cs.CHANNEL_IFACE_TUBE, dbus_interface=cs.PROPERTIES_IFACE) # the tube created using the new API is in the "not offered" state assert new_tube_props['State'] == cs.TUBE_CHANNEL_STATE_NOT_OFFERED _, stream_tube_channel_properties = find_stream_tube(new_sig.args[0]) assert cs.TUBE_STATE not in stream_tube_channel_properties assert cs.TUBE_PARAMETERS not in stream_tube_channel_properties # Offer the first tube created call_async(q, new_tube_iface, 'Offer', address_type, address2, access_control, new_sample_parameters) msg_event, state_event = q.expect_many( EventPattern('stream-message'), EventPattern('dbus-signal', signal='TubeChannelStateChanged')) assert state_event.args[0] == cs.TUBE_CHANNEL_STATE_REMOTE_PENDING message = msg_event.stanza assert message['to'] == bob_full_jid tube_nodes = xpath.queryForNodes('/message/tube[@xmlns="%s"]' % ns.TUBES, message) assert tube_nodes is not None assert len(tube_nodes) == 1 tube = tube_nodes[0] assert tube['service'] == 'newecho' assert tube['type'] == 'stream' assert not tube.hasAttribute('initiator') stream_tube_id = int(tube['id']) params = {} parameter_nodes = xpath.queryForNodes('/tube/parameters/parameter', tube) for node in parameter_nodes: assert node['name'] not in params params[node['name']] = (node['type'], str(node)) assert params == { 'ay': ('bytes', 'bmV3aGVsbG8='), 's': ('str', 'newhello'), 'i': ('int', '-123'), 'u': ('uint', '123'), } # The new tube has been offered, the parameters cannot be changed anymore # We need to use call_async to check the error tube_prop_iface = dbus.Interface(new_tube_chan, cs.PROPERTIES_IFACE) call_async(q, tube_prop_iface, 'Set', cs.CHANNEL_IFACE_TUBE, 'Parameters', dbus.Dictionary({dbus.String(u'foo2'): dbus.String(u'bar2')}, signature=dbus.Signature('sv')), dbus_interface=cs.PROPERTIES_IFACE) set_error = q.expect('dbus-error') # check it is *not* correctly changed new_tube_props = new_tube_chan.GetAll(cs.CHANNEL_IFACE_TUBE, dbus_interface=cs.PROPERTIES_IFACE, byte_arrays=True) assert new_tube_props.get("Parameters") == new_sample_parameters, \ new_tube_props.get("Parameters") # The CM is the server, so fake a client wanting to talk to it # Old API tube bytestream1 = bytestream_cls(stream, q, 'alpha', bob_full_jid, self_full_jid, True) iq, si = bytestream1.create_si_offer(ns.TUBES) stream_node = si.addElement((ns.TUBES, 'stream')) stream_node['tube'] = str(stream_tube_id) stream.send(iq) si_reply_event, _, new_conn_event, socket_event = q.expect_many( EventPattern('stream-iq', iq_type='result'), EventPattern('dbus-signal', signal='TubeChannelStateChanged', args=[cs.TUBE_STATE_OPEN]), EventPattern('dbus-signal', signal='NewRemoteConnection'), EventPattern('socket-connected')) bytestream1.check_si_reply(si_reply_event.stanza) tube = xpath.queryForNodes('/iq/si/tube[@xmlns="%s"]' % ns.TUBES, si_reply_event.stanza) assert len(tube) == 1 handle, access, id = new_conn_event.args assert handle == bob_handle protocol = socket_event.protocol # we don't want to echo the access control byte protocol.echoed = False # start to read from the transport so we can read the control byte protocol.transport.startReading() t.check_new_connection_access(q, access_control, access, protocol) protocol.echoed = True # The CM is the server, so fake a client wanting to talk to it # New API tube bytestream2 = bytestream_cls(stream, q, 'beta', bob_full_jid, self_full_jid, True) iq, si = bytestream2.create_si_offer(ns.TUBES) stream_node = si.addElement((ns.TUBES, 'stream')) stream_node['tube'] = str(stream_tube_id) stream.send(iq) si_reply_event, new_conn_event, socket_event = q.expect_many( EventPattern('stream-iq', iq_type='result'), EventPattern('dbus-signal', signal='NewRemoteConnection'), EventPattern('socket-connected')) bytestream2.check_si_reply(si_reply_event.stanza) tube = xpath.queryForNodes('/iq/si/tube[@xmlns="%s"]' % ns.TUBES, si_reply_event.stanza) assert len(tube) == 1 handle, access, conn_id = new_conn_event.args assert handle == bob_handle protocol = socket_event.protocol # we don't want to echo the access control byte protocol.echoed = False # start to read from the transport so we can read the control byte protocol.transport.startReading() t.check_new_connection_access(q, access_control, access, protocol) protocol.echoed = True # have the fake client open the stream bytestream1.open_bytestream() # have the fake client send us some data data = b'hello, world' bytestream1.send_data(data) binary = bytestream1.get_data(len(data)) assert binary == data, binary # have the fake client open the stream bytestream2.open_bytestream() # have the fake client send us some data data = b'hello, new world' bytestream2.send_data(data) binary = bytestream2.get_data(len(data)) assert binary == data, binary # peer closes the bytestream bytestream2.close() e = q.expect('dbus-signal', signal='ConnectionClosed') assertEquals(conn_id, e.args[0]) assertEquals(cs.CONNECTION_LOST, e.args[1]) t.cleanup()
def test(q, bus, conn, stream): iq_event, disco_event = q.expect_many( EventPattern('stream-iq', to=None, query_ns='vcard-temp', query_name='vCard'), EventPattern('stream-iq', to='localhost', query_ns=ns.DISCO_ITEMS)) acknowledge_iq(stream, iq_event.stanza) announce_socks5_proxy(q, stream, disco_event.stanza) join_muc(q, bus, conn, stream, '*****@*****.**') # bob offers a stream tube stream_tube_id = 1 presence = make_muc_presence('owner', 'moderator', '*****@*****.**', 'bob') tubes = presence.addElement((ns.TUBES, 'tubes')) tube = tubes.addElement((None, 'tube')) tube['type'] = 'stream' tube['service'] = 'echo' tube['id'] = str(stream_tube_id) parameters = tube.addElement((None, 'parameters')) stream.send(presence) def new_chan_predicate(e): path, props = e.args[0][0] return props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAM_TUBE e = q.expect('dbus-signal', signal='NewChannels', predicate=new_chan_predicate) channels = e.args[0] assert len(channels) == 1 path, props = channels[0] assert props[cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_STREAM_TUBE tube_chan = bus.get_object(conn.bus_name, path) tube_iface = dbus.Interface(tube_chan, cs.CHANNEL_TYPE_STREAM_TUBE) call_async(q, tube_iface, 'Accept', 0, 0, '', byte_arrays=True) accept_return_event, _ = q.expect_many( EventPattern('dbus-return', method='Accept'), EventPattern('dbus-signal', signal='TubeChannelStateChanged', args=[cs.TUBE_CHANNEL_STATE_OPEN])) unix_socket_adr = accept_return_event.value[0].decode() factory = EventProtocolClientFactory(q) reactor.connectUNIX(unix_socket_adr, factory) # expect SI request e = q.expect('stream-iq', to='[email protected]/bob', query_ns=ns.SI, query_name='si') bytestream, profile = create_from_si_offer(stream, q, BytestreamS5BRelay, e.stanza, '[email protected]/bob') result, si = bytestream.create_si_reply(e.stanza, 'test@localhost/Resource') si.addElement((ns.TUBES, 'tube')) stream.send(result) # wait SOCKS5 init iq id, mode, si, hosts = bytestream._expect_socks5_init() for jid, host, port in hosts: # the proxy is not announced because we are in a muc assert jid != 'proxy.localhost'
def test(q, bus, mc): params = dbus.Dictionary( { "account": "*****@*****.**", "password": "******" }, signature='sv') simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) # Two clients want to observe, approve and handle channels. Additionally, # Kopete recognises an "Urgent" flag on certain incoming channels, and # wants to bypass approval for them. empathy = SimulatedClient(q, bus, 'Empathy', observe=[text_fixed_properties], approve=[text_fixed_properties], handle=[text_fixed_properties], bypass_approval=False) kopete = SimulatedClient(q, bus, 'Kopete', observe=[contact_text_fixed_properties], approve=[contact_text_fixed_properties], handle=[contact_text_fixed_properties], bypass_approval=False) bypass = SimulatedClient(q, bus, 'Kopete.BypassApproval', observe=[], approve=[], handle=[urgent_fixed_properties], bypass_approval=True) # wait for MC to download the properties expect_client_setup(q, [empathy, kopete, bypass]) # subscribe to the OperationList interface (MC assumes that until this # property has been retrieved once, nobody cares) cd = bus.get_object(cs.CD, cs.CD_PATH) cd_props = dbus.Interface(cd, cs.PROPERTIES_IFACE) assert cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations') == [] # First, a non-urgent channel is created cdo_iface, chan, channel_properties, observe_events = announce_common( q, bus, empathy, kopete, account, conn, cd_props, False) # Both Observers indicate that they are ready to proceed for e in observe_events: q.dbus_return(e.message, signature='') expect_and_exercise_approval(q, bus, chan, channel_properties, empathy, kopete, cdo_iface, cd_props) # Now a channel that bypasses approval comes in. During this process, # we should never be asked to approve anything. approval = [ EventPattern('dbus-method-call', method='AddDispatchOperation'), ] q.forbid_events(approval) cdo_iface, chan, channel_properties, observe_events = announce_common( q, bus, empathy, kopete, account, conn, cd_props, True) # Both Observers indicate that they are ready to proceed for e in observe_events: q.dbus_return(e.message, signature='') # Kopete's BypassApproval part is asked to handle the channels e = q.expect('dbus-method-call', path=bypass.object_path, interface=cs.HANDLER, method='HandleChannels', handled=False) # Kopete accepts the channels q.dbus_return(e.message, signature='') q.unforbid_events(approval) # Regression test for fd.o #22670 closure = [ EventPattern('dbus-method-call', method='Close'), ] q.forbid_events(closure) bypass.release_name() sync_dbus(bus, q, mc) q.unforbid_events(closure) # Bring back that handler del bypass bypass = SimulatedClient(q, bus, 'Kopete.BypassApproval', observe=[], approve=[], handle=[urgent_fixed_properties], bypass_approval=True) expect_client_setup(q, [bypass]) # Another channel that bypasses approval comes in, but the handler that # bypasses approval fails. cdo_iface, chan, channel_properties, observe_events = announce_common( q, bus, empathy, kopete, account, conn, cd_props, True) # Both Observers indicate that they are ready to proceed for e in observe_events: q.dbus_return(e.message, signature='') # Kopete's BypassApproval part is asked to handle the channels e = q.expect('dbus-method-call', path=bypass.object_path, interface=cs.HANDLER, method='HandleChannels', handled=False) # Kopete's BypassApproval part fails to accept the channels q.dbus_raise(e.message, 'com.example.Broken', 'No way') # MC recovers by running the approvers and doing what they say expect_and_exercise_approval(q, bus, chan, channel_properties, empathy, kopete, cdo_iface, cd_props)
def test(q, bus, mc): params = dbus.Dictionary( { "account": "*****@*****.**", "password": "******" }, signature='sv') (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) srv_name = 'fu-bar-42' account_iface = dbus.Interface(account, cs.ACCOUNT) account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) # defaults to the empty string assertEquals(account_props.Get(cs.ACCOUNT, 'Service'), '') # set to a new value after creation call_async(q, account_props, 'Set', cs.ACCOUNT, 'Service', srv_name) q.expect_many( EventPattern('dbus-signal', path=account.object_path, signal='AccountPropertyChanged', interface=cs.ACCOUNT, args=[{ 'Service': srv_name }]), EventPattern('dbus-return', method='Set'), ) assertEquals(account_props.Get(cs.ACCOUNT, 'Service'), srv_name) # set to an invalid value (no actual change should occur) # leading non-alphabetic (make sure _ isn't considered alphabetic) call_async(q, account_props, 'Set', cs.ACCOUNT, 'Service', '_fu-bar') q.expect_many(EventPattern('dbus-error', method='Set')) assertEquals(account_props.Get(cs.ACCOUNT, 'Service'), srv_name) # leading non-alphabetic call_async(q, account_props, 'Set', cs.ACCOUNT, 'Service', '.moose') q.expect_many(EventPattern('dbus-error', method='Set')) assertEquals(account_props.Get(cs.ACCOUNT, 'Service'), srv_name) # gregexes have an option to be lenient about trailing newlines: # this makes sure we haven't made that mistake call_async(q, account_props, 'Set', cs.ACCOUNT, 'Service', srv_name + '\n') q.expect_many(EventPattern('dbus-error', method='Set')) assertEquals(account_props.Get(cs.ACCOUNT, 'Service'), srv_name) # set to an empty string call_async(q, account_props, 'Set', cs.ACCOUNT, 'Service', '') q.expect_many(EventPattern('dbus-return', method='Set')) assertEquals(account_props.Get(cs.ACCOUNT, 'Service'), '') # test creation with a service account_manager = bus.get_object(cs.AM, cs.AM_PATH) am_iface = dbus.Interface(account_manager, cs.AM) service_prop = dbus.Dictionary({ cs.ACCOUNT + '.Service': "moomin-troll", }, signature='sv') call_async( q, am_iface, 'CreateAccount', 'fakecm', # Connection_Manager 'fakeprotocol', # Protocol 'fakeaccount', # Display_Name params, # Parameters service_prop, # Properties ) ret = q.expect('dbus-return', method='CreateAccount') path = ret.value[0] account = bus.get_object(cs.tp_name_prefix + '.AccountManager', path) if_props = dbus.Interface(account, cs.PROPERTIES_IFACE) props = if_props.GetAll(cs.ACCOUNT) assertEquals(props.get('Service'), 'moomin-troll') # attempt creation with a bogus service service_prop = dbus.Dictionary({cs.ACCOUNT + '.Service': "1337"}, signature='sv') call_async( q, am_iface, 'CreateAccount', 'fakecm', # Connection_Manager 'fakeprotocol', # Protocol 'fakeaccount', # Display_Name params, # Parameters service_prop, # Properties ) ret = q.expect('dbus-error', method='CreateAccount')
def expect_close(q, path): q.expect_many(EventPattern('dbus-signal', signal='ChannelClosed', args=[path]), EventPattern('dbus-signal', signal='Closed', path=path))
""" Test connecting with different ContactList.DownloadAtConnection values """ import dbus from servicetest import EventPattern from gabbletest import exec_test, sync_stream, call_async import constants as cs import ns forbidden = [EventPattern('stream-iq', query_ns=ns.ROSTER)] def test_get_roster(q, bus, conn, stream): # DownloadAtConnection = True, so gabble should get the roster # automatically q.expect('stream-iq', query_ns=ns.ROSTER) # but calling ContactList.Download should not try and get the # roster again q.forbid_events(forbidden) conn.ContactList.Download() sync_stream(q, stream) q.unforbid_events(forbidden) def test_dont_get_roster(q, bus, conn, stream): # DownloadAtConnection = False, so let's make sure the roster # isn't fetched automatically q.forbid_events(forbidden)
def test(q, bus, conn, stream): conn.Connect() q.expect('dbus-signal', signal='StatusChanged', args=[0, 1]) alice_handle, bob_handle = conn.get_contact_handles_sync(['alice', 'bob']) call_async(q, conn.Requests, 'CreateChannel', { CHANNEL_TYPE: CHANNEL_TYPE_TEXT, TARGET_HANDLE_TYPE: HT_ROOM, TARGET_ID: room }) q.expect('stream-JOIN') event = q.expect('dbus-return', method='CreateChannel') path = event.value[0] channel = wrap_channel(bus.get_object(conn.bus_name, path), 'Text', ['Subject2']) assertContains(CHANNEL_IFACE_SUBJECT, channel.Properties.Get(CHANNEL, 'Interfaces')) # No topic set subject_props = channel.Properties.GetAll(CHANNEL_IFACE_SUBJECT) assertEquals('', subject_props['Subject']) assertEquals(0x7fffffffffffffff, subject_props['Timestamp']) assertEquals('', subject_props['Actor']) assertEquals(0, subject_props['ActorHandle']) # Before the topic arrives from the server, check that our API works okay. # FIXME: when we make SetSubject return asynchronously, this will need # revising. test_can_set(q, stream, channel) # We're told the channel's topic, and (in a separte message) who set it and # when. stream.sendMessage('332', stream.nick, room, ':Test123', prefix='idle.test.server') stream.sendMessage('333', stream.nick, room, 'bob', '1307802600', prefix='idle.test.server') # FIXME: signal these together, if possible. expect_subject_props_changed(q, { 'Subject': 'Test123' }) expect_subject_props_changed(q, { 'Timestamp': 1307802600, 'Actor': 'bob', 'ActorHandle': bob_handle, }, exact_timestamp=True) # Another user changes the topic. stream.sendMessage('TOPIC', room, ':I am as high as a kite', prefix='alice') expect_subject_props_changed(q, { 'Subject': 'I am as high as a kite', 'Actor': 'alice', 'ActorHandle': alice_handle, 'Timestamp': 1234, }) # BIP omits the : for the trailing parameter if it's a single word, make # sure we pass that as well stream.sendMessage('TOPIC', room, 'badgers!', prefix='alice') expect_subject_props_changed(q, { 'Subject': 'badgers!', 'Actor': 'alice', 'ActorHandle': alice_handle, 'Timestamp': 1234, }) test_can_set(q, stream, channel) # Topic is read/write, if we get ops it should stay that way forbidden = [ EventPattern('dbus-signal', signal='PropertiesChanged', predicate=lambda e: e.args[0] == CHANNEL_IFACE_SUBJECT) ] q.forbid_events(forbidden) # Set ops, check that t flag becomes a no-op change_channel_mode (stream, '+o ' + stream.nick) change_channel_mode (stream, '+t') change_channel_mode (stream, '-t') change_channel_mode (stream, '-o ' + stream.nick) # Check that other flags don't cause issues change_channel_mode (stream, '+n') change_channel_mode (stream, '+n') change_channel_mode (stream, '+to ' + stream.nick) change_channel_mode (stream, '-to ' + stream.nick) sync_stream(q, stream) sync_dbus(bus, q, conn) q.unforbid_events(forbidden) # back to normal? test_can_set(q, stream, channel) # Check if setting ops gives us write access on +t channels change_channel_mode (stream, '+t') expect_and_check_can_set(q, channel, False) change_channel_mode (stream, '+o ' + stream.nick) expect_and_check_can_set(q, channel, True) change_channel_mode (stream, '-o ' + stream.nick) expect_and_check_can_set(q, channel, False) change_channel_mode (stream, '-t') expect_and_check_can_set(q, channel, True) # And back to normal again ? test_can_set(q, stream, channel) channel.Subject2.SetSubject('') # Verify that we send an empty final parameter ("clear the topic") as # opposed to no final parameter ("what is the topic"). q.expect('stream-TOPIC', data=[room, ''])
def pickup(self): CallTest.pickup(self, held=True) q = self.q stream = self.stream chan = self.chan cstream = self.audio_stream assertEquals((cs.HS_HELD, cs.HSR_REQUESTED), chan.Hold.GetHoldState()) recv_state = cstream.Get(cs.CALL_STREAM_IFACE_MEDIA, "ReceivingState", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals(cs.CALL_STREAM_FLOW_STATE_STOPPED, recv_state) send_state = cstream.Get(cs.CALL_STREAM_IFACE_MEDIA, "SendingState", dbus_interface=dbus.PROPERTIES_IFACE) assertEquals(cs.CALL_STREAM_FLOW_STATE_STOPPED, send_state) # Now we decide we do actually want to speak to them, and unhold. # Ensure that if Gabble sent the <unhold/> stanza too early it's already # arrived. sync_stream(q, stream) q.forbid_events(self.unhold_event) call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='ReceivingStateChanged', args=[cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface=cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args=[cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface=cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-return', method='RequestHold', value=()), ) # Ensure that if Gabble sent the <unhold/> stanza too early it's already # arrived. sync_stream(q, stream) q.unforbid_events(self.unhold_event) cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_UNHELD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args=[cs.CALL_STREAM_FLOW_STATE_STARTED], interface=cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args=[cs.CALL_STREAM_FLOW_STATE_STARTED], interface=cs.CALL_STREAM_IFACE_MEDIA), *self.unhold_event) # Hooray! Now let's check that Hold works properly once the call's fully # established. # ---- Test 1: GetHoldState returns unheld and unhold is a no-op ---- hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_UNHELD, hold_state chan.Hold.RequestHold(False) # ---- Test 2: successful hold ---- call_async(q, chan.Hold, 'RequestHold', True) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args=[cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface=cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args=[cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface=cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-return', method='RequestHold', value=()), *self.hold_event) cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args=[cs.CALL_STREAM_FLOW_STATE_STOPPED], interface=cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args=[cs.CALL_STREAM_FLOW_STATE_STOPPED], interface=cs.CALL_STREAM_IFACE_MEDIA), ) # ---- Test 3: GetHoldState returns held and hold is a no-op ---- hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_HELD, hold_state chan.Hold.RequestHold(True) # ---- Test 4: successful unhold ---- q.forbid_events(self.unhold_event) call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='ReceivingStateChanged', args=[cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface=cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='SendingStateChanged', args=[cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface=cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-return', method='RequestHold', value=()), ) # Ensure that if Gabble sent the <unhold/> stanza too early it's already # arrived. sync_stream(q, stream) q.unforbid_events(self.unhold_event) cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_UNHELD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args=[cs.CALL_STREAM_FLOW_STATE_STARTED], interface=cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args=[cs.CALL_STREAM_FLOW_STATE_STARTED], interface=cs.CALL_STREAM_IFACE_MEDIA), *self.unhold_event) # ---- Test 5: GetHoldState returns False and unhold is a no-op ---- hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_UNHELD, hold_state chan.Hold.RequestHold(False) # ---- Test 6: 3 parallel calls to hold ---- hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_UNHELD, hold_state call_async(q, chan.Hold, 'RequestHold', True) call_async(q, chan.Hold, 'RequestHold', True) call_async(q, chan.Hold, 'RequestHold', True) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args=[cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface=cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args=[cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface=cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-return', method='RequestHold', value=()), *self.hold_event) cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='SendingStateChanged', args=[cs.CALL_STREAM_FLOW_STATE_STOPPED], interface=cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args=[cs.CALL_STREAM_FLOW_STATE_STOPPED], interface=cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_HELD, cs.HSR_REQUESTED]), ) # ---- Test 7: 3 parallel calls to unhold ---- q.forbid_events(self.unhold_event) call_async(q, chan.Hold, 'RequestHold', False) call_async(q, chan.Hold, 'RequestHold', False) call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args=[cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface=cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args=[cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface=cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-return', method='RequestHold', value=()), ) # Ensure that if Gabble sent the <unhold/> stanza too early it's already # arrived. sync_stream(q, stream) q.unforbid_events(self.unhold_event) cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_UNHELD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args=[cs.CALL_STREAM_FLOW_STATE_STARTED], interface=cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args=[cs.CALL_STREAM_FLOW_STATE_STARTED], interface=cs.CALL_STREAM_IFACE_MEDIA), *self.unhold_event) # ---- Test 8: hold, then change our minds before s-e has responded ---- hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_UNHELD, hold_state call_async(q, chan.Hold, 'RequestHold', True) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_HOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args=[cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface=cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args=[cs.CALL_STREAM_FLOW_STATE_PENDING_STOP], interface=cs.CALL_STREAM_IFACE_MEDIA), *self.hold_event) q.forbid_events(self.unhold_event) call_async(q, chan.Hold, 'RequestHold', False) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_PENDING_UNHOLD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args=[cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface=cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args=[cs.CALL_STREAM_FLOW_STATE_PENDING_START], interface=cs.CALL_STREAM_IFACE_MEDIA), # Gabble shouldn't send <unhold/> here because s-e might have # already relinquished the audio hardware. ) sync_stream(q, stream) q.unforbid_events(self.unhold_event) try: cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) except dbus.DBusException, e: assertEquals(cs.INVALID_ARGUMENT, e.get_dbus_name())
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()
try: cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STOPPED, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) except dbus.DBusException, e: assertEquals(cs.INVALID_ARGUMENT, e.get_dbus_name()) cstream.CompleteReceivingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) cstream.CompleteSendingStateChange( cs.CALL_STREAM_FLOW_STATE_STARTED, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) q.expect_many( EventPattern('dbus-signal', signal='HoldStateChanged', args=[cs.HS_UNHELD, cs.HSR_REQUESTED]), EventPattern('dbus-signal', signal='SendingStateChanged', args=[cs.CALL_STREAM_FLOW_STATE_STARTED], interface=cs.CALL_STREAM_IFACE_MEDIA), EventPattern('dbus-signal', signal='ReceivingStateChanged', args=[cs.CALL_STREAM_FLOW_STATE_STARTED], interface=cs.CALL_STREAM_IFACE_MEDIA), *self.unhold_event) hold_state = chan.Hold.GetHoldState() assert hold_state[0] == cs.HS_UNHELD, hold_state # ---- Test 9: unhold, then change our minds before s-e has responded --
def test(q, bus, conn, stream): jid = '*****@*****.**' # <message type="chat"><body>hello</body</message> m = domish.Element((None, 'message')) m['from'] = '[email protected]/Pidgin' m['type'] = 'chat' m.addElement('body', content='hello') stream.send(m) event = q.expect('dbus-signal', signal='NewChannels') assertEquals(cs.CHANNEL_TYPE_TEXT, event.args[0][0][1][cs.CHANNEL_TYPE]) assertEquals(cs.HT_CONTACT, event.args[0][0][1][cs.TARGET_HANDLE_TYPE]) assertEquals(jid, event.args[0][0][1][cs.TARGET_ID]) foo_at_bar_dot_com_handle = event.args[0][0][1][cs.TARGET_HANDLE] text_chan = bus.get_object(conn.bus_name, event.args[0][0][0]) # Exercise basic Channel Properties from spec 0.17.7 channel_props = text_chan.GetAll(cs.CHANNEL, dbus_interface=dbus.PROPERTIES_IFACE) assertEquals(foo_at_bar_dot_com_handle, channel_props.get('TargetHandle')) assert channel_props.get('TargetHandleType') == 1,\ channel_props.get('TargetHandleType') assertEquals(cs.CHANNEL_TYPE_TEXT, channel_props.get('ChannelType')) assertContains(cs.CHANNEL_IFACE_CHAT_STATE, channel_props.get('Interfaces', ())) assertContains(cs.CHANNEL_IFACE_MESSAGES, channel_props.get('Interfaces', ())) assert channel_props['TargetID'] == jid,\ (channel_props['TargetID'], jid) assert channel_props['Requested'] == False assertEquals(foo_at_bar_dot_com_handle, channel_props['InitiatorHandle']) assert channel_props['InitiatorID'] == jid,\ (channel_props['InitiatorID'], jid) message_received = q.expect('dbus-signal', signal='MessageReceived') 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 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'] dbus.Interface(text_chan, cs.CHANNEL_TYPE_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 an action using the Messages API # In Gabble, this is a Notice, but we don't support those. greeting = [ dbus.Dictionary({ 'message-type': cs.MT_ACTION, }, signature='sv'), { 'content-type': 'text/plain', 'content': u"waves", } ] dbus.Interface(text_chan, cs.CHANNEL_IFACE_MESSAGES ).SendMessage(greeting, dbus.UInt32(0)) stream_message, message_sent = q.expect_many( EventPattern('stream-message'), EventPattern('dbus-signal', signal='MessageSent'), ) elem = stream_message.stanza assert elem.name == 'message' assert elem['type'] == 'chat' found = False for e in elem.elements(): if e.name == 'body': found = True e.children[0] == u'/me waves' break assert found, elem.toXml() sent_message = message_sent.args[0] assert len(sent_message) == 2, sent_message header = sent_message[0] assert header['message-type'] == 1, header # Action body = sent_message[1] assert body['content-type'] == 'text/plain', body assert body['content'] == u'waves', body dbus.Interface(text_chan, cs.CHANNEL_IFACE_MESSAGES ).SendMessage([{}, { 'content-type': 'text/plain', 'content': 'goodbye', }], 0) stream_message, message_sent = q.expect_many( EventPattern('stream-message'), EventPattern('dbus-signal', signal='MessageSent'), ) elem = stream_message.stanza assert elem.name == 'message' assert elem['type'] == 'chat' found = False for e in elem.elements(): if e.name == 'body': found = True e.children[0] == u'goodbye' break assert found, elem.toXml() 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 conn.Disconnect() q.expect('dbus-signal', signal='StatusChanged', args=[2, 1])
def test(q, bus, mc): # Create an account params = dbus.Dictionary( { "account": "*****@*****.**", "password": "******" }, signature='sv') (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) # Events that indicate that Reconnect might have done something looks_like_reconnection = [ EventPattern('dbus-method-call', method='RequestConnection'), EventPattern('dbus-method-call', method='Disconnect'), ] q.forbid_events(looks_like_reconnection) # While we want to be online but the account is disabled, Reconnect is a # no-op. Set Enabled to False explicitly, so we're less reliant on initial # state. call_async(q, account, 'Set', cs.ACCOUNT, 'Enabled', False, dbus_interface=cs.PROPERTIES_IFACE) q.expect('dbus-return', method='Set') requested_presence = dbus.Struct( (dbus.UInt32(cs.PRESENCE_AVAILABLE), dbus.String(u'available'), dbus.String(u''))) call_async(q, account, 'Set', cs.ACCOUNT, 'RequestedPresence', requested_presence, dbus_interface=cs.PROPERTIES_IFACE) q.expect('dbus-return', method='Set') call_async(q, account, 'Reconnect', dbus_interface=cs.ACCOUNT) q.expect('dbus-return', method='Reconnect') sync_dbus(bus, q, account) # While we want to be offline but the account is enabled, Reconnect is # still a no-op. requested_presence = dbus.Struct( (dbus.UInt32(cs.PRESENCE_OFFLINE), dbus.String(u'offline'), dbus.String(u''))) call_async(q, account, 'Set', cs.ACCOUNT, 'RequestedPresence', requested_presence, dbus_interface=cs.PROPERTIES_IFACE) q.expect_many( EventPattern('dbus-return', method='Set'), EventPattern('dbus-signal', path=account.object_path, signal='AccountPropertyChanged', interface=cs.ACCOUNT), ) # Enable the account call_async(q, account, 'Set', cs.ACCOUNT, 'Enabled', True, dbus_interface=cs.PROPERTIES_IFACE) q.expect_many( EventPattern('dbus-return', method='Set'), EventPattern('dbus-signal', path=account.object_path, signal='AccountPropertyChanged', interface=cs.ACCOUNT), ) call_async(q, account, 'Reconnect', dbus_interface=cs.ACCOUNT) q.expect('dbus-return', method='Reconnect') sync_dbus(bus, q, account) # Actually go online now q.unforbid_events(looks_like_reconnection) requested_presence = dbus.Struct( (dbus.UInt32(cs.PRESENCE_AVAILABLE), dbus.String(u'brb'), dbus.String(u'Be back soon!'))) account.Set(cs.ACCOUNT, 'RequestedPresence', requested_presence, dbus_interface=cs.PROPERTIES_IFACE) e = q.expect('dbus-method-call', method='RequestConnection', args=['fakeprotocol', params], destination=tp_name_prefix + '.ConnectionManager.fakecm', path=tp_path_prefix + '/ConnectionManager/fakecm', interface=tp_name_prefix + '.ConnectionManager', handled=False) conn = SimulatedConnection(q, bus, 'fakecm', 'fakeprotocol', '_', 'myself') q.dbus_return(e.message, conn.bus_name, conn.object_path, signature='so') # MC does some setup, including fetching the list of Channels q.expect_many( EventPattern('dbus-method-call', interface=cs.PROPERTIES_IFACE, method='GetAll', args=[cs.CONN_IFACE_REQUESTS], path=conn.object_path, handled=True), ) # MC prepares the connection, does any pre-Connect setup, then # calls Connect q.expect('dbus-method-call', method='Connect', path=conn.object_path, handled=True) # Connect succeeds conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED) # Assert that the NormalizedName is harvested from the Connection at some # point while 1: e = q.expect('dbus-signal', interface=cs.ACCOUNT, signal='AccountPropertyChanged', path=account.object_path) if 'NormalizedName' in e.args[0]: assert e.args[0]['NormalizedName'] == 'myself', e.args break # Check the requested presence is online properties = account.GetAll(cs.ACCOUNT, dbus_interface=cs.PROPERTIES_IFACE) assert properties is not None assert properties.get('RequestedPresence') == requested_presence, \ properties.get('RequestedPresence') # Reconnect account.Reconnect(dbus_interface=cs.ACCOUNT) q.expect('dbus-method-call', method='Disconnect', path=conn.object_path, handled=True) e = q.expect('dbus-method-call', method='RequestConnection', args=['fakeprotocol', params], destination=tp_name_prefix + '.ConnectionManager.fakecm', path=tp_path_prefix + '/ConnectionManager/fakecm', interface=tp_name_prefix + '.ConnectionManager', handled=False) # The object path needs to be different from the first simulated # connection which we made above, because the object isn't removed # from this bus and it's actually hard to do so because it's not # really on a bus, it's on the queue. So let's just change the # object path and it's fine. conn = SimulatedConnection(q, bus, 'fakecm', 'fakeprotocol', 'second', 'myself') q.dbus_return(e.message, conn.bus_name, conn.object_path, signature='so') q.expect_many( EventPattern('dbus-method-call', interface=cs.PROPERTIES_IFACE, method='GetAll', args=[cs.CONN_IFACE_REQUESTS], path=conn.object_path, handled=True), ) q.expect('dbus-method-call', method='Connect', path=conn.object_path, handled=True) conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED) # Put the account offline requested_presence = (dbus.UInt32(cs.PRESENCE_OFFLINE), 'offline', '') account.Set(cs.ACCOUNT, 'RequestedPresence', requested_presence, dbus_interface=cs.PROPERTIES_IFACE) # In response, MC tells us to Disconnect, and we do q.expect('dbus-method-call', method='Disconnect', path=conn.object_path, handled=True) properties = account.GetAll(cs.ACCOUNT, dbus_interface=cs.PROPERTIES_IFACE) assert properties['Connection'] == '/' assert properties['ConnectionStatus'] == cs.CONN_STATUS_DISCONNECTED assert properties['CurrentPresence'] == requested_presence assert properties['RequestedPresence'] == requested_presence
def test(q, bus, mc): # Create an account (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) account_iface = dbus.Interface(account, cs.ACCOUNT) account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) call_async(q, account, 'Set', cs.ACCOUNT, 'Enabled', False, dbus_interface=cs.PROPERTIES_IFACE) q.expect('dbus-return', method='Set') # Enable the account call_async(q, account, 'Set', cs.ACCOUNT, 'Enabled', True, dbus_interface=cs.PROPERTIES_IFACE) # Set online presence presence = dbus.Struct( (dbus.UInt32(cs.PRESENCE_BUSY), 'busy', 'Fixing MC bugs'), signature='uss') call_async(q, account, 'Set', cs.ACCOUNT, 'RequestedPresence', presence, dbus_interface=cs.PROPERTIES_IFACE) e = q.expect('dbus-method-call', method='RequestConnection', args=['fakeprotocol', params], destination=tp_name_prefix + '.ConnectionManager.fakecm', path=tp_path_prefix + '/ConnectionManager/fakecm', interface=tp_name_prefix + '.ConnectionManager', handled=False) conn = SimulatedConnection(q, bus, 'fakecm', 'fakeprotocol', '_', 'myself', has_presence=True) q.dbus_return(e.message, conn.bus_name, conn.object_path, signature='so') # MC prepares the connection, does any pre-Connect setup, then # calls Connect q.expect('dbus-method-call', method='Connect', path=conn.object_path, handled=True) # Connect succeeds conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED) conn = drop_and_expect_reconnect(q, bus, conn) conn = drop_and_expect_reconnect(q, bus, conn) conn = drop_and_expect_reconnect(q, bus, conn) forbidden = [EventPattern('dbus-method-call', method='RequestConnection')] q.forbid_events(forbidden) # Connection falls over for a miscellaneous reason conn.StatusChanged(cs.CONN_STATUS_DISCONNECTED, cs.CSR_NETWORK_ERROR) # Right, that's it, I'm giving up... # This test can be considered to have succeeded if we don't # RequestConnection again before the test fails due to timeout. try: q.expect('the end of the world') except TimeoutError: return else: raise AssertionError('An impossible event happened')
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_gtalk_weirdness(q, bus, conn, stream, room_jid): """ There's a strange bug in the Google Talk MUC server where it sends the <conflict/> stanza twice. This has been reported to their server team; but in any case it triggered a crazy bug in Gabble, so here's a regression test. """ # Implementation detail: Gabble uses the first part of your jid (if you # don't have an alias) as your room nickname, and appends an underscore a # few times before giving up. jids = ['%s/test%s' % (room_jid, x) for x in ['', '_', '__']] member, member_, member__ = jids # Gabble should never get as far as trying to join as 'test__' since # joining as 'test_' will succeed. q.forbid_events([EventPattern('stream-presence', to=member__)]) call_async( q, conn.Requests, 'CreateChannel', dbus.Dictionary( { cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, cs.TARGET_HANDLE_TYPE: cs.HT_ROOM, cs.TARGET_ID: room_jid, }, signature='sv')) # Gabble first tries to join as test q.expect('stream-presence', to=member) # Google Talk says no from 'test', twice. presence = elem('presence', from_=member, type='error')(elem(ns.MUC, 'x'), elem('error', type='cancel')(elem( ns.STANZA, 'conflict'), )) stream.send(presence) stream.send(presence) # Gabble should try to join again as test_ q.expect('stream-presence', to=member_) # Since 'test_' is not in use in the MUC, joining should succeed. According # to XEP-0045 §7.1.3 <http://xmpp.org/extensions/xep-0045.html#enter-pres>: # The service MUST first send the complete list of the existing occupants # to the new occupant and only then send the new occupant's own presence # to the new occupant # but groupchat.google.com cheerfully violates this. stream.send(make_muc_presence('none', 'participant', room_jid, 'test_')) # Here's some other random person, who owns the MUC. stream.send( make_muc_presence('owner', 'moderator', room_jid, 'foobar_gmail.com')) # And here's our hypothetical other self. stream.send(make_muc_presence('none', 'participant', room_jid, 'test')) # The Gabble bug makes this time out: because Gabble thinks it's joining as # test__ it ignores the presence for test_, since it's not flagged with # code='210' to say “this is you”. (This is acceptable behaviour by the # server: it only needs to include code='210' if it's assigned the client a # name other than the one it asked for. # # The forbidden stream-presence event above doesn't blow up here because # servicetest doesn't process events on the 'stream-*' queue at all when # we're not waiting for one. But during disconnection in the test clean-up, # the forbidden event is encountered and correctly flagged up. event = q.expect('dbus-return', method='CreateChannel') path, _ = event.value text_chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text') # As far as Gabble's concerned, the two other participants joined # immediately after we did. We can't request handles for them before we # try to join the MUC, because until we do so, Gabble doesn't know that # room_jid is a MUC, and so considers these three JIDs to be different # resources of the same contact. There is no race between this method # returning and MembersChangedDetailed firing, because libdbus reorders # messages when you make blocking calls. handle, handle_, handle__, foobar_handle = conn.get_contact_handles_sync( jids + ['%s/foobar_gmail.com' % room_jid]) q.expect('dbus-signal', signal='MembersChangedDetailed', predicate=lambda e: e.args[0:4] == [[foobar_handle], [], [], []]) q.expect('dbus-signal', signal='MembersChangedDetailed', predicate=lambda e: e.args[0:4] == [[handle], [], [], []]) group_props = text_chan.Properties.GetAll(cs.CHANNEL_IFACE_GROUP) assertEquals(handle_, group_props['SelfHandle']) assertSameSets([handle, handle_, foobar_handle], group_props['Members'])
def test(q, bus, conn, stream): self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") # When Gabble initially requests its avatar from the server, it discovers # it has none. expect_and_handle_get_vcard(q, stream) handle, signalled_token = q.expect('dbus-signal', signal='AvatarUpdated').args assertEquals(self_handle, handle) assertEquals('', signalled_token) # The user sets an avatar. call_async(q, conn.Avatars, 'SetAvatar', AVATAR_1_DATA, AVATAR_1_MIME_TYPE) expect_and_handle_get_vcard(q, stream) expect_and_handle_set_vcard(q, stream) # It's signalled on D-Bus … set_ret, avatar_updated = q.expect_many( EventPattern('dbus-return', method='SetAvatar'), EventPattern('dbus-signal', signal='AvatarUpdated'), ) returned_token, = set_ret.value handle, signalled_token = avatar_updated.args assertEquals(self_handle, handle) assertEquals(returned_token, signalled_token) # … and also on XMPP. broadcast = q.expect('stream-presence', to=None) broadcast_hash = extract_hash_from_presence(broadcast.stanza) assertEquals(AVATAR_1_SHA1, broadcast_hash) # If applications ask Gabble for information about the user's own avatar, # it should be able to answer. (Strictly speaking, expecting Gabble to know # the avatar data is risky because Gabble discards cached vCards after a # while, but we happen to know it takes 20 seconds or so for that to # happen.) known = conn.Avatars.GetKnownAvatarTokens([self_handle]) assertEquals({self_handle: signalled_token}, known) conn.Avatars.RequestAvatars([self_handle]) retrieved = q.expect('dbus-signal', signal='AvatarRetrieved') handle, token, data, mime_type = retrieved.args assertEquals(self_handle, handle) assertEquals(signalled_token, token) assertEquals(AVATAR_1_DATA, data) assertEquals(AVATAR_1_MIME_TYPE, mime_type) # Well, that was quite easy. How about we join a MUC? XEP-0153 §4.1 says: # If a client supports the protocol defined herein, it […] SHOULD # also include the update child in directed presence stanzas (e.g., # directed presence sent when joining Multi-User Chat [5] rooms). # — http://xmpp.org/extensions/xep-0153.html#bizrules-presence join_event = try_to_join_muc(q, bus, conn, stream, MUC) directed_hash = extract_hash_from_presence(join_event.stanza) assertEquals(AVATAR_1_SHA1, directed_hash) # There are two others in the MUC: fredrik has no avatar, wendy has an # avatar. We, of course, have our own avatar. stream.send(make_muc_presence('none', 'participant', MUC, 'fredrik')) stream.send( make_muc_presence('none', 'participant', MUC, 'wendy', photo=AVATAR_2_SHA1)) stream.send( make_muc_presence('owner', 'moderator', MUC, 'test', photo=AVATAR_1_SHA1)) path, _ = q.expect('dbus-return', method='CreateChannel').value chan = wrap_channel(bus.get_object(conn.bus_name, path), 'Text') members = chan.Properties.Get(cs.CHANNEL_IFACE_GROUP, 'Members') assertLength(3, members) fredrik, wendy, muc_self_handle = conn.get_contact_handles_sync( ['%s/%s' % (MUC, x) for x in ["fredrik", "wendy", "test"]]) known = conn.Avatars.GetKnownAvatarTokens(members) # <https://bugs.freedesktop.org/show_bug.cgi?id=32017>: this assertion # failed, the MUC self handle's token was the empty string. assertEquals(AVATAR_1_SHA1, known[muc_self_handle]) assertEquals(AVATAR_2_SHA1, known[wendy]) assertEquals('', known[fredrik]) # 'k, cool. Wendy loves our avatar and switches to it. stream.send( make_muc_presence('none', 'participant', MUC, 'wendy', photo=AVATAR_1_SHA1)) # Okay this is technically assuming that we just expose the SHA1 sums # directly which is not guaranteed … but we do. q.expect('dbus-signal', signal='AvatarUpdated', args=[wendy, AVATAR_1_SHA1]) # Fredrik switches too. stream.send( make_muc_presence('none', 'participant', MUC, 'fredrik', photo=AVATAR_1_SHA1)) q.expect('dbus-signal', signal='AvatarUpdated', args=[fredrik, AVATAR_1_SHA1]) # And we switch to some other avatar. Gabble should update its vCard, and # then update its MUC presence (which the test, acting as the MUC server, # must echo). call_async(q, conn.Avatars, 'SetAvatar', AVATAR_2_DATA, AVATAR_2_MIME_TYPE) expect_and_handle_get_vcard(q, stream) expect_and_handle_set_vcard(q, stream) muc_presence = q.expect('stream-presence', to=('%s/test' % MUC)) directed_hash = extract_hash_from_presence(muc_presence.stanza) stream.send( make_muc_presence('owner', 'moderator', MUC, 'test', photo=directed_hash)) # Gabble should signal an avatar update for both our global self-handle and # our MUC self-handle. (The first of these of course does not need to wait # for the MUC server to echo our presence.) q.expect_many( EventPattern('dbus-signal', signal='AvatarUpdated', args=[self_handle, AVATAR_2_SHA1]), EventPattern('dbus-signal', signal='AvatarUpdated', args=[muc_self_handle, AVATAR_2_SHA1]), )
def test_channel_creation(q, bus, account, client, conn, ensure=False, prefer=None, channel_type=cs.CHANNEL_TYPE_TEXT): user_action_time = dbus.Int64(1238582606) hints = dbus.Dictionary({'badger': 42, 'snake': 'pony'}, signature='sv') if prefer is None: prefer = client cd = ChannelDispatcher(bus) assert cd.Properties.Get(cs.CD, "SupportsRequestHints") # chat UI calls ChannelDispatcher.EnsureChannelWithHints or # CreateChannelWithHints request = dbus.Dictionary( { cs.CHANNEL + '.ChannelType': channel_type, cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT, cs.CHANNEL + '.TargetID': 'juliet', }, signature='sv') call_async( q, cd, (ensure and 'EnsureChannelWithHints' or 'CreateChannelWithHints'), account.object_path, request, user_action_time, prefer.bus_name, hints, dbus_interface=cs.CD) ret = q.expect('dbus-return', method=(ensure and 'EnsureChannelWithHints' or 'CreateChannelWithHints')) request_path = ret.value[0] # chat UI connects to signals and calls ChannelRequest.Proceed() cr = bus.get_object(cs.AM, request_path) request_props = cr.GetAll(cs.CR, dbus_interface=cs.PROPERTIES_IFACE) assert request_props['Account'] == account.object_path assert request_props['Requests'] == [request] assert request_props['UserActionTime'] == user_action_time assert request_props['PreferredHandler'] == prefer.bus_name assert request_props['Interfaces'] == [] assertEquals(hints, request_props['Hints']) cr.Proceed(dbus_interface=cs.CR) # FIXME: should the EnsureChannel/CreateChannel call, and the AddRequest # call, be in a defined order? Probably not though, since CMs and Clients # aren't meant to be the same process! cm_request_call, add_request_call = q.expect_many( EventPattern('dbus-method-call', interface=cs.CONN_IFACE_REQUESTS, method=(ensure and 'EnsureChannel' or 'CreateChannel'), path=conn.object_path, args=[request], handled=False), EventPattern('dbus-method-call', handled=False, interface=cs.CLIENT_IFACE_REQUESTS, method='AddRequest'), ) assert add_request_call.args[0] == request_path assert add_request_call.path == prefer.object_path request_props = add_request_call.args[1] assert request_props[cs.CR + '.Account'] == account.object_path assert request_props[cs.CR + '.Requests'] == [request] assert request_props[cs.CR + '.UserActionTime'] == user_action_time assert request_props[cs.CR + '.PreferredHandler'] == prefer.bus_name assert request_props[cs.CR + '.Interfaces'] == [] q.dbus_return(add_request_call.message, signature='') # Time passes. A channel is returned. channel_immutable = dbus.Dictionary(request) channel_immutable[cs.CHANNEL + '.InitiatorID'] = conn.self_ident channel_immutable[cs.CHANNEL + '.InitiatorHandle'] = conn.self_handle channel_immutable[cs.CHANNEL + '.Requested'] = True channel_immutable[cs.CHANNEL + '.Interfaces'] = \ dbus.Array([], signature='s') channel_immutable[cs.CHANNEL + '.TargetHandle'] = \ conn.ensure_handle(cs.HT_CONTACT, 'juliet') channel = SimulatedChannel(conn, channel_immutable) # this order of events is guaranteed by telepathy-spec (since 0.17.14) if ensure: q.dbus_return( cm_request_call.message, True, # <- Yours channel.object_path, channel.immutable, signature='boa{sv}') else: # Create q.dbus_return(cm_request_call.message, channel.object_path, channel.immutable, signature='oa{sv}') channel.announce() if channel_type == cs.CHANNEL_TYPE_TEXT: # Observer should get told, processing waits for it e = q.expect('dbus-method-call', path=client.object_path, interface=cs.OBSERVER, method='ObserveChannels', handled=False) assert e.args[0] == account.object_path, e.args assert e.args[1] == conn.object_path, e.args assert e.args[3] == '/', e.args # no dispatch operation assert e.args[4] == [request_path], e.args channels = e.args[2] assert len(channels) == 1, channels assert channels[0][0] == channel.object_path, channels assert channels[0][1] == channel_immutable, channels info = e.args[5] assert info['request-properties'] == { request_path: request_props }, info # Observer says "OK, go" q.dbus_return(e.message, signature='') # Handler is next e = q.expect('dbus-method-call', path=prefer.object_path, interface=cs.HANDLER, method='HandleChannels', handled=False) assert e.args[0] == account.object_path, e.args assert e.args[1] == conn.object_path, e.args channels = e.args[2] assert len(channels) == 1, channels assert channels[0][0] == channel.object_path, channels assert channels[0][1] == channel_immutable, channels assert e.args[3] == [request_path], e.args assert e.args[4] == user_action_time assert isinstance(e.args[5], dict) assert len(e.args) == 6 # Handler accepts the Channels q.dbus_return(e.message, signature='') # SucceededWithChannel is fired first e = q.expect('dbus-signal', path=request_path, interface=cs.CR, signal='SucceededWithChannel') assertEquals(conn.object_path, e.args[0]) assert isinstance(e.args[1], dict), e.args[1] assertEquals(channel.object_path, e.args[2]) assertEquals(channel_immutable, e.args[3]) # CR emits Succeeded q.expect('dbus-signal', path=request_path, interface=cs.CR, signal='Succeeded') # Close the channel channel.close()
def test(q, bus, mc): params = dbus.Dictionary( { "account": "*****@*****.**", "password": "******" }, signature='sv') simulated_cm, account = create_fakecm_account(q, bus, mc, params) conn = enable_fakecm_account(q, bus, mc, account, params) text_fixed_properties = dbus.Dictionary( { cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT, cs.CHANNEL + '.ChannelType': cs.CHANNEL + '.Type.RespawnObservers' }, signature='sv') # Logger is an activatable Observer who will crash logger_bus = dbus.bus.BusConnection() logger_bus.set_exit_on_disconnect(False) # we'll disconnect later # Kopete is an Approver, Handler and will not crash kopete_bus = dbus.bus.BusConnection() q.attach_to_bus(logger_bus) q.attach_to_bus(kopete_bus) kopete = SimulatedClient(q, kopete_bus, 'Kopete', observe=[], approve=[text_fixed_properties], handle=[text_fixed_properties], bypass_approval=False) # wait for MC to download the properties expect_client_setup(q, [kopete]) # subscribe to the OperationList interface (MC assumes that until this # property has been retrieved once, nobody cares) cd = bus.get_object(cs.CD, cs.CD_PATH) cd_props = dbus.Interface(cd, cs.PROPERTIES_IFACE) assert cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations') == [] channel_properties = dbus.Dictionary(text_fixed_properties, signature='sv') channel_properties[cs.CHANNEL + '.TargetID'] = 'juliet' channel_properties[cs.CHANNEL + '.TargetHandle'] = \ conn.ensure_handle(cs.HT_CONTACT, 'juliet') channel_properties[cs.CHANNEL + '.InitiatorID'] = 'juliet' channel_properties[cs.CHANNEL + '.InitiatorHandle'] = \ conn.ensure_handle(cs.HT_CONTACT, 'juliet') channel_properties[cs.CHANNEL + '.Requested'] = False channel_properties[cs.CHANNEL + '.Interfaces'] = dbus.Array(signature='s') chan = SimulatedChannel(conn, channel_properties) chan.announce() # A channel dispatch operation is created e = q.expect('dbus-signal', path=cs.CD_PATH, interface=cs.CD_IFACE_OP_LIST, signal='NewDispatchOperation') cdo_path = e.args[0] cdo_properties = e.args[1] assert cdo_properties[cs.CDO + '.Account'] == account.object_path assert cdo_properties[cs.CDO + '.Connection'] == conn.object_path assert cs.CDO + '.Interfaces' in cdo_properties handlers = cdo_properties[cs.CDO + '.PossibleHandlers'][:] handlers.sort() assert handlers == [cs.tp_name_prefix + '.Client.Kopete'], handlers assert cs.CD_IFACE_OP_LIST in cd_props.Get(cs.CD, 'Interfaces') assert cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations') ==\ [(cdo_path, cdo_properties)] cdo = bus.get_object(cs.CD, cdo_path) cdo_iface = dbus.Interface(cdo, cs.CDO) cdo_props_iface = dbus.Interface(cdo, cs.PROPERTIES_IFACE) assert cdo_props_iface.Get(cs.CDO, 'Interfaces') == \ cdo_properties[cs.CDO + '.Interfaces'] assert cdo_props_iface.Get(cs.CDO, 'Connection') == conn.object_path assert cdo_props_iface.Get(cs.CDO, 'Account') == account.object_path assert cdo_props_iface.Get(cs.CDO, 'Channels') == [(chan.object_path, channel_properties)] assert cdo_props_iface.Get(cs.CDO, 'PossibleHandlers') == \ cdo_properties[cs.CDO + '.PossibleHandlers'] k = q.expect('dbus-method-call', path=kopete.object_path, interface=cs.APPROVER, method='AddDispatchOperation', handled=False) assert k.args == [[(chan.object_path, channel_properties)], cdo_path, cdo_properties] q.dbus_return(k.message, bus=kopete_bus, signature='') # The fake Logger implementation is run e = q.expect( 'dbus-signal', path=cs.tp_path_prefix + '/RegressionTests', interface=cs.tp_name_prefix + '.RegressionTests', signal='FakeStartup', args=[cs.tp_name_prefix + '.Client.Logger'], ) # We take on its identity to be able to continue with the test logger = SimulatedClient(q, bus, 'Logger', observe=[text_fixed_properties]) # Logger is told about the new channel e = q.expect('dbus-method-call', path=logger.object_path, interface=cs.OBSERVER, method='ObserveChannels', handled=False) assert e.args[0] == account.object_path, e.args assert e.args[1] == conn.object_path, e.args assert e.args[3] == cdo_path, e.args assert e.args[4] == [], e.args # no requests satisfied channels = e.args[2] assert len(channels) == 1, channels assert channels[0][0] == chan.object_path, channels assert channels[0][1] == channel_properties, channels # Logger indicates that it is ready to proceed q.dbus_return(e.message, bus=logger_bus, signature='') # The Approver (Kopete) is next # The user responds to Kopete call_async(q, cdo_iface, 'HandleWith', cs.tp_name_prefix + '.Client.Kopete') # Kopete is asked to handle the channels k = q.expect('dbus-method-call', path=kopete.object_path, interface=cs.HANDLER, method='HandleChannels', handled=False) # Kopete accepts the channels q.dbus_return(k.message, bus=kopete_bus, signature='') q.expect_many( EventPattern('dbus-return', method='HandleWith'), EventPattern('dbus-signal', interface=cs.CDO, signal='Finished'), EventPattern('dbus-signal', interface=cs.CD_IFACE_OP_LIST, signal='DispatchOperationFinished'), ) # Now there are no more active channel dispatch operations assert cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations') == [] # Logger crashes logger.release_name() e = q.expect( 'dbus-signal', signal='NameOwnerChanged', predicate=(lambda e: e.args[0] == logger.bus_name and e.args[2] == ''), ) logger_unique_name = e.args[1] logger_bus.flush() # Logger gets restart request e = q.expect( 'dbus-signal', path=cs.tp_path_prefix + '/RegressionTests', interface=cs.tp_name_prefix + '.RegressionTests', signal='FakeStartup', args=[cs.tp_name_prefix + '.Client.Logger'], ) # Logger gets restarted logger.reacquire_name() e = q.expect( 'dbus-signal', signal='NameOwnerChanged', predicate=(lambda e: e.args[0] == logger.bus_name and e.args[1] == ''), ) logger_unique_name = e.args[2] e = q.expect('dbus-method-call', path=logger.object_path, interface=cs.OBSERVER, method='ObserveChannels', handled=False) # FIXME: assert the same things as before, except CDO (which we don't # have) and account path (which we don't know how to get); also check # that the recovering observer info key is set assert e.args[0] == account.object_path, e.args assert e.args[1] == conn.object_path, e.args assert e.args[4] == [], e.args # no requests satisfied assert e.args[5]['recovering'] == 1, e.args # due to observer recovery channels = e.args[2] assert len(channels) == 1, channels assert channels[0][0] == chan.object_path, channels assert channels[0][1] == channel_properties, channels # Logger indicates that it is ready to proceed q.dbus_return(e.message, bus=logger_bus, signature='') sync_dbus(bus, q, mc)
def test(q, bus, conn, stream): self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") call_async(q, conn.ContactList, 'GetContactListAttributes', [cs.CONN_IFACE_CONTACT_GROUPS], False) r = q.expect('dbus-return', method='GetContactListAttributes') assertLength(0, r.value[0].keys()) # request subscription handle = conn.get_contact_handle_sync('*****@*****.**') call_async(q, conn.ContactList, 'RequestSubscription', [handle], 'half past monsoon') # libpurple puts him on our blist as soon as we've asked; there doesn't # seem to be any concept of remote-pending state. # # It also puts him in the default group, probably "Buddies". set_iq, _, _, _, groups_changed = q.expect_many( EventPattern('stream-iq', iq_type='set', query_ns=ns.ROSTER, query_name='query'), EventPattern('stream-presence', presence_type='subscribe', to='*****@*****.**'), EventPattern('dbus-return', method='RequestSubscription', value=()), EventPattern('dbus-signal', signal='ContactsChangedWithID', args=[{ handle: (cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_UNKNOWN, ''), }, {handle: '*****@*****.**'}, {}]), EventPattern('dbus-signal', signal='GroupsChanged', predicate=lambda e: e.args[0] == [handle]), ) assertEquals('*****@*****.**', set_iq.query.item['jid']) acknowledge_iq(stream, set_iq.stanza) assertLength(1, groups_changed.args[1]) assertLength(0, groups_changed.args[2]) def_group = groups_changed.args[1][0] # Suggs accepts our subscription request presence = domish.Element(('jabber:client', 'presence')) presence['from'] = '*****@*****.**' presence['type'] = 'subscribed' stream.send(presence) # ... but nothing much happens, because there's no concept of pending # state in libpurple # put a contact into the *group* explicitly: this shouldn't ask for # subscription, but it does, because libpurple handle = conn.get_contact_handle_sync('*****@*****.**') call_async(q, conn.ContactGroups, 'AddToGroup', def_group, [handle]) # libpurple puts her on our blist as soon as we've asked; there doesn't # seem to be any concept of remote-pending state. It also puts her in the # same group. set_iq, _, _, _, _ = q.expect_many( EventPattern('stream-iq', iq_type='set', query_ns=ns.ROSTER, query_name='query'), EventPattern('stream-presence', presence_type='subscribe', to='*****@*****.**'), EventPattern('dbus-return', method='AddToGroup', value=()), EventPattern('dbus-signal', signal='ContactsChangedWithID', args=[{ handle: (cs.SUBSCRIPTION_STATE_YES, cs.SUBSCRIPTION_STATE_UNKNOWN, ''), }, {handle: '*****@*****.**'}, {}]), EventPattern('dbus-signal', signal='GroupsChanged', args=[[handle], [def_group], []]), ) acknowledge_iq(stream, set_iq.stanza) assertEquals('*****@*****.**', set_iq.query.item['jid']) # cybergoths are less receptive to random subscription requests, so it # gets rejected presence = domish.Element(('jabber:client', 'presence')) presence['from'] = '*****@*****.**' presence['type'] = 'unsubscribed' stream.send(presence) # nothing happens, because there's no concept of pending state... sync_dbus(bus, q, conn) sync_stream(q, stream)
def run_outgoing_test(q, bus, conn, stream, close_channel=False): jp = JingleProtocol031 () jt = JingleTest2(jp, conn, q, stream, 'test@localhost', muc + '/bob') jt.prepare() self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") # Not allowed to have muji related presences before we accept the channel forbidden = [ no_muji_presences (muc) ] (path, props) = create_muji_channel (q, conn, stream, muc) q.forbid_events(forbidden) general_tests (jp, q, bus, conn, stream, path, props) channel = bus.get_object (conn.bus_name, path) props = channel.GetAll (cs.CHANNEL_TYPE_CALL, dbus_interface = dbus.PROPERTIES_IFACE) content = bus.get_object (conn.bus_name, props['Contents'][0]) md = jt.get_call_audio_md_dbus() check_and_accept_offer (q, bus, conn, content, md) # Accept the channel, which means we can get muji presences q.unforbid_events (forbidden) channel.Accept() e = q.expect('stream-presence', to = muc + "/test") echo_muc_presence (q, stream, e.stanza, 'none', 'participant') mujinode = xpath.queryForNodes("/presence/muji", e.stanza) assertLength (1, mujinode) # The one with the codecs e = q.expect('stream-presence', to = muc + "/test") echo_muc_presence (q, stream, e.stanza, 'none', 'participant') # Gabble shouldn't send new presences for a while q.forbid_events(forbidden) presence = make_muc_presence('owner', 'moderator', muc, 'bob') presence.addElement ((ns.MUJI, 'muji')).addElement('preparing') stream.send(presence) presence = make_muc_presence('owner', 'moderator', muc, 'bob') muji = ('muji', ns.MUJI, {}, [('content', ns.MUJI, { "name": "Audio" }, [( 'description', ns.JINGLE_RTP, {"media": "audio"}, jt.generate_payloads(jt.audio_codecs))])]) presence.addChild(jp._simple_xml(muji)) stream.send(presence) q.expect('dbus-signal', signal = 'CallStateChanged') # Bob appears and starts a session right afterwards q.expect('dbus-signal', signal = 'CallMembersChanged') q.unforbid_events(forbidden) e = q.expect('dbus-signal', signal = 'NewMediaDescriptionOffer') offer = bus.get_object (conn.bus_name, e.args[0]) offer.Accept(md, dbus_interface=cs.CALL_CONTENT_MEDIA_DESCRIPTION) jt.incoming_call(audio = "Audio") e = q.expect ('dbus-signal', signal = 'StreamsAdded') cstream = bus.get_object (conn.bus_name, e.args[0][0]) candidates = jt.get_call_remote_transports_dbus () cstream.AddCandidates (candidates, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) # Fake our endpoint being connected endpoints = cstream.Get(cs.CALL_STREAM_IFACE_MEDIA, "Endpoints", dbus_interface=dbus.PROPERTIES_IFACE) assertLength (1, endpoints) endpoint = bus.get_object (conn.bus_name, endpoints[0]) endpoint.SetEndpointState (1, cs.MEDIA_STREAM_STATE_CONNECTED, dbus_interface=cs.CALL_STREAM_ENDPOINT) endpoint.SetEndpointState (2, cs.MEDIA_STREAM_STATE_CONNECTED, dbus_interface=cs.CALL_STREAM_ENDPOINT) e = q.expect ('stream-iq', predicate = jp.action_predicate ('session-accept')) stream.send(jp.xml(jp.ResultIq(jt.peer, e.stanza, []))) # But we want video as well ! c = channel.AddContent ("Camera!", cs.MEDIA_STREAM_TYPE_VIDEO, dbus_interface=cs.CHANNEL_TYPE_CALL) q.expect('dbus-signal', signal = 'ContentAdded') content = bus.get_object (conn.bus_name, c) # wait for the CodecOffer and Accept it q.expect('dbus-signal', signal = 'NewMediaDescriptionOffer') md = jt.get_call_video_md_dbus() check_and_accept_offer (q, bus, conn, content, md) # preparing e = q.expect('stream-presence', to = muc + "/test") echo_muc_presence (q, stream, e.stanza, 'none', 'participant') #codecs e = q.expect('stream-presence', to = muc + "/test") echo_muc_presence (q, stream, e.stanza, 'none', 'participant') # Bob would like to join our video party presence = make_muc_presence('owner', 'moderator', muc, 'bob') muji = ('muji', ns.MUJI, {}, [('content', ns.MUJI, { "name": "Audio" }, [( 'description', ns.JINGLE_RTP, {"media": "audio"}, jt.generate_payloads(jt.audio_codecs))]), ('content', ns.MUJI, { "name": "Camera!" }, [( 'description', ns.JINGLE_RTP, {"media": "video"}, jt.generate_payloads(jt.video_codecs))]), ]) presence.addChild(jp._simple_xml(muji)) stream.send(presence) # new codec offer as bob threw in some codecs q.expect('dbus-signal', signal='NewMediaDescriptionOffer') check_and_accept_offer (q, bus, conn, self_handle, content, codecs, check_codecs_changed = False) # Bob sends a content node = jp.SetIq(jt.peer, jt.jid, [ jp.Jingle(jt.sid, jt.peer, 'content-add', [ jp.Content('videostream', 'initiator', 'both', jp.Description('video', [ jp.PayloadType(name, str(rate), str(id), parameters) for (name, id, rate, parameters) in jt.video_codecs ]), jp.TransportGoogleP2P()) ]) ]) stream.send(jp.xml(node)) # We get a new stream q.expect('dbus-signal', signal = 'StreamsAdded') # Sync up the stream to ensure we sent out all the xmpp traffic that was # the result of a stream being added sync_stream (q, stream) # happiness.. Now let's hang up if close_channel: channel.Close() hangup_event = EventPattern ('dbus-signal', signal = "Closed", path = path) else: channel.Hangup (0, "", "", dbus_interface=cs.CHANNEL_TYPE_CALL) hangup_event = EventPattern ('dbus-signal', signal='CallStateChanged') # Should change the call state to ended, send a session-terminate to our # only peer and send a muc presence without any mention of muji q.forbid_events(forbidden) q.expect_many (EventPattern ('stream-presence', to = muc + "/test"), EventPattern ('stream-iq', predicate=jp.action_predicate ('session-terminate')), hangup_event) if not close_channel: channel.Close() q.expect ('dbus-signal', signal="Closed", path = path) try: channel.Close() raise AssertionError ("Channel didn't actually close") except DBusException: pass
def test(q, bus, conn, stream): event = q.expect('stream-iq', query_ns=ns.ROSTER) amy, bob, che, dre, eve = conn.get_contact_handles_sync([ '*****@*****.**', '*****@*****.**', '*****@*****.**', '*****@*****.**', '*****@*****.**' ]) assertEquals( { amy: UNKNOWN, bob: UNKNOWN, che: UNKNOWN, dre: UNKNOWN, eve: UNKNOWN, }, get_contacts_presences_sync(conn, [amy, bob, che, dre, eve])) # Before the server sends Gabble the roster, it relays an 'unavailable' # presence for one of the contacts we're subscribed to. This seems to # happen in practice when using Prosody with a shared roster: the presence # probes start coming back negatively before the shared roster is retrieved # and returned to the client. stream.send(make_presence('*****@*****.**', type='unavailable')) # Dre's presence is still unknown, since we don't have the roster. This # isn't a change per se---we checked above, and Dre's presence was # unknown---so it shouldn't be signalled. q.forbid_events([ EventPattern('dbus-signal', signal='PresencesChanged', args=[{ dre: UNKNOWN }]) ]) # We also receive an available presence from Eve before the roster arrives: # this presence should behave normally. stream.send(make_presence('*****@*****.**')) q.expect('dbus-signal', signal='PresencesChanged', args=[{eve: AVAILABLE}]) # We also get a message from a contact before we get the roster (presumably # they sent this while we were offline?). This shouldn't affect the contact # being reported as offline when we finally do get the roster, but it used # to: <https://bugs.freedesktop.org/show_bug.cgi?id=41743>. stream.send( elem('message', from_='*****@*****.**', type='chat')(elem('body')(u'why are you never online?'))) q.expect('dbus-signal', signal='MessageReceived') event.stanza['type'] = 'result' event.query.addChild(make_roster_item('*****@*****.**', 'both')) event.query.addChild(make_roster_item('*****@*****.**', 'from')) event.query.addChild(make_roster_item('*****@*****.**', 'to')) event.query.addChild(make_roster_item('*****@*****.**', 'both')) event.query.addChild(make_roster_item('*****@*****.**', 'both')) stream.send(event.stanza) # The presence for contacts on the roster whose subscription is 'to' or # 'both' but for whom we haven't already received presence should change # from 'unknown' (as checked above) to 'offline'. e = q.expect('dbus-signal', signal='PresencesChanged') changed_presences, = e.args assertEquals({ amy: OFFLINE, che: OFFLINE, dre: OFFLINE, }, changed_presences) assertEquals( { amy: OFFLINE, bob: UNKNOWN, che: OFFLINE, dre: OFFLINE, eve: AVAILABLE, }, get_contacts_presences_sync(conn, [amy, bob, che, dre, eve]))
def run_incoming_test(q, bus, conn, stream, bob_leaves_room = False): jp = JingleProtocol031 () jt = JingleTest2(jp, conn, q, stream, 'test@localhost', muc + "/bob") jt.prepare() forbidden = [ no_muji_presences (muc) ] self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") _, _, test_handle, bob_handle = \ join_muc_and_check(q, bus, conn, stream, muc) presence = make_muc_presence('owner', 'moderator', muc, 'bob') muji = ('muji', ns.MUJI, {}, [('content', ns.MUJI, { "name": "Voice" }, [( 'description', ns.JINGLE_RTP, {"media": "audio"}, jt.generate_payloads(jt.audio_codecs))])]) presence.addChild(jp._simple_xml(muji)) stream.send(presence) e = q.expect ('dbus-signal', signal='NewChannels', predicate=lambda e: \ e.args[0][0][1][cs.CHANNEL_TYPE] == cs.CHANNEL_TYPE_CALL ) (path, props) = e.args[0][0] assertContains((cs.CHANNEL_TYPE_CALL + '.InitialAudio', True), props.items()) assertContains((cs.CHANNEL_TYPE_CALL + '.InitialVideo', False), props.items()) general_tests (jp, q, bus, conn, stream, path, props) channel = bus.get_object (conn.bus_name, path) props = channel.GetAll (cs.CHANNEL_TYPE_CALL, dbus_interface = dbus.PROPERTIES_IFACE) content = bus.get_object (conn.bus_name, props['Contents'][0]) check_state (q, channel, cs.CALL_STATE_PENDING_RECEIVER) md = jt.get_call_audio_md_dbus() check_and_accept_offer (q, bus, conn, self_handle, content, md) channel.Accept (dbus_interface=cs.CHANNEL_TYPE_CALL) # Preparing stanza e = q.expect('stream-presence', to = muc + "/test") echo_muc_presence (q, stream, e.stanza, 'none', 'participant') # Codecs stanza e = q.expect('stream-presence', to = muc + "/test") echo_muc_presence (q, stream, e.stanza, 'none', 'participant') # Gabble shouldn't send new presences for a while q.forbid_events(forbidden) e = q.expect ('dbus-signal', signal = 'StreamsAdded') cstream = bus.get_object (conn.bus_name, e.args[0][0]) cstream.SetCredentials(jt.ufrag, jt.pwd, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) candidates = jt.get_call_remote_transports_dbus () cstream.AddCandidates (candidates, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) e = q.expect('stream-iq', predicate=jp.action_predicate('session-initiate')) jt.parse_session_initiate (e.query) jt.accept() # Bob adds a Video content presence = make_muc_presence('owner', 'moderator', muc, 'bob') presence.addElement ((ns.MUJI, 'muji')).addElement('preparing') stream.send(presence) presence = make_muc_presence('owner', 'moderator', muc, 'bob') muji = ('muji', ns.MUJI, {}, [('content', ns.MUJI, { "name": "Voice" }, [( 'description', ns.JINGLE_RTP, {"media": "audio"}, jt.generate_payloads(jt.audio_codecs))]), ('content', ns.MUJI, { "name": "Camera" }, [( 'description', ns.JINGLE_RTP, {"media": "video"}, jt.generate_payloads(jt.video_codecs))]), ]) presence.addChild(jp._simple_xml(muji)) stream.send(presence) # Gabble noticed bob added a content e = q.expect('dbus-signal', signal = 'ContentAdded') q.unforbid_events (forbidden) content = bus.get_object (conn.bus_name, e.args[0]) check_and_accept_offer (q, bus, conn, self_handle, content, jt.get_call_video_codecs_dbus(), check_codecs_changed = False) # Gabble sends a presence to prepare e = q.expect('stream-presence', to = muc + "/test") echo_muc_presence (q, stream, e.stanza, 'none', 'participant') # Gabble sends a presence with the video codecs e = q.expect('stream-presence', to = muc + "/test") echo_muc_presence (q, stream, e.stanza, 'none', 'participant') # Gabble adds a content to the jingle session and thus a stream is added e = q.expect ('dbus-signal', signal = 'StreamsAdded') cstream = bus.get_object (conn.bus_name, e.args[0][0]) candidates = jt.get_call_remote_transports_dbus () cstream.AddCandidates (candidates, dbus_interface=cs.CALL_STREAM_IFACE_MEDIA) # And now the content-add on the jingle streams e = q.expect('stream-iq', to = muc + "/bob", predicate = lambda x: \ xpath.queryForNodes("/iq/jingle[@action='content-add']", x.stanza)) # Bob leaves the call, bye bob if bob_leaves_room: presence = make_muc_presence('owner', 'moderator', muc, 'bob') presence['type'] = 'unavailable' else: presence = make_muc_presence('owner', 'moderator', muc, 'bob') stream.send(presence) (cmembers, _, _) = q.expect_many( EventPattern ('dbus-signal', signal = 'CallMembersChanged'), # Audio and video stream EventPattern ('dbus-signal', signal = 'StreamsRemoved'), EventPattern ('dbus-signal', signal = 'StreamsRemoved')) # Just bob left assertLength (1, cmembers.args[1])
def announce_common(q, bus, empathy, kopete, account, conn, cd_props, urgent=False): if urgent: jid = 'friar.lawrence' else: jid = 'juliet' channel_properties = dbus.Dictionary(contact_text_fixed_properties, signature='sv') channel_properties[cs.CHANNEL + '.TargetID'] = jid channel_properties[cs.CHANNEL + '.TargetHandle'] = \ conn.ensure_handle(cs.HT_CONTACT, jid) channel_properties[cs.CHANNEL + '.InitiatorID'] = jid channel_properties[cs.CHANNEL + '.InitiatorHandle'] = \ conn.ensure_handle(cs.HT_CONTACT, jid) channel_properties[cs.CHANNEL + '.Requested'] = False channel_properties[cs.CHANNEL + '.Interfaces'] = dbus.Array(signature='s') if urgent: channel_properties['com.example.Urgency.Urgent'] = True chan = SimulatedChannel(conn, channel_properties) chan.announce() # A channel dispatch operation is created e = q.expect('dbus-signal', path=cs.CD_PATH, interface=cs.CD_IFACE_OP_LIST, signal='NewDispatchOperation') cdo_path = e.args[0] cdo_properties = e.args[1] assert cdo_properties[cs.CDO + '.Account'] == account.object_path assert cdo_properties[cs.CDO + '.Connection'] == conn.object_path assert cs.CDO + '.Interfaces' in cdo_properties handlers = cdo_properties[cs.CDO + '.PossibleHandlers'][:] if urgent: # The handler with BypassApproval is first assert handlers[ 0] == cs.tp_name_prefix + '.Client.Kopete.BypassApproval' # Kopete's filter is more specific than Empathy's, so it comes next assert handlers[1] == cs.tp_name_prefix + '.Client.Kopete' # Empathy's filter is the least specific, so it's last assert handlers[2] == cs.tp_name_prefix + '.Client.Empathy' assert len(handlers) == 3 else: handlers.sort() assert handlers == [ cs.tp_name_prefix + '.Client.Empathy', cs.tp_name_prefix + '.Client.Kopete' ], handlers assert cs.CD_IFACE_OP_LIST in cd_props.Get(cs.CD, 'Interfaces') assert cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations') ==\ [(cdo_path, cdo_properties)] cdo = bus.get_object(cs.CD, cdo_path) cdo_iface = dbus.Interface(cdo, cs.CDO) # Both Observers are told about the new channel e, k = q.expect_many( EventPattern('dbus-method-call', path=empathy.object_path, interface=cs.OBSERVER, method='ObserveChannels', handled=False), EventPattern('dbus-method-call', path=kopete.object_path, interface=cs.OBSERVER, method='ObserveChannels', handled=False), ) assert e.args[0] == account.object_path, e.args assert e.args[1] == conn.object_path, e.args assert e.args[3] == cdo_path, e.args assert e.args[4] == [], e.args # no requests satisfied channels = e.args[2] assert len(channels) == 1, channels assert channels[0][0] == chan.object_path, channels assert channels[0][1] == channel_properties, channels assert k.args == e.args return cdo_iface, chan, channel_properties, [e, k]
def test(q, bus, conn, stream): self_handle = conn.Properties.Get(cs.CONN, "SelfHandle") romeo, juliet, duncan = conn.get_contact_handles_sync( ['*****@*****.**', '*****@*****.**', '*****@*****.**']) # receive some roster pushes for the "initial" state iq = IQ(stream, 'set') iq['id'] = 'roster-push' query = iq.addElement(('jabber:iq:roster', 'query')) item = query.addElement('item') item['jid'] = '*****@*****.**' item['subscription'] = 'both' group = item.addElement('group', content='Still alive') group = item.addElement('group', content='Capulets') stream.send(iq) iq = IQ(stream, 'set') iq['id'] = 'roster-push' query = iq.addElement(('jabber:iq:roster', 'query')) item = query.addElement('item') item['jid'] = '*****@*****.**' item['subscription'] = 'both' group = item.addElement('group', content='Still alive') stream.send(iq) iq = IQ(stream, 'set') iq['id'] = 'roster-push' query = iq.addElement(('jabber:iq:roster', 'query')) item = query.addElement('item') item['jid'] = '*****@*****.**' item['subscription'] = 'both' stream.send(iq) sync_dbus(bus, q, conn) sync_stream(q, stream) # the XMPP prpl puts people into some sort of group, probably called # Buddies groups = conn.Properties.Get(cs.CONN_IFACE_CONTACT_GROUPS, 'Groups') default_group = None for group in groups: if group in ('Capulets', 'Still alive'): continue if default_group is not None: raise AssertionError('Two unexplained groups: %s, %s' % (group, default_group)) default_group = group call_async(q, conn.ContactList, 'GetContactListAttributes', [cs.CONN_IFACE_CONTACT_GROUPS], False) r = q.expect('dbus-return', method='GetContactListAttributes') assertSameSets(['Still alive'], r.value[0][romeo][cs.ATTR_GROUPS]) assertSameSets(['Still alive', 'Capulets'], r.value[0][juliet][cs.ATTR_GROUPS]) assertSameSets([default_group], r.value[0][duncan][cs.ATTR_GROUPS]) # We can't remove Duncan from the default group, because it's his only # group call_async(q, conn.ContactGroups, 'RemoveFromGroup', default_group, [duncan]) q.expect('dbus-error', method='RemoveFromGroup', name=cs.NOT_AVAILABLE) call_async(q, conn.ContactGroups, 'SetGroupMembers', default_group, []) q.expect('dbus-error', method='SetGroupMembers', name=cs.NOT_AVAILABLE) # SetContactGroups just doesn't do anything in this situation call_async(q, conn.ContactGroups, 'SetContactGroups', duncan, []) q.expect('dbus-return', method='SetContactGroups') call_async(q, conn.ContactList, 'GetContactListAttributes', [cs.CONN_IFACE_CONTACT_GROUPS], False) r = q.expect('dbus-return', method='GetContactListAttributes') assertSameSets([default_group], r.value[0][duncan][cs.ATTR_GROUPS]) # Make a new group and add Duncan to it call_async(q, conn.ContactGroups, 'AddToGroup', 'Scots', [duncan]) iq, _, _ = q.expect_many( EventPattern('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER), EventPattern('dbus-signal', signal='GroupsChanged', args=[[duncan], ['Scots'], []]), EventPattern('dbus-return', method='AddToGroup'), ) assertEquals('*****@*****.**', iq.stanza.query.item['jid']) groups = set([ str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq.stanza) ]) assertLength(2, groups) assertContains(default_group, groups) assertContains('Scots', groups) # Now we can remove him from the default group. Much rejoicing. call_async(q, conn.ContactGroups, 'RemoveFromGroup', default_group, [duncan]) iq, _, _ = q.expect_many( EventPattern('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER), EventPattern('dbus-signal', signal='GroupsChanged', args=[[duncan], [], [default_group]]), EventPattern('dbus-return', method='RemoveFromGroup'), ) assertEquals('*****@*****.**', iq.stanza.query.item['jid']) groups = set([ str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq.stanza) ]) assertLength(1, groups) assertContains('Scots', groups) # Test SetContactGroups, which didn't previously have proper coverage call_async(q, conn.ContactGroups, 'SetContactGroups', duncan, ['Scottish former kings']) iq, _, _, _ = q.expect_many( EventPattern('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER), EventPattern('dbus-signal', signal='GroupsChanged', args=[[duncan], ['Scottish former kings'], []]), EventPattern('dbus-signal', signal='GroupsChanged', args=[[duncan], [], ['Scots']]), EventPattern('dbus-return', method='SetContactGroups'), ) assertEquals('*****@*****.**', iq.stanza.query.item['jid']) groups = set([ str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq.stanza) ]) assertLength(2, groups) assertContains('Scots', groups) assertContains('Scottish former kings', groups) iq, = q.expect_many( EventPattern('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER), ) assertEquals('*****@*****.**', iq.stanza.query.item['jid']) groups = set([ str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq.stanza) ]) assertLength(1, groups) assertContains('Scottish former kings', groups) # Romeo dies. If he drops off the roster as a result, that would be # fd.o #21294. However, to fix that bug, Haze now puts him in the # default group. call_async(q, conn.ContactGroups, 'RemoveFromGroup', 'Still alive', [romeo]) iq1, iq2, _, _, _ = q.expect_many( EventPattern('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER), EventPattern('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER), EventPattern('dbus-signal', signal='GroupsChanged', args=[[romeo], [default_group], []]), EventPattern('dbus-signal', signal='GroupsChanged', args=[[romeo], [], ['Still alive']]), EventPattern('dbus-return', method='RemoveFromGroup'), ) assertEquals('*****@*****.**', iq1.stanza.query.item['jid']) groups = set([ str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq1.stanza) ]) assertLength(2, groups) assertContains('Still alive', groups) assertContains(default_group, groups) assertEquals('*****@*****.**', iq2.stanza.query.item['jid']) groups = set([ str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq2.stanza) ]) assertLength(1, groups) assertContains(default_group, groups) # Juliet dies. She's in another group already, so the workaround for # fd.o #21294 is not active. call_async(q, conn.ContactGroups, 'SetGroupMembers', 'Still alive', []) iq, _, _ = q.expect_many( EventPattern('stream-iq', iq_type='set', query_name='query', query_ns=ns.ROSTER), EventPattern('dbus-signal', signal='GroupsChanged', args=[[juliet], [], ['Still alive']]), EventPattern('dbus-return', method='SetGroupMembers'), ) assertEquals('*****@*****.**', iq.stanza.query.item['jid']) groups = set([ str(x) for x in xpath.queryForNodes('/iq/query/item/group', iq.stanza) ]) assertLength(1, groups) assertContains('Capulets', groups) # At the end of a tragedy, everyone dies, so there's no need for this # group. call_async(q, conn.ContactGroups, 'RemoveGroup', 'Still alive') q.expect('dbus-signal', signal='GroupsRemoved', args=[['Still alive']]) # Deleting a non-empty group is allowed. (It removes everyone.) call_async(q, conn.ContactGroups, 'RemoveGroup', 'Capulets') q.expect_many( EventPattern('dbus-signal', signal='GroupsRemoved', args=[['Capulets']]), EventPattern('dbus-signal', signal='GroupsChanged', args=[[juliet], [default_group], []]), EventPattern('dbus-signal', signal='GroupsChanged', args=[[juliet], [], ['Capulets']]), )
def test(q, bus, mc): params = dbus.Dictionary( { "account": "yum yum network manager", "password": "******", }, signature='sv') (simulated_cm, account) = create_fakecm_account(q, bus, mc, params) # While we're not connected to the internet, RequestConnection should not # be called. request_connection_event = [ EventPattern('dbus-method-call', method='RequestConnection'), ] q.forbid_events(request_connection_event) account.Properties.Set( cs.ACCOUNT, 'RequestedPresence', (dbus.UInt32(cs.PRESENCE_BUSY), 'busy', 'hlaghalgh')) # Turn the account on, re-request an online presence, and even tell it to # connect automatically, to check that none of these make it sign in. call_async(q, account.Properties, 'Set', cs.ACCOUNT, 'Enabled', True) q.expect('dbus-return', method='Set') requested_presence = (dbus.UInt32(cs.PRESENCE_BUSY), 'busy', 'gtfo') call_async(q, account.Properties, 'Set', cs.ACCOUNT, 'RequestedPresence', requested_presence) q.expect('dbus-return', method='Set') call_async(q, account.Properties, 'Set', cs.ACCOUNT, 'ConnectAutomatically', True) q.expect('dbus-return', method='Set') # (but actually let's turn ConnectAutomatically off: we want to check that # MC continues to try to apply RequestedPresence if the network connection # goes away and comes back again, regardless of this setting) call_async(q, account.Properties, 'Set', cs.ACCOUNT, 'ConnectAutomatically', False) q.expect('dbus-return', method='Set') sync_dbus(bus, q, mc) q.unforbid_events(request_connection_event) # Okay, I'm satisfied. Turn the network on. mc.connectivity.go_online() expect_fakecm_connection(q, bus, mc, account, params, has_presence=True, expect_before_connect=[ EventPattern('dbus-method-call', method='SetPresence', args=list( requested_presence[1:])), ]) if config.HAVE_NM: # If NetworkManager tells us that it is going to disconnect soon, # the connection should be banished. GNetworkMonitor can't tell us # that; either it's online or it isn't. mc.connectivity.go_indeterminate() q.expect('dbus-method-call', method='Disconnect') mc.connectivity.go_offline() sync_connectivity_state(mc) # When we turn the network back on, MC should try to sign us back on. # In the process, our RequestedPresence should not have been # trampled on, as below. mc.connectivity.go_online() expect_fakecm_connection(q, bus, mc, account, params, has_presence=True, expect_before_connect=[ EventPattern('dbus-method-call', method='SetPresence', args=list( requested_presence[1:])), ]) assertEquals(requested_presence, account.Properties.Get(cs.ACCOUNT, 'RequestedPresence')) # If we turn the network off, the connection should be banished. mc.connectivity.go_offline() q.expect('dbus-method-call', method='Disconnect') # When we turn the network back on, MC should try to sign us back on. mc.connectivity.go_online() e = q.expect('dbus-method-call', method='RequestConnection') # In the process, our RequestedPresence should not have been trampled on. # (Historically, MC would replace it with the AutomaticPresence, but that # behaviour was unexpected: if the user explicitly set a status or message, # why should the network connection cutting out and coming back up cause # that to be lost?) assertEquals(requested_presence, account.Properties.Get(cs.ACCOUNT, 'RequestedPresence')) # But if we get disconnected before RequestConnection returns, MC should # clean up the new connection when it does, rather than trying to sign it # in. connect_event = [ EventPattern('dbus-method-call', method='Connect'), ] q.forbid_events(connect_event) mc.connectivity.go_offline() # Make sure that MC has noticed that the network connection has gone away. sync_connectivity_state(mc) conn = SimulatedConnection(q, bus, 'fakecm', 'fakeprotocol', account.object_path.split('/')[-1], 'myself') q.dbus_return(e.message, conn.bus_name, conn.object_path, signature='so') q.expect('dbus-method-call', method='Disconnect') # So now the user gives up and sets their RequestedPresence to offline. # Because this account does not ConnectAutomatically, if the network # connection comes back up the account should not be brought back online. q.forbid_events(request_connection_event) account.Properties.Set(cs.ACCOUNT, 'RequestedPresence', (dbus.UInt32(cs.PRESENCE_OFFLINE), 'offline', '')) mc.connectivity.go_online() # Make sure MC has noticed that the network connection has come back. sync_connectivity_state(mc)
def test_complex_success(q, bus, conn, stream, with_extra_data=True, accept_early=False): chan, props = connect_and_get_sasl_channel(q, bus, conn) assertSameSets(MECHANISMS + ['X-TELEPATHY-PASSWORD'], props.get(cs.SASL_AVAILABLE_MECHANISMS)) call_async(q, chan.SASLAuthentication, 'StartMechanismWithData', "FOO", "") q.expect('dbus-error', method='StartMechanismWithData', name=cs.NOT_IMPLEMENTED) if with_extra_data: chan.SASLAuthentication.StartMechanismWithData("SCOTTISH-PLAY", INITIAL_RESPONSE) e = q.expect('sasl-auth', initial_response=INITIAL_RESPONSE) else: chan.SASLAuthentication.StartMechanism("SCOTTISH-PLAY") e = q.expect('sasl-auth', has_initial_response=False) authenticator = e.authenticator q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_IN_PROGRESS, '', {}]) if not with_extra_data: # send the stage directions in-band instead authenticator.challenge(b'') e = q.expect('dbus-signal', signal='NewChallenge', interface=cs.CHANNEL_IFACE_SASL_AUTH) # this ought to be '' but dbus-python has fd.o #28131 assert e.args in ([b''], [b'None']) chan.SASLAuthentication.Respond(INITIAL_RESPONSE) q.expect('sasl-response', response=INITIAL_RESPONSE) for challenge, response in CR_PAIRS: authenticator.challenge(challenge) q.expect('dbus-signal', signal='NewChallenge', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[challenge]) chan.SASLAuthentication.Respond(response) q.expect('sasl-response', response=response) if with_extra_data: authenticator.success(SUCCESS_DATA) else: # The success data is sent in-band as a challenge authenticator.challenge(SUCCESS_DATA) q.expect('dbus-signal', signal='NewChallenge', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[SUCCESS_DATA]) if accept_early: # the UI can tell that this challenge isn't actually a challenge, # it's a success in disguise chan.SASLAuthentication.AcceptSASL() q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_CLIENT_ACCEPTED, '', {}]) else: chan.SASLAuthentication.Respond(dbus.ByteArray(b'')) if with_extra_data: # Wocky removes the distinction between a challenge containing # success data followed by a plain success, and a success # containing initial data, so we won't get to Server_Succeeded # til we "respond" to the "challenge". However, at the XMPP level, # we shouldn't get a response to a success. q.forbid_events([EventPattern('sasl-response')]) else: q.expect('sasl-response', response=b'') authenticator.success(None) if not accept_early: # *now* we accept q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_SERVER_SUCCEEDED, '', {}]) # We're willing to accept this SASL transaction chan.SASLAuthentication.AcceptSASL() q.expect('dbus-signal', signal='SASLStatusChanged', interface=cs.CHANNEL_IFACE_SASL_AUTH, args=[cs.SASL_STATUS_SUCCEEDED, '', {}]) q.expect('dbus-signal', signal='StatusChanged', args=[cs.CONN_STATUS_CONNECTED, cs.CSR_REQUESTED]) chan.Close() # ... and check that the Connection is still OK conn.Properties.Get(cs.CONN, "SelfHandle")
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 roomlist 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_ROOM_LIST, tp_name_prefix + '.Channel.TargetHandleType': 0, }, [], ) in properties.get('RequestableChannelClasses'),\ properties['RequestableChannelClasses'] requestotron = dbus.Interface( conn, tp_name_prefix + '.Connection.Interface.Requests') # create roomlist channel using new API call_async( q, requestotron, 'CreateChannel', { tp_name_prefix + '.Channel.ChannelType': cs.CHANNEL_TYPE_ROOM_LIST, tp_name_prefix + '.Channel.TargetHandleType': 0, }) ret, new_sig = q.expect_many( EventPattern('dbus-return', method='CreateChannel'), EventPattern('dbus-signal', signal='NewChannels'), ) path2 = ret.value[0] chan2 = wrap_channel(bus.get_object(conn.bus_name, path2), "RoomList") props = ret.value[1] assert props[tp_name_prefix + '.Channel.ChannelType'] ==\ cs.CHANNEL_TYPE_ROOM_LIST assert props[tp_name_prefix + '.Channel.TargetHandleType'] == 0 assert props[tp_name_prefix + '.Channel.TargetHandle'] == 0 assert props[tp_name_prefix + '.Channel.TargetID'] == '' 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 props[tp_name_prefix + '.Channel.Type.RoomList.Server'] == '' assert new_sig.args[0][0][0] == path2 assert new_sig.args[0][0][1] == props assert chan2.Properties.Get(cs.CHANNEL_TYPE_ROOM_LIST, 'Server') == '' # ensure roomlist channel yours, ensured_path, ensured_props = requestotron.EnsureChannel({ tp_name_prefix + '.Channel.ChannelType': cs.CHANNEL_TYPE_ROOM_LIST, tp_name_prefix + '.Channel.TargetHandleType': 0, }) assert not yours assert ensured_path == path2, (ensured_path, path2) # Closing roomlist channels crashed Salut for a while. chan2.Close() q.expect_many( EventPattern('dbus-signal', signal='Closed', path=path2), EventPattern('dbus-signal', signal='ChannelClosed', args=[path2]), ) conn.Disconnect() q.expect_many( EventPattern('dbus-signal', signal='StatusChanged', args=[2, 1]), )
def test(q, bus, conn, stream): caps = { 'node': client, 'ver': '0.1', } update_contact_caps(q, conn, stream, '[email protected]/Foo', caps) update_contact_caps(q, conn, stream, '[email protected]/Foo', caps) # Meredith signs in from one resource. update_contact_caps(q, conn, stream, '[email protected]/One', caps) # Meredith signs in from another resource with the same client. We don't # need to disco her, even though we don't trust this caps node in general # yet, because she's already told us what it means. meredith_two = '[email protected]/Two' q.forbid_events([ EventPattern('stream-iq', to=meredith_two, query_ns=ns.DISCO_INFO) ]) stream.send(make_presence(meredith_two, 'hello', caps=caps)) sync_stream(q, stream) # Jens signs in from one resource, which is slow to answer the disco query. jens_one = '[email protected]/One' j = send_presence(q, conn, stream, jens_one, caps) j_stanza = expect_disco(q, jens_one, client, caps) # Jens now signs in elsewhere with the same client; we disco it (maybe # it'll reply sooner? Maybe his first client's network connection went away # and the server hasn't noticed yet?) and it replies immediately. update_contact_caps (q, conn, stream, '[email protected]/Two', caps, initial=False) # Jens' first client replies. We don't expect any caps changes here, and # this shouldn't count as a second point towards the five we need to trust # this caps node. send_disco_reply(stream, j_stanza, [], features) check_caps (conn, j) update_contact_caps (q, conn, stream, '[email protected]/Foo', caps) # Now five distinct contacts have told us what this caps node means, we # trust it. update_contact_caps (q, conn, stream, '[email protected]/Foo', caps, disco = False) update_contact_caps (q, conn, stream, '[email protected]/Foo', caps, disco = False) caps = { 'node': client, 'ver': compute_caps_hash([], features, fake_client_dataforms), 'hash': 'sha-1', } update_contact_caps(q, conn, stream, '[email protected]/Foo', caps, dataforms = fake_client_dataforms) # We can verify the reply for these caps against the hash, and thus never # need to disco it again. update_contact_caps(q, conn, stream, '[email protected]/Foo', caps, disco = False, dataforms = fake_client_dataforms) update_contact_caps(q, conn, stream, '[email protected]/Foo', caps, disco = False, dataforms = fake_client_dataforms)