diff options
Diffstat (limited to 'tests/twisted/account-manager')
23 files changed, 3210 insertions, 0 deletions
diff --git a/tests/twisted/account-manager/account-basics.py b/tests/twisted/account-manager/account-basics.py new file mode 100644 index 00000000..51328ce9 --- /dev/null +++ b/tests/twisted/account-manager/account-basics.py @@ -0,0 +1,248 @@ +# Copyright (C) 2009 Nokia Corporation +# Copyright (C) 2009 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +import dbus +import dbus.service + +from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ + call_async +from mctest import exec_test, create_fakecm_account, get_account_manager +import constants as cs + +def test(q, bus, mc): + # Get the AccountManager interface + account_manager = get_account_manager(bus) + account_manager_iface = dbus.Interface(account_manager, cs.AM) + + # Introspect AccountManager for debugging purpose + account_manager_introspected = account_manager.Introspect( + dbus_interface=cs.INTROSPECTABLE_IFACE) + #print account_manager_introspected + + # Check AccountManager has D-Bus property interface + properties = account_manager.GetAll(cs.AM, + dbus_interface=cs.PROPERTIES_IFACE) + assert properties is not None + assert properties.get('ValidAccounts') == [], \ + properties.get('ValidAccounts') + assert properties.get('InvalidAccounts') == [], \ + properties.get('InvalidAccounts') + interfaces = properties.get('Interfaces') + + # assert that current functionality exists + assert cs.AM_IFACE_NOKIA_QUERY in interfaces, interfaces + + params = dbus.Dictionary({"account": "someguy@example.com", + "password": "secrecy"}, signature='sv') + (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) + + account_path = account.__dbus_object_path__ + + # Check the account is correctly created + properties = account_manager.GetAll(cs.AM, + dbus_interface=cs.PROPERTIES_IFACE) + assert properties is not None + assert properties.get('ValidAccounts') == [account_path], properties + account_path = properties['ValidAccounts'][0] + assert isinstance(account_path, dbus.ObjectPath), repr(account_path) + assert properties.get('InvalidAccounts') == [], properties + + account_iface = dbus.Interface(account, cs.ACCOUNT) + account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) + # Introspect Account for debugging purpose + account_introspected = account.Introspect( + dbus_interface=cs.INTROSPECTABLE_IFACE) + #print account_introspected + + # Check Account has D-Bus property interface + properties = account_props.GetAll(cs.ACCOUNT) + assert properties is not None + + assert properties.get('DisplayName') == 'fakeaccount', \ + properties.get('DisplayName') + assert properties.get('Icon') == '', properties.get('Icon') + assert properties.get('Valid') == True, properties.get('Valid') + assert properties.get('Enabled') == False, properties.get('Enabled') + #assert properties.get('Nickname') == 'fakenick', properties.get('Nickname') + assert properties.get('Parameters') == params, properties.get('Parameters') + assert properties.get('Connection') == '/', properties.get('Connection') + assert properties.get('NormalizedName') == '', \ + properties.get('NormalizedName') + + interfaces = properties.get('Interfaces') + assert cs.ACCOUNT_IFACE_AVATAR in interfaces, interfaces + assert cs.ACCOUNT_IFACE_NOKIA_COMPAT in interfaces, interfaces + assert cs.ACCOUNT_IFACE_NOKIA_CONDITIONS in interfaces, interfaces + + # sanity check + for k in properties: + assert account_props.Get(cs.ACCOUNT, k) == properties[k], k + + # Alter some miscellaneous r/w properties + + call_async(q, account_props, 'Set', cs.ACCOUNT, 'DisplayName', + 'Work account') + q.expect_many( + EventPattern('dbus-signal', + path=account_path, + signal='AccountPropertyChanged', + interface=cs.ACCOUNT, + args=[{'DisplayName': 'Work account'}]), + EventPattern('dbus-return', method='Set'), + ) + assert account_props.Get(cs.ACCOUNT, 'DisplayName') == 'Work account' + + call_async(q, account_props, 'Set', cs.ACCOUNT, 'Icon', 'im-jabber') + q.expect_many( + EventPattern('dbus-signal', + path=account_path, + signal='AccountPropertyChanged', + interface=cs.ACCOUNT, + args=[{'Icon': 'im-jabber'}]), + EventPattern('dbus-return', method='Set'), + ) + assert account_props.Get(cs.ACCOUNT, 'Icon') == 'im-jabber' + + assert account_props.Get(cs.ACCOUNT, 'HasBeenOnline') == False + call_async(q, account_props, 'Set', cs.ACCOUNT, 'Nickname', 'Joe Bloggs') + q.expect_many( + EventPattern('dbus-signal', + path=account_path, + signal='AccountPropertyChanged', + interface=cs.ACCOUNT, + args=[{'Nickname': 'Joe Bloggs'}]), + EventPattern('dbus-return', method='Set'), + ) + assert account_props.Get(cs.ACCOUNT, 'Nickname') == 'Joe Bloggs' + + call_async(q, dbus.Interface(account, cs.ACCOUNT_IFACE_NOKIA_COMPAT), + 'SetHasBeenOnline') + q.expect_many( + EventPattern('dbus-signal', + path=account_path, + signal='AccountPropertyChanged', + interface=cs.ACCOUNT, + args=[{'HasBeenOnline': True}]), + EventPattern('dbus-return', method='SetHasBeenOnline'), + ) + assert account_props.Get(cs.ACCOUNT, 'HasBeenOnline') == True + + call_async(q, account_props, 'Set', cs.ACCOUNT_IFACE_NOKIA_COMPAT, + 'SecondaryVCardFields', + ['x-badger', 'x-mushroom']) + # there's no change notification for the Compat properties + q.expect_many( + EventPattern('dbus-return', method='Set'), + ) + assert account_props.Get(cs.ACCOUNT_IFACE_NOKIA_COMPAT, + 'SecondaryVCardFields') == ['x-badger', 'x-mushroom'] + + call_async(q, account_props, 'Set', cs.ACCOUNT_IFACE_NOKIA_CONDITIONS, + 'Condition', + dbus.Dictionary({':foo': 'bar'}, signature='ss')) + # there's no change notification for the Condition + q.expect_many( + EventPattern('dbus-return', method='Set'), + ) + assert account_props.Get(cs.ACCOUNT_IFACE_NOKIA_CONDITIONS, + 'Condition') == {':foo': 'bar'} + + # Set some properties to invalidly typed values - this currently succeeds + # but is a no-op, although in future it should change to raising an + # exception + + # this variable's D-Bus type must differ from the types of all known + # properties + badly_typed = dbus.Struct(('wrongly typed',), signature='s') + + for p in ('DisplayName', 'Icon', 'Enabled', 'Nickname', + 'AutomaticPresence', 'ConnectAutomatically', 'RequestedPresence'): + try: + account_props.Set(cs.ACCOUNT, p, badly_typed) + except dbus.DBusException, e: + assert e.get_dbus_name() == cs.INVALID_ARGUMENT, \ + (p, e.get_dbus_name()) + else: + raise AssertionError('Setting %s with wrong type should fail' % p) + + for p in ('Avatar',): + try: + account_props.Set(cs.ACCOUNT_IFACE_AVATAR, p, badly_typed) + except dbus.DBusException, e: + assert e.get_dbus_name() == cs.INVALID_ARGUMENT, \ + (p, e.get_dbus_name()) + else: + raise AssertionError('Setting %s with wrong type should fail' % p) + + for p in ('Profile', 'SecondaryVCardFields'): + try: + account_props.Set(cs.ACCOUNT_IFACE_NOKIA_COMPAT, p, badly_typed) + except dbus.DBusException, e: + assert e.get_dbus_name() == cs.INVALID_ARGUMENT, \ + (p, e.get_dbus_name()) + else: + raise AssertionError('Setting %s with wrong type should fail' % p) + + for p in ('Condition',): + try: + account_props.Set(cs.ACCOUNT_IFACE_NOKIA_CONDITIONS, p, + badly_typed) + except dbus.DBusException, e: + assert e.get_dbus_name() == cs.INVALID_ARGUMENT, \ + (p, e.get_dbus_name()) + else: + raise AssertionError('Setting %s with wrong type should fail' % p) + + # Make sure MC hasn't crashed yet, and make sure some properties are what + # we expect them to be + + properties = account_props.GetAll(cs.ACCOUNT) + assert properties['DisplayName'] == 'Work account' + assert properties['Icon'] == 'im-jabber' + properties = account_props.GetAll(cs.ACCOUNT_IFACE_AVATAR) + assert properties['Avatar'] == ([], '') + properties = account_props.GetAll(cs.ACCOUNT_IFACE_NOKIA_COMPAT) + assert properties['SecondaryVCardFields'] == ['x-badger', 'x-mushroom'] + + # Delete the account + assert account_iface.Remove() is None + account_event, account_manager_event = q.expect_many( + EventPattern('dbus-signal', + path=account_path, + signal='Removed', + interface=cs.ACCOUNT, + args=[] + ), + EventPattern('dbus-signal', + path=cs.AM_PATH, + signal='AccountRemoved', + interface=cs.AM, + args=[account_path] + ), + ) + + # Check the account is correctly deleted + properties = account_manager.GetAll(cs.AM, + dbus_interface=cs.PROPERTIES_IFACE) + assert properties is not None + assert properties.get('ValidAccounts') == [], properties + assert properties.get('InvalidAccounts') == [], properties + + +if __name__ == '__main__': + exec_test(test, {}) diff --git a/tests/twisted/account-manager/auto-away.py b/tests/twisted/account-manager/auto-away.py new file mode 100644 index 00000000..6d2fc81b --- /dev/null +++ b/tests/twisted/account-manager/auto-away.py @@ -0,0 +1,208 @@ +# Copyright (C) 2009 Nokia Corporation +# Copyright (C) 2009 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +import dbus +import dbus.service + +from servicetest import EventPattern, tp_name_prefix, tp_path_prefix +from mctest import exec_test, SimulatedConnection, create_fakecm_account,\ + SimulatedChannel +import constants as cs + +def test(q, bus, mc): + cm_name_ref = dbus.service.BusName( + tp_name_prefix + '.ConnectionManager.fakecm', bus=bus) + + # Create an account + params = dbus.Dictionary({"account": "someguy@example.com", + "password": "secrecy"}, signature='sv') + (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) + + # The account is initially valid but disabled + assert not account.Get(cs.ACCOUNT, 'Enabled', + dbus_interface=cs.PROPERTIES_IFACE) + assert account.Get(cs.ACCOUNT, 'Valid', + dbus_interface=cs.PROPERTIES_IFACE) + + # Enable the account + account.Set(cs.ACCOUNT, 'Enabled', True, + dbus_interface=cs.PROPERTIES_IFACE) + q.expect('dbus-signal', + path=account.object_path, + signal='AccountPropertyChanged', + interface=cs.ACCOUNT) + + assert account.Get(cs.ACCOUNT, 'Enabled', + dbus_interface=cs.PROPERTIES_IFACE) + assert account.Get(cs.ACCOUNT, 'Valid', + dbus_interface=cs.PROPERTIES_IFACE) + + # Check the requested presence is offline + properties = account.GetAll(cs.ACCOUNT, + dbus_interface=cs.PROPERTIES_IFACE) + assert properties is not None + assert properties.get('RequestedPresence') == \ + dbus.Struct((dbus.UInt32(cs.PRESENCE_TYPE_OFFLINE), + 'offline', '')), \ + properties.get('RequestedPresence') + + # Go online + requested_presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_TYPE_AVAILABLE), + dbus.String(u'available'), dbus.String(u'staring at the sea'))) + 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', has_presence=True) + conn.statuses = dbus.Dictionary({ + 'available': (cs.PRESENCE_TYPE_AVAILABLE, True, True), + 'away': (cs.PRESENCE_TYPE_AWAY, True, True), + 'busy': (cs.PRESENCE_TYPE_BUSY, True, True), + 'offline': (cs.PRESENCE_TYPE_OFFLINE, False, False), + }, signature='s(ubb)') + + q.dbus_return(e.message, conn.bus_name, conn.object_path, signature='so') + + # MC calls GetStatus (maybe) and then Connect + + q.expect('dbus-method-call', method='Connect', + path=conn.object_path, handled=True) + + # Connect succeeds + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + conn.presence = dbus.Struct((cs.PRESENCE_TYPE_AVAILABLE, 'available', ''), + signature='uss') + + # MC does some setup, including fetching the list of Channels + + get_statuses = q.expect('dbus-method-call', + interface=cs.PROPERTIES_IFACE, method='Get', + args=[cs.CONN_IFACE_SIMPLE_PRESENCE, 'Statuses'], + path=conn.object_path, handled=True) + + call, signal = q.expect_many( + EventPattern('dbus-method-call', + path=conn.object_path, + interface=cs.CONN_IFACE_SIMPLE_PRESENCE, method='SetPresence', + args=['available', 'staring at the sea'], + handled=True), + EventPattern('dbus-signal', + path=account.object_path, + interface=cs.ACCOUNT, signal='AccountPropertyChanged', + predicate = lambda e: 'CurrentPresence' in e.args[0]), + ) + assert signal.args[0]['CurrentPresence'] == (cs.PRESENCE_TYPE_AVAILABLE, + 'available', '') + + e = q.expect('dbus-signal', + path=account.object_path, + interface=cs.ACCOUNT, signal='AccountPropertyChanged', + predicate = lambda e: 'CurrentPresence' in e.args[0]) + assert e.args[0]['CurrentPresence'] == requested_presence + + # 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') + + # This is normally a C API, only exposed to D-Bus here for testing + secret_debug_api = dbus.Interface(bus.get_object(cs.AM, "/"), + 'org.freedesktop.Telepathy.MissionControl5.RegressionTests') + MCD_SYSTEM_IDLE = 32 + + # Set the idle flag + secret_debug_api.ChangeSystemFlags(dbus.UInt32(MCD_SYSTEM_IDLE), + dbus.UInt32(0)) + + e = q.expect('dbus-method-call', + path=conn.object_path, + interface=cs.CONN_IFACE_SIMPLE_PRESENCE, method='SetPresence', + args=['away', ''], + handled=True) + + # Unset the idle flag + secret_debug_api.ChangeSystemFlags(dbus.UInt32(0), + dbus.UInt32(MCD_SYSTEM_IDLE)) + + # MC puts the account back online + + e = q.expect('dbus-method-call', + path=conn.object_path, + interface=cs.CONN_IFACE_SIMPLE_PRESENCE, method='SetPresence', + args=['available', 'staring at the sea'], + handled=True) + + # Go to a non-Available status + requested_presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_TYPE_BUSY), + dbus.String(u'busy'), dbus.String(u'in the great below'))) + account.Set(cs.ACCOUNT, + 'RequestedPresence', requested_presence, + dbus_interface=cs.PROPERTIES_IFACE) + e = q.expect('dbus-method-call', + path=conn.object_path, + interface=cs.CONN_IFACE_SIMPLE_PRESENCE, method='SetPresence', + args=['busy', 'in the great below'], + handled=True) + + forbidden = [EventPattern('dbus-method-call', + path=conn.object_path, + interface=cs.CONN_IFACE_SIMPLE_PRESENCE, method='SetPresence')] + q.forbid_events(forbidden) + + # Set the idle flag + secret_debug_api.ChangeSystemFlags(dbus.UInt32(MCD_SYSTEM_IDLE), + dbus.UInt32(0)) + + # MC does not put the account away + + # Unset the idle flag + secret_debug_api.ChangeSystemFlags(dbus.UInt32(0), + dbus.UInt32(MCD_SYSTEM_IDLE)) + + # MC does not put the account back online + + q.unforbid_events(forbidden) + + # Put the account offline + requested_presence = (dbus.UInt32(cs.PRESENCE_TYPE_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 + +if __name__ == '__main__': + exec_test(test, {}) diff --git a/tests/twisted/account-manager/auto-connect.py b/tests/twisted/account-manager/auto-connect.py new file mode 100644 index 00000000..badcf7fc --- /dev/null +++ b/tests/twisted/account-manager/auto-connect.py @@ -0,0 +1,175 @@ +# Copyright (C) 2009 Nokia Corporation +# Copyright (C) 2009 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +import dbus +"""Feature test for automatically signing in and setting presence etc. +""" + +import os + +import dbus +import dbus.service + +from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ + call_async, assertEquals +from mctest import exec_test, SimulatedConnection, create_fakecm_account, \ + make_mc +import constants as cs + +cm_name_ref = dbus.service.BusName( + cs.tp_name_prefix + '.ConnectionManager.fakecm', bus=dbus.SessionBus()) + +account_id = 'fakecm/fakeprotocol/jc_2edenton_40unatco_2eint' + +def preseed(): + + accounts_dir = os.environ['MC_ACCOUNT_DIR'] + + accounts_cfg = open(accounts_dir + '/accounts.cfg', 'w') + + # As a regression test for part of fd.o #28557, the password starts and + # ends with a double backslash, which is represented in the file as a + # quadruple backslash. + accounts_cfg.write(r"""# Telepathy accounts +[%s] +manager=fakecm +protocol=fakeprotocol +DisplayName=Work account +NormalizedName=jc.denton@unatco.int +param-account=jc.denton@unatco.int +param-password=\\\\ionstorm\\\\ +Enabled=1 +ConnectAutomatically=1 +AutomaticPresenceType=2 +AutomaticPresenceStatus=available +AutomaticPresenceMessage=My vision is augmented +Nickname=JC +AvatarMime=image/jpeg +""" % account_id) + accounts_cfg.close() + + os.makedirs(accounts_dir + '/' + account_id) + avatar_bin = open(accounts_dir + '/' + account_id + '/avatar.bin', 'w') + avatar_bin.write('Deus Ex') + avatar_bin.close() + + account_connections_file = open(accounts_dir + '/.mc_connections', 'w') + account_connections_file.write("") + account_connections_file.close() + +def test(q, bus, unused): + + expected_params = { + 'account': 'jc.denton@unatco.int', + 'password': r'\\ionstorm\\', + } + + mc = make_mc(bus, q.append) + + request_conn, prop_changed, _ = q.expect_many( + EventPattern('dbus-method-call', method='RequestConnection', + args=['fakeprotocol', expected_params], + destination=cs.tp_name_prefix + '.ConnectionManager.fakecm', + path=cs.tp_path_prefix + '/ConnectionManager/fakecm', + interface=cs.tp_name_prefix + '.ConnectionManager', + handled=False), + EventPattern('dbus-signal', signal='AccountPropertyChanged', + predicate=(lambda e: 'ConnectionStatus' in e.args[0])), + EventPattern('dbus-signal', signal='NameOwnerChanged', + predicate=lambda e: e.args[0] == cs.AM and e.args[2]), + ) + + conn = SimulatedConnection(q, bus, 'fakecm', 'fakeprotocol', '_', + 'myself', has_presence=True, has_aliasing=True, has_avatars=True) + + assertEquals('/', prop_changed.args[0].get('Connection')) + assertEquals('', prop_changed.args[0].get('ConnectionError')) + assertEquals({}, prop_changed.args[0].get('ConnectionErrorDetails')) + assertEquals(cs.CONN_STATUS_CONNECTING, + prop_changed.args[0].get('ConnectionStatus')) + assertEquals(cs.CONN_STATUS_REASON_REQUESTED, + prop_changed.args[0].get('ConnectionStatusReason')) + + q.dbus_return(request_conn.message, conn.bus_name, conn.object_path, + signature='so') + + account_path = (cs.tp_path_prefix + '/Account/' + account_id) + account = bus.get_object( + cs.tp_name_prefix + '.AccountManager', + account_path) + + prop_changed, _ = q.expect_many( + EventPattern('dbus-signal', signal='AccountPropertyChanged', + predicate=(lambda e: 'ConnectionStatus' in e.args[0])), + EventPattern('dbus-method-call', method='Connect', + path=conn.object_path, handled=True, interface=cs.CONN), + ) + + assertEquals(conn.object_path, prop_changed.args[0].get('Connection')) + assertEquals('', prop_changed.args[0].get('ConnectionError')) + assertEquals({}, prop_changed.args[0].get('ConnectionErrorDetails')) + assertEquals(cs.CONN_STATUS_CONNECTING, + prop_changed.args[0].get('ConnectionStatus')) + assertEquals(cs.CONN_STATUS_REASON_REQUESTED, + prop_changed.args[0].get('ConnectionStatusReason')) + + props = account.GetAll(cs.ACCOUNT, dbus_interface=cs.PROPERTIES_IFACE) + assert props['Connection'] == conn.object_path + assert props['ConnectionStatus'] == cs.CONN_STATUS_CONNECTING + assert props['ConnectionStatusReason'] == cs.CONN_STATUS_REASON_REQUESTED + + print "becoming connected" + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + + set_aliases, set_presence, set_avatar, prop_changed = q.expect_many( + EventPattern('dbus-method-call', + interface=cs.CONN_IFACE_ALIASING, method='SetAliases', + args=[{ conn.self_handle: 'JC' }], + handled=False), + EventPattern('dbus-method-call', path=conn.object_path, + interface=cs.CONN_IFACE_SIMPLE_PRESENCE, method='SetPresence', + handled=True), + EventPattern('dbus-method-call', + interface=cs.CONN_IFACE_AVATARS, method='SetAvatar', + args=['Deus Ex', 'image/jpeg'], + handled=True), + EventPattern('dbus-signal', signal='AccountPropertyChanged', + path=account_path, interface=cs.ACCOUNT, + predicate=(lambda e: + e.args[0].get('ConnectionStatus') == + cs.CONN_STATUS_CONNECTED), + ), + ) + + assertEquals(conn.object_path, prop_changed.args[0].get('Connection')) + assertEquals('', prop_changed.args[0].get('ConnectionError')) + assertEquals({}, prop_changed.args[0].get('ConnectionErrorDetails')) + assertEquals(cs.CONN_STATUS_CONNECTED, + prop_changed.args[0].get('ConnectionStatus')) + assertEquals(cs.CONN_STATUS_REASON_REQUESTED, + prop_changed.args[0].get('ConnectionStatusReason')) + + assert account.Get(cs.ACCOUNT, 'CurrentPresence', + dbus_interface=cs.PROPERTIES_IFACE) == (cs.PRESENCE_TYPE_AVAILABLE, + 'available', 'My vision is augmented') + + q.dbus_return(set_aliases.message, signature='') + +if __name__ == '__main__': + preseed() + exec_test(test, {}, preload_mc=False) diff --git a/tests/twisted/account-manager/avatar-persist.py b/tests/twisted/account-manager/avatar-persist.py new file mode 100644 index 00000000..e1334435 --- /dev/null +++ b/tests/twisted/account-manager/avatar-persist.py @@ -0,0 +1,143 @@ +# Copyright (C) 2009 Nokia Corporation +# Copyright (C) 2009 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +import dbus +"""Feature test for signing in and setting an avatar, on CMs like Gabble where +the avatar is stored by the server. +""" + +import os + +import dbus +import dbus.service + +from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ + call_async +from mctest import exec_test, SimulatedConnection, create_fakecm_account, \ + make_mc +import constants as cs + +cm_name_ref = dbus.service.BusName( + cs.tp_name_prefix + '.ConnectionManager.fakecm', bus=dbus.SessionBus()) + +account_id = 'fakecm/fakeprotocol/jc_2edenton_40unatco_2eint' + +def preseed(): + + accounts_dir = os.environ['MC_ACCOUNT_DIR'] + + accounts_cfg = open(accounts_dir + '/accounts.cfg', 'w') + accounts_cfg.write("""# Telepathy accounts +[%s] +manager=fakecm +protocol=fakeprotocol +DisplayName=Work account +NormalizedName=jc.denton@unatco.int +param-account=jc.denton@unatco.int +param-password=ionstorm +Enabled=1 +ConnectAutomatically=1 +AutomaticPresenceType=2 +AutomaticPresenceStatus=available +AutomaticPresenceMessage=My vision is augmented +Nickname=JC +AvatarMime=image/jpeg +avatar_token=Deus Ex +""" % account_id) + accounts_cfg.close() + + os.makedirs(accounts_dir + '/' + account_id) + avatar_bin = open(accounts_dir + '/' + account_id + '/avatar.bin', 'w') + avatar_bin.write('Deus Ex') + avatar_bin.close() + + account_connections_file = open(accounts_dir + '/.mc_connections', 'w') + account_connections_file.write("") + account_connections_file.close() + +def test(q, bus, unused): + + expected_params = { + 'account': 'jc.denton@unatco.int', + 'password': 'ionstorm', + } + + mc = make_mc(bus, q.append) + + e, _ = q.expect_many( + EventPattern('dbus-method-call', method='RequestConnection', + args=['fakeprotocol', expected_params], + destination=cs.tp_name_prefix + '.ConnectionManager.fakecm', + path=cs.tp_path_prefix + '/ConnectionManager/fakecm', + interface=cs.tp_name_prefix + '.ConnectionManager', + handled=False), + EventPattern('dbus-signal', signal='NameOwnerChanged', + predicate=lambda e: e.args[0] == cs.AM and e.args[2]), + ) + + conn = SimulatedConnection(q, bus, 'fakecm', 'fakeprotocol', '_', + 'myself', has_avatars=True, avatars_persist=True) + conn.avatar = dbus.Struct((dbus.ByteArray('MJ12'), 'image/png'), + signature='ays') + + q.dbus_return(e.message, conn.bus_name, conn.object_path, signature='so') + + account_path = (cs.tp_path_prefix + '/Account/' + account_id) + + q.expect('dbus-method-call', method='Connect', + path=conn.object_path, handled=True, interface=cs.CONN) + + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + + # We haven't changed the avatar since we last signed in, so we don't set + # it - on the contrary, we pick up the remote avatar (which has changed + # since we were last here) to store it in the Account + _, request_avatars_call, e = q.expect_many( + EventPattern('dbus-method-call', + interface=cs.CONN_IFACE_AVATARS, method='GetKnownAvatarTokens', + args=[[conn.self_handle]], + handled=True), + EventPattern('dbus-method-call', + interface=cs.CONN_IFACE_AVATARS, method='RequestAvatars', + args=[[conn.self_handle]], + handled=False), + EventPattern('dbus-signal', signal='AccountPropertyChanged', + path=account_path, interface=cs.ACCOUNT, + predicate=(lambda e: + e.args[0].get('ConnectionStatus') == + cs.CONN_STATUS_CONNECTED), + ), + ) + + q.dbus_return(request_avatars_call.message, signature='') + + q.dbus_emit(conn.object_path, cs.CONN_IFACE_AVATARS, 'AvatarRetrieved', + conn.self_handle, str(conn.avatar[0]), + dbus.ByteArray(conn.avatar[0]), conn.avatar[1], signature='usays') + + q.expect('dbus-signal', path=account_path, + interface=cs.ACCOUNT_IFACE_AVATAR, signal='AvatarChanged'), + + account = bus.get_object(cs.AM, account_path) + account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) + assert account_props.Get(cs.ACCOUNT_IFACE_AVATAR, 'Avatar', + byte_arrays=True) == conn.avatar + +if __name__ == '__main__': + preseed() + exec_test(test, {}, preload_mc=False) diff --git a/tests/twisted/account-manager/avatar-refresh.py b/tests/twisted/account-manager/avatar-refresh.py new file mode 100644 index 00000000..7cc2d2c8 --- /dev/null +++ b/tests/twisted/account-manager/avatar-refresh.py @@ -0,0 +1,124 @@ +# Copyright (C) 2009 Nokia Corporation +# Copyright (C) 2009 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +import dbus +"""Feature test for signing in and setting an avatar, on CMs like Salut where +the avatar must be reset every time you sign in. +""" + +import os + +import dbus +import dbus.service + +from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ + call_async +from mctest import exec_test, SimulatedConnection, create_fakecm_account, \ + make_mc +import constants as cs + +cm_name_ref = dbus.service.BusName( + cs.tp_name_prefix + '.ConnectionManager.fakecm', bus=dbus.SessionBus()) + +account_id = 'fakecm/fakeprotocol/jc_2edenton_40unatco_2eint' + +def preseed(): + + accounts_dir = os.environ['MC_ACCOUNT_DIR'] + + accounts_cfg = open(accounts_dir + '/accounts.cfg', 'w') + accounts_cfg.write("""# Telepathy accounts +[%s] +manager=fakecm +protocol=fakeprotocol +DisplayName=Work account +NormalizedName=jc.denton@unatco.int +param-account=jc.denton@unatco.int +param-password=ionstorm +Enabled=1 +ConnectAutomatically=1 +AutomaticPresenceType=2 +AutomaticPresenceStatus=available +AutomaticPresenceMessage=My vision is augmented +Nickname=JC +AvatarMime=image/jpeg +avatar_token=Deus Ex +""" % account_id) + accounts_cfg.close() + + os.makedirs(accounts_dir + '/' + account_id) + avatar_bin = open(accounts_dir + '/' + account_id + '/avatar.bin', 'w') + avatar_bin.write('Deus Ex') + avatar_bin.close() + + account_connections_file = open(accounts_dir + '/.mc_connections', 'w') + account_connections_file.write("") + account_connections_file.close() + +def test(q, bus, unused): + + expected_params = { + 'account': 'jc.denton@unatco.int', + 'password': 'ionstorm', + } + + mc = make_mc(bus, q.append) + + e, _ = q.expect_many( + EventPattern('dbus-method-call', method='RequestConnection', + args=['fakeprotocol', expected_params], + destination=cs.tp_name_prefix + '.ConnectionManager.fakecm', + path=cs.tp_path_prefix + '/ConnectionManager/fakecm', + interface=cs.tp_name_prefix + '.ConnectionManager', + handled=False), + EventPattern('dbus-signal', signal='NameOwnerChanged', + predicate=lambda e: e.args[0] == cs.AM and e.args[2]), + ) + + conn = SimulatedConnection(q, bus, 'fakecm', 'fakeprotocol', '_', + 'myself', has_avatars=True, avatars_persist=False) + + q.dbus_return(e.message, conn.bus_name, conn.object_path, signature='so') + + account_path = (cs.tp_path_prefix + '/Account/' + account_id) + + q.expect('dbus-method-call', method='Connect', + path=conn.object_path, handled=True, interface=cs.CONN) + + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + + _, _, e = q.expect_many( + EventPattern('dbus-method-call', + interface=cs.CONN_IFACE_AVATARS, method='GetKnownAvatarTokens', + args=[[conn.self_handle]], + handled=True), + EventPattern('dbus-method-call', + interface=cs.CONN_IFACE_AVATARS, method='SetAvatar', + args=['Deus Ex', 'image/jpeg'], + handled=True), + EventPattern('dbus-signal', signal='AccountPropertyChanged', + path=account_path, interface=cs.ACCOUNT, + predicate=(lambda e: + e.args[0].get('ConnectionStatus') == + cs.CONN_STATUS_CONNECTED), + ), + ) + +if __name__ == '__main__': + preseed() + exec_test(test, {}, preload_mc=False) diff --git a/tests/twisted/account-manager/avatar.py b/tests/twisted/account-manager/avatar.py new file mode 100644 index 00000000..c616e146 --- /dev/null +++ b/tests/twisted/account-manager/avatar.py @@ -0,0 +1,112 @@ +# Copyright (C) 2009 Nokia Corporation +# Copyright (C) 2009 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +import dbus +import dbus +import dbus.service + +from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ + call_async, assertEquals +from mctest import exec_test, create_fakecm_account, enable_fakecm_account +import constants as cs + +def test(q, bus, mc): + params = dbus.Dictionary({"account": "me@example.com", + "password": "secrecy"}, signature='sv') + (cm_name_ref, 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_props, 'Set', cs.ACCOUNT_IFACE_AVATAR, 'Avatar', + dbus.Struct((dbus.ByteArray('AAAA'), 'image/jpeg'))) + q.expect_many( + EventPattern('dbus-signal', + path=account.object_path, + signal='AvatarChanged', + interface=cs.ACCOUNT_IFACE_AVATAR, + args=[]), + EventPattern('dbus-return', method='Set'), + ) + assert account_props.Get(cs.ACCOUNT_IFACE_AVATAR, 'Avatar', + byte_arrays=True) == ('AAAA', 'image/jpeg') + + # OK, let's go online. The avatar is set regardless of the CM + conn, e = enable_fakecm_account(q, bus, mc, account, params, + has_avatars=True, avatars_persist=True, + expect_after_connect=[ + EventPattern('dbus-method-call', + interface=cs.CONN_IFACE_AVATARS, method='SetAvatar', + handled=True, args=['AAAA', 'image/jpeg']), + ]) + + # Change avatar after going online + call_async(q, account_props, 'Set', cs.ACCOUNT_IFACE_AVATAR, 'Avatar', + (dbus.ByteArray('BBBB'), 'image/png')) + + q.expect_many( + EventPattern('dbus-method-call', + interface=cs.CONN_IFACE_AVATARS, method='SetAvatar', + args=['BBBB', 'image/png'], + handled=True), + EventPattern('dbus-signal', path=account.object_path, + interface=cs.ACCOUNT_IFACE_AVATAR, signal='AvatarChanged'), + EventPattern('dbus-return', method='Set') + ) + + assert account_props.Get(cs.ACCOUNT_IFACE_AVATAR, 'Avatar', + byte_arrays=True) == ('BBBB', 'image/png') + + someone_else = conn.ensure_handle(cs.HT_CONTACT, 'alberto@example.com') + + # Another contact changes their avatar: ignored + q.dbus_emit(conn.object_path, cs.CONN_IFACE_AVATARS, 'AvatarUpdated', + someone_else, "mardy's avatar token", signature='us') + + # Another client changes our avatar remotely + q.dbus_emit(conn.object_path, cs.CONN_IFACE_AVATARS, 'AvatarUpdated', + conn.self_handle, 'CCCC', signature='us') + + e = q.expect('dbus-method-call', + interface=cs.CONN_IFACE_AVATARS, method='RequestAvatars', + args=[[conn.self_handle]], + handled=False) + q.dbus_return(e.message, signature='') + + q.dbus_emit(conn.object_path, cs.CONN_IFACE_AVATARS, + 'AvatarRetrieved', conn.self_handle, 'CCCC', + dbus.ByteArray('CCCC'), 'image/svg', signature='usays') + q.expect('dbus-signal', path=account.object_path, + interface=cs.ACCOUNT_IFACE_AVATAR, signal='AvatarChanged'), + + assert account_props.Get(cs.ACCOUNT_IFACE_AVATAR, 'Avatar', + byte_arrays=True) == ('CCCC', 'image/svg') + + # empty avatar tests + conn.forget_avatar() + q.dbus_emit(conn.object_path, cs.CONN_IFACE_AVATARS, 'AvatarUpdated', + conn.self_handle, '', signature='us') + q.expect('dbus-method-call', method='GetKnownAvatarTokens') + q.expect('dbus-signal', path=account.object_path, + interface=cs.ACCOUNT_IFACE_AVATAR, signal='AvatarChanged') + + assertEquals(account_props.Get(cs.ACCOUNT_IFACE_AVATAR, 'Avatar', + byte_arrays=False), ([], '')) + +if __name__ == '__main__': + exec_test(test, {}) diff --git a/tests/twisted/account-manager/bad-cm.py b/tests/twisted/account-manager/bad-cm.py new file mode 100644 index 00000000..dea5860a --- /dev/null +++ b/tests/twisted/account-manager/bad-cm.py @@ -0,0 +1,79 @@ +# Copyright (C) 2009 Nokia Corporation +# Copyright (C) 2009 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +"""Regression test for https://bugs.freedesktop.org/show_bug.cgi?id=20880 +""" + +import dbus + +from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ + call_async +from fakecm import start_fake_connection_manager +from mctest import exec_test, get_account_manager +import constants as cs + +FakeCM_bus_name = "com.example.FakeCM" +ConnectionManager_object_path = "/com/example/FakeCM/ConnectionManager" + + +def test(q, bus, mc): + # Get the AccountManager interface + account_manager = get_account_manager(bus) + account_manager_iface = dbus.Interface(account_manager, cs.AM) + + # Create an account with a bad Connection_Manager - it should fail + + params = dbus.Dictionary({"account": "someguy@example.com", + "password": "secrecy"}, signature='sv') + call_async(q, account_manager_iface, 'CreateAccount', + 'nonexistent_cm', # Connection_Manager + 'fakeprotocol', # Protocol + 'fakeaccount', #Display_Name + params, # Parameters + {}, # Properties + ) + q.expect('dbus-error', method='CreateAccount') + + # Create an account with a bad Protocol - it should fail + + params = dbus.Dictionary({"account": "someguy@example.com", + "password": "secrecy"}, signature='sv') + call_async(q, account_manager_iface, 'CreateAccount', + 'fakecm', # Connection_Manager + 'nonexistent-protocol', # Protocol + 'fakeaccount', #Display_Name + params, # Parameters + {}, # Properties + ) + q.expect('dbus-error', method='CreateAccount') + + # Create an account with incomplete Parameters - it should fail + + params = dbus.Dictionary({"account": "someguy@example.com"}, + signature='sv') + call_async(q, account_manager_iface, 'CreateAccount', + 'fakecm', # Connection_Manager + 'fakeprotocol', # Protocol + 'fakeaccount', #Display_Name + params, # Parameters + {}, # Properties + ) + q.expect('dbus-error', method='CreateAccount') + +if __name__ == '__main__': + exec_test(test, {}) diff --git a/tests/twisted/account-manager/create-auto-connect.py b/tests/twisted/account-manager/create-auto-connect.py new file mode 100644 index 00000000..fa2cb470 --- /dev/null +++ b/tests/twisted/account-manager/create-auto-connect.py @@ -0,0 +1,68 @@ +# Copyright (C) 2009 Nokia Corporation +# Copyright (C) 2009 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +import dbus +import dbus +import dbus.service + +from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ + call_async +from mctest import exec_test, create_fakecm_account +import constants as cs + +def test(q, bus, mc): + params = dbus.Dictionary({"account": "smcv@example.com", + "password": "secrecy"}, signature='sv') + (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) + + account_iface = dbus.Interface(account, cs.ACCOUNT) + account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) + + # Ensure that it's enabled but has offline RP + + call_async(q, account_props, 'Set', cs.ACCOUNT, 'RequestedPresence', + (dbus.UInt32(cs.PRESENCE_TYPE_OFFLINE), 'offline', '')) + q.expect('dbus-return', method='Set') + + call_async(q, account_props, 'Set', cs.ACCOUNT, 'AutomaticPresence', + (dbus.UInt32(cs.PRESENCE_TYPE_BUSY), 'busy', + 'Testing automatic presence')) + q.expect('dbus-return', method='Set') + q.expect('dbus-signal', signal='AccountPropertyChanged', + predicate=lambda e: + e.args[0].get('AutomaticPresence', (None, None, None))[1] + == 'busy') + + call_async(q, account_props, 'Set', cs.ACCOUNT, 'Enabled', True) + q.expect('dbus-return', method='Set') + q.expect('dbus-signal', signal='AccountPropertyChanged', + predicate=lambda e: e.args[0].get('Enabled')) + + # Go online by telling it to connect automatically + call_async(q, account_props, 'Set', cs.ACCOUNT, 'ConnectAutomatically', + True) + + e = q.expect('dbus-method-call', method='RequestConnection', + args=['fakeprotocol', params], + destination=cs.tp_name_prefix + '.ConnectionManager.fakecm', + path=cs.tp_path_prefix + '/ConnectionManager/fakecm', + interface=cs.tp_name_prefix + '.ConnectionManager', + handled=False) + +if __name__ == '__main__': + exec_test(test, {}) diff --git a/tests/twisted/account-manager/create-twice.py b/tests/twisted/account-manager/create-twice.py new file mode 100644 index 00000000..2f0c0e0c --- /dev/null +++ b/tests/twisted/account-manager/create-twice.py @@ -0,0 +1,64 @@ +# Copyright (C) 2009 Nokia Corporation +# Copyright (C) 2009-2010 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +import dbus +import dbus +import dbus.service + +from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ + call_async +from mctest import exec_test, create_fakecm_account, get_account_manager +import constants as cs + +def test(q, bus, mc): + account_manager = get_account_manager(bus) + account_manager_iface = dbus.Interface(account_manager, cs.AM) + + # fd.o #25684: creating similarly-named accounts in very quick succession + # used to fail + + params = dbus.Dictionary({"account": "create-twice", + "password": "secrecy"}, signature='sv') + + cm_name_ref = dbus.service.BusName(cs.tp_name_prefix + + '.ConnectionManager.fakecm', bus=bus) + account_manager = bus.get_object(cs.AM, cs.AM_PATH) + am_iface = dbus.Interface(account_manager, cs.AM) + + call_async(q, am_iface, 'CreateAccount', + 'fakecm', + 'fakeprotocol', + 'fakeaccount', + params, + {}) + call_async(q, am_iface, 'CreateAccount', + 'fakecm', + 'fakeprotocol', + 'fakeaccount', + params, + {}) + + ret1 = q.expect('dbus-return', method='CreateAccount') + ret2 = q.expect('dbus-return', method='CreateAccount') + + path1 = ret1.value[0] + path2 = ret2.value[0] + assert path1 != path2 + +if __name__ == '__main__': + exec_test(test, {}) diff --git a/tests/twisted/account-manager/create-with-properties.py b/tests/twisted/account-manager/create-with-properties.py new file mode 100644 index 00000000..5aabfc76 --- /dev/null +++ b/tests/twisted/account-manager/create-with-properties.py @@ -0,0 +1,174 @@ +# Copyright (C) 2009 Nokia Corporation +# Copyright (C) 2009 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +import dbus +import dbus +import dbus.service + +from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ + call_async +from mctest import exec_test, create_fakecm_account, get_account_manager +import constants as cs + +def test(q, bus, mc): + # Get the AccountManager interface + account_manager = get_account_manager(bus) + account_manager_iface = dbus.Interface(account_manager, cs.AM) + + # Introspect AccountManager for debugging purpose + account_manager_introspected = account_manager.Introspect( + dbus_interface=cs.INTROSPECTABLE_IFACE) + #print account_manager_introspected + + # Check AccountManager has D-Bus property interface + properties = account_manager.GetAll(cs.AM, + dbus_interface=cs.PROPERTIES_IFACE) + assert properties is not None + assert properties.get('ValidAccounts') == [], \ + properties.get('ValidAccounts') + assert properties.get('InvalidAccounts') == [], \ + properties.get('InvalidAccounts') + interfaces = properties.get('Interfaces') + supported = properties.get('SupportedAccountProperties') + + # assert that current functionality exists + assert cs.AM_IFACE_NOKIA_QUERY in interfaces, interfaces + + assert (cs.ACCOUNT + '.AutomaticPresence') in supported + assert (cs.ACCOUNT + '.Enabled') in supported + assert (cs.ACCOUNT + '.Icon') in supported + assert (cs.ACCOUNT + '.Nickname') in supported + assert (cs.ACCOUNT + '.ConnectAutomatically') in supported + assert (cs.ACCOUNT_IFACE_AVATAR + '.Avatar') in supported + assert (cs.ACCOUNT_IFACE_NOKIA_COMPAT + '.Profile') in supported + assert (cs.ACCOUNT_IFACE_NOKIA_COMPAT + '.SecondaryVCardFields') in supported + assert (cs.ACCOUNT_IFACE_NOKIA_CONDITIONS + '.Condition') in supported + + assert (cs.ACCOUNT + '.RequestedPresence') in supported + + params = dbus.Dictionary({"account": "anarki@example.com", + "password": "secrecy"}, signature='sv') + + cm_name_ref = dbus.service.BusName(cs.tp_name_prefix + + '.ConnectionManager.fakecm', bus=bus) + account_manager = bus.get_object(cs.AM, cs.AM_PATH) + am_iface = dbus.Interface(account_manager, cs.AM) + + creation_properties = dbus.Dictionary({ + cs.ACCOUNT + '.Enabled': True, + cs.ACCOUNT + '.AutomaticPresence': dbus.Struct(( + dbus.UInt32(cs.PRESENCE_TYPE_BUSY), + 'busy', 'Exploding'), signature='uss'), + cs.ACCOUNT + '.RequestedPresence': dbus.Struct(( + dbus.UInt32(cs.PRESENCE_TYPE_AWAY), + 'away', 'Respawning'), signature='uss'), + cs.ACCOUNT + '.Icon': 'quake3arena', + cs.ACCOUNT + '.Nickname': 'AnArKi', + cs.ACCOUNT + '.ConnectAutomatically': True, + cs.ACCOUNT_IFACE_AVATAR + '.Avatar': (dbus.ByteArray('foo'), + 'image/jpeg'), + cs.ACCOUNT_IFACE_NOKIA_COMPAT + '.Profile': 'openarena', + cs.ACCOUNT_IFACE_NOKIA_COMPAT + '.SecondaryVCardFields': + dbus.Array(['x-ioquake3', 'x-quake3'], signature='s'), + cs.ACCOUNT_IFACE_NOKIA_CONDITIONS + '.Condition': + dbus.Dictionary({ 'has-quad-damage': ':y' }, signature='ss'), + }, signature='sv') + + call_async(q, am_iface, 'CreateAccount', + 'fakecm', + 'fakeprotocol', + 'fakeaccount', + params, + creation_properties) + + # The spec has no order guarantee here. + # FIXME: MC ought to also introspect the CM and find out that the params + # are in fact sufficient + + am_signal, ret, rc = q.expect_many( + EventPattern('dbus-signal', path=cs.AM_PATH, + signal='AccountValidityChanged', interface=cs.AM), + EventPattern('dbus-return', method='CreateAccount'), + EventPattern('dbus-method-call', method='RequestConnection'), + ) + account_path = ret.value[0] + assert am_signal.args == [account_path, True], am_signal.args + + assert account_path is not None + + account = bus.get_object( + cs.tp_name_prefix + '.AccountManager', + account_path) + account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) + + properties = account_props.GetAll(cs.ACCOUNT) + assert properties.get('AutomaticPresence') == (cs.PRESENCE_TYPE_BUSY, + 'busy', 'Exploding'), \ + properties.get('AutomaticPresence') + assert properties.get('RequestedPresence') == (cs.PRESENCE_TYPE_AWAY, + 'away', 'Respawning'), \ + properties.get('RequestedPresence') + assert properties.get('ConnectAutomatically') == True, \ + properties.get('ConnectAutomatically') + assert properties.get('Enabled') == True, \ + properties.get('Enabled') + assert properties.get('Valid') == True, \ + properties.get('Valid') + assert properties.get('Icon') == 'quake3arena', \ + properties.get('Icon') + assert properties.get('Nickname') == 'AnArKi', \ + properties.get('Nickname') + + properties = account_props.GetAll(cs.ACCOUNT_IFACE_AVATAR) + assert properties.get('Avatar') == ([ord('f'), ord('o'), ord('o')], + 'image/jpeg') + + properties = account_props.GetAll(cs.ACCOUNT_IFACE_NOKIA_COMPAT) + assert properties.get('Profile') == 'openarena' + assert sorted(properties.get('SecondaryVCardFields')) == \ + ['x-ioquake3', 'x-quake3'] + + properties = account_props.GetAll(cs.ACCOUNT_IFACE_NOKIA_CONDITIONS) + assert properties.get('Condition') == { + 'has-quad-damage': ':y', + } + + # tests for errors when creating an account + + creation_properties2 = creation_properties.copy() + creation_properties2[cs.ACCOUNT + '.NonExistent'] = 'foo' + call_async(q, am_iface, 'CreateAccount', + 'fakecm', + 'fakeprotocol', + 'fakeaccount', + params, + creation_properties2) + q.expect('dbus-error', method='CreateAccount') + + params2 = params.copy() + params2['fake_param'] = 'foo' + call_async(q, am_iface, 'CreateAccount', + 'fakecm', + 'fakeprotocol', + 'fakeaccount', + params2, + creation_properties) + q.expect('dbus-error', method='CreateAccount') + +if __name__ == '__main__': + exec_test(test, {}) diff --git a/tests/twisted/account-manager/device-idle.py b/tests/twisted/account-manager/device-idle.py new file mode 100644 index 00000000..089a0661 --- /dev/null +++ b/tests/twisted/account-manager/device-idle.py @@ -0,0 +1,129 @@ +# Copyright (C) 2009 Nokia Corporation +# Copyright (C) 2009 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +import config + +if config.HAVE_MCE: + print "NOTE: built with real MCE support; skipping idleness test" + raise SystemExit(77) + +import dbus.service + +from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ + call_async, unwrap, sync_dbus +from mctest import exec_test, create_fakecm_account, SimulatedConnection, \ + enable_fakecm_account +import constants as cs + +# Fake MCE constants, cloned from mce-slacker.c +MCE_SERVICE = "org.freedesktop.Telepathy.MissionControl.Tests.MCE" + +MCE_SIGNAL_IF = "org.freedesktop.Telepathy.MissionControl.Tests.MCE" + +MCE_REQUEST_IF = "org.freedesktop.Telepathy.MissionControl.Tests.MCE" +MCE_REQUEST_PATH = "/org/freedesktop/Telepathy/MissionControl/Tests/MCE" + +class SimulatedMCE(object): + def __init__(self, q, bus, inactive=False): + self.bus = bus + self.q = q + self.inactive = inactive + self.object_path = MCE_REQUEST_PATH + self._name_ref = dbus.service.BusName(MCE_SERVICE, bus) + + q.add_dbus_method_impl(self.GetInactivity, + path=self.object_path, interface=MCE_REQUEST_IF, + method='GetInactivity') + + + def GetInactivity(self, e): + self.q.dbus_return(e.message, self.inactive, signature='b') + + def InactivityChanged(self, new_value): + self.inactive = new_value + self.q.dbus_emit(self.object_path, MCE_SIGNAL_IF, "InactivityChanged", + self.inactive, signature="b") + + def release_name(self): + del self._name_ref + +def _create_and_enable(q, bus, mc, account_name, power_saving_supported, + expect_after_connect=[]): + extra_interfaces = [] + if power_saving_supported: + extra_interfaces = [cs.CONN_IFACE_POWER_SAVING] + params = dbus.Dictionary({"account": account_name, "password": "secrecy"}, + signature='sv') + cm_name_ref, account = create_fakecm_account(q, bus, mc, params) + conn = enable_fakecm_account(q, bus, mc, account, params, has_requests=False, + extra_interfaces=extra_interfaces, + expect_after_connect=expect_after_connect) + + if isinstance(conn, tuple): + conn = conn[0] + + return account, conn + +def _disable_account(q, bus, mc, account, conn): + account.Set(cs.ACCOUNT, 'Enabled', False, + dbus_interface=cs.PROPERTIES_IFACE) + + q.expect('dbus-method-call', method='Disconnect', + path=conn.object_path, handled=True) + +def test(q, bus, mc): + mce = SimulatedMCE(q, bus, True) + + account1, conn1 = _create_and_enable( + q, bus, mc, "first@example.com", True, + [EventPattern('dbus-method-call', method='SetPowerSaving', args=[True])]) + account2, conn2 = _create_and_enable(q, bus, mc, "second@example.com", False) + + # Second account does not support PowerSaving interface, don't call SetPowerSaving + forbid_no_iface =[EventPattern('dbus-method-call', method='SetPowerSaving', + path=conn2.object_path)] + + q.forbid_events(forbid_no_iface) + + for enabled in [False, True, False]: + mce.InactivityChanged(enabled) + q.expect('dbus-method-call', method='SetPowerSaving', + args=[enabled], interface=cs.CONN_IFACE_POWER_SAVING, + path=conn1.object_path) + + _disable_account(q, bus, mc, account1, conn1) + _disable_account(q, bus, mc, account2, conn2) + + q.unforbid_events(forbid_no_iface) + + # Make sure we don't call SetPowerSaving on a disconnected connection. + + forbid_when_disconnected =[EventPattern('dbus-method-call', method='SetPowerSaving')] + + q.forbid_events(forbid_when_disconnected) + + mce.InactivityChanged(True) + + sync_dbus(bus, q, account1) + + q.unforbid_events(forbid_when_disconnected) + + mce.release_name() + +if __name__ == '__main__': + exec_test(test, {}) diff --git a/tests/twisted/account-manager/enable-auto-connect.py b/tests/twisted/account-manager/enable-auto-connect.py new file mode 100644 index 00000000..2cb91c31 --- /dev/null +++ b/tests/twisted/account-manager/enable-auto-connect.py @@ -0,0 +1,69 @@ +# Copyright (C) 2009 Nokia Corporation +# Copyright (C) 2009 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +import dbus +import dbus +import dbus.service + +from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ + call_async +from mctest import exec_test, create_fakecm_account +import constants as cs + +def test(q, bus, mc): + params = dbus.Dictionary({"account": "smcv@example.com", + "password": "secrecy"}, signature='sv') + (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) + + account_iface = dbus.Interface(account, cs.ACCOUNT) + account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) + + # Ensure that it's enabled but has offline RP + + call_async(q, account_props, 'Set', cs.ACCOUNT, 'RequestedPresence', + (dbus.UInt32(cs.PRESENCE_TYPE_OFFLINE), 'offline', '')) + q.expect('dbus-return', method='Set') + + call_async(q, account_props, 'Set', cs.ACCOUNT, 'AutomaticPresence', + (dbus.UInt32(cs.PRESENCE_TYPE_BUSY), 'busy', + 'Testing automatic presence')) + q.expect('dbus-return', method='Set') + q.expect('dbus-signal', signal='AccountPropertyChanged', + predicate=lambda e: + e.args[0].get('AutomaticPresence', (None, None, None))[1] + == 'busy') + + call_async(q, account_props, 'Set', cs.ACCOUNT, 'Enabled', False) + q.expect('dbus-return', method='Set') + + call_async(q, account_props, 'Set', cs.ACCOUNT, 'ConnectAutomatically', + True) + q.expect('dbus-return', method='Set') + + # Go online by enabling the account + call_async(q, account_props, 'Set', cs.ACCOUNT, 'Enabled', True) + + e = q.expect('dbus-method-call', method='RequestConnection', + args=['fakeprotocol', params], + destination=cs.tp_name_prefix + '.ConnectionManager.fakecm', + path=cs.tp_path_prefix + '/ConnectionManager/fakecm', + interface=cs.tp_name_prefix + '.ConnectionManager', + handled=False) + +if __name__ == '__main__': + exec_test(test, {}) diff --git a/tests/twisted/account-manager/enable.py b/tests/twisted/account-manager/enable.py new file mode 100644 index 00000000..50e489d1 --- /dev/null +++ b/tests/twisted/account-manager/enable.py @@ -0,0 +1,62 @@ +# Copyright (C) 2009 Nokia Corporation +# Copyright (C) 2009 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +import dbus +import dbus +import dbus.service + +from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ + call_async +from mctest import exec_test, create_fakecm_account +import constants as cs + +def test(q, bus, mc): + params = dbus.Dictionary({"account": "smcv@example.com", + "password": "secrecy"}, signature='sv') + (cm_name_ref, 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_props, 'Set', cs.ACCOUNT, 'RequestedPresence', + (dbus.UInt32(cs.PRESENCE_TYPE_OFFLINE), 'offline', '')) + q.expect('dbus-return', method='Set') + + call_async(q, account_props, 'Set', cs.ACCOUNT, 'Enabled', False) + q.expect('dbus-return', method='Set') + + call_async(q, account_props, 'Set', cs.ACCOUNT, 'ConnectAutomatically', + False) + q.expect('dbus-return', method='Set') + + call_async(q, account_props, 'Set', cs.ACCOUNT, 'RequestedPresence', + (dbus.UInt32(cs.PRESENCE_TYPE_BUSY), 'busy', 'Testing Enabled')) + q.expect('dbus-return', method='Set') + + # Go online by setting Enabled + call_async(q, account_props, 'Set', cs.ACCOUNT, 'Enabled', True) + + e = q.expect('dbus-method-call', method='RequestConnection', + args=['fakeprotocol', params], + destination=cs.tp_name_prefix + '.ConnectionManager.fakecm', + path=cs.tp_path_prefix + '/ConnectionManager/fakecm', + interface=cs.tp_name_prefix + '.ConnectionManager', + handled=False) + +if __name__ == '__main__': + exec_test(test, {}) diff --git a/tests/twisted/account-manager/make-valid.py b/tests/twisted/account-manager/make-valid.py new file mode 100644 index 00000000..17d58049 --- /dev/null +++ b/tests/twisted/account-manager/make-valid.py @@ -0,0 +1,237 @@ +# Copyright (C) 2009 Nokia Corporation +# Copyright (C) 2009 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +import dbus +"""Feature test for accounts becoming valid. +""" + +import os + +import dbus +import dbus.service + +from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ + call_async, sync_dbus +from mctest import exec_test, SimulatedConnection, create_fakecm_account, \ + make_mc +import constants as cs + +cm_name_ref = dbus.service.BusName( + cs.tp_name_prefix + '.ConnectionManager.fakecm', bus=dbus.SessionBus()) + +account1_id = 'fakecm/fakeprotocol/jc_2edenton_40unatco_2eint' +account2_id = 'fakecm/fakeprotocol/jc_2edenton_40example_2ecom' + +def preseed(): + + accounts_dir = os.environ['MC_ACCOUNT_DIR'] + + # The passwords are missing, so the accounts can't connect yet. + accounts_cfg = open(accounts_dir + '/accounts.cfg', 'w') + accounts_cfg.write("""# Telepathy accounts +[%s] +manager=fakecm +protocol=fakeprotocol +DisplayName=Work account +NormalizedName=jc.denton@unatco.int +param-account=jc.denton@unatco.int +Enabled=1 +ConnectAutomatically=1 +AutomaticPresenceType=2 +AutomaticPresenceStatus=available +AutomaticPresenceMessage=My vision is augmented +Nickname=JC +AvatarMime=image/jpeg + +[%s] +manager=fakecm +protocol=fakeprotocol +DisplayName=Personal account +NormalizedName=jc.denton@example.com +param-account=jc.denton@example.com +Enabled=1 +ConnectAutomatically=0 +AutomaticPresenceType=2 +AutomaticPresenceStatus=available +AutomaticPresenceMessage=My vision is augmented +Nickname=JC +AvatarMime=image/jpeg +""" % (account1_id, account2_id)) + accounts_cfg.close() + + os.makedirs(accounts_dir + '/' + account1_id) + avatar_bin = open(accounts_dir + '/' + account1_id + '/avatar.bin', 'w') + avatar_bin.write('Deus Ex') + avatar_bin.close() + + os.makedirs(accounts_dir + '/' + account2_id) + avatar_bin = open(accounts_dir + '/' + account2_id + '/avatar.bin', 'w') + avatar_bin.write('Invisible War') + avatar_bin.close() + + account_connections_file = open(accounts_dir + '/.mc_connections', 'w') + account_connections_file.write("") + account_connections_file.close() + +def test(q, bus, unused): + # make sure RequestConnection doesn't get called yet + events = [EventPattern('dbus-method-call', method='RequestConnection')] + q.forbid_events(events) + + # Wait for MC to load + mc = make_mc(bus, q.append) + + q.expect_many( + EventPattern('dbus-signal', signal='NameOwnerChanged', + predicate=lambda e: e.args[0] == cs.AM), + EventPattern('dbus-signal', signal='NameOwnerChanged', + predicate=lambda e: e.args[0] == cs.CD), + ) + + # Trying to make a channel on account 1 doesn't work, because it's + # not valid + + account_path = (cs.tp_path_prefix + '/Account/' + account1_id) + + cd = bus.get_object(cs.CD, cs.CD_PATH) + + user_action_time = dbus.Int64(1238582606) + request = dbus.Dictionary({ + cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_TEXT, + cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT, + cs.CHANNEL + '.TargetID': 'juliet', + }, signature='sv') + call_async(q, cd, 'CreateChannel', + account_path, request, user_action_time, "", + dbus_interface=cs.CD) + ret = q.expect('dbus-return', method='CreateChannel') + request_path = ret.value[0] + + cr = bus.get_object(cs.CD, request_path) + request_props = cr.GetAll(cs.CR, dbus_interface=cs.PROPERTIES_IFACE) + assert request_props['Account'] == account_path + assert request_props['Requests'] == [request] + assert request_props['UserActionTime'] == user_action_time + assert request_props['PreferredHandler'] == "" + assert request_props['Interfaces'] == [] + + sync_dbus(bus, q, mc) + + cr.Proceed(dbus_interface=cs.CR) + + # FIXME: error isn't specified (NotAvailable perhaps?) + q.expect('dbus-signal', path=cr.object_path, + interface=cs.CR, signal='Failed') + + # Make account 1 valid: it should connect automatically + + account_path = (cs.tp_path_prefix + '/Account/' + account1_id) + account = bus.get_object(cs.MC, account_path) + + sync_dbus(bus, q, mc) + q.unforbid_events(events) + + call_async(q, account, 'UpdateParameters', {'password': 'nanotech'}, [], + dbus_interface=cs.ACCOUNT) + + expected_params = {'password': 'nanotech', + 'account': 'jc.denton@unatco.int'} + + e = q.expect('dbus-method-call', method='RequestConnection', + args=['fakeprotocol', expected_params], + destination=cs.tp_name_prefix + '.ConnectionManager.fakecm', + path=cs.tp_path_prefix + '/ConnectionManager/fakecm', + interface=cs.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') + + q.expect('dbus-method-call', method='Connect', + path=conn.object_path, handled=True, interface=cs.CONN) + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + + set_presence, e = q.expect_many( + EventPattern('dbus-method-call', path=conn.object_path, + interface=cs.CONN_IFACE_SIMPLE_PRESENCE, method='SetPresence', + handled=True), + EventPattern('dbus-signal', signal='AccountPropertyChanged', + path=account_path, interface=cs.ACCOUNT, + predicate=lambda e: 'CurrentPresence' in e.args[0] + and e.args[0]['CurrentPresence'][2] != ''), + ) + + assert e.args[0]['CurrentPresence'] == (cs.PRESENCE_TYPE_AVAILABLE, + 'available', 'My vision is augmented') + + # Request an online presence on account 2, then make it valid + + q.forbid_events(events) + + account_path = (cs.tp_path_prefix + '/Account/' + account2_id) + account = bus.get_object(cs.MC, account_path) + + requested_presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_TYPE_BUSY), + 'busy', 'Talking to Illuminati')) + account.Set(cs.ACCOUNT, 'RequestedPresence', + dbus.Struct(requested_presence, variant_level=1), + dbus_interface=cs.PROPERTIES_IFACE) + + sync_dbus(bus, q, mc) + q.unforbid_events(events) + + # Make the account valid + call_async(q, account, 'UpdateParameters', {'password': 'nanotech'}, [], + dbus_interface=cs.ACCOUNT) + + expected_params = {'password': 'nanotech', + 'account': 'jc.denton@example.com'} + + e = q.expect('dbus-method-call', method='RequestConnection', + args=['fakeprotocol', expected_params], + destination=cs.tp_name_prefix + '.ConnectionManager.fakecm', + path=cs.tp_path_prefix + '/ConnectionManager/fakecm', + interface=cs.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') + + q.expect('dbus-method-call', method='Connect', + path=conn.object_path, handled=True, interface=cs.CONN) + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + + set_presence = q.expect('dbus-method-call', path=conn.object_path, + interface=cs.CONN_IFACE_SIMPLE_PRESENCE, method='SetPresence', + handled=True) + + e = q.expect('dbus-signal', signal='AccountPropertyChanged', + path=account_path, interface=cs.ACCOUNT, + predicate=lambda e: 'CurrentPresence' in e.args[0] + and e.args[0]['CurrentPresence'][1] == 'busy') + + assert e.args[0]['CurrentPresence'] == (cs.PRESENCE_TYPE_BUSY, + 'busy', 'Talking to Illuminati') + +if __name__ == '__main__': + preseed() + exec_test(test, {}, preload_mc=False) diff --git a/tests/twisted/account-manager/nickname.py b/tests/twisted/account-manager/nickname.py new file mode 100644 index 00000000..8e2fe799 --- /dev/null +++ b/tests/twisted/account-manager/nickname.py @@ -0,0 +1,93 @@ +# Copyright (C) 2009 Nokia Corporation +# Copyright (C) 2009 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +import dbus +import dbus +import dbus.service + +from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ + call_async +from mctest import exec_test, create_fakecm_account, enable_fakecm_account +import constants as cs + +def test(q, bus, mc): + params = dbus.Dictionary({"account": "wjt@example.com", + "password": "secrecy"}, signature='sv') + (cm_name_ref, 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_props, 'Set', cs.ACCOUNT, 'Nickname', + 'resiak') + q.expect_many( + EventPattern('dbus-signal', + path=account.object_path, + signal='AccountPropertyChanged', + interface=cs.ACCOUNT, + args=[{'Nickname': 'resiak'}]), + EventPattern('dbus-return', method='Set'), + ) + assert account_props.Get(cs.ACCOUNT, 'Nickname') == 'resiak' + + # OK, let's go online + conn, get_aliases, set_aliases = enable_fakecm_account(q, bus, mc, + account, params, has_aliasing=True, + expect_after_connect=[ + EventPattern('dbus-method-call', + interface=cs.CONN_IFACE_ALIASING, method='GetAliases', + handled=False), + EventPattern('dbus-method-call', + interface=cs.CONN_IFACE_ALIASING, method='SetAliases', + handled=False), + ]) + + assert get_aliases.args[0] == [ conn.self_handle ] + q.dbus_return(get_aliases.message, { conn.self_handle: 'wjt@example.com' }, + signature='a{us}') + + assert set_aliases.args[0] == { conn.self_handle: 'resiak' } + q.dbus_return(set_aliases.message, signature='') + + # Change alias after going online + call_async(q, account_props, 'Set', cs.ACCOUNT, 'Nickname', + 'Will Thomspon') + + e = q.expect('dbus-method-call', + interface=cs.CONN_IFACE_ALIASING, method='SetAliases', + args=[{ conn.self_handle: 'Will Thomspon' }], + handled=False) + + # Set returns immediately; the change happens asynchronously + q.expect('dbus-return', method='Set') + + q.dbus_return(e.message, signature='') + + someone_else = conn.ensure_handle(cs.HT_CONTACT, 'alberto@example.com') + + # Another client changes our alias remotely + q.dbus_emit(conn.object_path, cs.CONN_IFACE_ALIASING, 'AliasesChanged', + dbus.Array([(conn.self_handle, 'wjt'), (someone_else, 'mardy')], + signature='(us)'), signature='a(us)') + + q.expect('dbus-signal', path=account.object_path, + signal='AccountPropertyChanged', interface=cs.ACCOUNT, + args=[{'Nickname': 'wjt'}]) + +if __name__ == '__main__': + exec_test(test, {}) diff --git a/tests/twisted/account-manager/param-types.py b/tests/twisted/account-manager/param-types.py new file mode 100644 index 00000000..8dafd7f1 --- /dev/null +++ b/tests/twisted/account-manager/param-types.py @@ -0,0 +1,86 @@ +# Copyright (C) 2009 Nokia Corporation +# Copyright (C) 2009 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +import dbus +import dbus +import dbus.service + +from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ + call_async +from mctest import exec_test, create_fakecm_account, get_account_manager +import constants as cs + +def test(q, bus, mc): + cm_name_ref = dbus.service.BusName( + cs.tp_name_prefix + '.ConnectionManager.onewitheverything', + bus=bus) + + # Get the AccountManager interface + account_manager = get_account_manager(bus) + account_manager_iface = dbus.Interface(account_manager, cs.AM) + + params = dbus.Dictionary({ + 's': 'lalala', + 'o': dbus.ObjectPath('/lalala'), + 'b': False, + 'q': dbus.UInt16(42), + 'u': dbus.UInt32(0xFFFFFFFFL), + 't': dbus.UInt64(0xFFFFffffFFFFffffL), + 'n': dbus.Int16(-42), + 'i': dbus.Int32(-42), + 'x': dbus.Int64(-1 * 0x7FFFffffFFFFffffL), + 'd': 4.5, + 'y': dbus.Byte(42), + 'as': dbus.Array(['one', 'two', 'three'], signature='s') + }, signature='sv') + + # Create an account + call_async(q, account_manager_iface, 'CreateAccount', + 'onewitheverything', # Connection_Manager + 'serializable', # Protocol + 'fakeaccount', #Display_Name + params, # Parameters + {}, # Properties + ) + + am_signal, ret = q.expect_many( + EventPattern('dbus-signal', path=cs.AM_PATH, + interface=cs.AM, signal='AccountValidityChanged'), + EventPattern('dbus-return', method='CreateAccount'), + ) + account_path = ret.value[0] + assert am_signal.args == [account_path, True], am_signal.args + assert account_path is not None + + account = bus.get_object( + cs.tp_name_prefix + '.AccountManager', + account_path) + account_iface = dbus.Interface(account, cs.ACCOUNT) + account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) + + stored_params = account_props.Get(cs.ACCOUNT, 'Parameters') + + for k in stored_params: + assert k in params, k + + for k in params: + assert k in stored_params, k + assert stored_params[k] == params[k], (k, stored_params[k], params[k]) + +if __name__ == '__main__': + exec_test(test, {}) diff --git a/tests/twisted/account-manager/presence.py b/tests/twisted/account-manager/presence.py new file mode 100755 index 00000000..abef3b00 --- /dev/null +++ b/tests/twisted/account-manager/presence.py @@ -0,0 +1,136 @@ +# Copyright (C) 2009 Nokia Corporation +# Copyright (C) 2009 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +import dbus +import dbus +import dbus.service + +from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ + call_async, sync_dbus +from mctest import exec_test, create_fakecm_account, enable_fakecm_account +import constants as cs + +def test(q, bus, mc): + params = dbus.Dictionary({"account": "jc.denton@example.com", + "password": "ionstorm"}, signature='sv') + (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) + + account_iface = dbus.Interface(account, cs.ACCOUNT) + account_props = dbus.Interface(account, cs.PROPERTIES_IFACE) + + # Go online with a particular presence + presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_TYPE_BUSY), 'busy', + 'Fighting conspiracies'), signature='uss') + + log = [] + + # FIXME: using predicate for its side-effects here is weird + + conn, _, _, _, _, _, _ = enable_fakecm_account(q, bus, mc, account, params, + has_presence=True, + requested_presence=presence, + expect_before_connect=[ + EventPattern('dbus-method-call', + interface=cs.CONN, method='GetInterfaces', + args=[], + handled=True, + predicate=(lambda e: log.append('GetInterfaces') or True)), + EventPattern('dbus-method-call', + interface=cs.PROPERTIES_IFACE, method='Get', + args=[cs.CONN_IFACE_SIMPLE_PRESENCE, 'Statuses'], + handled=True, + predicate=(lambda e: log.append('Get(Statuses)[1]') or True)), + EventPattern('dbus-method-call', + interface=cs.CONN_IFACE_SIMPLE_PRESENCE, + method='SetPresence', + args=list(presence[1:]), + handled=True, + predicate=(lambda e: log.append('SetPresence[1]') or True)), + ], + expect_after_connect=[ + EventPattern('dbus-method-call', + interface=cs.PROPERTIES_IFACE, method='Get', + args=[cs.CONN_IFACE_SIMPLE_PRESENCE, 'Statuses'], + handled=True, + predicate=(lambda e: log.append('Get(Statuses)[2]') or True)), + EventPattern('dbus-method-call', + interface=cs.CONN_IFACE_SIMPLE_PRESENCE, + method='SetPresence', + args=list(presence[1:]), + handled=True, + predicate=(lambda e: log.append('SetPresence[2]') or True)), + EventPattern('dbus-signal', path=account.object_path, + interface=cs.ACCOUNT, signal='AccountPropertyChanged', + predicate=lambda e: + e.args[0].get('CurrentPresence') == presence), + ]) + + # The events before Connect must happen in this order. GetInterfaces() may + # be called once or 2 times + if len(log) == 5: + assert log == ['GetInterfaces', 'Get(Statuses)[1]', 'SetPresence[1]', + 'Get(Statuses)[2]', 'SetPresence[2]'], log + else: + assert log == ['GetInterfaces', 'GetInterfaces', 'Get(Statuses)[1]', 'SetPresence[1]', + 'Get(Statuses)[2]', 'SetPresence[2]'], log + + # Change requested presence after going online + presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_TYPE_AWAY), 'away', + 'In Hong Kong'), signature='uss') + call_async(q, account_props, 'Set', cs.ACCOUNT, 'RequestedPresence', + presence) + + e, _, _ = q.expect_many( + EventPattern('dbus-method-call', + interface=cs.CONN_IFACE_SIMPLE_PRESENCE, method='SetPresence', + args=list(presence[1:]), + handled=True), + EventPattern('dbus-signal', path=account.object_path, + interface=cs.ACCOUNT, signal='AccountPropertyChanged', + predicate=lambda e: e.args[0].get('ChangingPresence') == True and + e.args[0].get('RequestedPresence') == presence), + EventPattern('dbus-signal', path=account.object_path, + interface=cs.ACCOUNT, signal='AccountPropertyChanged', + predicate=lambda e: e.args[0].get('CurrentPresence') == presence and + e.args[0].get('ChangingPresence') == False)) + + # Setting RequestedPresence=RequestedPresence causes a (possibly redundant) + # call to the CM, so we get any side-effects there might be, either in the + # CM or in MC (e.g. asking connectivity services to go online). However, + # AccountPropertyChanged is not emitted for RequestedPresence. + + sync_dbus(bus, q, mc) + events = [EventPattern('dbus-signal', signal='AccountPropertyChanged', + predicate=lambda e: e.args[0].get('RequestedPresence') is not None)] + q.forbid_events(events) + + presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_TYPE_AWAY), 'away', + 'In Hong Kong'), signature='uss') + call_async(q, account_props, 'Set', cs.ACCOUNT, 'RequestedPresence', + presence) + + e = q.expect('dbus-method-call', + interface=cs.CONN_IFACE_SIMPLE_PRESENCE, method='SetPresence', + args=list(presence[1:]), + handled=True) + + sync_dbus(bus, q, mc) + q.unforbid_events(events) + +if __name__ == '__main__': + exec_test(test, {}) diff --git a/tests/twisted/account-manager/reconnect.py b/tests/twisted/account-manager/reconnect.py new file mode 100644 index 00000000..0c6ea3ad --- /dev/null +++ b/tests/twisted/account-manager/reconnect.py @@ -0,0 +1,202 @@ +# Copyright (C) 2009 Nokia Corporation +# Copyright (C) 2009 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +import dbus +import dbus +import dbus.service + +from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ + call_async, sync_dbus +from mctest import exec_test, SimulatedConnection, create_fakecm_account,\ + SimulatedChannel +import constants as cs + +def test(q, bus, mc): + cm_name_ref = dbus.service.BusName( + tp_name_prefix + '.ConnectionManager.fakecm', bus=bus) + + # Create an account + params = dbus.Dictionary({"account": "someguy@example.com", + "password": "secrecy"}, signature='sv') + (cm_name_ref, 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_TYPE_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_TYPE_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_TYPE_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 calls GetStatus (maybe) and then Connect + + q.expect('dbus-method-call', method='Connect', + path=conn.object_path, handled=True) + + # Connect succeeds + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + + # 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) + conn = SimulatedConnection(q, bus, 'fakecm', 'fakeprotocol', '_', + '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.CONN_STATUS_REASON_NONE) + + # Put the account offline + requested_presence = (dbus.UInt32(cs.PRESENCE_TYPE_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) + + # MC terminates the channel + # FIXME: it shouldn't do this! + #q.expect('dbus-method-call', method='Close', + # path=chan.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 + +if __name__ == '__main__': + exec_test(test, {}) diff --git a/tests/twisted/account-manager/recover-from-disconnect.py b/tests/twisted/account-manager/recover-from-disconnect.py new file mode 100644 index 00000000..aba60598 --- /dev/null +++ b/tests/twisted/account-manager/recover-from-disconnect.py @@ -0,0 +1,197 @@ +# Copyright (C) 2009 Nokia Corporation +# Copyright (C) 2009-2010 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +import dbus +import dbus +import dbus.service + +from servicetest import (EventPattern, tp_name_prefix, tp_path_prefix, + call_async, sync_dbus, assertEquals) +from mctest import exec_test, SimulatedConnection, create_fakecm_account,\ + SimulatedChannel +import constants as cs + +def test(q, bus, mc): + cm_name_ref = dbus.service.BusName( + tp_name_prefix + '.ConnectionManager.fakecm', bus=bus) + + # Create an account. We're setting register=True here to verify + # that after one successful connection, it'll be removed (fd.o #28118). + params = dbus.Dictionary({"account": "someguy@example.com", + "password": "secrecy", + "register": True}, signature='sv') + (cm_name_ref, 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_TYPE_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 calls GetStatus (maybe) and then Connect + + q.expect('dbus-method-call', method='Connect', + path=conn.object_path, handled=True) + + # Connect succeeds + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + + q.expect('dbus-method-call', + interface=cs.CONN_IFACE_SIMPLE_PRESENCE, + method='SetPresence', + args=list(presence[1:]), + handled=True) + + # Connection falls over for a miscellaneous reason + conn.ConnectionError('com.example.My.Network.Is.Full.Of.Eels', + {'eels': 23, 'capacity': 23, 'debug-message': 'Too many eels'}) + conn.StatusChanged(cs.CONN_STATUS_DISCONNECTED, + cs.CONN_STATUS_REASON_NETWORK_ERROR) + + # MC reconnects. This time, we expect it to have deleted the 'register' + # parameter. + del params['register'] + + disconnected, connecting, e = q.expect_many( + EventPattern('dbus-signal', signal='AccountPropertyChanged', + predicate=(lambda e: + e.args[0].get('ConnectionStatus') == + cs.CONN_STATUS_DISCONNECTED), + ), + EventPattern('dbus-signal', signal='AccountPropertyChanged', + predicate=(lambda e: + e.args[0].get('ConnectionStatus') == + cs.CONN_STATUS_CONNECTING), + ), + EventPattern('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), + ) + + assertEquals('/', disconnected.args[0].get('Connection')) + assertEquals('com.example.My.Network.Is.Full.Of.Eels', + disconnected.args[0].get('ConnectionError')) + assertEquals( + {'eels': 23, 'capacity': 23, 'debug-message': 'Too many eels'}, + disconnected.args[0].get('ConnectionErrorDetails')) + assertEquals(cs.CONN_STATUS_DISCONNECTED, + disconnected.args[0].get('ConnectionStatus')) + assertEquals(cs.CONN_STATUS_REASON_NETWORK_ERROR, + disconnected.args[0].get('ConnectionStatusReason')) + + assertEquals('/', connecting.args[0].get('Connection')) + assertEquals('com.example.My.Network.Is.Full.Of.Eels', + connecting.args[0].get('ConnectionError')) + assertEquals( + {'eels': 23, 'capacity': 23, 'debug-message': 'Too many eels'}, + connecting.args[0].get('ConnectionErrorDetails')) + assertEquals(cs.CONN_STATUS_CONNECTING, + connecting.args[0].get('ConnectionStatus')) + assertEquals(cs.CONN_STATUS_REASON_REQUESTED, + connecting.args[0].get('ConnectionStatusReason')) + + conn = SimulatedConnection(q, bus, 'fakecm', 'fakeprotocol', '_', + 'myself', has_presence=True) + + q.dbus_return(e.message, conn.bus_name, conn.object_path, signature='so') + + # MC calls GetStatus (maybe) and then Connect + + connecting, _ = q.expect_many( + EventPattern('dbus-signal', signal='AccountPropertyChanged', + predicate=(lambda e: + e.args[0].get('ConnectionStatus') == + cs.CONN_STATUS_CONNECTING), + ), + EventPattern('dbus-method-call', method='Connect', + path=conn.object_path, handled=True), + ) + + assertEquals(conn.object_path, connecting.args[0].get('Connection')) + assertEquals('com.example.My.Network.Is.Full.Of.Eels', + connecting.args[0].get('ConnectionError')) + assertEquals( + {'eels': 23, 'capacity': 23, 'debug-message': 'Too many eels'}, + connecting.args[0].get('ConnectionErrorDetails')) + assertEquals(cs.CONN_STATUS_CONNECTING, + connecting.args[0].get('ConnectionStatus')) + assertEquals(cs.CONN_STATUS_REASON_REQUESTED, + connecting.args[0].get('ConnectionStatusReason')) + + assertEquals('com.example.My.Network.Is.Full.Of.Eels', + account_props.Get(cs.ACCOUNT, 'ConnectionError')) + assertEquals( + {'eels': 23, 'capacity': 23, 'debug-message': 'Too many eels'}, + account_props.Get(cs.ACCOUNT, 'ConnectionErrorDetails')) + + # Connect succeeds + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + + connected, _ = q.expect_many( + EventPattern('dbus-signal', signal='AccountPropertyChanged', + predicate=(lambda e: + e.args[0].get('ConnectionStatus') == + cs.CONN_STATUS_CONNECTED), + ), + EventPattern('dbus-method-call', + interface=cs.CONN_IFACE_SIMPLE_PRESENCE, + method='SetPresence', + args=list(presence[1:]), + handled=True), + ) + + assertEquals(conn.object_path, connected.args[0].get('Connection')) + assertEquals('', connected.args[0].get('ConnectionError')) + assertEquals({}, connected.args[0].get('ConnectionErrorDetails')) + assertEquals(cs.CONN_STATUS_CONNECTED, + connected.args[0].get('ConnectionStatus')) + assertEquals(cs.CONN_STATUS_REASON_REQUESTED, + connected.args[0].get('ConnectionStatusReason')) + + assertEquals('', account_props.Get(cs.ACCOUNT, 'ConnectionError')) + assertEquals({}, account_props.Get(cs.ACCOUNT, 'ConnectionErrorDetails')) + +if __name__ == '__main__': + exec_test(test, {}) diff --git a/tests/twisted/account-manager/request-online.py b/tests/twisted/account-manager/request-online.py new file mode 100644 index 00000000..83f34f43 --- /dev/null +++ b/tests/twisted/account-manager/request-online.py @@ -0,0 +1,170 @@ +# Python is really rubbish. vim: set fileencoding=utf-8 : +# Copyright © 2009–2010 Nokia Corporation +# Copyright © 2009–2010 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +import dbus +import dbus.service + +from servicetest import ( + EventPattern, tp_name_prefix, tp_path_prefix, assertEquals, +) +from mctest import ( + exec_test, SimulatedConnection, create_fakecm_account, + SimulatedChannel, SimulatedClient, expect_client_setup, +) +import constants as cs + +def test(q, bus, mc): + cm_name_ref = dbus.service.BusName( + tp_name_prefix + '.ConnectionManager.fakecm', bus=bus) + + http_fixed_properties = dbus.Dictionary({ + cs.CHANNEL + '.TargetHandleType': 1L, + cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_STREAM_TUBE, + cs.CHANNEL_TYPE_STREAM_TUBE + '.Service': + 'http' + }, signature='sv') + caps = dbus.Array([http_fixed_properties], signature='a{sv}') + + # Be a Client + client = SimulatedClient(q, bus, 'downloader', + observe=[], approve=[], handle=[http_fixed_properties], + bypass_approval=False) + expect_client_setup(q, [client]) + + # Create an account + params = dbus.Dictionary({"account": "someguy@example.com", + "password": "secrecy"}, signature='sv') + (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) + + # The account is initially valid but disabled, and hence offline + props = account.GetAll(cs.ACCOUNT, dbus_interface=cs.PROPERTIES_IFACE) + assert not props['Enabled'] + assert props['Valid'] + # The spec says it should be (Offline, "", "") but I don't think the + # strings really matter. If anything, the second one should start out at + # "offline". + assertEquals(cs.PRESENCE_TYPE_OFFLINE, props['CurrentPresence'][0]) + + # Enable the account + account.Set(cs.ACCOUNT, 'Enabled', True, + dbus_interface=cs.PROPERTIES_IFACE) + q.expect('dbus-signal', + path=account.object_path, + signal='AccountPropertyChanged', + interface=cs.ACCOUNT) + + props = account.GetAll(cs.ACCOUNT, dbus_interface=cs.PROPERTIES_IFACE) + assert props['Enabled'] + assert props['Valid'] + # Ditto above re. string fields. + assertEquals(cs.PRESENCE_TYPE_OFFLINE, props['CurrentPresence'][0]) + + # Go online + requested_presence = dbus.Struct((dbus.UInt32(2L), 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 calls GetStatus (maybe) and then Connect + + q.expect('dbus-method-call', method='Connect', + path=conn.object_path, handled=True) + + # Connect succeeds + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + + # 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('HasBeenOnline') + assertEquals(requested_presence, properties.get('RequestedPresence')) + + # Since this Connection doesn't support SimplePresence, but it's online, + # the spec says that CurrentPresence should be Unset. + assertEquals((cs.PRESENCE_TYPE_UNSET, "", ""), + properties.get('CurrentPresence')) + + new_channel = http_fixed_properties + buddy_handle = conn.ensure_handle(cs.HT_CONTACT, "buddy") + new_channel[cs.CHANNEL + '.TargetID'] = "buddy" + new_channel[cs.CHANNEL + '.TargetHandle'] = buddy_handle + new_channel[cs.CHANNEL + '.Requested'] = False + new_channel[cs.CHANNEL + '.Interfaces'] = dbus.Array(signature='s') + + chan = SimulatedChannel(conn, new_channel) + chan.announce() + + e = q.expect('dbus-method-call', method='HandleChannels') + q.dbus_return(e.message, signature='') + + # Put the account offline + requested_presence = (dbus.UInt32(cs.PRESENCE_TYPE_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) + + # MC terminates the channel + # FIXME: it shouldn't do this! + #q.expect('dbus-method-call', method='Close', + # path=chan.object_path, handled=True) + + properties = account.GetAll(cs.ACCOUNT, dbus_interface=cs.PROPERTIES_IFACE) + assertEquals('/', properties['Connection']) + assertEquals(cs.CONN_STATUS_DISCONNECTED, properties['ConnectionStatus']) + assertEquals(requested_presence, properties['CurrentPresence']) + assertEquals(requested_presence, properties['RequestedPresence']) + +if __name__ == '__main__': + exec_test(test, {}) diff --git a/tests/twisted/account-manager/server-drops-us.py b/tests/twisted/account-manager/server-drops-us.py new file mode 100644 index 00000000..b4db423f --- /dev/null +++ b/tests/twisted/account-manager/server-drops-us.py @@ -0,0 +1,128 @@ +# Copyright (C) 2009 Nokia Corporation +# Copyright (C) 2009 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +import dbus +import dbus +import dbus.service + +from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ + call_async, sync_dbus, TimeoutError +from mctest import exec_test, SimulatedConnection, create_fakecm_account,\ + SimulatedChannel +import constants as cs + +params = dbus.Dictionary({"account": "someguy@example.com", + "password": "secrecy"}, signature='sv') + +def test(q, bus, mc): + cm_name_ref = dbus.service.BusName( + tp_name_prefix + '.ConnectionManager.fakecm', bus=bus) + + # Create an account + (cm_name_ref, 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_TYPE_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 calls GetStatus (maybe) and then Connect + + q.expect('dbus-method-call', method='Connect', + path=conn.object_path, handled=True) + + # Connect succeeds + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + + 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.CONN_STATUS_REASON_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 drop_and_expect_reconnect(q, bus, conn): + # Connection falls over for a miscellaneous reason + conn.StatusChanged(cs.CONN_STATUS_DISCONNECTED, + cs.CONN_STATUS_REASON_NETWORK_ERROR) + + # MC reconnects + + 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 calls GetStatus (maybe) and then Connect + + q.expect('dbus-method-call', method='Connect', + path=conn.object_path, handled=True) + + # Connect succeeds + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + + return conn + +if __name__ == '__main__': + exec_test(test, {}) diff --git a/tests/twisted/account-manager/service.py b/tests/twisted/account-manager/service.py new file mode 100644 index 00000000..f7df9903 --- /dev/null +++ b/tests/twisted/account-manager/service.py @@ -0,0 +1,112 @@ +# Copyright (C) 2010 Nokia Corporation +# Copyright (C) 2010 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +import dbus +import dbus +import dbus.service + +from servicetest import EventPattern, tp_name_prefix, call_async, assertEquals +from mctest import exec_test, create_fakecm_account +import constants as cs + +def test(q, bus, mc): + params = dbus.Dictionary({"account": "wjt@example.com", + "password": "secrecy"}, signature='sv') + (cm_name_ref, 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') + +if __name__ == '__main__': + exec_test(test, {}) diff --git a/tests/twisted/account-manager/update-parameters.py b/tests/twisted/account-manager/update-parameters.py new file mode 100644 index 00000000..347a3135 --- /dev/null +++ b/tests/twisted/account-manager/update-parameters.py @@ -0,0 +1,194 @@ +# Copyright (C) 2009 Nokia Corporation +# Copyright (C) 2009 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA + +import os +import time + +import dbus +import dbus.service + +from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ + call_async, assertEquals +from mctest import exec_test, SimulatedConnection, create_fakecm_account,\ + SimulatedChannel +import constants as cs + +def test(q, bus, mc): + cm_name_ref = dbus.service.BusName( + tp_name_prefix + '.ConnectionManager.fakecm', bus=bus) + + # Create an account + params = dbus.Dictionary({"account": "someguy@example.com", + "password": "secrecy", 'nickname': 'albinoblacksheep'}, signature='sv') + (cm_name_ref, account) = create_fakecm_account(q, bus, mc, params) + + # Enable the account + account.Set(cs.ACCOUNT, 'Enabled', True, + dbus_interface=cs.PROPERTIES_IFACE) + q.expect('dbus-signal', + path=account.object_path, + signal='AccountPropertyChanged', + interface=cs.ACCOUNT) + + # Go online + requested_presence = dbus.Struct((dbus.UInt32(2L), 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 calls GetStatus (maybe) and then Connect + + q.expect('dbus-method-call', method='Connect', + path=conn.object_path, handled=True) + + # Connect succeeds + conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) + + # 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') + + # Set some parameters. They include setting account to \\, as a regression + # test for part of fd.o #28557. + call_async(q, account, 'UpdateParameters', + { + 'account': r'\\', + 'secret-mushroom': '/Amanita muscaria/', + 'snakes': dbus.UInt32(42), + 'com.example.Badgerable.Badgered': True, + }, + [], + dbus_interface=cs.ACCOUNT) + + set_call, ret, _ = q.expect_many( + EventPattern('dbus-method-call', + path=conn.object_path, + interface=cs.PROPERTIES_IFACE, method='Set', + args=['com.example.Badgerable', 'Badgered', True], + handled=False), + EventPattern('dbus-return', + method='UpdateParameters'), + EventPattern('dbus-signal', + path=account.object_path, + interface=cs.ACCOUNT, signal='AccountPropertyChanged', + args=[{'Parameters': { + 'account': r'\\', + 'com.example.Badgerable.Badgered': True, + 'password': 'secrecy', + 'nickname': 'albinoblacksheep', + 'secret-mushroom': '/Amanita muscaria/', + 'snakes': 42, + }}]), + ) + + # the D-Bus property should be set instantly; the others will take effect + # on reconnection + not_yet = ret.value[0] + not_yet.sort() + assert not_yet == ['account', 'secret-mushroom', 'snakes'], not_yet + + # Unset some parameters + call_async(q, account, 'UpdateParameters', + {}, + ['nickname', 'com.example.Badgerable.Badgered'], + dbus_interface=cs.ACCOUNT) + + ret, _ = q.expect_many( + EventPattern('dbus-return', + method='UpdateParameters'), + EventPattern('dbus-signal', + path=account.object_path, + interface=cs.ACCOUNT, signal='AccountPropertyChanged', + args=[{'Parameters': { + 'account': r'\\', + 'password': 'secrecy', + 'secret-mushroom': '/Amanita muscaria/', + 'snakes': 42, + }}]), + ) + + # there's no well-defined way to unset a D-Bus property, so it'll go back + # to its implied default value only after reconnection + # + # FIXME: in a perfect implementation, we know that this particular D-Bus + # property has a default, so maybe we should set it back to that? + not_yet = ret.value[0] + not_yet.sort() + assert not_yet == ['com.example.Badgerable.Badgered', 'nickname'], not_yet + + accounts_dir = os.environ['MC_ACCOUNT_DIR'] + + # fd.o #28557: when the file has been updated, the account parameter + # has its two backslashes doubled to 4 (because of the .desktop encoding), + # but they are not doubled again. + i = 0 + updated = False + while i < 500: + + for line in open(accounts_dir + + '/mcp-test-diverted-account-plugin.conf', 'r'): + if line.startswith('param-account=') and '\\' in line: + assertEquals(r'param-account=\\\\' + '\n', line) + updated = True + + if updated: + break + + # just to not busy-wait + time.sleep(0.1) + i += 1 + + assert updated + +if __name__ == '__main__': + exec_test(test, {}) |