summaryrefslogtreecommitdiff
path: root/qt4/tests/lib/python/account-manager.py
diff options
context:
space:
mode:
Diffstat (limited to 'qt4/tests/lib/python/account-manager.py')
-rwxr-xr-xqt4/tests/lib/python/account-manager.py394
1 files changed, 394 insertions, 0 deletions
diff --git a/qt4/tests/lib/python/account-manager.py b/qt4/tests/lib/python/account-manager.py
new file mode 100755
index 000000000..0f1fd9f9b
--- /dev/null
+++ b/qt4/tests/lib/python/account-manager.py
@@ -0,0 +1,394 @@
+#!/usr/bin/python
+#
+# A small implementation of a Telepathy AccountManager.
+
+import sys
+import re
+
+import dbus
+from dbus.bus import NAME_FLAG_DO_NOT_QUEUE, REQUEST_NAME_REPLY_EXISTS
+from dbus.mainloop.glib import DBusGMainLoop
+from dbus.service import Object, method, signal
+from gobject import MainLoop
+
+TP = 'org.freedesktop.Telepathy'
+
+AM_IFACE = TP + '.AccountManager'
+AM_BUS_NAME = AM_IFACE
+AM_OBJECT_PATH = '/' + AM_IFACE.replace('.', '/')
+
+ACCOUNT_IFACE = TP + '.Account'
+ACCOUNT_IFACE_AVATAR_IFACE = ACCOUNT_IFACE + '.Interface.Avatar'
+ACCOUNT_OBJECT_PATH_BASE = '/' + ACCOUNT_IFACE.replace('.', '/') + '/'
+
+
+Connection_Status_Connected = dbus.UInt32(0)
+Connection_Status_Connecting = dbus.UInt32(1)
+Connection_Status_Disconnected = dbus.UInt32(2)
+Connection_Status_Reason_None_Specified = dbus.UInt32(0)
+Connection_Status_Reason_Requested = dbus.UInt32(1)
+Connection_Status_Reason_Network_Error = dbus.UInt32(2)
+Connection_Presence_Type_Offline = dbus.UInt32(1)
+Connection_Presence_Type_Available = dbus.UInt32(2)
+
+
+VALID_CONNECTION_MANAGER_NAME = re.compile(r'^[A-Za-z0-9][_A-Za-z0-9]+$')
+VALID_PROTOCOL_NAME = re.compile(r'^[A-Za-z0-9][-A-Za-z0-9]+$')
+
+TELEPATHY_ERROR = "org.freedesktop.Telepathy.Error"
+TELEPATHY_ERROR_DISCONNECTED = TELEPATHY_ERROR + ".Disconnected"
+TELEPATHY_ERROR_CANCELLED = TELEPATHY_ERROR + ".Cancelled"
+TELEPATHY_ERROR_NETWORK_ERROR = TELEPATHY_ERROR + ".NetworkError"
+
+class AccountManager(Object):
+ def __init__(self, bus=None):
+ #: map from object path to Account
+ self._valid_accounts = {}
+ #: map from object path to Account
+ self._invalid_accounts = {}
+
+ if bus is None:
+ bus = dbus.SessionBus()
+
+ ret = bus.request_name(AM_BUS_NAME, NAME_FLAG_DO_NOT_QUEUE)
+ if ret == REQUEST_NAME_REPLY_EXISTS:
+ raise dbus.NameExistsException(AM_BUS_NAME)
+
+ Object.__init__(self, bus, AM_OBJECT_PATH)
+
+ def _am_props(self):
+ return dbus.Dictionary({
+ 'Interfaces': dbus.Array([], signature='s'),
+ 'ValidAccounts': dbus.Array(self._valid_accounts.keys(),
+ signature='o'),
+ 'InvalidAccounts': dbus.Array(self._invalid_accounts.keys(),
+ signature='o'),
+ 'SupportedAccountProperties': dbus.Array([ACCOUNT_IFACE + '.Enabled'], signature='s'),
+ }, signature='sv')
+
+ @method(dbus.PROPERTIES_IFACE,
+ in_signature='s',
+ out_signature='a{sv}')
+ def GetAll(self, iface):
+ if iface == AM_IFACE:
+ return self._am_props()
+ else:
+ raise ValueError('No such interface')
+
+ @method(dbus.PROPERTIES_IFACE,
+ in_signature='ss',
+ out_signature='v')
+ def Get(self, iface, prop):
+ if iface == AM_IFACE:
+ props = self._am_props()
+ else:
+ raise ValueError('No such interface')
+
+ if prop in props:
+ return props[prop]
+ else:
+ raise ValueError('No such property')
+
+ @method(dbus.PROPERTIES_IFACE,
+ in_signature='ssv')
+ def Set(self, iface, prop, value):
+ raise NotImplementedError('No mutable properties')
+
+ @signal(AM_IFACE, signature='ob')
+ def AccountValidityChanged(self, path, valid):
+ if valid:
+ assert path in self._invalid_accounts
+ assert path not in self._valid_accounts
+ self._valid_accounts[path] = self._invalid_accounts.pop(path)
+ else:
+ assert path in self._valid_accounts
+ assert path not in self._invalid_accounts
+ self._invalid_accounts[path] = self._valid_accounts.pop(path)
+ print "Emitting AccountValidityChanged(%s, %s)" % (path, valid)
+
+ @signal(AM_IFACE, signature='o')
+ def AccountRemoved(self, path):
+ assert path in self._valid_accounts or path in self._invalid_accounts
+ self._valid_accounts.pop(path, None)
+ self._invalid_accounts.pop(path, None)
+ print "Emitting AccountRemoved(%s)" % path
+
+ @method(AM_IFACE, in_signature='sssa{sv}a{sv}', out_signature='o')
+ def CreateAccount(self, cm, protocol, display_name, parameters,
+ properties):
+
+ if not VALID_CONNECTION_MANAGER_NAME.match(cm):
+ raise ValueError('Invalid CM name')
+
+ if not VALID_PROTOCOL_NAME.match(protocol):
+ raise ValueError('Invalid protocol name')
+
+ if properties:
+ raise ValueError('This AM does not support setting properties at'
+ 'account creation')
+
+ base = ACCOUNT_OBJECT_PATH_BASE + cm + '/' + protocol.replace('-', '_')
+
+ # FIXME: This is a stupid way to generate the paths - we should
+ # incorporate the display name somehow. However, it's spec-compliant
+ i = 0
+ while 1:
+ path = '%s/Account%d' % (base, i)
+
+ if (path not in self._valid_accounts and
+ path not in self._invalid_accounts):
+ account = Account(self, path,
+ '%s (account %d)' % (display_name, i), parameters)
+
+ # put it in the wrong set and move it to the right one -
+ # that's probably the simplest implementation
+ if account._is_valid():
+ self._invalid_accounts[path] = account
+ self.AccountValidityChanged(path, True)
+ assert path not in self._invalid_accounts
+ assert path in self._valid_accounts
+ else:
+ self._valid_accounts[path] = account
+ self.AccountValidityChanged(path, False)
+ assert path not in self._valid_accounts
+ assert path in self._invalid_accounts
+
+ return path
+
+ i += 1
+
+ raise AssertionError('Not reached')
+
+class Account(Object):
+ def __init__(self, am, path, display_name, parameters):
+ Object.__init__(self, am.connection, path)
+ self._am = am
+
+ self._connection = dbus.ObjectPath('/')
+ self._connection_status = Connection_Status_Disconnected
+ self._connection_status_reason = Connection_Status_Reason_None_Specified
+ self._connection_error = u''
+ self._connection_error_details = dbus.Dictionary({}, signature='sv')
+ self._service = u''
+ self._display_name = display_name
+ self._icon = u'bob.png'
+ self._enabled = True
+ self._nickname = u'Bob'
+ self._parameters = parameters
+ self._has_been_online = False
+ self._connect_automatically = False
+ self._normalized_name = u'bob'
+ self._automatic_presence = dbus.Struct(
+ (Connection_Presence_Type_Available, 'available', ''),
+ signature='uss')
+ self._current_presence = dbus.Struct(
+ (Connection_Presence_Type_Offline, 'offline', ''),
+ signature='uss')
+ self._requested_presence = dbus.Struct(
+ (Connection_Presence_Type_Offline, 'offline', ''),
+ signature='uss')
+ self._avatar = dbus.Struct(
+ (dbus.ByteArray(''), 'image/png'),
+ signature='ays')
+ self._interfaces = [ACCOUNT_IFACE_AVATAR_IFACE,]
+
+ def _is_valid(self):
+ return True
+
+ @method(ACCOUNT_IFACE, in_signature='a{sv}as', out_signature='as')
+ def UpdateParameters(self, set_, unset):
+ print ("%s: entering UpdateParameters(\n %r,\n %r \n)"
+ % (self.__dbus_object_path__, set_, unset))
+ for (key, value) in set_.iteritems():
+ self._parameters[key] = value
+ for key in unset:
+ self._parameters.pop(key, None)
+ print ("%s: UpdateParameters(...) -> success"
+ % self.__dbus_object_path__)
+
+ self.AccountPropertyChanged({'Parameters': self._parameters})
+
+ return []
+
+ @signal(ACCOUNT_IFACE, signature='a{sv}')
+ def AccountPropertyChanged(self, delta):
+ print ("%s: emitting AccountPropertyChanged(\n %r \n)"
+ % (self.__dbus_object_path__, delta))
+
+ @signal(ACCOUNT_IFACE_AVATAR_IFACE, signature='')
+ def AvatarChanged(self):
+ print ("%s: emitting AvatarChanged"
+ % (self.__dbus_object_path__))
+
+ @method(ACCOUNT_IFACE, in_signature='', out_signature='')
+ def Remove(self):
+ print "%s: entering Remove()" % self.__dbus_object_path__
+ self.Removed()
+ self.remove_from_connection()
+ print "%s: Remove() -> success" % self.__dbus_object_path__
+
+ @signal(ACCOUNT_IFACE, signature='')
+ def Removed(self):
+ self._am.AccountRemoved(self.__dbus_object_path__)
+ print "%s: Emitting Removed()" % self.__dbus_object_path__
+
+ def _account_props(self):
+ return dbus.Dictionary({
+ 'Interfaces': dbus.Array(self._interfaces, signature='s'),
+ 'Service': self._service,
+ 'DisplayName': self._display_name,
+ 'Icon': self._icon,
+ 'Valid': self._is_valid(),
+ 'Enabled': self._enabled,
+ 'Nickname': self._nickname,
+ 'Parameters': self._parameters,
+ 'AutomaticPresence': self._automatic_presence,
+ 'CurrentPresence': self._current_presence,
+ 'RequestedPresence': self._requested_presence,
+ 'HasBeenOnline': self._has_been_online,
+ 'ConnectAutomatically': self._connect_automatically,
+ 'Connection': self._connection,
+ 'ConnectionStatus': self._connection_status,
+ 'ConnectionStatusReason': self._connection_status_reason,
+ 'ConnectionError': self._connection_error,
+ 'ConnectionErrorDetails': self._connection_error_details,
+ 'NormalizedName': self._normalized_name,
+ }, signature='sv')
+
+ def _account_avatar_props(self):
+ return dbus.Dictionary({
+ 'Avatar': self._avatar
+ }, signature='sv')
+
+ @method(dbus.PROPERTIES_IFACE,
+ in_signature='s',
+ out_signature='a{sv}')
+ def GetAll(self, iface):
+ if iface == ACCOUNT_IFACE:
+ return self._account_props()
+ elif iface == ACCOUNT_IFACE_AVATAR_IFACE:
+ return self._account_avatar_props()
+ else:
+ raise ValueError('No such interface')
+
+ @method(dbus.PROPERTIES_IFACE,
+ in_signature='ss',
+ out_signature='v')
+ def Get(self, iface, prop):
+ if iface == ACCOUNT_IFACE:
+ props = self._account_props()
+ elif iface == ACCOUNT_IFACE_AVATAR_IFACE:
+ props = self._account_avatar_props()
+ else:
+ raise ValueError('No such interface')
+
+ if prop in props:
+ return props[prop]
+ else:
+ raise ValueError('No such property')
+
+ @method(dbus.PROPERTIES_IFACE,
+ in_signature='ssv', byte_arrays=True)
+ def Set(self, iface, prop, value):
+ if iface == ACCOUNT_IFACE:
+ props = {}
+ if prop == 'Service':
+ self._service = unicode(value)
+ elif prop == 'DisplayName':
+ self._display_name = unicode(value)
+ elif prop == 'Icon':
+ self._icon = unicode(value)
+ elif prop == 'Enabled':
+ self._enabled = bool(value)
+ elif prop == 'Nickname':
+ self._nickname = unicode(value)
+ elif prop == 'AutomaticPresence':
+ self._automatic_presence = dbus.Struct(
+ (dbus.UInt32(value[0]), unicode(value[1]),
+ unicode(value[2])),
+ signature='uss')
+ elif prop == 'RequestedPresence':
+ self._requested_presence = dbus.Struct(
+ (dbus.UInt32(value[0]), unicode(value[1]),
+ unicode(value[2])),
+ signature='uss')
+ # pretend to put the account online, if the presence != offline
+ if value[0] != Connection_Presence_Type_Offline:
+ # simulate that we are connecting/changing presence
+ props["ChangingPresence"] = True
+
+ if self._connection_status == Connection_Status_Disconnected:
+ self._connection_status = Connection_Status_Connecting
+ props["ConnectionStatus"] = self._connection_status
+
+ props[prop] = self._account_props()[prop]
+ self.AccountPropertyChanged(props)
+
+ props["ChangingPresence"] = False
+ if "(deny)" in self._requested_presence[2]:
+ self._connection_status = Connection_Status_Disconnected
+ self._connection_status_reason = Connection_Status_Reason_Network_Error
+ self._connection_error = TELEPATHY_ERROR_NETWORK_ERROR
+ self._connection_error_details = dbus.Dictionary(
+ {'debug-message': u'You asked for it'},
+ signature='sv')
+ self._current_presence = dbus.Struct(
+ (Connection_Presence_Type_Offline, 'offline', ''),
+ signature='uss')
+ else:
+ self._connection_status = Connection_Status_Connected
+ self._connection_status_reason = Connection_Status_Reason_None_Specified
+ self._connection_error = u''
+ self._connection_error_details = dbus.Dictionary({}, signature='sv')
+ self._current_presence = self._requested_presence
+ if self._has_been_online == False:
+ self._has_been_online = True
+ props["HasBeenOnline"] = self._has_been_online
+ else:
+ self._connection_status = Connection_Status_Disconnected
+ self._connection_status_reason = Connection_Status_Reason_Requested
+ self._connection_error = TELEPATHY_ERROR_CANCELLED
+ self._connection_error_details = dbus.Dictionary(
+ {'debug-message': u'You asked for it'},
+ signature='sv')
+ self._current_presence = dbus.Struct(
+ (Connection_Presence_Type_Offline, 'offline', ''),
+ signature='uss')
+
+ props["ConnectionStatus"] = self._connection_status
+ props["ConnectionStatusReason"] = self._connection_status_reason
+ props["ConnectionError"] = self._connection_error
+ props["ConnectionErrorDetails"] = self._connection_error_details
+ props["CurrentPresence"] = self._current_presence
+ elif prop == 'ConnectAutomatically':
+ self._connect_automatically = bool(value)
+ elif prop == 'Connection':
+ self._connection = dbus.ObjectPath(value)
+ else:
+ raise ValueError('Read-only or nonexistent property')
+
+ props[prop] = self._account_props()[prop]
+ self.AccountPropertyChanged(props)
+ elif iface == ACCOUNT_IFACE_AVATAR_IFACE:
+ if prop == 'Avatar':
+ self._avatar = dbus.Struct(
+ (dbus.ByteArray(value[0]), unicode(value[1])),
+ signature='ays')
+ self.AvatarChanged()
+ else:
+ raise ValueError('Nonexistent property')
+ else:
+ raise ValueError('No such interface')
+
+if __name__ == '__main__':
+ DBusGMainLoop(set_as_default=True)
+
+ try:
+ am = AccountManager()
+ except dbus.NameExistsException:
+ print >> sys.stderr, 'AccountManager already running'
+ sys.exit(1)
+
+ print "AccountManager running..."
+ mainloop = MainLoop()
+ mainloop.run()