diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/twisted/Makefile.am | 9 | ||||
-rw-r--r-- | tests/twisted/account-manager/auto-connect.py | 9 | ||||
-rw-r--r-- | tests/twisted/account-manager/avatar-refresh.py | 22 | ||||
-rw-r--r-- | tests/twisted/account-manager/bad-cm.py | 11 | ||||
-rw-r--r-- | tests/twisted/account-storage/5-12.py | 29 | ||||
-rw-r--r-- | tests/twisted/account-storage/5-14.py | 29 | ||||
-rw-r--r-- | tests/twisted/account-storage/create-new.py | 136 | ||||
-rw-r--r-- | tests/twisted/account-storage/default-keyring-storage.py | 317 | ||||
-rw-r--r-- | tests/twisted/account-storage/load-keyfiles.py | 274 | ||||
-rw-r--r-- | tests/twisted/account-storage/storage_helper.py | 174 | ||||
-rw-r--r-- | tests/twisted/dbus-account-plugin.c | 521 | ||||
-rw-r--r-- | tests/twisted/mc-debug-server.c | 30 | ||||
-rw-r--r-- | tests/twisted/mcp-account-diversion.c | 259 | ||||
-rw-r--r-- | tests/twisted/mctest.py | 38 |
14 files changed, 1106 insertions, 752 deletions
diff --git a/tests/twisted/Makefile.am b/tests/twisted/Makefile.am index 587d6b0c..2a96647f 100644 --- a/tests/twisted/Makefile.am +++ b/tests/twisted/Makefile.am @@ -74,8 +74,12 @@ TWISTED_BASIC_TESTS = \ # account-storage/*.py need their own instances. TWISTED_SPECIAL_BUILD_TESTS = \ account-manager/connectivity.py \ - account-storage/default-keyring-storage.py \ - account-storage/diverted-storage.py + account-storage/diverted-storage.py \ + account-storage/5-12.py \ + account-storage/5-14.py \ + account-storage/create-new.py \ + account-storage/load-keyfiles.py \ + $(NULL) # Tests that are usually too slow to run. TWISTED_SLOW_TESTS = \ @@ -105,6 +109,7 @@ TWISTED_OTHER_FILES = \ fakeconnectivity.py \ mctest.py \ servicetest.py \ + account-storage/storage_helper.py \ telepathy/clients/README \ telepathy/clients/AbiWord.client \ telepathy/clients/Logger.client \ diff --git a/tests/twisted/account-manager/auto-connect.py b/tests/twisted/account-manager/auto-connect.py index 46b13364..28cfb2ab 100644 --- a/tests/twisted/account-manager/auto-connect.py +++ b/tests/twisted/account-manager/auto-connect.py @@ -21,6 +21,7 @@ import dbus """ import os +import os.path import dbus import dbus.service @@ -65,8 +66,12 @@ def preseed(q, bus, fake_accounts_service): 'password': r'\\\\ionstorm\\\\', }) - os.makedirs(accounts_dir + '/' + account_id) - avatar_bin = open(accounts_dir + '/' + account_id + '/avatar.bin', 'w') + avatar_filename = account_id + avatar_filename = avatar_filename.replace('/', '-') + '.avatar' + avatar_filename = (os.environ['XDG_DATA_HOME'] + + '/telepathy/mission-control/' + avatar_filename) + os.makedirs(os.path.dirname(avatar_filename)) + avatar_bin = open(avatar_filename, 'w') avatar_bin.write('Deus Ex') avatar_bin.close() diff --git a/tests/twisted/account-manager/avatar-refresh.py b/tests/twisted/account-manager/avatar-refresh.py index f76d7fb6..d914c439 100644 --- a/tests/twisted/account-manager/avatar-refresh.py +++ b/tests/twisted/account-manager/avatar-refresh.py @@ -90,15 +90,6 @@ class Account(object): avatar_bin = open(avatar_filename, 'w') avatar_bin.write(local_avatar) avatar_bin.close() - elif not avatars_persist: - self.avatar_location = 'old' - # exercise migration from ~/.mission-control in a - # situation where MC should "win" - os.makedirs(accounts_dir + '/' + self.id) - avatar_bin = open( - accounts_dir + '/' + self.id + '/avatar.bin', 'w') - avatar_bin.write(local_avatar) - avatar_bin.close() else: # store it in the normal location self.avatar_location = 'home' @@ -258,18 +249,7 @@ class Account(object): self.test_migration(bus, q, conn, account_proxy) def test_migration(self, bus, q, conn, account_proxy): - if self.avatar_location == 'old': - # The avatar got migrated to the new location. - assert not os.path.exists(os.environ['MC_ACCOUNT_DIR'] + '/' + - self.id + '/avatar.bin') - assert not os.path.exists(os.environ['MC_ACCOUNT_DIR'] + '/fakecm') - avatar_filename = self.id - avatar_filename = avatar_filename.replace('/', '-') + '.avatar' - avatar_filename = (os.environ['XDG_DATA_HOME'] + - '/telepathy/mission-control/' + avatar_filename) - assertEquals(conn.avatar[0], ''.join(open(avatar_filename, - 'r').readlines())) - elif self.avatar_location == 'datadir' and self.winner == 'service': + if self.avatar_location == 'datadir' and self.winner == 'service': # The avatar wasn't deleted from $XDG_DATA_DIRS, but it was # overridden. assert not os.path.exists(os.environ['MC_ACCOUNT_DIR'] + '/' + diff --git a/tests/twisted/account-manager/bad-cm.py b/tests/twisted/account-manager/bad-cm.py index 74277b46..008ac605 100644 --- a/tests/twisted/account-manager/bad-cm.py +++ b/tests/twisted/account-manager/bad-cm.py @@ -22,11 +22,12 @@ import dbus from servicetest import call_async, assertEquals, assertContains -from mctest import exec_test, AccountManager +from mctest import (exec_test, SimulatedConnectionManager, AccountManager) import constants as cs def test(q, bus, mc): am = AccountManager(bus) + simulated_cm = SimulatedConnectionManager(q, bus) def call_create(cm='fakecm', protocol='fakeprotocol', parameters=None): if parameters is None: @@ -74,5 +75,13 @@ def test(q, bus, mc): assertEquals(cs.INVALID_ARGUMENT, e.name) assertContains("password", e.message) + # Create an account that will fail IdentifyAccount + call_create(parameters={ "account": "", + "password": "ohai", + }) + e = q.expect('dbus-error', method='CreateAccount') + assertEquals(cs.INVALID_HANDLE, e.name) + assertContains("Invalid account name", e.message) + if __name__ == '__main__': exec_test(test, {}) diff --git a/tests/twisted/account-storage/5-12.py b/tests/twisted/account-storage/5-12.py new file mode 100644 index 00000000..9075d767 --- /dev/null +++ b/tests/twisted/account-storage/5-12.py @@ -0,0 +1,29 @@ +# Test for a former default account storage backend: +# ~/.mission-control/accounts.cfg, as used in MC 5.0 to 5.13.1 +# +# Copyright (C) 2009-2010 Nokia Corporation +# Copyright (C) 2009-2014 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 + +from storage_helper import test_keyfile +from mctest import exec_test + +def test_5_12(q, bus, mc): + test_keyfile(q, bus, mc, '5.12') + +if __name__ == '__main__': + exec_test(test_5_12, {}, preload_mc=False, use_fake_accounts_service=False) diff --git a/tests/twisted/account-storage/5-14.py b/tests/twisted/account-storage/5-14.py new file mode 100644 index 00000000..42deb3ec --- /dev/null +++ b/tests/twisted/account-storage/5-14.py @@ -0,0 +1,29 @@ +# Test for a former default account storage backend: +# XDG_DATA_HOME/telepathy/mission-control/accounts.cfg, as used in MC 5.14 +# +# Copyright (C) 2009-2010 Nokia Corporation +# Copyright (C) 2009-2014 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 + +from storage_helper import test_keyfile +from mctest import exec_test + +def test_5_14(q, bus, mc): + test_keyfile(q, bus, mc, '5.14') + +if __name__ == '__main__': + exec_test(test_5_14, {}, preload_mc=False, use_fake_accounts_service=False) diff --git a/tests/twisted/account-storage/create-new.py b/tests/twisted/account-storage/create-new.py new file mode 100644 index 00000000..f77624a9 --- /dev/null +++ b/tests/twisted/account-storage/create-new.py @@ -0,0 +1,136 @@ +# Test for "stringified GVariant per account" storage backend introduced in +# Mission Control 5.16, when creating a new account stored in this default +# backend +# +# Copyright (C) 2009-2010 Nokia Corporation +# Copyright (C) 2009-2014 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 time +import os +import os.path +import signal + +import dbus +import dbus.service + +from servicetest import ( + EventPattern, assertEquals, + ) +from mctest import ( + exec_test, create_fakecm_account, connect_to_mc, + ) +from storage_helper import (account_store) +import constants as cs + +def test(q, bus, mc): + ctl_dir = os.environ['MC_ACCOUNT_DIR'] + old_key_file_name = os.path.join(ctl_dir, 'accounts.cfg') + newer_key_file_name = os.path.join(os.environ['XDG_DATA_HOME'], + 'telepathy', 'mission-control', 'accounts.cfg') + new_variant_file_name = os.path.join(os.environ['XDG_DATA_HOME'], + 'telepathy', 'mission-control', + 'fakecm-fakeprotocol-dontdivert_40example_2ecom0.account') + + account_manager, properties, interfaces = connect_to_mc(q, bus, mc) + + assert properties.get('ValidAccounts') == [], \ + properties.get('ValidAccounts') + assert properties.get('InvalidAccounts') == [], \ + properties.get('InvalidAccounts') + + params = dbus.Dictionary({"account": "dontdivert@example.com", + "password": "secrecy", + "snakes": dbus.UInt32(23)}, signature='sv') + (simulated_cm, 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) + + # Alter some miscellaneous r/w properties + + account_props.Set(cs.ACCOUNT, 'DisplayName', 'Work account') + account_props.Set(cs.ACCOUNT, 'Icon', 'im-jabber') + account_props.Set(cs.ACCOUNT, 'Nickname', 'Joe Bloggs') + account_props.Set(cs.ACCOUNT, 'ConnectAutomatically', True) + account_props.Set(cs.ACCOUNT, 'AutomaticPresence', + (dbus.UInt32(cs.PRESENCE_EXTENDED_AWAY), 'xa', + 'never online')) + + # .. let's check the keyfile + assert not os.path.exists(old_key_file_name) + assert not os.path.exists(newer_key_file_name) + assert os.path.exists(new_variant_file_name) + assert 'Joe Bloggs' in open(new_variant_file_name).read() + assertEquals("'fakecm'", account_store('get', 'variant-file', 'manager')) + assertEquals("'fakeprotocol'", account_store('get', 'variant-file', + 'protocol')) + assertEquals("'Work account'", account_store('get', 'variant-file', + 'DisplayName')) + assertEquals("'im-jabber'", account_store('get', 'variant-file', + 'Icon')) + assertEquals("'Joe Bloggs'", account_store('get', 'variant-file', + 'Nickname')) + assertEquals('true', account_store('get', 'variant-file', + 'ConnectAutomatically')) + assertEquals("(uint32 4, 'xa', 'never online')", + account_store('get', 'variant-file', 'AutomaticPresence')) + assertEquals("'dontdivert@example.com'", + account_store('get', 'variant-file', 'param-account')) + assertEquals("uint32 23", + account_store('get', 'variant-file', 'param-snakes')) + assertEquals("'secrecy'", + account_store('get', 'variant-file', 'param-password')) + + assertEquals({'password': 'secrecy', 'account': 'dontdivert@example.com', + 'snakes': 23}, account.Properties.Get(cs.ACCOUNT, 'Parameters')) + + # 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 + assert not os.path.exists(old_key_file_name) + assert not os.path.exists(newer_key_file_name) + assert not os.path.exists(new_variant_file_name) + +if __name__ == '__main__': + exec_test(test, {}, timeout=10, use_fake_accounts_service=False) diff --git a/tests/twisted/account-storage/default-keyring-storage.py b/tests/twisted/account-storage/default-keyring-storage.py deleted file mode 100644 index 27b45e51..00000000 --- a/tests/twisted/account-storage/default-keyring-storage.py +++ /dev/null @@ -1,317 +0,0 @@ -# Test for default account storage backend. -# -# Copyright (C) 2009-2010 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 time -import os -import os.path -import signal - -import dbus -import dbus.service - -from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ - call_async, assertEquals, assertContains, assertDoesNotContain -from mctest import ( - exec_test, create_fakecm_account, get_fakecm_account, connect_to_mc, - keyfile_read, tell_mc_to_die, resuscitate_mc - ) -import constants as cs - -# This doesn't escape its parameters before passing them to the shell, -# so be careful. -def account_store(op, backend, key=None, value=None, - account='fakecm/fakeprotocol/dontdivert_40example_2ecom0'): - cmd = [ '../account-store', op, backend, account ] - if key: - cmd.append(key) - if value: - cmd.append(value) - - lines = os.popen(' '.join(cmd)).read() - ret = [] - for line in lines.split('\n'): - if line.startswith('** '): - continue - - if line: - ret.append(line) - - if len(ret) > 0: - return ret[0] - else: - return None - -def test(q, bus, mc): - ctl_dir = os.environ['MC_ACCOUNT_DIR'] - old_key_file_name = os.path.join(ctl_dir, 'accounts.cfg') - newer_key_file_name = os.path.join(os.environ['XDG_DATA_HOME'], - 'telepathy', 'mission-control', 'accounts.cfg') - new_variant_file_name = os.path.join(os.environ['XDG_DATA_HOME'], - 'telepathy', 'mission-control', - 'fakecm-fakeprotocol-dontdivert_40example_2ecom0.account') - group = 'fakecm/fakeprotocol/dontdivert_40example_2ecom0' - - account_manager, properties, interfaces = connect_to_mc(q, bus, mc) - - assert properties.get('ValidAccounts') == [], \ - properties.get('ValidAccounts') - assert properties.get('InvalidAccounts') == [], \ - properties.get('InvalidAccounts') - - params = dbus.Dictionary({"account": "dontdivert@example.com", - "password": "secrecy", - "snakes": dbus.UInt32(23)}, signature='sv') - (simulated_cm, 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) - - # Alter some miscellaneous r/w properties - - account_props.Set(cs.ACCOUNT, 'DisplayName', 'Work account') - account_props.Set(cs.ACCOUNT, 'Icon', 'im-jabber') - account_props.Set(cs.ACCOUNT, 'Nickname', 'Joe Bloggs') - account_props.Set(cs.ACCOUNT, 'ConnectAutomatically', True) - account_props.Set(cs.ACCOUNT, 'AutomaticPresence', - (dbus.UInt32(cs.PRESENCE_EXTENDED_AWAY), 'xa', - 'never online')) - - tell_mc_to_die(q, bus) - - # .. let's check the keyfile - assert not os.path.exists(old_key_file_name) - assert not os.path.exists(newer_key_file_name) - assert 'Joe Bloggs' in open(new_variant_file_name).read() - assertEquals("'fakecm'", account_store('get', 'variant-file', 'manager')) - assertEquals("'fakeprotocol'", account_store('get', 'variant-file', - 'protocol')) - assertEquals("'Work account'", account_store('get', 'variant-file', - 'DisplayName')) - assertEquals("'im-jabber'", account_store('get', 'variant-file', - 'Icon')) - assertEquals("'Joe Bloggs'", account_store('get', 'variant-file', - 'Nickname')) - assertEquals('true', account_store('get', 'variant-file', - 'ConnectAutomatically')) - assertEquals("(uint32 4, 'xa', 'never online')", - account_store('get', 'variant-file', 'AutomaticPresence')) - assertEquals("'dontdivert@example.com'", - account_store('get', 'variant-file', 'param-account')) - assertEquals("uint32 23", - account_store('get', 'variant-file', 'param-snakes')) - assertEquals("'secrecy'", - account_store('get', 'variant-file', 'param-password')) - - # Reactivate MC - account_manager, properties, interfaces = resuscitate_mc(q, bus, mc) - account = get_fakecm_account(bus, mc, account_path) - account_iface = dbus.Interface(account, cs.ACCOUNT) - - assertEquals({'password': 'secrecy', 'account': 'dontdivert@example.com', - 'snakes': 23}, account.Properties.Get(cs.ACCOUNT, 'Parameters')) - - # 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 - assert not os.path.exists(old_key_file_name) - assert not os.path.exists(newer_key_file_name) - assert not os.path.exists(new_variant_file_name) - - # Tell MC to die, again - tell_mc_to_die(q, bus) - - low_prio_variant_file_name = os.path.join( - os.environ['XDG_DATA_DIRS'].split(':')[0], - 'telepathy', 'mission-control', - 'fakecm-fakeprotocol-dontdivert_40example_2ecom0.account') - os.makedirs(os.path.dirname(low_prio_variant_file_name), 0700) - - # This is deliberately a lower-priority location - open(low_prio_variant_file_name, 'w').write( -"""{ -'manager': <'fakecm'>, -'protocol': <'fakeprotocol'>, -'DisplayName': <'New and improved account'>, -'AutomaticPresence': <(uint32 2, 'available', '')>, -'KeyFileParameters': <{ - 'account': 'dontdivert@example.com', - 'password': 'password_in_variant_file', - 'snakes': '42' - }> -} -""") - - # This version of this account will be used - open(new_variant_file_name.replace('.account', 'priority.account'), - 'w').write("""{ -'manager': <'fakecm'>, -'protocol': <'fakeprotocol'>, -'DisplayName': <'Visible'>, -'AutomaticPresence': <(uint32 2, 'available', '')>, -'KeyFileParameters': <{'account': 'dontdivert@example.com', - 'password': 'password_in_variant_file'}> -} -""") - # This one won't, because it's "masked" by the higher-priority one - open(low_prio_variant_file_name.replace('.account', 'priority.account'), - 'w').write("""{ -'manager': <'fakecm'>, -'protocol': <'fakeprotocol'>, -'DisplayName': <'Hidden'>, -'Nickname': <'Hidden'>, -'AutomaticPresence': <(uint32 2, 'available', '')>, -'KeyFileParameters': <{'account': 'dontdivert@example.com', - 'password': 'password_in_variant_file'}> -} -""") - - # This empty file is considered to "mask" the lower-priority one - open(new_variant_file_name.replace('.account', 'masked.account'), - 'w').write('') - open(low_prio_variant_file_name.replace('.account', 'masked.account'), - 'w').write("""{ -'manager': <'fakecm'>, -'protocol': <'fakeprotocol'>, -'AutomaticPresence': <(uint32 2, 'available', '')>, -'KeyFileParameters': <{'account': 'dontdivert@example.com', - 'password': 'password_in_variant_file'}> -} -""") - - account_manager, properties, interfaces = resuscitate_mc(q, bus, mc) - assertContains(account_path, properties['ValidAccounts']) - account = get_fakecm_account(bus, mc, account_path) - account_iface = dbus.Interface(account, cs.ACCOUNT) - - assertEquals(42, - account.Properties.Get(cs.ACCOUNT, 'Parameters')['snakes']) - assertEquals(dbus.UInt32, - type(account.Properties.Get(cs.ACCOUNT, 'Parameters')['snakes'])) - - # Files in lower-priority XDG locations aren't copied until something - # actually changes, and they aren't deleted. - assert not os.path.exists(new_variant_file_name) - assert os.path.exists(low_prio_variant_file_name) - - # Delete the password (only), like Empathy 3.0-3.4 do when migrating - account_iface.UpdateParameters({}, ['password']) - q.expect('dbus-signal', - path=account_path, - signal='AccountPropertyChanged', - interface=cs.ACCOUNT, - predicate=(lambda e: - 'Parameters' in e.args[0]), - ) - - # test that "masking" works - assertDoesNotContain(account_path + "masked", properties['ValidAccounts']) - assertDoesNotContain(account_path + "masked", - properties['InvalidAccounts']) - - # test that priority works - assertContains(account_path + "priority", properties['ValidAccounts']) - priority_account = get_fakecm_account(bus, mc, account_path + "priority") - assertEquals('', priority_account.Properties.Get(cs.ACCOUNT, 'Nickname')) - assertEquals('Visible', - priority_account.Properties.Get(cs.ACCOUNT, 'DisplayName')) - - # test what happens when we delete an account that has a lower-priority - # "other self" - assert priority_account.Remove() is None - - # Tell MC to die yet again - tell_mc_to_die(q, bus) - - # Check the account has copied (not moved! XDG_DATA_DIRS are, - # conceptually, read-only) from the old to the new name - assert not os.path.exists(old_key_file_name) - assert not os.path.exists(newer_key_file_name) - assert os.path.exists(low_prio_variant_file_name) - assert os.path.exists(new_variant_file_name) - assert open(new_variant_file_name.replace('.account', 'masked.account'), - 'r').read() == '' - assert open(new_variant_file_name.replace('.account', 'priority.account'), - 'r').read() == '' - - pwd = account_store('get', 'variant-file', 'param-password') - assertEquals(None, pwd) - - # Write out an account configuration in the old keyfile, to test - # migration from there - os.remove(new_variant_file_name) - os.remove(new_variant_file_name.replace('.account', 'masked.account')) - os.remove(new_variant_file_name.replace('.account', 'priority.account')) - os.remove(low_prio_variant_file_name) - os.remove(low_prio_variant_file_name.replace('.account', 'masked.account')) - os.remove(low_prio_variant_file_name.replace('.account', 'priority.account')) - open(old_key_file_name, 'w').write( -r"""# Telepathy accounts -[%s] -manager=fakecm -protocol=fakeprotocol -param-account=dontdivert@example.com -DisplayName=Ye olde account -AutomaticPresence=2;available;; -""" % group) - - account_manager, properties, interfaces = resuscitate_mc(q, bus, mc) - account = get_fakecm_account(bus, mc, account_path) - account_iface = dbus.Interface(account, cs.ACCOUNT) - - # This time it *does* get deleted automatically during MC startup, - # after copying its contents to the new name/format - assert not os.path.exists(old_key_file_name) - assert not os.path.exists(low_prio_variant_file_name) - assertEquals("'Ye olde account'", - account_store('get', 'variant-file', 'DisplayName')) - -if __name__ == '__main__': - ctl_dir = os.environ['MC_ACCOUNT_DIR'] - try: - os.mkdir(ctl_dir, 0700) - except OSError: - pass - exec_test(test, {}, timeout=10, use_fake_accounts_service=False) diff --git a/tests/twisted/account-storage/load-keyfiles.py b/tests/twisted/account-storage/load-keyfiles.py new file mode 100644 index 00000000..dfba53b4 --- /dev/null +++ b/tests/twisted/account-storage/load-keyfiles.py @@ -0,0 +1,274 @@ +# Test for "stringified GVariant per account" storage backend introduced in +# Mission Control 5.16, when loading pre-prepared files +# +# Copyright (C) 2009-2010 Nokia Corporation +# Copyright (C) 2009-2014 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 errno +import os +import os.path + +import dbus + +from servicetest import ( + assertEquals, assertContains, assertDoesNotContain, + ) +from mctest import ( + MC, exec_test, get_fakecm_account, connect_to_mc, + SimulatedConnectionManager, + ) +from storage_helper import ( + account_store, + ) +import constants as cs + +def test(q, bus, mc): + simulated_cm = SimulatedConnectionManager(q, bus) + + ctl_dir = os.environ['MC_ACCOUNT_DIR'] + old_key_file_name = os.path.join(ctl_dir, 'accounts.cfg') + newer_key_file_name = os.path.join(os.environ['XDG_DATA_HOME'], + 'telepathy', 'mission-control', 'accounts.cfg') + + # We do several scenarios in one MC run, to speed up testing a bit. + scenarios = ('low', 'priority', 'masked', 'migration', 'absentcm') + + variant_file_names = {} + low_prio_variant_file_names = {} + account_paths = {} + tails = {} + + for s in scenarios: + variant_file_names[s] = os.path.join(os.environ['XDG_DATA_HOME'], + 'telepathy', 'mission-control', + 'fakecm-fakeprotocol-dontdivert%s_40example_2ecom0.account' + % s) + tails[s] = ('fakecm/fakeprotocol/dontdivert%s_40example_2ecom0' % s) + account_paths[s] = cs.ACCOUNT_PATH_PREFIX + tails[s] + low_prio_variant_file_names[s] = os.path.join( + os.environ['XDG_DATA_DIRS'].split(':')[0], + 'telepathy', 'mission-control', + 'fakecm-fakeprotocol-dontdivert%s_40example_2ecom0.account' % + s) + + try: + os.makedirs(os.path.dirname(variant_file_names[s]), 0700) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + try: + os.makedirs(os.path.dirname(low_prio_variant_file_names[s]), 0700) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + # This is deliberately a lower-priority location + open(low_prio_variant_file_names['low'], 'w').write( +"""{ +'manager': <'fakecm'>, +'protocol': <'fakeprotocol'>, +'DisplayName': <'Account in a low-priority location'>, +'AutomaticPresence': <(uint32 2, 'available', '')>, +'Parameters': <{ + 'account': <'dontdivertlow@example.com'>, + 'password': <'password_in_variant_file'>, + 'snakes': <uint32 42> + }> +} +""") + + # This is in a lower-priority location and we don't know the + # parameters' types yet + open(low_prio_variant_file_names['migration'], 'w').write( +"""{ +'manager': <'fakecm'>, +'protocol': <'fakeprotocol'>, +'DisplayName': <'Account in a low-priority location with KeyFileParameters'>, +'AutomaticPresence': <(uint32 2, 'available', '')>, +'KeyFileParameters': <{ + 'account': 'dontdivertmigration@example.com', + 'password': 'password_in_variant_file', + 'snakes': '42' + }> +} +""") + + # This is in a lower-priority location, and we don't know the + # parameters' types, and we can't learn them by asking the CM + # because it isn't installed + open(low_prio_variant_file_names['absentcm'], 'w').write( +"""{ +'manager': <'absentcm'>, +'protocol': <'absentprotocol'>, +'DisplayName': <'Account in a low-priority location with absent CM'>, +'AutomaticPresence': <(uint32 2, 'available', '')>, +'KeyFileParameters': <{ + 'account': 'dontdivertabsentcm@example.com', + 'password': 'hello', + 'snakes': '42' + }> +} +""") + + # This version of this account will be used + open(variant_file_names['priority'], 'w').write("""{ +'manager': <'fakecm'>, +'protocol': <'fakeprotocol'>, +'DisplayName': <'Visible'>, +'AutomaticPresence': <(uint32 2, 'available', '')>, +'KeyFileParameters': <{'account': 'dontdivertpriority@example.com', + 'password': 'password_in_variant_file', + 'snakes': '42' + }> +} +""") + # This one won't, because it's "masked" by the higher-priority one + open(low_prio_variant_file_names['priority'], 'w').write("""{ +'manager': <'fakecm'>, +'protocol': <'fakeprotocol'>, +'DisplayName': <'Hidden'>, +'Nickname': <'Hidden'>, +'AutomaticPresence': <(uint32 2, 'available', '')>, +'KeyFileParameters': <{'account': 'dontdivertpriority@example.com', + 'password': 'password_in_variant_file', + 'snakes': '42' + }> +} +""") + + # This empty file is considered to "mask" the lower-priority one + open(variant_file_names['masked'], 'w').write('') + open(low_prio_variant_file_names['masked'], 'w').write("""{ +'manager': <'fakecm'>, +'protocol': <'fakeprotocol'>, +'AutomaticPresence': <(uint32 2, 'available', '')>, +'KeyFileParameters': <{'account': 'dontdivert@example.com', + 'password': 'password_in_variant_file', + 'snakes': '42' + }> +} +""") + + mc = MC(q, bus) + account_manager, properties, interfaces = connect_to_mc(q, bus, mc) + + for s in scenarios: + if s == 'masked': + assertDoesNotContain(account_paths[s], properties['ValidAccounts']) + assertDoesNotContain(account_paths[s], properties['InvalidAccounts']) + elif s == 'absentcm': + assertContains(account_paths[s], properties['InvalidAccounts']) + assertDoesNotContain(account_paths[s], properties['ValidAccounts']) + else: + assertContains(account_paths[s], properties['ValidAccounts']) + assertDoesNotContain(account_paths[s], properties['InvalidAccounts']) + + accounts = {} + account_ifaces = {} + + for s in scenarios: + if s != 'masked': + accounts[s] = get_fakecm_account(bus, mc, account_paths[s]) + account_ifaces[s] = dbus.Interface(accounts[s], cs.ACCOUNT) + + if s not in ('masked', 'absentcm'): + # We can't get untyped parameters if we don't know what types + # the CM gives them. + assertEquals(42, accounts[s].Properties.Get(cs.ACCOUNT, + 'Parameters')['snakes']) + assertEquals(dbus.UInt32, + type(accounts[s].Properties.Get(cs.ACCOUNT, + 'Parameters')['snakes'])) + + # Files in lower-priority XDG locations aren't copied until something + # actually changes, and they aren't deleted. + + if s == 'low': + assert os.path.exists(low_prio_variant_file_names[s]) + + # Delete the password (only), like Empathy 3.0-3.4 do when migrating. + # This results in the higher-priority file being written out. + account_ifaces['low'].UpdateParameters({}, ['password']) + q.expect('dbus-signal', + path=account_paths['low'], + signal='AccountPropertyChanged', + interface=cs.ACCOUNT, + predicate=(lambda e: + 'Parameters' in e.args[0]), + ) + # Check the account has copied (not moved! XDG_DATA_DIRS are, + # conceptually, read-only) 'low' from the old to the new name + assert not os.path.exists(old_key_file_name) + assert not os.path.exists(newer_key_file_name) + assert os.path.exists(low_prio_variant_file_names['low']) + assert os.path.exists(variant_file_names['low']) + + # test that priority works + assertContains(account_paths["priority"], properties['ValidAccounts']) + assertEquals('', + accounts['priority'].Properties.Get(cs.ACCOUNT, 'Nickname')) + assertEquals('Visible', + accounts['priority'].Properties.Get(cs.ACCOUNT, 'DisplayName')) + + # test what happens when we delete an account that has a lower-priority + # "other self": it becomes masked + assert accounts['priority'].Remove() is None + assert not os.path.exists(old_key_file_name) + assert not os.path.exists(newer_key_file_name) + assert os.path.exists(low_prio_variant_file_names['priority']) + assert os.path.exists(variant_file_names['priority']) + assert open(variant_file_names['priority'], 'r').read() == '' + assertContains('password_in_variant_file', + open(low_prio_variant_file_names['priority'], 'r').read()) + + # The masked account is still masked + assert open(variant_file_names['masked'], 'r').read() == '' + + # Teach the one that knows its CM that the 'password' is a string. + # This results in the higher-priority file being written out. + account_ifaces['migration'].UpdateParameters({'password': 'hello'}, []) + q.expect('dbus-signal', + path=account_paths['migration'], + signal='AccountPropertyChanged', + interface=cs.ACCOUNT, + predicate=(lambda e: + 'Parameters' in e.args[0]), + ) + # Check the account has copied (not moved! XDG_DATA_DIRS are, + # conceptually, read-only) 'migration' from the old to the new name + assert not os.path.exists(old_key_file_name) + assert not os.path.exists(newer_key_file_name) + assert os.path.exists(low_prio_variant_file_names['migration']) + assert os.path.exists(variant_file_names['migration']) + assertEquals("'hello'", account_store('get', 'variant-file', + 'param-password', account=tails['migration'])) + # Parameters whose types are still unknown are copied too, but their + # types are still unknown + assertEquals("keyfile-escaped '42'", account_store('get', 'variant-file', + 'param-snakes', account=tails['migration'])) + + # 'absentcm' is still only in the low-priority location: we can't + # known the types of its parameters + assert not os.path.exists(old_key_file_name) + assert not os.path.exists(newer_key_file_name) + assert os.path.exists(low_prio_variant_file_names['absentcm']) + assert not os.path.exists(variant_file_names['absentcm']) + +if __name__ == '__main__': + exec_test(test, {}, preload_mc=False, use_fake_accounts_service=False) diff --git a/tests/twisted/account-storage/storage_helper.py b/tests/twisted/account-storage/storage_helper.py new file mode 100644 index 00000000..ebbe78e6 --- /dev/null +++ b/tests/twisted/account-storage/storage_helper.py @@ -0,0 +1,174 @@ +# Helper code for former default account storage backends +# +# Copyright (C) 2009-2010 Nokia Corporation +# Copyright (C) 2009-2014 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 errno +import os +import os.path + +import dbus + +from servicetest import ( + assertEquals, assertContains, assertLength, + ) +from mctest import ( + exec_test, get_fakecm_account, connect_to_mc, + MC, SimulatedConnectionManager, + ) +import constants as cs + +# This doesn't escape its parameters before passing them to the shell, +# so be careful. +def account_store(op, backend, key=None, value=None, + account='fakecm/fakeprotocol/dontdivert_40example_2ecom0'): + cmd = [ '../account-store', op, backend, account ] + if key: + cmd.append(key) + if value: + cmd.append(value) + + lines = os.popen(' '.join(cmd)).read() + ret = [] + for line in lines.split('\n'): + if line.startswith('** '): + continue + + if line: + ret.append(line) + + if len(ret) > 0: + return ret[0] + else: + return None + +def test_keyfile(q, bus, mc, how_old='5.12'): + simulated_cm = SimulatedConnectionManager(q, bus) + + if how_old == '5.12': + # This is not actually ~/.mission-control, but it uses the same + # code paths. + dot_mission_control = os.environ['MC_ACCOUNT_DIR'] + old_key_file_name = os.path.join(dot_mission_control, 'accounts.cfg') + + os.makedirs(dot_mission_control + + '/fakecm/fakeprotocol/dontdivert1_40example_2ecom0') + avatar_bin = open(dot_mission_control + + '/fakecm/fakeprotocol/dontdivert1_40example_2ecom0/avatar.bin', + 'w') + avatar_bin.write('hello, world') + avatar_bin.close() + elif how_old == '5.14': + # Same format, different location. + old_key_file_name = os.path.join(os.environ['XDG_DATA_HOME'], + 'telepathy', 'mission-control', 'accounts.cfg') + + # exercise override of an avatar in XDG_DATA_DIRS + avatar_dir = (os.environ['XDG_DATA_DIRS'].split(':')[0] + + '/telepathy/mission-control') + os.makedirs(avatar_dir) + avatar_bin = open(avatar_dir + + '/fakecm-fakeprotocol-dontdivert1_40example_2ecom0.avatar', + 'w') + avatar_bin.write('hello, world') + avatar_bin.close() + else: + raise AssertionError('Unsupported value for how_old') + + a1_new_variant_file_name = os.path.join(os.environ['XDG_DATA_HOME'], + 'telepathy', 'mission-control', + 'fakecm-fakeprotocol-dontdivert1_40example_2ecom0.account') + a1_tail = 'fakecm/fakeprotocol/dontdivert1_40example_2ecom0' + + a2_new_variant_file_name = os.path.join(os.environ['XDG_DATA_HOME'], + 'telepathy', 'mission-control', + 'fakecm-fakeprotocol-dontdivert2_40example_2ecom0.account') + a2_tail = 'fakecm/fakeprotocol/dontdivert2_40example_2ecom0' + + try: + os.makedirs(os.path.dirname(old_key_file_name), 0700) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + open(old_key_file_name, 'w').write( +r"""# Telepathy accounts +[%s] +manager=fakecm +protocol=fakeprotocol +param-account=dontdivert1@example.com +param-password=1 +DisplayName=First among equals +AutomaticPresence=2;available;; +AvatarMime=text/plain +avatar_token=hello, world + +[%s] +manager=fakecm +protocol=fakeprotocol +param-account=dontdivert2@example.com +param-password=2 +DisplayName=Second to none +AutomaticPresence=2;available;; +""" % (a1_tail, a2_tail)) + + mc = MC(q, bus) + account_manager, properties, interfaces = connect_to_mc(q, bus, mc) + + # During MC startup, it moved the old keyfile's contents into + # variant-based files, and deleted the old keyfile. + assert not os.path.exists(old_key_file_name) + assert os.path.exists(a1_new_variant_file_name) + assert os.path.exists(a2_new_variant_file_name) + assertEquals("'First among equals'", + account_store('get', 'variant-file', 'DisplayName', + account=a1_tail)) + assertEquals("'Second to none'", + account_store('get', 'variant-file', 'DisplayName', + account=a2_tail)) + # MC doesn't currently ensure that parameters are stored with their + # proper types. + assertEquals("keyfile-escaped 'dontdivert1@example.com'", + account_store('get', 'variant-file', 'param-account', + account=a1_tail)) + assertEquals("keyfile-escaped 'dontdivert2@example.com'", + account_store('get', 'variant-file', 'param-account', + account=a2_tail)) + + # Also, MC has both accounts in memory... + assertContains(cs.ACCOUNT_PATH_PREFIX + a1_tail, + properties['ValidAccounts']) + account = get_fakecm_account(bus, mc, cs.ACCOUNT_PATH_PREFIX + a1_tail) + assertEquals('dontdivert1@example.com', + account.Properties.Get(cs.ACCOUNT, 'Parameters')['account']) + assertEquals('First among equals', + account.Properties.Get(cs.ACCOUNT, 'DisplayName')) + assertEquals((dbus.ByteArray('hello, world'), 'text/plain'), + account.Properties.Get(cs.ACCOUNT_IFACE_AVATAR, 'Avatar', + byte_arrays=True)) + + assertContains(cs.ACCOUNT_PATH_PREFIX + a2_tail, + properties['ValidAccounts']) + account = get_fakecm_account(bus, mc, cs.ACCOUNT_PATH_PREFIX + a2_tail) + assertEquals('dontdivert2@example.com', + account.Properties.Get(cs.ACCOUNT, 'Parameters')['account']) + assertEquals('Second to none', + account.Properties.Get(cs.ACCOUNT, 'DisplayName')) + + # ... and no other accounts. + assertLength(2, properties['ValidAccounts']) diff --git a/tests/twisted/dbus-account-plugin.c b/tests/twisted/dbus-account-plugin.c index a8a2e4df..446a2ad7 100644 --- a/tests/twisted/dbus-account-plugin.c +++ b/tests/twisted/dbus-account-plugin.c @@ -27,6 +27,8 @@ #include <telepathy-glib/telepathy-glib-dbus.h> #define DEBUG(format, ...) g_debug ("%s: " format, G_STRFUNC, ##__VA_ARGS__) +#define CRITICAL(format, ...) \ + g_critical ("%s: " format, G_STRFUNC, ##__VA_ARGS__) #define TESTDOT "org.freedesktop.Telepathy.Test." #define TESTSLASH "/org/freedesktop/Telepathy/Test/" @@ -54,7 +56,7 @@ typedef struct { GHashTable *parameter_flags; /* set of strings */ GHashTable *uncommitted_parameters; - enum { UNCOMMITTED_CREATION, UNCOMMITTED_DELETION } flags; + enum { UNCOMMITTED_CREATION = 1 } flags; TpStorageRestrictionFlags restrictions; } Account; @@ -183,7 +185,6 @@ ensure_account (TestDBusAccountPlugin *self, g_hash_table_insert (self->accounts, g_strdup (account_name), account); } - account->flags &= ~UNCOMMITTED_DELETION; return account; } @@ -211,18 +212,14 @@ service_vanished_cb (GDBusConnection *bus, { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (user_data); GHashTableIter iter; - gpointer k, v; + gpointer k; self->active = FALSE; g_hash_table_iter_init (&iter, self->accounts); - while (g_hash_table_iter_next (&iter, &k, &v)) + while (g_hash_table_iter_next (&iter, &k, NULL)) { - Account *account = v; - - if ((account->flags & UNCOMMITTED_DELETION) == 0) - mcp_account_storage_emit_deleted (MCP_ACCOUNT_STORAGE (self), k); - + mcp_account_storage_emit_deleted (MCP_ACCOUNT_STORAGE (self), k); g_hash_table_iter_remove (&iter); } @@ -376,7 +373,7 @@ test_dbus_account_plugin_process_account_deletion (TestDBusAccountPlugin *self, if (account == NULL) { - g_warning ("accounts service deleted %s but we don't " + CRITICAL ("accounts service deleted %s but we don't " "have any record of that account", account_name); } else @@ -414,7 +411,7 @@ test_dbus_account_plugin_process_attributes (TestDBusAccountPlugin *self, if (account == NULL) { - g_warning ("accounts service altered %s but we don't " + CRITICAL ("accounts service altered %s but we don't " "have any record of that account", account_name); } else @@ -448,8 +445,6 @@ test_dbus_account_plugin_process_attributes (TestDBusAccountPlugin *self, g_hash_table_insert (account->attributes, g_strdup (attr), g_variant_ref (value)); - mcp_account_manager_set_attribute (self->feedback, - account_name, attr, value, flags); mcp_account_storage_emit_altered_one ( MCP_ACCOUNT_STORAGE (self), account_name, attr); @@ -473,8 +468,6 @@ test_dbus_account_plugin_process_attributes (TestDBusAccountPlugin *self, DEBUG ("%s deleted", attr); g_hash_table_remove (account->attributes, attr); - mcp_account_manager_set_attribute (self->feedback, - account_name, attr, NULL, 0); mcp_account_storage_emit_altered_one ( MCP_ACCOUNT_STORAGE (self), account_name, attr); @@ -525,7 +518,7 @@ test_dbus_account_plugin_process_parameters (TestDBusAccountPlugin *self, if (account == NULL) { - g_warning ("accounts service altered %s but we don't " + CRITICAL ("accounts service altered %s but we don't " "have any record of that account", account_name); } else @@ -557,8 +550,6 @@ test_dbus_account_plugin_process_parameters (TestDBusAccountPlugin *self, g_hash_table_insert (account->parameters, g_strdup (param), g_variant_ref (value)); key = g_strdup_printf ("param-%s", param); - mcp_account_manager_set_parameter (self->feedback, - account_name, param, value, flags); mcp_account_storage_emit_altered_one ( MCP_ACCOUNT_STORAGE (self), account_name, key); g_free (key); @@ -586,11 +577,10 @@ test_dbus_account_plugin_process_parameters (TestDBusAccountPlugin *self, (stored == NULL || !g_variant_equal (value, stored))) { + g_hash_table_remove (account->parameters, param); g_hash_table_insert (account->untyped_parameters, g_strdup (param), g_strdup (escaped)); key = g_strdup_printf ("param-%s", param); - mcp_account_manager_set_value (self->feedback, - account_name, key, escaped); mcp_account_storage_emit_altered_one ( MCP_ACCOUNT_STORAGE (self), account_name, key); g_free (key); @@ -620,8 +610,6 @@ test_dbus_account_plugin_process_parameters (TestDBusAccountPlugin *self, g_hash_table_remove (account->untyped_parameters, param); key = g_strdup_printf ("param-%s", param); - mcp_account_manager_set_parameter (self->feedback, - account_name, param, NULL, 0); mcp_account_storage_emit_altered_one ( MCP_ACCOUNT_STORAGE (self), account_name, key); g_free (key); @@ -760,8 +748,8 @@ parameters_changed_cb (GDBusConnection *bus, } static GList * -test_dbus_account_plugin_list (const McpAccountStorage *storage, - const McpAccountManager *am) +test_dbus_account_plugin_list (McpAccountStorage *storage, + McpAccountManager *am) { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); GError *error = NULL; @@ -870,8 +858,8 @@ test_dbus_account_plugin_list (const McpAccountStorage *storage, } static void -test_dbus_account_plugin_ready (const McpAccountStorage *storage, - const McpAccountManager *am) +test_dbus_account_plugin_ready (McpAccountStorage *storage, + McpAccountManager *am) { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); @@ -885,8 +873,8 @@ test_dbus_account_plugin_ready (const McpAccountStorage *storage, } static gchar * -test_dbus_account_plugin_create (const McpAccountStorage *storage, - const McpAccountManager *am, +test_dbus_account_plugin_create (McpAccountStorage *storage, + McpAccountManager *am, const gchar *manager, const gchar *protocol, const gchar *identifier, @@ -908,218 +896,139 @@ test_dbus_account_plugin_create (const McpAccountStorage *storage, return name; } -static gboolean -test_dbus_account_plugin_delete (const McpAccountStorage *storage, - const McpAccountManager *am, +static void delete_account_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data); + +static void +test_dbus_account_plugin_delete_async (McpAccountStorage *storage, + McpAccountManager *am, const gchar *account_name, - const gchar *key) + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); Account *account = lookup_account (self, account_name); + GTask *task = g_task_new (self, cancellable, callback, user_data); - DEBUG ("called"); - - if (account == NULL || !self->active) - return FALSE; - - if (key == NULL) - { - account->flags |= UNCOMMITTED_DELETION; - g_hash_table_remove_all (account->attributes); - g_hash_table_remove_all (account->parameters); - g_hash_table_remove_all (account->untyped_parameters); - g_hash_table_remove_all (account->attribute_flags); - g_hash_table_remove_all (account->parameter_flags); - - account->flags &= ~UNCOMMITTED_CREATION; - g_hash_table_remove_all (account->uncommitted_attributes); - g_hash_table_remove_all (account->uncommitted_parameters); + g_task_set_task_data (task, g_strdup (user_data), g_free); - g_dbus_connection_emit_signal (self->bus, NULL, - TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, - "DeferringDelete", g_variant_new_parsed ("(%o,)", account->path), - NULL); - } - else if (g_str_has_prefix (key, "param-")) - { - g_hash_table_remove (account->parameters, key + 6); - g_hash_table_remove (account->untyped_parameters, key + 6); - g_hash_table_remove (account->parameter_flags, key + 6); - g_hash_table_add (account->uncommitted_parameters, g_strdup (key + 6)); + DEBUG ("called"); - g_dbus_connection_emit_signal (self->bus, NULL, - TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, - "DeferringDeleteParameter", - g_variant_new_parsed ("(%o, %s)", account->path, key + 6), NULL); - } - else - { - g_hash_table_remove (account->attributes, key); - g_hash_table_remove (account->attribute_flags, key); - g_hash_table_add (account->uncommitted_attributes, g_strdup (key)); + g_return_if_fail (self->active); + g_return_if_fail (account != NULL); - g_dbus_connection_emit_signal (self->bus, NULL, - TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, - "DeferringDeleteAttribute", - g_variant_new_parsed ("(%o, %s)", account->path, key), NULL); - } + /* deletion used to be delayed, so the regression tests will expect this + * to happen - leave them unmodified for now */ + g_dbus_connection_emit_signal (self->bus, NULL, + TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, + "DeferringDelete", g_variant_new_parsed ("(%o,)", account->path), + NULL); + g_dbus_connection_emit_signal (self->bus, NULL, + TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, + "CommittingOne", g_variant_new_parsed ("(%o,)", account->path), NULL); - return TRUE; + g_dbus_connection_call (self->bus, + TEST_DBUS_ACCOUNT_SERVICE, + TEST_DBUS_ACCOUNT_SERVICE_PATH, + TEST_DBUS_ACCOUNT_SERVICE_IFACE, + "DeleteAccount", + g_variant_new_parsed ("(%s,)", account_name), + G_VARIANT_TYPE_UNIT, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* no cancellable */ + delete_account_cb, + task); } static gboolean -test_dbus_account_plugin_get (const McpAccountStorage *storage, - const McpAccountManager *am, +test_dbus_account_plugin_delete_finish (McpAccountStorage *storage, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static GVariant * +test_dbus_account_plugin_get_attribute (McpAccountStorage *storage, + McpAccountManager *am, const gchar *account_name, - const gchar *key) + const gchar *attribute, + const GVariantType *type, + McpAttributeFlags *flags) { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); Account *account = lookup_account (self, account_name); + GVariant *v; - if (!self->active || account == NULL || (account->flags & UNCOMMITTED_DELETION)) - return FALSE; - - if (key == NULL) - { - GHashTableIter iter; - gpointer k, v; - - /* get everything */ - g_dbus_connection_emit_signal (self->bus, NULL, - TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, - "GetAllKeys", - g_variant_new_parsed ("(%o,)", account->path), NULL); - - g_hash_table_iter_init (&iter, account->attributes); - - while (g_hash_table_iter_next (&iter, &k, &v)) - { - gchar *escaped = mcp_account_manager_escape_variant_for_keyfile (am, - v); - - mcp_account_manager_set_value (am, account_name, k, escaped); - g_free (escaped); - } - - g_hash_table_iter_init (&iter, account->untyped_parameters); - - while (g_hash_table_iter_next (&iter, &k, &v)) - { - gchar *param_foo; - McpParameterFlags flags; - - param_foo = g_strdup_printf ("param-%s", (const gchar *) k); - mcp_account_manager_set_value (am, account_name, param_foo, v); + if (flags != NULL) + *flags = 0; - flags = GPOINTER_TO_UINT (g_hash_table_lookup ( - account->parameter_flags, k)); + g_return_val_if_fail (self->active, NULL); + g_return_val_if_fail (account != NULL, NULL); - if (flags & MCP_PARAMETER_FLAG_SECRET) - mcp_account_manager_parameter_make_secret (am, account_name, - param_foo); + v = g_hash_table_lookup (account->attributes, attribute); - g_free (param_foo); - } + g_dbus_connection_emit_signal (self->bus, NULL, + TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, + "GetAttribute", + g_variant_new_parsed ("(%o, %s)", account->path, attribute), NULL); - g_hash_table_iter_init (&iter, account->parameters); + if (v != NULL) + { + return g_variant_ref (v); + } + else + { + return NULL; + } +} - while (g_hash_table_iter_next (&iter, &k, &v)) - { - gchar *param_foo; - guint32 flags; - gchar *escaped = mcp_account_manager_escape_variant_for_keyfile (am, - v); +static GVariant * +test_dbus_account_plugin_get_parameter (McpAccountStorage *storage, + McpAccountManager *am, + const gchar *account_name, + const gchar *parameter, + const GVariantType *type, + McpParameterFlags *flags) +{ + TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); + Account *account = lookup_account (self, account_name); + GVariant *v; + const gchar *s; - param_foo = g_strdup_printf ("param-%s", (const gchar *) k); - mcp_account_manager_set_value (am, account_name, param_foo, escaped); - g_free (escaped); + if (flags != NULL) + *flags = 0; - flags = GPOINTER_TO_UINT (g_hash_table_lookup (account->parameter_flags, - k)); + g_return_val_if_fail (self->active, NULL); + g_return_val_if_fail (account != NULL, NULL); - if (flags & MCP_PARAMETER_FLAG_SECRET) - mcp_account_manager_parameter_make_secret (am, account_name, - param_foo); + g_dbus_connection_emit_signal (self->bus, NULL, + TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, + "GetParameter", + g_variant_new_parsed ("(%o, %s)", account->path, parameter), NULL); - g_free (param_foo); - } + v = g_hash_table_lookup (account->parameters, parameter); + s = g_hash_table_lookup (account->untyped_parameters, parameter); - return TRUE; + if (v != NULL) + { + return g_variant_ref (v); } - - /* get one parameter */ - - if (g_str_has_prefix (key, "param-")) + else if (s != NULL) { - GVariant *v = g_hash_table_lookup (account->parameters, key + 6); - const gchar *s = g_hash_table_lookup (account->untyped_parameters, key + 6); - guint32 flags = GPOINTER_TO_UINT ( - g_hash_table_lookup (account->parameter_flags, key + 6)); - - g_dbus_connection_emit_signal (self->bus, NULL, - TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, - "GetParameter", - g_variant_new_parsed ("(%o, %s)", account->path, key + 6), NULL); - - if (flags & MCP_PARAMETER_FLAG_SECRET) - mcp_account_manager_parameter_make_secret (am, account_name, key); - - if (v != NULL) - { - gchar *escaped = mcp_account_manager_escape_variant_for_keyfile (am, - v); - - mcp_account_manager_set_value (am, account_name, key, escaped); - g_free (escaped); - } - else if (s != NULL) - { - mcp_account_manager_set_value (am, account_name, key, s); - } - else - { - return FALSE; - } + return mcp_account_manager_unescape_variant_from_keyfile (am, + s, type, NULL); } else { - GVariant *v = g_hash_table_lookup (account->attributes, key); - - g_dbus_connection_emit_signal (self->bus, NULL, - TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, - "GetAttribute", - g_variant_new_parsed ("(%o, %s)", account->path, key), NULL); - - if (v != NULL) - { - gchar *escaped = mcp_account_manager_escape_variant_for_keyfile (am, - v); - - mcp_account_manager_set_value (am, account_name, key, escaped); - g_free (escaped); - } - else - { - return FALSE; - } + return NULL; } - - return TRUE; -} - -static gboolean -test_dbus_account_plugin_set (const McpAccountStorage *storage, - const McpAccountManager *am, - const gchar *account_name, - const gchar *key, - const gchar *value) -{ - /* Now that we implement set_attribute and set_parameter, this no longer - * needs a real implementation. */ - return FALSE; } -static gboolean +static McpAccountStorageSetResult test_dbus_account_plugin_set_attribute (McpAccountStorage *storage, McpAccountManager *am, const gchar *account_name, @@ -1129,17 +1038,40 @@ test_dbus_account_plugin_set_attribute (McpAccountStorage *storage, { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); Account *account = lookup_account (self, account_name); + GVariant *old; + guint old_flags; g_return_val_if_fail (account_name != NULL, FALSE); g_return_val_if_fail (attribute != NULL, FALSE); - /* for deletions, MC would call delete() instead */ - g_return_val_if_fail (value != NULL, FALSE); DEBUG ("%s of %s", attribute, account_name); - if (!self->active || account == NULL || - (account->flags & UNCOMMITTED_DELETION)) - return FALSE; + g_return_val_if_fail (self->active, MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED); + g_return_val_if_fail (account != NULL, MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED); + + if (value == NULL) + { + if (!g_hash_table_contains (account->attributes, attribute)) + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; + + g_hash_table_remove (account->attributes, attribute); + g_hash_table_remove (account->attribute_flags, attribute); + g_hash_table_add (account->uncommitted_attributes, g_strdup (attribute)); + + g_dbus_connection_emit_signal (self->bus, NULL, + TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, + "DeferringDeleteAttribute", + g_variant_new_parsed ("(%o, %s)", account->path, attribute), NULL); + + return MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED; + } + + old = g_hash_table_lookup (account->attributes, attribute); + old_flags = GPOINTER_TO_UINT (g_hash_table_lookup ( + account->attribute_flags, attribute)); + + if (old != NULL && g_variant_equal (old, value) && old_flags == flags) + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; g_hash_table_insert (account->attributes, g_strdup (attribute), g_variant_ref (value)); @@ -1153,10 +1085,10 @@ test_dbus_account_plugin_set_attribute (McpAccountStorage *storage, g_variant_new_parsed ("(%o, %s, %v)", account->path, attribute, value), NULL); - return TRUE; + return MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED; } -static gboolean +static McpAccountStorageSetResult test_dbus_account_plugin_set_parameter (McpAccountStorage *storage, McpAccountManager *am, const gchar *account_name, @@ -1166,17 +1098,42 @@ test_dbus_account_plugin_set_parameter (McpAccountStorage *storage, { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); Account *account = lookup_account (self, account_name); + GVariant *old; + guint old_flags; g_return_val_if_fail (account_name != NULL, FALSE); g_return_val_if_fail (parameter != NULL, FALSE); - /* for deletions, MC would call delete() instead */ - g_return_val_if_fail (value != NULL, FALSE); DEBUG ("%s of %s", parameter, account_name); - if (!self->active || account == NULL || - (account->flags & UNCOMMITTED_DELETION)) - return FALSE; + g_return_val_if_fail (self->active, MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED); + g_return_val_if_fail (account != NULL, MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED); + + if (value == NULL) + { + if (!g_hash_table_contains (account->parameters, parameter) && + !g_hash_table_contains (account->untyped_parameters, parameter)) + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; + + g_hash_table_remove (account->parameters, parameter); + g_hash_table_remove (account->untyped_parameters, parameter); + g_hash_table_remove (account->parameter_flags, parameter); + g_hash_table_add (account->uncommitted_parameters, g_strdup (parameter)); + + g_dbus_connection_emit_signal (self->bus, NULL, + TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, + "DeferringDeleteParameter", + g_variant_new_parsed ("(%o, %s)", account->path, parameter), NULL); + + return TRUE; + } + + old = g_hash_table_lookup (account->parameters, parameter); + old_flags = GPOINTER_TO_UINT (g_hash_table_lookup ( + account->parameter_flags, parameter)); + + if (old != NULL && g_variant_equal (old, value) && old_flags == flags) + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; g_hash_table_remove (account->untyped_parameters, parameter); g_hash_table_insert (account->parameters, g_strdup (parameter), @@ -1191,37 +1148,7 @@ test_dbus_account_plugin_set_parameter (McpAccountStorage *storage, g_variant_new_parsed ("(%o, %s, %v)", account->path, parameter, value), NULL); - return TRUE; -} - -static gboolean -test_dbus_account_plugin_commit (const McpAccountStorage *storage, - const McpAccountManager *am) -{ - TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); - GHashTableIter iter; - gpointer k; - - DEBUG ("called"); - - if (!self->active) - return FALSE; - - g_dbus_connection_emit_signal (self->bus, NULL, - TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, - "CommittingAll", NULL, NULL); - - g_hash_table_iter_init (&iter, self->accounts); - - while (g_hash_table_iter_next (&iter, &k, NULL)) - { - if (!mcp_account_storage_commit_one (storage, am, k)) - { - g_warning ("declined to commit account %s", (const gchar *) k); - } - } - - return TRUE; + return MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED; } static void @@ -1229,27 +1156,31 @@ delete_account_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { - AsyncData *ad = user_data; + GTask *task = user_data; GVariant *tuple; GError *error = NULL; + TestDBusAccountPlugin *self = g_task_get_source_object (task); + const gchar *account_name = g_task_get_task_data (task); - tuple = g_dbus_connection_call_finish (ad->self->bus, res, &error); + tuple = g_dbus_connection_call_finish (self->bus, res, &error); if (tuple != NULL) { - g_hash_table_remove (ad->self->accounts, ad->account_name); + /* we'll emit ::deleted when we see the signal, which probably + * already happened */ + g_hash_table_remove (self->accounts, account_name); g_variant_unref (tuple); + g_task_return_boolean (task, TRUE); } else { - g_warning ("Unable to delete account %s: %s", ad->account_name, - error->message); - g_clear_error (&error); - /* FIXME: we could roll back the deletion by claiming that - * the service re-created the account? */ + g_prefix_error (&error, "Unable to delete account %s: ", + account_name); + g_warning ("%s", error->message); + g_task_return_error (task, error); } - async_data_free (ad); + g_object_unref (task); } static void @@ -1355,12 +1286,12 @@ update_parameters_cb (GObject *source_object, } static gboolean -test_dbus_account_plugin_commit_one (const McpAccountStorage *storage, - const McpAccountManager *am, +test_dbus_account_plugin_commit (McpAccountStorage *storage, + McpAccountManager *am, const gchar *account_name) { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); - Account *account = lookup_account (self, account_name); + Account *account; GHashTableIter iter; gpointer k; GVariantBuilder a_sv_builder; @@ -1370,36 +1301,15 @@ test_dbus_account_plugin_commit_one (const McpAccountStorage *storage, DEBUG ("%s", account_name); - /* MC does not call @commit_one with parameter %NULL (meaning "all accounts") - * if we also implement commit(), which, as it happens, we do */ - g_return_val_if_fail (account_name != NULL, FALSE); + account = lookup_account (self, account_name); - if (!self->active || account == NULL) - return FALSE; + g_return_val_if_fail (self->active, FALSE); + g_return_val_if_fail (account != NULL, FALSE); g_dbus_connection_emit_signal (self->bus, NULL, TEST_DBUS_ACCOUNT_PLUGIN_PATH, TEST_DBUS_ACCOUNT_PLUGIN_IFACE, "CommittingOne", g_variant_new_parsed ("(%o,)", account->path), NULL); - if (account->flags & UNCOMMITTED_DELETION) - { - g_dbus_connection_call (self->bus, - TEST_DBUS_ACCOUNT_SERVICE, - TEST_DBUS_ACCOUNT_SERVICE_PATH, - TEST_DBUS_ACCOUNT_SERVICE_IFACE, - "DeleteAccount", - g_variant_new_parsed ("(%s,)", account_name), - G_VARIANT_TYPE_UNIT, - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, /* no cancellable */ - delete_account_cb, - async_data_new (self, account_name)); - - /* this doesn't mean we succeeded: it means we tried */ - return TRUE; - } - if (account->flags & UNCOMMITTED_CREATION) { g_dbus_connection_call (self->bus, @@ -1526,7 +1436,7 @@ test_dbus_account_plugin_commit_one (const McpAccountStorage *storage, } static void -test_dbus_account_plugin_get_identifier (const McpAccountStorage *storage, +test_dbus_account_plugin_get_identifier (McpAccountStorage *storage, const gchar *account_name, GValue *identifier) { @@ -1535,8 +1445,8 @@ test_dbus_account_plugin_get_identifier (const McpAccountStorage *storage, DEBUG ("%s", account_name); - if (!self->active || account == NULL || (account->flags & UNCOMMITTED_DELETION)) - return; + g_return_if_fail (self->active); + g_return_if_fail (account != NULL); /* Our "library-specific unique identifier" is just the object-path * as a string. */ @@ -1545,7 +1455,7 @@ test_dbus_account_plugin_get_identifier (const McpAccountStorage *storage, } static GHashTable * -test_dbus_account_plugin_get_additional_info (const McpAccountStorage *storage, +test_dbus_account_plugin_get_additional_info (McpAccountStorage *storage, const gchar *account_name) { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); @@ -1554,8 +1464,8 @@ test_dbus_account_plugin_get_additional_info (const McpAccountStorage *storage, DEBUG ("%s", account_name); - if (!self->active || account == NULL || (account->flags & UNCOMMITTED_DELETION)) - return NULL; + g_return_val_if_fail (self->active, NULL); + g_return_val_if_fail (account != NULL, NULL); ret = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) tp_g_value_slice_free); @@ -1566,7 +1476,7 @@ test_dbus_account_plugin_get_additional_info (const McpAccountStorage *storage, } static guint -test_dbus_account_plugin_get_restrictions (const McpAccountStorage *storage, +test_dbus_account_plugin_get_restrictions (McpAccountStorage *storage, const gchar *account_name) { TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); @@ -1574,28 +1484,12 @@ test_dbus_account_plugin_get_restrictions (const McpAccountStorage *storage, DEBUG ("%s", account_name); - if (!self->active || account == NULL || (account->flags & UNCOMMITTED_DELETION)) - return 0; + g_return_val_if_fail (self->active, 0); + g_return_val_if_fail (account != NULL, 0); return account->restrictions; } -static gboolean -test_dbus_account_plugin_owns (McpAccountStorage *storage, - McpAccountManager *am, - const gchar *account_name) -{ - TestDBusAccountPlugin *self = TEST_DBUS_ACCOUNT_PLUGIN (storage); - Account *account = lookup_account (self, account_name); - - DEBUG ("%s", account_name); - - if (!self->active || account == NULL || (account->flags & UNCOMMITTED_DELETION)) - return FALSE; - - return TRUE; -} - static void account_storage_iface_init (McpAccountStorageIface *iface) { @@ -1604,18 +1498,17 @@ account_storage_iface_init (McpAccountStorageIface *iface) /* this should be higher priority than the diverted-keyfile one */ iface->priority = MCP_ACCOUNT_STORAGE_PLUGIN_PRIO_NORMAL + 100; - iface->get = test_dbus_account_plugin_get; - iface->set = test_dbus_account_plugin_set; + iface->get_attribute = test_dbus_account_plugin_get_attribute; + iface->get_parameter = test_dbus_account_plugin_get_parameter; iface->set_attribute = test_dbus_account_plugin_set_attribute; iface->set_parameter = test_dbus_account_plugin_set_parameter; iface->list = test_dbus_account_plugin_list; iface->ready = test_dbus_account_plugin_ready; - iface->delete = test_dbus_account_plugin_delete; + iface->delete_async = test_dbus_account_plugin_delete_async; + iface->delete_finish = test_dbus_account_plugin_delete_finish; iface->commit = test_dbus_account_plugin_commit; - iface->commit_one = test_dbus_account_plugin_commit_one; iface->get_identifier = test_dbus_account_plugin_get_identifier; iface->get_additional_info = test_dbus_account_plugin_get_additional_info; iface->get_restrictions = test_dbus_account_plugin_get_restrictions; iface->create = test_dbus_account_plugin_create; - iface->owns = test_dbus_account_plugin_owns; } diff --git a/tests/twisted/mc-debug-server.c b/tests/twisted/mc-debug-server.c index 48f02484..559f3f58 100644 --- a/tests/twisted/mc-debug-server.c +++ b/tests/twisted/mc-debug-server.c @@ -154,6 +154,7 @@ main (int argc, char **argv) { GError *error = NULL; GDBusConnection *gdbus = NULL; + GDBusConnection *gdbus_system = NULL; DBusConnection *connection = NULL; int ret = 1; GMainLoop *teardown_loop; @@ -174,26 +175,18 @@ main (int argc, char **argv) G_LOG_FATAL_MASK | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING); gdbus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); - - if (gdbus == NULL) - { - g_warning ("%s", error->message); - g_error_free (error); - error = NULL; - goto out; - } - + g_assert_no_error (error); + g_assert (gdbus != NULL); g_dbus_connection_set_exit_on_close (gdbus, FALSE); - bus_daemon = tp_dbus_daemon_dup (&error); + gdbus_system = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); + g_assert_no_error (error); + g_assert (gdbus_system != NULL); + g_dbus_connection_set_exit_on_close (gdbus_system, FALSE); - if (bus_daemon == NULL) - { - g_warning ("%s", error->message); - g_error_free (error); - error = NULL; - goto out; - } + bus_daemon = tp_dbus_daemon_dup (&error); + g_assert_no_error (error); + g_assert (bus_daemon != NULL); /* It appears that dbus-glib registers a filter that wrongly returns * DBUS_HANDLER_RESULT_HANDLED for signals, so for *our* filter to have any @@ -230,14 +223,13 @@ main (int argc, char **argv) g_main_loop_run (teardown_loop); -out: - if (connection != NULL) { dbus_connection_flush (connection); } tp_clear_object (&gdbus); + tp_clear_object (&gdbus_system); tp_clear_object (&bus_daemon); dbus_shutdown (); diff --git a/tests/twisted/mcp-account-diversion.c b/tests/twisted/mcp-account-diversion.c index 923f51b4..466d3ee3 100644 --- a/tests/twisted/mcp-account-diversion.c +++ b/tests/twisted/mcp-account-diversion.c @@ -111,109 +111,201 @@ _create_config (void) DEBUG ("created %s", file); } -static gboolean -_set (const McpAccountStorage *self, - const McpAccountManager *am, - const gchar *account, - const gchar *key, - const gchar *val) +static McpAccountStorageSetResult +_set (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account, + const gchar *key, + GVariant *val, + McpParameterFlags flags) { AccountDiversionPlugin *adp = ACCOUNT_DIVERSION_PLUGIN (self); + gchar *val_str; + gboolean changed; if (g_str_has_prefix (account, DONT_DIVERT)) - return FALSE; + return MCP_ACCOUNT_STORAGE_SET_RESULT_FAILED; - adp->save = TRUE; - g_key_file_set_value (adp->keyfile, account, key, val); - - return TRUE; -} + if (val == NULL) + { + gsize n; + GStrv keys; -static gboolean -_get (const McpAccountStorage *self, - const McpAccountManager *am, - const gchar *account, - const gchar *key) -{ - AccountDiversionPlugin *adp = ACCOUNT_DIVERSION_PLUGIN (self); + if (g_key_file_remove_key (adp->keyfile, account, key, NULL)) + { + adp->save = TRUE; + changed = TRUE; + } - if (key != NULL) - { - gchar *v = g_key_file_get_value (adp->keyfile, account, key, NULL); + keys = g_key_file_get_keys (adp->keyfile, account, &n, NULL); - if (v == NULL) - return FALSE; + if (keys == NULL || n == 0) + g_key_file_remove_group (adp->keyfile, account, NULL); - mcp_account_manager_set_value (am, account, key, v); - g_free (v); + g_strfreev (keys); } else { - gsize i; - gsize n; - GStrv keys = g_key_file_get_keys (adp->keyfile, account, &n, NULL); + gchar *old; - if (keys == NULL) - n = 0; + val_str = mcp_account_manager_escape_variant_for_keyfile (am, val); - for (i = 0; i < n; i++) - { - gchar *v = g_key_file_get_value (adp->keyfile, account, keys[i], NULL); + old = g_key_file_get_value (adp->keyfile, account, key, NULL); - if (v != NULL) - mcp_account_manager_set_value (am, account, keys[i], v); - - g_free (v); + if (tp_strdiff (old, val_str)) + { + g_key_file_set_value (adp->keyfile, account, key, val_str); + adp->save = TRUE; + changed = TRUE; } - g_strfreev (keys); + g_free (val_str); + g_free (old); } - return TRUE; + if (changed) + return MCP_ACCOUNT_STORAGE_SET_RESULT_CHANGED; + else + return MCP_ACCOUNT_STORAGE_SET_RESULT_UNCHANGED; } -static gboolean -_delete (const McpAccountStorage *self, - const McpAccountManager *am, +static McpAccountStorageSetResult +_set_attribute (McpAccountStorage *self, + McpAccountManager *am, const gchar *account, - const gchar *key) + const gchar *attribute, + GVariant *val, + McpAttributeFlags flags) +{ + return _set (self, am, account, attribute, val, flags); +} + +static McpAccountStorageSetResult +_set_parameter (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account, + const gchar *parameter, + GVariant *val, + McpParameterFlags flags) +{ + gchar *param = g_strdup_printf ("param-%s", parameter); + gboolean ret; + + ret = _set (self, am, account, param, val, flags); + g_free (param); + + return ret; +} + +static GVariant * +_get_attribute (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account, + const gchar *attribute, + const GVariantType *type, + McpAttributeFlags *flags) { AccountDiversionPlugin *adp = ACCOUNT_DIVERSION_PLUGIN (self); + gchar *v; + GVariant *ret; - if (key == NULL) - { - if (g_key_file_remove_group (adp->keyfile, account, NULL)) - adp->save = TRUE; - } - else - { - gsize n; - GStrv keys; + if (flags != NULL) + *flags = 0; - if (g_key_file_remove_key (adp->keyfile, account, key, NULL)) - adp->save = TRUE; + v = g_key_file_get_value (adp->keyfile, account, attribute, NULL); - keys = g_key_file_get_keys (adp->keyfile, account, &n, NULL); + if (v == NULL) + return NULL; - if (keys == NULL || n == 0) - g_key_file_remove_group (adp->keyfile, account, NULL); + ret = mcp_account_manager_unescape_variant_from_keyfile (am, v, type, NULL); + g_free (v); + return ret; +} - g_strfreev (keys); +static GVariant * +_get_parameter (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account, + const gchar *parameter, + const GVariantType *type, + McpParameterFlags *flags) +{ + AccountDiversionPlugin *adp = ACCOUNT_DIVERSION_PLUGIN (self); + gchar *key; + gchar *v; + GVariant *ret; + + if (flags != NULL) + *flags = 0; + + key = g_strdup_printf ("param-%s", parameter); + v = g_key_file_get_value (adp->keyfile, account, key, NULL); + g_free (key); + + if (v == NULL) + return NULL; + + ret = mcp_account_manager_unescape_variant_from_keyfile (am, v, type, NULL); + g_free (v); + return ret; +} + +static gboolean _commit (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account_name); + +static void +delete_async (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + AccountDiversionPlugin *adp = ACCOUNT_DIVERSION_PLUGIN (self); + GTask *task = g_task_new (adp, cancellable, callback, user_data); + + if (g_key_file_remove_group (adp->keyfile, account, NULL)) + adp->save = TRUE; + + if (_commit (self, am, account)) + { + mcp_account_storage_emit_deleted (self, account); + g_task_return_boolean (task, TRUE); + } + else + { + g_task_return_new_error (task, TP_ERROR, TP_ERROR_NOT_AVAILABLE, + "_commit()'s error handling is not good enough to know why " + "I couldn't commit the deletion of %s", account); } - return TRUE; + g_object_unref (task); } +static gboolean +delete_finish (McpAccountStorage *storage, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} static gboolean -_commit (const McpAccountStorage *self, - const McpAccountManager *am) +_commit (McpAccountStorage *self, + McpAccountManager *am, + const gchar *account_name G_GNUC_UNUSED) { gsize n; gchar *data; AccountDiversionPlugin *adp = ACCOUNT_DIVERSION_PLUGIN (self); gboolean rval = FALSE; + /* This simple implementation ignores account_name and commits everything: + * we're writing out the whole keyfile anyway. If MC is looping over + * accounts, the second and subsequent accounts will find that + * adp->save is false, so there's no write-amplification. */ + if (!adp->save) return TRUE; @@ -229,8 +321,8 @@ _commit (const McpAccountStorage *self, } static GList * -_list (const McpAccountStorage *self, - const McpAccountManager *am) +_list (McpAccountStorage *self, + McpAccountManager *am) { gsize i; gsize n; @@ -255,6 +347,35 @@ _list (const McpAccountStorage *self, return rval; } +static gchar * +create (McpAccountStorage *self, + McpAccountManager *am, + const gchar *manager, + const gchar *protocol, + const gchar *identification, + GError **error) +{ + gchar *unique_name; + + unique_name = mcp_account_manager_get_unique_name (MCP_ACCOUNT_MANAGER (am), + manager, protocol, + identification); + + g_return_val_if_fail (unique_name != NULL, NULL); + + if (g_str_has_prefix (unique_name, DONT_DIVERT)) + { + g_free (unique_name); + return NULL; + } + + /* No need to actually create anything: we'll happily return values + * from get(., ., ., NULL) regardless of whether we have that account + * in our list */ + + return unique_name; +} + static void account_storage_iface_init (McpAccountStorageIface *iface, gpointer unused G_GNUC_UNUSED) @@ -263,11 +384,15 @@ account_storage_iface_init (McpAccountStorageIface *iface, iface->desc = PLUGIN_DESCRIPTION; iface->priority = PLUGIN_PRIORITY; - iface->get = _get; - iface->set = _set; - iface->delete = _delete; + iface->get_attribute = _get_attribute; + iface->get_parameter = _get_parameter; + iface->set_attribute = _set_attribute; + iface->set_parameter = _set_parameter; + iface->delete_async = delete_async; + iface->delete_finish = delete_finish; iface->commit = _commit; iface->list = _list; + iface->create = create; } diff --git a/tests/twisted/mctest.py b/tests/twisted/mctest.py index 35ccec97..2229525b 100644 --- a/tests/twisted/mctest.py +++ b/tests/twisted/mctest.py @@ -1018,12 +1018,11 @@ class SimulatedConnectionManager(object): for protocol_name in protocol_names: assert '-' not in protocol_name - q.add_dbus_method_impl(self.IdentifyAccount, - path=self.object_path + '/' + protocol_name, - interface=cs.PROTOCOL, method='IdentifyAccount') - q.add_dbus_method_impl(self.NormalizeContact, - path=self.object_path + '/' + protocol_name, - interface=cs.PROTOCOL, method='NormalizeContact') + + q.add_dbus_method_impl(self.IdentifyAccount, + interface=cs.PROTOCOL, method='IdentifyAccount') + q.add_dbus_method_impl(self.NormalizeContact, + interface=cs.PROTOCOL, method='NormalizeContact') def release_name(self): del self._bus_name_ref @@ -1132,11 +1131,32 @@ class SimulatedConnectionManager(object): }, signature='a{sv}', bus=self.bus) def IdentifyAccount(self, e): + if e.path.startswith(self.object_path + '/'): + protocol = e.path[len(self.object_path + '/'):] + + if protocol not in self.protocol_names: + self.q.dbus_raise(e.message, cs.DBUS_ERROR_UNKNOWN_METHOD, + 'Not my protocol') + return + else: + self.q.dbus_raise(e.message, cs.DBUS_ERROR_UNKNOWN_METHOD, + 'Not even my object path') + return + + if protocol in ('serializable', 'defaults') and 's' in e.args[0]: + ret = e.args[0]['s'].lower() + if ret: + self.q.dbus_return(e.message, ret, signature='s') + return + if 'account' in e.args[0]: ret = e.args[0]['account'].lower() - else: - ret = 'account' - self.q.dbus_return(e.message, ret, signature='s') + if ret: + self.q.dbus_return(e.message, ret, signature='s') + return + + self.q.dbus_raise(e.message, cs.INVALID_HANDLE, + 'Invalid account name %r' % e.args[0].get('account')) def NormalizeContact(self, e): self.q.dbus_return(e.message, e.args[0].lower(), signature='s') |