1# python sucks! vim: set fileencoding=utf-8 : 2# Copyright © 2009–2010 Nokia Corporation 3# Copyright © 2009–2010 Collabora Ltd. 4# 5# This library is free software; you can redistribute it and/or 6# modify it under the terms of the GNU Lesser General Public 7# License as published by the Free Software Foundation; either 8# version 2.1 of the License, or (at your option) any later version. 9# 10# This library is distributed in the hope that it will be useful, but 11# WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13# Lesser General Public License for more details. 14# 15# You should have received a copy of the GNU Lesser General Public 16# License along with this library; if not, write to the Free Software 17# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 18# 02110-1301 USA 19 20""" 21Infrastructure code for testing Mission Control 22""" 23 24import base64 25import os 26import sys 27 28import constants as cs 29import servicetest 30import twisted 31from twisted.internet import reactor 32 33import dbus 34import dbus.service 35 36from fakeaccountsservice import FakeAccountsService 37from fakeconnectivity import FakeConnectivity 38 39def install_colourer(): 40 def red(s): 41 return '\x1b[31m%s\x1b[0m' % s 42 43 def green(s): 44 return '\x1b[32m%s\x1b[0m' % s 45 46 patterns = { 47 'handled': green, 48 'not handled': red, 49 } 50 51 class Colourer: 52 def __init__(self, fh, patterns): 53 self.fh = fh 54 self.patterns = patterns 55 56 def write(self, s): 57 f = self.patterns.get(s, lambda x: x) 58 self.fh.write(f(s)) 59 60 sys.stdout = Colourer(sys.stdout, patterns) 61 return sys.stdout 62 63class MC(dbus.proxies.ProxyObject): 64 def __init__(self, queue, bus, wait_for_names=True, initially_online=True): 65 """ 66 Arguments: 67 68 queue: an event queue 69 bus: a D-Bus connection 70 wait_for_names: if True, the constructor will wait for MC to have 71 been service-activated before returning. if False, 72 the caller may later call wait_for_names(). 73 initially_online: whether the fake implementations of Network Manager 74 and ConnMan should claim to be online or offline. 75 """ 76 dbus.proxies.ProxyObject.__init__(self, 77 conn=bus, 78 bus_name=cs.MC, 79 object_path=cs.MC_PATH, 80 follow_name_owner_changes=True) 81 82 self.connectivity = FakeConnectivity(queue, bus, initially_online) 83 self.q = queue 84 self.bus = bus 85 86 if wait_for_names: 87 self.wait_for_names() 88 89 def wait_for_names(self, *also_expect): 90 """ 91 Waits for MC to have claimed all its bus names, along with the 92 (optional) EventPatterns passed as arguments. 93 """ 94 95 patterns = [ 96 servicetest.EventPattern('dbus-signal', signal='NameOwnerChanged', 97 predicate=lambda e, name=name: e.args[0] == name and e.args[2] != '') 98 for name in [cs.AM, cs.CD, cs.MC] 99 if not self.bus.name_has_owner(name)] 100 101 patterns.extend(also_expect) 102 103 events = self.q.expect_many(*patterns) 104 105 return events[3:] 106 107def exec_test_deferred (fun, params, protocol=None, timeout=None, 108 preload_mc=True, initially_online=True, use_fake_accounts_service=True, 109 pass_kwargs=False): 110 colourer = None 111 112 if sys.stdout.isatty(): 113 colourer = install_colourer() 114 115 queue = servicetest.IteratingEventQueue(timeout) 116 queue.verbose = ( 117 os.environ.get('CHECK_TWISTED_VERBOSE', '') != '' 118 or '-v' in sys.argv) 119 120 bus = dbus.SessionBus() 121 queue.attach_to_bus(bus) 122 error = None 123 124 if preload_mc: 125 try: 126 mc = MC(queue, bus, initially_online=initially_online) 127 except Exception as e: 128 import traceback 129 traceback.print_exc() 130 os._exit(1) 131 else: 132 mc = None 133 134 if use_fake_accounts_service: 135 fake_accounts_service = FakeAccountsService(queue, bus) 136 137 if preload_mc: 138 queue.expect('dbus-signal', 139 path=cs.TEST_DBUS_ACCOUNT_PLUGIN_PATH, 140 interface=cs.TEST_DBUS_ACCOUNT_PLUGIN_IFACE, 141 signal='Active') 142 else: 143 fake_accounts_service = None 144 145 if pass_kwargs: 146 kwargs=dict(fake_accounts_service=fake_accounts_service) 147 else: 148 kwargs=dict() 149 150 try: 151 fun(queue, bus, mc, **kwargs) 152 except Exception as e: 153 import traceback 154 traceback.print_exc() 155 error = e 156 queue.verbose = False 157 158 # Clean up any accounts which are left over from the test. 159 try: 160 am = AccountManager(bus) 161 am_props = am.Properties.GetAll(cs.AM) 162 163 for a in (am_props.get('ValidAccounts', []) + 164 am_props.get('InvalidAccounts', [])): 165 account = Account(bus, a) 166 167 try: 168 account.Properties.Set(cs.ACCOUNT, 'RequestedPresence', 169 (dbus.UInt32(cs.PRESENCE_TYPE_OFFLINE), 'offline', 170 '')) 171 except dbus.DBusException as e: 172 print("Can't set %s offline: %s" % (a, e), file=sys.stderr) 173 174 try: 175 account.Properties.Set(cs.ACCOUNT, 'Enabled', False) 176 except dbus.DBusException as e: 177 print("Can't disable %s: %s" % (a, e), file=sys.stderr) 178 179 try: 180 account.Remove() 181 except dbus.DBusException as e: 182 print("Can't remove %s: %s" % (a, e), file=sys.stderr) 183 184 servicetest.sync_dbus(bus, queue, am) 185 186 except dbus.DBusException as e: 187 print("Couldn't clean up left-over accounts: %s" % e, file=sys.stderr) 188 189 except Exception as e: 190 import traceback 191 traceback.print_exc() 192 error = e 193 194 queue.cleanup() 195 196 if error is None: 197 reactor.callLater(0, reactor.stop) 198 else: 199 # please ignore the POSIX behind the curtain 200 os._exit(1) 201 202 if colourer: 203 sys.stdout = colourer.fh 204 205def exec_test(fun, params=None, protocol=None, timeout=None, 206 preload_mc=True, initially_online=True, 207 use_fake_accounts_service=True, pass_kwargs=False): 208 reactor.callWhenRunning (exec_test_deferred, fun, params, protocol, timeout, 209 preload_mc, initially_online, use_fake_accounts_service, pass_kwargs) 210 reactor.run() 211 212class SimulatedConnection(object): 213 214 def ensure_handle(self, type, identifier): 215 if (type, identifier) in self._handles: 216 return self._handles[(type, identifier)] 217 218 self._last_handle += 1 219 self._handles[(type, identifier)] = self._last_handle 220 self._identifiers[(type, self._last_handle)] = identifier 221 return self._last_handle 222 223 def __init__(self, q, bus, cmname, protocol, account_part, self_ident, 224 self_alias=None, 225 implement_get_interfaces=True, has_requests=True, 226 has_presence=False, has_aliasing=False, has_avatars=False, 227 avatars_persist=True, extra_interfaces=[], has_hidden=False, 228 implement_get_aliases=True, initial_avatar=None, 229 server_delays_avatar=False): 230 self.q = q 231 self.bus = bus 232 233 if self_alias is None: 234 self_alias = self_ident 235 236 self.bus_name = '.'.join([cs.tp_name_prefix, 'Connection', 237 cmname, protocol.replace('-', '_'), account_part]) 238 self._bus_name_ref = dbus.service.BusName(self.bus_name, self.bus) 239 self.object_path = '/' + self.bus_name.replace('.', '/') 240 241 self._last_handle = 41 242 self._handles = {} 243 self._identifiers = {} 244 self.status = cs.CONN_STATUS_DISCONNECTED 245 self.reason = cs.CONN_STATUS_CONNECTING 246 self.self_ident = self_ident 247 self.self_alias = self_alias 248 self.self_handle = self.ensure_handle(cs.HT_CONTACT, self_ident) 249 self.channels = [] 250 self.has_requests = has_requests 251 self.has_presence = has_presence 252 self.has_aliasing = has_aliasing 253 self.has_avatars = has_avatars 254 self.avatars_persist = avatars_persist 255 self.extra_interfaces = extra_interfaces[:] 256 self.avatar_delayed = server_delays_avatar 257 258 self.interfaces = [] 259 self.interfaces.append(cs.CONN_IFACE_CONTACTS) 260 261 if self.has_requests: 262 self.interfaces.append(cs.CONN_IFACE_REQUESTS) 263 if self.has_aliasing: 264 self.interfaces.append(cs.CONN_IFACE_ALIASING) 265 if self.has_avatars: 266 self.interfaces.append(cs.CONN_IFACE_AVATARS) 267 if self.has_presence: 268 self.interfaces.append(cs.CONN_IFACE_SIMPLE_PRESENCE) 269 if self.extra_interfaces: 270 self.interfaces.extend(self.extra_interfaces) 271 272 if initial_avatar is not None: 273 self.avatar = initial_avatar 274 elif self.avatars_persist: 275 self.avatar = dbus.Struct((dbus.ByteArray(b'my old avatar'), 276 'text/plain'), signature='ays') 277 else: 278 self.avatar = None 279 280 q.add_dbus_method_impl(self.Connect, 281 path=self.object_path, interface=cs.CONN, method='Connect') 282 q.add_dbus_method_impl(self.Disconnect, 283 path=self.object_path, interface=cs.CONN, method='Disconnect') 284 q.add_dbus_method_impl(self.GetSelfHandle, 285 path=self.object_path, 286 interface=cs.CONN, method='GetSelfHandle') 287 q.add_dbus_method_impl(self.GetStatus, 288 path=self.object_path, interface=cs.CONN, method='GetStatus') 289 290 q.add_dbus_method_impl(self.GetAll_Connection, 291 path=self.object_path, 292 interface=cs.PROPERTIES_IFACE, method='GetAll', 293 args=[cs.CONN]) 294 295 if implement_get_interfaces: 296 q.add_dbus_method_impl(self.GetInterfaces, 297 path=self.object_path, interface=cs.CONN, 298 method='GetInterfaces') 299 300 q.add_dbus_method_impl(self.RequestHandles, 301 path=self.object_path, interface=cs.CONN, 302 method='RequestHandles') 303 q.add_dbus_method_impl(self.InspectHandles, 304 path=self.object_path, interface=cs.CONN, 305 method='InspectHandles') 306 q.add_dbus_method_impl(self.HoldHandles, 307 path=self.object_path, interface=cs.CONN, 308 method='HoldHandles') 309 q.add_dbus_method_impl(self.GetAll_Requests, 310 path=self.object_path, 311 interface=cs.PROPERTIES_IFACE, method='GetAll', 312 args=[cs.CONN_IFACE_REQUESTS]) 313 314 q.add_dbus_method_impl(self.GetContactAttributes, 315 path=self.object_path, 316 interface=cs.CONN_IFACE_CONTACTS, method='GetContactAttributes') 317 q.add_dbus_method_impl(self.GetContactByID, 318 path=self.object_path, 319 interface=cs.CONN_IFACE_CONTACTS, method='GetContactByID') 320 q.add_dbus_method_impl(self.Get_ContactAttributeInterfaces, 321 path=self.object_path, 322 interface=cs.PROPERTIES_IFACE, method='Get', 323 args=[cs.CONN_IFACE_CONTACTS, 'ContactAttributeInterfaces']) 324 q.add_dbus_method_impl(self.GetAll_Contacts, 325 path=self.object_path, 326 interface=cs.PROPERTIES_IFACE, method='GetAll', 327 args=[cs.CONN_IFACE_CONTACTS]) 328 329 if not has_requests: 330 q.add_dbus_method_impl(self.ListChannels, 331 path=self.object_path, interface=cs.CONN, 332 method='ListChannels') 333 334 if has_presence: 335 q.add_dbus_method_impl(self.SetPresence, path=self.object_path, 336 interface=cs.CONN_IFACE_SIMPLE_PRESENCE, 337 method='SetPresence') 338 q.add_dbus_method_impl(self.GetPresences, path=self.object_path, 339 interface=cs.CONN_IFACE_SIMPLE_PRESENCE, 340 method='GetPresences') 341 q.add_dbus_method_impl(self.Get_SimplePresenceStatuses, 342 path=self.object_path, interface=cs.PROPERTIES_IFACE, 343 method='Get', 344 args=[cs.CONN_IFACE_SIMPLE_PRESENCE, 'Statuses']) 345 q.add_dbus_method_impl(self.GetAll_SimplePresence, 346 path=self.object_path, interface=cs.PROPERTIES_IFACE, 347 method='GetAll', 348 args=[cs.CONN_IFACE_SIMPLE_PRESENCE]) 349 350 if has_aliasing: 351 q.add_dbus_method_impl(self.GetAliasFlags, 352 path=self.object_path, interface=cs.CONN_IFACE_ALIASING, 353 method='GetAliasFlags', 354 args=[]) 355 356 if implement_get_aliases: 357 q.add_dbus_method_impl(self.GetAliases, 358 path=self.object_path, 359 interface=cs.CONN_IFACE_ALIASING, method='GetAliases') 360 361 if has_avatars: 362 q.add_dbus_method_impl(self.GetAvatarRequirements, 363 path=self.object_path, interface=cs.CONN_IFACE_AVATARS, 364 method='GetAvatarRequirements', args=[]) 365 q.add_dbus_method_impl(self.GetAll_Avatars, 366 path=self.object_path, interface=cs.PROPERTIES_IFACE, 367 method='GetAll', args=[cs.CONN_IFACE_AVATARS]) 368 q.add_dbus_method_impl(self.GetKnownAvatarTokens, 369 path=self.object_path, interface=cs.CONN_IFACE_AVATARS, 370 method='GetKnownAvatarTokens') 371 q.add_dbus_method_impl(self.SetAvatar, 372 path=self.object_path, interface=cs.CONN_IFACE_AVATARS, 373 method='SetAvatar') 374 375 self.statuses = dbus.Dictionary({ 376 'available': (cs.PRESENCE_TYPE_AVAILABLE, True, True), 377 'away': (cs.PRESENCE_TYPE_AWAY, True, True), 378 'lunch': (cs.PRESENCE_TYPE_XA, True, True), 379 'busy': (cs.PRESENCE_TYPE_BUSY, True, True), 380 'phone': (cs.PRESENCE_TYPE_BUSY, True, True), 381 'offline': (cs.PRESENCE_TYPE_OFFLINE, False, False), 382 'error': (cs.PRESENCE_TYPE_ERROR, False, False), 383 'unknown': (cs.PRESENCE_TYPE_UNKNOWN, False, False), 384 }, signature='s(ubb)') 385 386 if has_hidden: 387 self.statuses['hidden'] = (cs.PRESENCE_TYPE_HIDDEN, True, True) 388 389 # "dbus.UInt32" to work around 390 # https://bugs.freedesktop.org/show_bug.cgi?id=69967 391 self.presence = dbus.Struct((dbus.UInt32(cs.PRESENCE_TYPE_OFFLINE), 392 'offline', ''), signature='uss') 393 394 def change_self_ident(self, ident): 395 self.self_ident = ident 396 self.self_handle = self.ensure_handle(cs.HT_CONTACT, ident) 397 self.q.dbus_emit(self.object_path, cs.CONN, 'SelfHandleChanged', 398 self.self_handle, signature='u') 399 400 def change_self_alias(self, alias): 401 self.self_alias = alias 402 self.q.dbus_emit(self.object_path, cs.CONN_IFACE_ALIASING, 403 'AliasesChanged', [(self.self_handle, self.self_alias)], 404 signature='a(us)') 405 406 def release_name(self): 407 del self._bus_name_ref 408 409 def GetAll_Connection(self, e): 410 self.q.dbus_return(e.message, { 411 'Interfaces': dbus.Array(self.interfaces, signature='s'), 412 'SelfHandle': dbus.UInt32(self.self_handle), 413 'Status': dbus.UInt32(self.status), 414 'HasImmortalHandles': dbus.Boolean(True), 415 }, signature='a{sv}') 416 417 def forget_avatar(self): 418 self.avatar = (dbus.ByteArray(b''), '') 419 self.avatar_delayed = False 420 421 # not actually very relevant for MC so hard-code 0 for now 422 def GetAliasFlags(self, e): 423 self.q.dbus_return(e.message, 0, signature='u') 424 425 def GetAliases(self, e): 426 ret = dbus.Dictionary(signature='us') 427 if self.self_handle in e.args[0]: 428 ret[self.self_handle] = self.self_alias 429 430 self.q.dbus_return(e.message, ret, signature='a{us}') 431 432 # mostly for the UI's benefit; for now hard-code the requirements from XMPP 433 def GetAvatarRequirements(self, e): 434 self.q.dbus_return(e.message, ['image/jpeg'], 0, 0, 96, 96, 8192, 435 signature='asqqqqu') 436 437 def GetAll_Avatars(self, e): 438 self.q.dbus_return(e.message, { 439 'SupportedAvatarMIMETypes': ['image/jpeg'], 440 'MinimumAvatarWidth': 0, 441 'RecommendedAvatarWidth': 64, 442 'MaximumAvatarWidth': 96, 443 'MinimumAvatarHeight': 0, 444 'RecommendedAvatarHeight': 64, 445 'MaximumAvatarHeight': 96, 446 'MaximumAvatarBytes': 8192, 447 }, signature='a{sv}') 448 449 def GetKnownAvatarTokens(self, e): 450 ret = dbus.Dictionary(signature='us') 451 452 # the user has an avatar already, if they persist; nobody else does 453 if self.self_handle in e.args[0]: 454 if self.avatar is None: 455 # GetKnownAvatarTokens has the special case that "where 456 # the avatar does not persist between connections, a CM 457 # should omit the self handle from the returned map until 458 # an avatar is explicitly set or cleared". We'd have been 459 # better off with a more explicit design, but it's too 460 # late now... 461 assert not self.avatars_persist 462 else: 463 # "a CM must always have the tokens for the self handle 464 # if one is set (even if it is set to no avatar)" 465 # so behave as though we'd done a network round-trip to 466 # check what our token was, and found our configured 467 # token 468 if self.avatar_delayed: 469 self.q.dbus_emit(self.object_path, cs.CONN_IFACE_AVATARS, 470 'AvatarUpdated', self.self_handle, 471 str(self.avatar[0]), signature='us') 472 473 # we just stringify the avatar as the token 474 # (also, empty avatar => no avatar => empty token) 475 ret[self.self_handle] = str(self.avatar[0]) 476 477 self.q.dbus_return(e.message, ret, signature='a{us}') 478 479 def SetAvatar(self, e): 480 self.avatar = dbus.Struct(e.args, signature='ays') 481 self.avatar_delayed = False 482 483 # we just stringify the avatar as the token 484 self.q.dbus_return(e.message, str(self.avatar[0]), signature='s') 485 self.q.dbus_emit(self.object_path, cs.CONN_IFACE_AVATARS, 486 'AvatarRetrieved', self.self_handle, str(self.avatar[0]), 487 self.avatar[0], self.avatar[1], signature='usays') 488 489 def GetPresences(self, e): 490 ret = dbus.Dictionary(signature='u(uss)') 491 contacts = e.args[0] 492 for contact in contacts: 493 if contact == self.self_handle: 494 ret[contact] = self.presence 495 else: 496 # stub - MC doesn't care 497 ret[contact] = dbus.Struct( 498 (cs.PRESENCE_TYPE_UNKNOWN, 'unknown', ''), 499 signature='uss') 500 self.q.dbus_return(e.message, ret, signature='a{u(uss)}') 501 502 def SetPresence(self, e): 503 if e.args[0] in self.statuses: 504 # "dbus.UInt32" to work around 505 # https://bugs.freedesktop.org/show_bug.cgi?id=69967 506 presence = dbus.Struct((dbus.UInt32(self.statuses[e.args[0]][0]), 507 e.args[0], e.args[1]), signature='uss') 508 509 old_presence = self.presence 510 511 if presence != old_presence: 512 self.presence = presence 513 514 self.q.dbus_emit(self.object_path, 515 cs.CONN_IFACE_SIMPLE_PRESENCE, 'PresencesChanged', 516 { self.self_handle : presence }, 517 signature='a{u(uss)}') 518 519 self.q.dbus_return(e.message, signature='') 520 else: 521 self.q.dbus_raise(cs.INVALID_ARGUMENT, 'Unknown status') 522 523 def Get_SimplePresenceStatuses(self, e): 524 self.q.dbus_return(e.message, self.statuses, signature='v') 525 526 def GetAll_SimplePresence(self, e): 527 self.q.dbus_return(e.message, 528 {'Statuses': self.statuses}, signature='a{sv}') 529 530 def GetInterfaces(self, e): 531 self.q.dbus_return(e.message, self.interfaces, signature='as') 532 533 def Connect(self, e): 534 self.StatusChanged(cs.CONN_STATUS_CONNECTING, 535 cs.CONN_STATUS_REASON_REQUESTED) 536 self.q.dbus_return(e.message, signature='') 537 538 def Disconnect(self, e): 539 self.StatusChanged(cs.CONN_STATUS_DISCONNECTED, 540 cs.CONN_STATUS_REASON_REQUESTED) 541 self.q.dbus_return(e.message, signature='') 542 for c in self.channels: 543 c.close() 544 545 def inspect_handles(self, handles, htype=cs.HT_CONTACT): 546 ret = [] 547 548 for h in handles: 549 if (htype, h) in self._identifiers: 550 ret.append(self._identifiers[(htype, h)]) 551 else: 552 raise Exception(h) 553 554 return ret 555 556 def InspectHandles(self, e): 557 htype, hs = e.args 558 559 try: 560 ret = self.inspect_handles(hs, htype) 561 self.q.dbus_return(e.message, ret, signature='as') 562 except e: 563 self.q.dbus_raise(e.message, INVALID_HANDLE, str(e.args[0])) 564 565 def RequestHandles(self, e): 566 htype, idents = e.args 567 self.q.dbus_return(e.message, 568 [self.ensure_handle(htype, i) for i in idents], 569 signature='au') 570 571 def GetStatus(self, e): 572 self.q.dbus_return(e.message, self.status, signature='u') 573 574 def ConnectionError(self, error, details): 575 self.q.dbus_emit(self.object_path, cs.CONN, 'ConnectionError', 576 error, details, signature='sa{sv}') 577 578 def StatusChanged(self, status, reason): 579 self.status = status 580 self.reason = reason 581 self.q.dbus_emit(self.object_path, cs.CONN, 'StatusChanged', 582 status, reason, signature='uu') 583 if self.status == cs.CONN_STATUS_CONNECTED and self.has_presence: 584 if self.presence[0] == cs.PRESENCE_TYPE_OFFLINE: 585 # "dbus.UInt32" to work around 586 # https://bugs.freedesktop.org/show_bug.cgi?id=69967 587 self.presence = dbus.Struct(( 588 dbus.UInt32(cs.PRESENCE_TYPE_AVAILABLE), 589 'available', ''), signature='uss') 590 591 self.q.dbus_emit(self.object_path, 592 cs.CONN_IFACE_SIMPLE_PRESENCE, 'PresencesChanged', 593 { self.self_handle : self.presence }, 594 signature='a{u(uss)}') 595 596 def ListChannels(self, e): 597 arr = dbus.Array(signature='(osuu)') 598 599 for c in self.channels: 600 arr.append(dbus.Struct( 601 (c.object_path, 602 c.immutable[cs.CHANNEL + '.ChannelType'], 603 c.immutable.get(cs.CHANNEL + '.TargetHandleType', 0), 604 c.immutable.get(cs.CHANNEL + '.TargetHandle', 0) 605 ), signature='osuu')) 606 607 self.q.dbus_return(e.message, arr, signature='a(osuu)') 608 609 def get_channel_details(self): 610 return dbus.Array([(c.object_path, c.immutable) 611 for c in self.channels], signature='(oa{sv})') 612 613 def GetAll_Requests(self, e): 614 if self.has_requests: 615 self.q.dbus_return(e.message, { 616 'Channels': self.get_channel_details(), 617 }, signature='a{sv}') 618 else: 619 self.q.dbus_raise(e.message, cs.NOT_IMPLEMENTED, 'no Requests') 620 621 def GetSelfHandle(self, e): 622 self.q.dbus_return(e.message, self.self_handle, signature='u') 623 624 def HoldHandles(self, e): 625 # do nothing 626 self.q.dbus_return(e.message, signature='') 627 628 def NewChannels(self, channels): 629 for channel in channels: 630 assert not channel.announced 631 channel.announced = True 632 self.channels.append(channel) 633 634 self.q.dbus_emit(self.object_path, cs.CONN, 635 'NewChannel', 636 channel.object_path, 637 channel.immutable[cs.CHANNEL + '.ChannelType'], 638 channel.immutable.get(cs.CHANNEL + '.TargetHandleType', 0), 639 channel.immutable.get(cs.CHANNEL + '.TargetHandle', 0), 640 channel.immutable.get(cs.CHANNEL + '.Requested', False), 641 signature='osuub') 642 643 if self.has_requests: 644 self.q.dbus_emit(self.object_path, cs.CONN_IFACE_REQUESTS, 645 'NewChannels', 646 [(channel.object_path, channel.immutable) 647 for channel in channels], 648 signature='a(oa{sv})') 649 650 def get_contact_attributes(self, h, ifaces): 651 id = self.inspect_handles([h])[0] 652 ifaces = set(ifaces).intersection( 653 self.get_contact_attribute_interfaces()) 654 655 ret = dbus.Dictionary({}, signature='sv') 656 ret[cs.ATTR_CONTACT_ID] = id 657 658 if cs.CONN_IFACE_ALIASING in ifaces: 659 if h == self.self_handle: 660 ret[cs.ATTR_ALIAS] = self.self_alias 661 else: 662 ret[cs.ATTR_ALIAS] = id 663 664 if cs.CONN_IFACE_AVATARS in ifaces: 665 if h == self.self_handle: 666 if self.avatar is not None and not self.avatar_delayed: 667 # We just stringify the avatar as the token 668 # (also, empty avatar => no avatar => empty token). 669 # This doesn't have the same special case that 670 # GetKnownAvatarTokens does - if we don't know the 671 # token yet, we don't wait. 672 ret[cs.ATTR_AVATAR_TOKEN] = str(self.avatar[0]) 673 674 if cs.CONN_IFACE_SIMPLE_PRESENCE in ifaces: 675 if h == self.self_handle: 676 ret[cs.ATTR_PRESENCE] = self.presence 677 else: 678 # stub - MC doesn't care 679 # "dbus.UInt32" to work around 680 # https://bugs.freedesktop.org/show_bug.cgi?id=69967 681 ret[cs.ATTR_PRESENCE] = (dbus.UInt32(cs.PRESENCE_UNKNOWN), 682 'unknown', '') 683 684 return ret 685 686 def get_contact_attribute_interfaces(self): 687 return set(self.interfaces).intersection(set([ 688 cs.CONN_IFACE_ALIASING, 689 cs.CONN_IFACE_AVATARS, 690 cs.CONN_IFACE_SIMPLE_PRESENCE, 691 ])) 692 693 def GetContactAttributes(self, e): 694 ret = dbus.Dictionary({}, signature='ua{sv}') 695 696 try: 697 for h in e.args[0]: 698 ret[dbus.UInt32(h)] = self.get_contact_attributes(h, e.args[1]) 699 700 self.q.dbus_return(e.message, ret, signature='a{ua{sv}}') 701 except e: 702 self.q.dbus_raise(e.message, INVALID_HANDLE, str(e.args[0])) 703 704 def GetContactByID(self, e): 705 h = self.ensure_handle(e.args[0]) 706 self.q.dbus_return(e.message, h, 707 self.get_contact_attributes(h, e.args[1]), signature='ua{sv}') 708 709 def GetAll_Contacts(self, e): 710 self.q.dbus_return(e.message, { 711 'ContactAttributeInterfaces': 712 self.get_contact_attribute_interfaces(), 713 }, signature='a{sv}') 714 715 def Get_ContactAttributeInterfaces(self, e): 716 self.q.dbus_return(e.message, 717 dbus.Array(self.get_contact_attribute_interfaces(), signature='s'), 718 signature='v') 719 720class SimulatedChannel(object): 721 def __init__(self, conn, immutable, mutable={}, 722 destroyable=False, group=False): 723 self.conn = conn 724 self.q = conn.q 725 self.bus = conn.bus 726 self.object_path = conn.object_path + ('/_%x' % id(self)) 727 self.immutable = immutable 728 self.properties = dbus.Dictionary({}, signature='sv') 729 self.properties.update(immutable) 730 self.properties.update(mutable) 731 732 self.q.add_dbus_method_impl(self.GetAll, 733 path=self.object_path, 734 interface=cs.PROPERTIES_IFACE, method='GetAll') 735 self.q.add_dbus_method_impl(self.Get, 736 path=self.object_path, 737 interface=cs.PROPERTIES_IFACE, method='Get') 738 self.q.add_dbus_method_impl(self.Close, 739 path=self.object_path, 740 interface=cs.CHANNEL, method='Close') 741 self.q.add_dbus_method_impl(self.GetInterfaces, 742 path=self.object_path, 743 interface=cs.CHANNEL, method='GetInterfaces') 744 745 if destroyable: 746 self.q.add_dbus_method_impl(self.Close, 747 path=self.object_path, 748 interface=cs.CHANNEL_IFACE_DESTROYABLE, 749 method='Destroy') 750 751 if group: 752 self.q.add_dbus_method_impl(self.GetGroupFlags, 753 path=self.object_path, 754 interface=cs.CHANNEL_IFACE_GROUP, 755 method='GetGroupFlags') 756 self.q.add_dbus_method_impl(self.GetSelfHandle, 757 path=self.object_path, 758 interface=cs.CHANNEL_IFACE_GROUP, 759 method='GetSelfHandle') 760 self.q.add_dbus_method_impl(self.GetAllMembers, 761 path=self.object_path, 762 interface=cs.CHANNEL_IFACE_GROUP, 763 method='GetAllMembers') 764 self.q.add_dbus_method_impl(self.GetLocalPendingMembersWithInfo, 765 path=self.object_path, 766 interface=cs.CHANNEL_IFACE_GROUP, 767 method='GetLocalPendingMembersWithInfo') 768 self.properties[cs.CHANNEL_IFACE_GROUP + '.SelfHandle'] \ 769 = self.conn.self_handle 770 771 self.announced = False 772 self.closed = False 773 774 def GetGroupFlags(self, e): 775 self.q.dbus_return(e.message, 0, signature='u') 776 777 def GetSelfHandle(self, e): 778 self.q.dbus_return(e.message, 779 self.properties[cs.CHANNEL_IFACE_GROUP + '.SelfHandle'], 780 signature='u') 781 782 def GetAllMembers(self, e): 783 # stub 784 self.q.dbus_return(e.message, 785 [self.properties[cs.CHANNEL_IFACE_GROUP + '.SelfHandle']], 786 [], [], 787 signature='auauau') 788 789 def GetLocalPendingMembersWithInfo(self, e): 790 # stub 791 self.q.dbus_return(e.message, [], signature='a(uuus)') 792 793 def announce(self): 794 self.conn.NewChannels([self]) 795 796 def Close(self, e): 797 if not self.closed: 798 self.close() 799 self.q.dbus_return(e.message, signature='') 800 801 def close(self): 802 assert self.announced 803 assert not self.closed 804 self.closed = True 805 self.conn.channels.remove(self) 806 self.q.dbus_emit(self.object_path, cs.CHANNEL, 'Closed', signature='') 807 self.q.dbus_emit(self.conn.object_path, cs.CONN_IFACE_REQUESTS, 808 'ChannelClosed', self.object_path, signature='o') 809 810 def GetInterfaces(self, e): 811 self.q.dbus_return(e.message, 812 self.properties[cs.CHANNEL + '.Interfaces'], signature='as') 813 814 def GetAll(self, e): 815 iface = e.args[0] + '.' 816 817 ret = dbus.Dictionary({}, signature='sv') 818 for k in self.properties: 819 if k.startswith(iface): 820 tail = k[len(iface):] 821 if '.' not in tail: 822 ret[tail] = self.properties[k] 823 assert ret # die on attempts to get unimplemented interfaces 824 self.q.dbus_return(e.message, ret, signature='a{sv}') 825 826 def Get(self, e): 827 prop = e.args[0] + '.' + e.args[1] 828 self.q.dbus_return(e.message, self.properties[prop], 829 signature='v') 830 831def aasv(x): 832 return dbus.Array([dbus.Dictionary(d, signature='sv') for d in x], 833 signature='a{sv}') 834 835class SimulatedClient(object): 836 def __init__(self, q, bus, clientname, 837 observe=[], approve=[], handle=[], 838 cap_tokens=[], bypass_approval=False, wants_recovery=False, 839 request_notification=True, implement_get_interfaces=True, 840 is_handler=None, bypass_observers=False, delay_approvers=False): 841 self.q = q 842 self.bus = bus 843 self.bus_name = '.'.join([cs.tp_name_prefix, 'Client', clientname]) 844 self._bus_name_ref = dbus.service.BusName(self.bus_name, self.bus) 845 self.object_path = '/' + self.bus_name.replace('.', '/') 846 self.observe = aasv(observe) 847 self.approve = aasv(approve) 848 self.handle = aasv(handle) 849 self.bypass_approval = bool(bypass_approval) 850 self.bypass_observers = bool(bypass_observers) 851 self.delay_approvers = bool(delay_approvers) 852 self.wants_recovery = bool(wants_recovery) 853 self.request_notification = bool(request_notification) 854 self.handled_channels = dbus.Array([], signature='o') 855 self.cap_tokens = dbus.Array(cap_tokens, signature='s') 856 self.is_handler = is_handler 857 858 if self.is_handler is None: 859 self.is_handler = bool(handle) 860 861 if implement_get_interfaces: 862 q.add_dbus_method_impl(self.Get_Interfaces, 863 path=self.object_path, interface=cs.PROPERTIES_IFACE, 864 method='Get', args=[cs.CLIENT, 'Interfaces']) 865 q.add_dbus_method_impl(self.GetAll_Client, 866 path=self.object_path, 867 interface=cs.PROPERTIES_IFACE, method='GetAll', 868 args=[cs.CLIENT]) 869 870 q.add_dbus_method_impl(self.Get_ObserverChannelFilter, 871 path=self.object_path, interface=cs.PROPERTIES_IFACE, 872 method='Get', args=[cs.OBSERVER, 'ObserverChannelFilter']) 873 q.add_dbus_method_impl(self.GetAll_Observer, 874 path=self.object_path, 875 interface=cs.PROPERTIES_IFACE, method='GetAll', 876 args=[cs.OBSERVER]) 877 878 q.add_dbus_method_impl(self.Get_ApproverChannelFilter, 879 path=self.object_path, interface=cs.PROPERTIES_IFACE, 880 method='Get', args=[cs.APPROVER, 'ApproverChannelFilter']) 881 q.add_dbus_method_impl(self.GetAll_Approver, 882 path=self.object_path, 883 interface=cs.PROPERTIES_IFACE, method='GetAll', 884 args=[cs.APPROVER]) 885 886 q.add_dbus_method_impl(self.Get_HandlerChannelFilter, 887 path=self.object_path, interface=cs.PROPERTIES_IFACE, 888 method='Get', args=[cs.HANDLER, 'HandlerChannelFilter']) 889 q.add_dbus_method_impl(self.Get_Capabilities, 890 path=self.object_path, interface=cs.PROPERTIES_IFACE, 891 method='Get', args=[cs.HANDLER, 'Capabilities']) 892 q.add_dbus_method_impl(self.Get_HandledChannels, 893 path=self.object_path, interface=cs.PROPERTIES_IFACE, 894 method='Get', args=[cs.HANDLER, 'HandledChannels']) 895 q.add_dbus_method_impl(self.Get_BypassApproval, 896 path=self.object_path, interface=cs.PROPERTIES_IFACE, 897 method='Get', args=[cs.HANDLER, 'BypassApproval']) 898 q.add_dbus_method_impl(self.Get_Recover, 899 path=self.object_path, interface=cs.PROPERTIES_IFACE, 900 method='Get', args=[cs.OBSERVER, 'Recover']) 901 q.add_dbus_method_impl(self.GetAll_Handler, 902 path=self.object_path, 903 interface=cs.PROPERTIES_IFACE, method='GetAll', 904 args=[cs.HANDLER]) 905 906 def release_name(self): 907 del self._bus_name_ref 908 909 def reacquire_name(self): 910 self._bus_name_ref = dbus.service.BusName(self.bus_name, self.bus) 911 912 def get_interfaces(self): 913 ret = dbus.Array([], signature='s', variant_level=1) 914 915 if self.observe: 916 ret.append(cs.OBSERVER) 917 918 if self.approve: 919 ret.append(cs.APPROVER) 920 921 if self.is_handler: 922 ret.append(cs.HANDLER) 923 924 if self.request_notification: 925 ret.append(cs.CLIENT_IFACE_REQUESTS) 926 927 return ret 928 929 def Get_Interfaces(self, e): 930 self.q.dbus_return(e.message, self.get_interfaces(), signature='v', 931 bus=self.bus) 932 933 def GetAll_Client(self, e): 934 self.q.dbus_return(e.message, {'Interfaces': self.get_interfaces()}, 935 signature='a{sv}', bus=self.bus) 936 937 def GetAll_Observer(self, e): 938 assert self.observe 939 self.q.dbus_return(e.message, { 940 'ObserverChannelFilter': self.observe, 941 'Recover': dbus.Boolean(self.wants_recovery), 942 'DelayApprovers': dbus.Boolean(self.delay_approvers), 943 }, 944 signature='a{sv}', bus=self.bus) 945 946 def Get_ObserverChannelFilter(self, e): 947 assert self.observe 948 self.q.dbus_return(e.message, self.observe, signature='v', 949 bus=self.bus) 950 951 def GetAll_Approver(self, e): 952 assert self.approve 953 self.q.dbus_return(e.message, {'ApproverChannelFilter': self.approve}, 954 signature='a{sv}', bus=self.bus) 955 956 def Get_ApproverChannelFilter(self, e): 957 assert self.approve 958 self.q.dbus_return(e.message, self.approve, signature='v', 959 bus=self.bus) 960 961 def GetAll_Handler(self, e): 962 assert self.is_handler 963 self.q.dbus_return(e.message, { 964 'HandlerChannelFilter': self.handle, 965 'BypassApproval': self.bypass_approval, 966 'BypassObservers': self.bypass_observers, 967 'HandledChannels': self.handled_channels, 968 'Capabilities': self.cap_tokens, 969 }, 970 signature='a{sv}', bus=self.bus) 971 972 def Get_Capabilities(self, e): 973 self.q.dbus_return(e.message, self.cap_tokens, signature='v', 974 bus=self.bus) 975 976 def Get_HandledChannels(self, e): 977 self.q.dbus_return(e.message, self.handled_channels, signature='v', 978 bus=self.bus) 979 980 def Get_HandlerChannelFilter(self, e): 981 assert self.handle 982 self.q.dbus_return(e.message, self.handle, signature='v', 983 bus=self.bus) 984 985 def Get_BypassApproval(self, e): 986 assert self.handle 987 self.q.dbus_return(e.message, self.bypass_approval, signature='v', 988 bus=self.bus) 989 990 def Get_BypassApproval(self, e): 991 assert self.handle 992 self.q.dbus_return(e.message, self.bypass_observers, signature='v', 993 bus=self.bus) 994 995 def Get_Recover(self, e): 996 assert self.handle 997 self.q.dbus_return(e.message, self.recover, signature='v', 998 bus=self.bus) 999 1000def take_fakecm_name(bus): 1001 return dbus.service.BusName(cs.CM + '.fakecm', bus=bus) 1002 1003def create_fakecm_account(q, bus, mc, params, properties={}, 1004 cm_bus=None): 1005 """Create a fake connection manager and an account that uses it. 1006 1007 Optional keyword arguments: 1008 properties -- a dictionary from qualified property names to values to pass 1009 to CreateAccount. If provided, this function will check that 1010 the newly-created account has these properties. 1011 cm_bus -- if not None, a BusConnection via which to claim the CM's 1012 name. If None, 'bus' will be used. 1013 1014 Returns: (a BusName for the fake CM, an Account proxy)""" 1015 1016 if cm_bus is None: 1017 cm_bus = bus 1018 1019 cm_name_ref = take_fakecm_name(cm_bus) 1020 1021 account_manager = AccountManager(bus) 1022 1023 servicetest.call_async(q, account_manager, 'CreateAccount', 1024 'fakecm', 'fakeprotocol', 'fakeaccount', params, properties) 1025 1026 # Check whether the account being created is to be hidden; if so, then 1027 # expect a different signal. It annoys me that this has to be in here, but, 1028 # eh. 1029 if properties.get(cs.ACCOUNT_IFACE_HIDDEN + '.Hidden', False): 1030 validity_changed_pattern = servicetest.EventPattern('dbus-signal', 1031 path=cs.AM_PATH, signal='HiddenAccountValidityChanged', 1032 interface=cs.AM_IFACE_HIDDEN) 1033 else: 1034 validity_changed_pattern = servicetest.EventPattern('dbus-signal', 1035 path=cs.AM_PATH, signal='AccountValidityChanged', interface=cs.AM) 1036 1037 # The spec has no order guarantee here. 1038 # FIXME: MC ought to also introspect the CM and find out that the params 1039 # are in fact sufficient 1040 a_signal, am_signal, ret = q.expect_many( 1041 servicetest.EventPattern('dbus-signal', 1042 signal='AccountPropertyChanged', interface=cs.ACCOUNT, 1043 predicate=(lambda e: 'Valid' in e.args[0])), 1044 validity_changed_pattern, 1045 servicetest.EventPattern('dbus-return', method='CreateAccount'), 1046 ) 1047 account_path = ret.value[0] 1048 assert am_signal.args == [account_path, True], am_signal.args 1049 assert a_signal.args[0]['Valid'] == True, a_signal.args 1050 1051 assert account_path is not None 1052 1053 account = Account(bus, account_path) 1054 1055 for key, value in properties.items(): 1056 interface, prop = key.rsplit('.', 1) 1057 servicetest.assertEqual(value, account.Properties.Get(interface, prop)) 1058 1059 return (cm_name_ref, account) 1060 1061def get_fakecm_account(bus, mc, account_path): 1062 account = Account(bus, account_path) 1063 1064 # Introspect Account for debugging purpose 1065 account_introspected = account.Introspect( 1066 dbus_interface=cs.INTROSPECTABLE_IFACE) 1067 #print account_introspected 1068 1069 return account 1070 1071def enable_fakecm_account(q, bus, mc, account, expected_params, **kwargs): 1072 # I'm too lazy to manually pass all the other kwargs to 1073 # expect_fakecm_connection 1074 try: 1075 requested_presence = kwargs['requested_presence'] 1076 del kwargs['requested_presence'] 1077 except KeyError: 1078 requested_presence = (2, 'available', '') 1079 1080 # Enable the account 1081 account.Properties.Set(cs.ACCOUNT, 'Enabled', True) 1082 1083 if requested_presence is not None: 1084 requested_presence = dbus.Struct( 1085 (dbus.UInt32(requested_presence[0]),) + 1086 tuple(requested_presence[1:]), 1087 signature='uss') 1088 account.Properties.Set(cs.ACCOUNT, 1089 'RequestedPresence', requested_presence) 1090 1091 return expect_fakecm_connection(q, bus, mc, account, expected_params, **kwargs) 1092 1093def expect_fakecm_connection(q, bus, mc, account, expected_params, 1094 has_requests=True, has_presence=False, has_aliasing=False, 1095 has_avatars=False, avatars_persist=True, 1096 extra_interfaces=[], 1097 expect_before_connect=(), expect_after_connect=(), 1098 has_hidden=False, 1099 self_ident='myself'): 1100 # make (safely) mutable copies 1101 expect_before_connect = list(expect_before_connect) 1102 expect_after_connect = list(expect_after_connect) 1103 1104 e = q.expect('dbus-method-call', method='RequestConnection', 1105 args=['fakeprotocol', expected_params], 1106 destination=cs.tp_name_prefix + '.ConnectionManager.fakecm', 1107 path=cs.tp_path_prefix + '/ConnectionManager/fakecm', 1108 interface=cs.tp_name_prefix + '.ConnectionManager', 1109 handled=False) 1110 1111 conn = SimulatedConnection(q, bus, 'fakecm', 'fakeprotocol', 1112 account.object_path.split('/')[-1], 1113 self_ident, has_requests=has_requests, has_presence=has_presence, 1114 has_aliasing=has_aliasing, has_avatars=has_avatars, 1115 avatars_persist=avatars_persist, extra_interfaces=extra_interfaces, 1116 has_hidden=has_hidden) 1117 1118 q.dbus_return(e.message, conn.bus_name, conn.object_path, signature='so') 1119 1120 if has_requests: 1121 expect_before_connect.append( 1122 servicetest.EventPattern('dbus-method-call', 1123 interface=cs.PROPERTIES_IFACE, method='GetAll', 1124 args=[cs.CONN_IFACE_REQUESTS], 1125 path=conn.object_path, handled=True)) 1126 1127 if expect_before_connect: 1128 events = list(q.expect_many(*expect_before_connect)) 1129 if has_requests: 1130 del events[-1] 1131 else: 1132 events = [] 1133 1134 q.expect('dbus-method-call', method='Connect', 1135 path=conn.object_path, handled=True) 1136 conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CONN_STATUS_REASON_NONE) 1137 1138 expect_after_connect = list(expect_after_connect) 1139 1140 if not has_requests: 1141 expect_after_connect.append( 1142 servicetest.EventPattern('dbus-method-call', 1143 interface=cs.CONN, method='ListChannels', args=[], 1144 path=conn.object_path, handled=True)) 1145 1146 events = events + list(q.expect_many(*expect_after_connect)) 1147 1148 if not has_requests: 1149 del events[-1] 1150 1151 if events: 1152 return (conn,) + tuple(events) 1153 1154 return conn 1155 1156def expect_client_setup(q, clients, got_interfaces_already=False): 1157 patterns = [] 1158 1159 def is_client_setup(e): 1160 if e.method == 'Get' and e.args == [cs.CLIENT, 'Interfaces']: 1161 return True 1162 if e.method == 'GetAll' and e.args == [cs.CLIENT]: 1163 return True 1164 return False 1165 1166 def is_approver_setup(e): 1167 if e.method == 'Get' and \ 1168 e.args == [cs.APPROVER, 'ApproverChannelFilter']: 1169 return True 1170 if e.method == 'GetAll' and e.args == [cs.APPROVER]: 1171 return True 1172 return False 1173 1174 def is_observer_setup(e): 1175 if e.method == 'Get' and \ 1176 e.args == [cs.OBSERVER, 'ObserverChannelFilter']: 1177 return True 1178 if e.method == 'GetAll' and e.args == [cs.OBSERVER]: 1179 return True 1180 return False 1181 1182 def is_handler_setup(e): 1183 if e.method == 'Get' and \ 1184 e.args == [cs.HANDLER, 'HandlerChannelFilter']: 1185 return True 1186 if e.method == 'GetAll' and e.args == [cs.HANDLER]: 1187 return True 1188 return False 1189 1190 for client in clients: 1191 if not got_interfaces_already: 1192 patterns.append(servicetest.EventPattern('dbus-method-call', 1193 interface=cs.PROPERTIES_IFACE, 1194 path=client.object_path, handled=True, 1195 predicate=is_client_setup)) 1196 1197 if client.observe: 1198 patterns.append(servicetest.EventPattern('dbus-method-call', 1199 interface=cs.PROPERTIES_IFACE, 1200 path=client.object_path, handled=True, 1201 predicate=is_observer_setup)) 1202 1203 if client.approve: 1204 patterns.append(servicetest.EventPattern('dbus-method-call', 1205 interface=cs.PROPERTIES_IFACE, 1206 path=client.object_path, handled=True, 1207 predicate=is_approver_setup)) 1208 1209 if client.handle: 1210 patterns.append(servicetest.EventPattern('dbus-method-call', 1211 interface=cs.PROPERTIES_IFACE, 1212 path=client.object_path, predicate=is_handler_setup)) 1213 1214 q.expect_many(*patterns) 1215 1216def get_account_manager(bus): 1217 """ 1218 A backwards-compatibility synonym for constructing a new AccountManager 1219 object. Please don't use this in new tests. 1220 """ 1221 return AccountManager(bus) 1222 1223class AccountManager(servicetest.ProxyWrapper): 1224 def __init__(self, bus): 1225 bare_am = bus.get_object(cs.AM, cs.AM_PATH, 1226 follow_name_owner_changes=True) 1227 1228 servicetest.ProxyWrapper.__init__(self, bare_am, cs.AM, {}) 1229 1230class Account(servicetest.ProxyWrapper): 1231 def __init__(self, bus, account_path): 1232 servicetest.ProxyWrapper.__init__(self, 1233 bus.get_object(cs.AM, account_path), 1234 cs.ACCOUNT, {}) 1235 1236class ChannelDispatcher(servicetest.ProxyWrapper): 1237 def __init__(self, bus): 1238 bare_cd = bus.get_object(cs.CD, cs.CD_PATH, 1239 follow_name_owner_changes=True) 1240 1241 servicetest.ProxyWrapper.__init__(self, bare_cd, cs.CD, {}) 1242 1243class ChannelDispatchOperation(servicetest.ProxyWrapper): 1244 def __init__(self, bus, path): 1245 bare_cdo = bus.get_object(cs.CD, path) 1246 servicetest.ProxyWrapper.__init__(self, bare_cdo, cs.CDO, {}) 1247 1248class ChannelRequest(servicetest.ProxyWrapper): 1249 def __init__(self, bus, path): 1250 bare_cr = bus.get_object(cs.CD, path) 1251 servicetest.ProxyWrapper.__init__(self, bare_cr, cs.CR, {}) 1252 1253def connect_to_mc(q, bus, mc): 1254 account_manager = AccountManager(bus) 1255 1256 # Introspect AccountManager for debugging purpose 1257 account_manager_introspected = account_manager.Introspect( 1258 dbus_interface=cs.INTROSPECTABLE_IFACE) 1259 #print account_manager_introspected 1260 1261 # Check AccountManager has D-Bus property interface 1262 properties = account_manager.Properties.GetAll(cs.AM) 1263 assert properties is not None 1264 interfaces = properties.get('Interfaces') 1265 1266 return account_manager, properties, interfaces 1267 1268def tell_mc_to_die(q, bus): 1269 """Instructs the running Mission Control to die via a magic method call in 1270 the version built for tests.""" 1271 1272 secret_debug_api = dbus.Interface(bus.get_object(cs.AM, "/"), 1273 'org.freedesktop.Telepathy.MissionControl5.RegressionTests') 1274 secret_debug_api.Abort() 1275 1276 # Make sure MC exits 1277 q.expect('dbus-signal', signal='NameOwnerChanged', 1278 predicate=(lambda e: 1279 e.args[0] == 'org.freedesktop.Telepathy.AccountManager' and 1280 e.args[2] == '')) 1281 1282def resuscitate_mc(q, bus, mc): 1283 """Having killed MC with tell_mc_to_die(), this function revives it.""" 1284 # We kick the daemon asynchronously because nm-glib makes blocking calls 1285 # back to us during initialization... 1286 bus.call_async(dbus.BUS_DAEMON_NAME, dbus.BUS_DAEMON_PATH, 1287 dbus.BUS_DAEMON_IFACE, 'StartServiceByName', 'su', (cs.MC, 0), 1288 reply_handler=None, error_handler=None) 1289 1290 # Wait until it's up 1291 mc.wait_for_names() 1292 1293 return connect_to_mc(q, bus, mc) 1294 1295def keyfile_read(fname): 1296 groups = { None: {} } 1297 group = None 1298 for line in open(fname): 1299 line = line[:-1].decode('utf-8').strip() 1300 if not line or line.startswith('#'): 1301 continue 1302 1303 if line.startswith('[') and line.endswith(']'): 1304 group = line[1:-1] 1305 groups[group] = {} 1306 continue 1307 1308 if '=' in line: 1309 k, v = line.split('=', 1) 1310 else: 1311 k = line 1312 v = None 1313 1314 groups[group][k] = v 1315 return groups 1316 1317def read_account_keyfile(): 1318 """Reads the keyfile used by the 'diverted' storage plugin used by most of 1319 the tests.""" 1320 key_file_name = os.path.join(os.getenv('XDG_CACHE_HOME'), 1321 'mcp-test-diverted-account-plugin.conf') 1322 return keyfile_read(key_file_name) 1323