コード例 #1
0
def test(q, bus, unused, **kwargs):
    simulated_cm = SimulatedConnectionManager(q, bus)

    fake_accounts_service = kwargs['fake_accounts_service']
    accounts = preseed(q, bus, fake_accounts_service)

    mc = MC(q, bus)

    for account in accounts:
        account.test(q, bus, mc)
コード例 #2
0
def test(q, bus, unused, **kwargs):
    fake_accounts_service = kwargs['fake_accounts_service']
    preseed(q, bus, fake_accounts_service)

    text_fixed_properties = dbus.Dictionary(
        {
            cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT,
            cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_TEXT,
        },
        signature='sv')

    user_action_time = dbus.Int64(1238582606)

    # A client and a CM are already running
    client = SimulatedClient(q,
                             bus,
                             'Empathy',
                             observe=[text_fixed_properties],
                             approve=[text_fixed_properties],
                             handle=[text_fixed_properties],
                             bypass_approval=False,
                             implement_get_interfaces=False)
    simulated_cm = SimulatedConnectionManager(q, bus)

    # service-activate MC; it will try to introspect the running client.
    mc = MC(q, bus, wait_for_names=False)
    get_interfaces, = mc.wait_for_names(
        EventPattern('dbus-method-call',
                     path=client.object_path,
                     interface=cs.PROPERTIES_IFACE,
                     method='Get',
                     args=[cs.CLIENT, 'Interfaces'],
                     handled=False))

    # The client doesn't reply just yet; meanwhile, immediately make a channel
    # request
    account = bus.get_object(
        cs.MC, cs.tp_path_prefix +
        '/Account/fakecm/fakeprotocol/jc_2edenton_40unatco_2eint')
    cd = bus.get_object(cs.MC, cs.CD_PATH)
    cd_props = dbus.Interface(cd, cs.PROPERTIES_IFACE)

    request = dbus.Dictionary(
        {
            cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_TEXT,
            cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT,
            cs.CHANNEL + '.TargetID': '*****@*****.**',
        },
        signature='sv')
    call_async(q,
               cd,
               'CreateChannel',
               account.object_path,
               request,
               user_action_time,
               client.bus_name,
               dbus_interface=cs.CD)

    ret = q.expect('dbus-return', method='CreateChannel')
    request_path = ret.value[0]

    # chat UI connects to signals and calls ChannelRequest.Proceed()
    cr = bus.get_object(cs.MC, request_path)
    request_props = cr.GetAll(cs.CR, dbus_interface=cs.PROPERTIES_IFACE)
    assert request_props['Account'] == account.object_path
    assert request_props['Requests'] == [request]
    assert request_props['UserActionTime'] == user_action_time
    assert request_props['PreferredHandler'] == client.bus_name
    assert request_props['Interfaces'] == []

    call_async(q, cr, 'Proceed', dbus_interface=cs.CR)

    e = q.expect('dbus-method-call',
                 method='RequestConnection',
                 args=[
                     'fakeprotocol', {
                         'account': '*****@*****.**',
                         'password': '******',
                     }
                 ],
                 destination=cs.tp_name_prefix + '.ConnectionManager.fakecm',
                 path=cs.tp_path_prefix + '/ConnectionManager/fakecm',
                 interface=cs.tp_name_prefix + '.ConnectionManager',
                 handled=False)
    conn = SimulatedConnection(q, bus, 'fakecm', 'fakeprotocol', 'the_conn',
                               '*****@*****.**')
    q.dbus_return(e.message, conn.bus_name, conn.object_path, signature='so')

    q.expect('dbus-method-call',
             method='Connect',
             path=conn.object_path,
             handled=True)
    conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED)

    # A channel appears spontaneously

    announcement_immutable = dbus.Dictionary(text_fixed_properties)
    announcement_immutable[cs.CHANNEL + '.TargetID'] = '*****@*****.**'
    announcement_immutable[cs.CHANNEL + '.TargetHandle'] = \
            conn.ensure_handle(cs.HT_CONTACT, '*****@*****.**')
    announcement_immutable[cs.CHANNEL + '.InitiatorHandle'] = \
        announcement_immutable[cs.CHANNEL + '.TargetHandle']
    announcement_immutable[cs.CHANNEL + '.InitiatorID'] = \
        announcement_immutable[cs.CHANNEL + '.TargetID']
    announcement_immutable[cs.CHANNEL + '.Interfaces'] = \
            dbus.Array([], signature='s')
    announcement_immutable[cs.CHANNEL + '.Requested'] = False
    announcement = SimulatedChannel(conn, announcement_immutable)
    announcement.announce()

    # Now the Client returns its info
    q.dbus_return(
        get_interfaces.message,
        dbus.Array(
            [cs.HANDLER, cs.OBSERVER, cs.APPROVER, cs.CLIENT_IFACE_REQUESTS],
            signature='s'),
        signature='v')

    expect_client_setup(q, [client], got_interfaces_already=True)

    # Now that the dispatcher is ready to go, we start looking for channels,
    # and also make the actual request
    # Empathy observes the channel we originally requested.
    _, a, cm_request_call = q.expect_many(
        EventPattern('dbus-method-call',
                     interface=cs.PROPERTIES_IFACE,
                     method='GetAll',
                     args=[cs.CONN_IFACE_REQUESTS],
                     path=conn.object_path,
                     handled=True),
        EventPattern('dbus-method-call',
                     path=client.object_path,
                     interface=cs.OBSERVER,
                     method='ObserveChannels',
                     handled=False),
        EventPattern('dbus-method-call',
                     interface=cs.CONN_IFACE_REQUESTS,
                     method='CreateChannel',
                     path=conn.object_path,
                     args=[request],
                     handled=False),
    )

    assert a.args[0] == account.object_path, a.args
    assert a.args[1] == conn.object_path, a.args
    assert a.args[3] != '/', a.args  # there is a dispatch operation
    assert a.args[4] == [], a.args
    channels = a.args[2]
    assert len(channels) == 1, channels
    assert channels[0][0] == announcement.object_path, channels
    assert channels[0][1] == announcement_immutable, channels

    # Time passes. A channel is returned.

    channel_immutable = dbus.Dictionary(request)
    channel_immutable[cs.CHANNEL + '.InitiatorID'] = conn.self_ident
    channel_immutable[cs.CHANNEL + '.InitiatorHandle'] = conn.self_handle
    channel_immutable[cs.CHANNEL + '.Requested'] = True
    channel_immutable[cs.CHANNEL + '.Interfaces'] = \
        dbus.Array([], signature='s')
    channel_immutable[cs.CHANNEL + '.TargetHandle'] = \
        conn.ensure_handle(cs.HT_CONTACT, '*****@*****.**')
    channel = SimulatedChannel(conn, channel_immutable)

    q.dbus_return(cm_request_call.message,
                  channel.object_path,
                  channel.immutable,
                  signature='oa{sv}')
    channel.announce()

    # Empathy observes the newly-created channel.
    e = q.expect('dbus-method-call',
                 path=client.object_path,
                 interface=cs.OBSERVER,
                 method='ObserveChannels',
                 handled=False)

    assert e.args[0] == account.object_path, e.args
    assert e.args[1] == conn.object_path, e.args
    assert e.args[3] == '/', e.args  # no dispatch operation
    assert e.args[4] == [request_path], e.args
    channels = e.args[2]
    assert len(channels) == 1, channels
    assert channels[0][0] == channel.object_path, channels
    assert channels[0][1] == channel_immutable, channels

    # Observer says "OK, go"
    q.dbus_return(a.message, signature='')
    q.dbus_return(e.message, signature='')

    # Empathy is asked to handle the channel
    e = q.expect('dbus-method-call',
                 path=client.object_path,
                 interface=cs.HANDLER,
                 method='HandleChannels',
                 handled=False)
    assert e.args[0] == account.object_path, e.args
    assert e.args[1] == conn.object_path, e.args
    channels = e.args[2]
    assert len(channels) == 1, channels
    assert channels[0][0] == channel.object_path, channels
    assert channels[0][1] == channel_immutable, channels
    assert e.args[3] == [request_path], e.args
    assert e.args[4] == user_action_time
    assert isinstance(e.args[5], dict)
    assert len(e.args) == 6
def test(q, bus, unused, **kwargs):
    fake_accounts_service = kwargs['fake_accounts_service']
    preseed(q, bus, fake_accounts_service)

    text_fixed_properties = dbus.Dictionary({
        cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT,
        cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_TEXT,
        }, signature='sv')

    user_action_time = dbus.Int64(1238582606)

    # A client and a CM are already running
    client = SimulatedClient(q, bus, 'Empathy',
            observe=[text_fixed_properties], approve=[text_fixed_properties],
            handle=[text_fixed_properties], bypass_approval=False,
            implement_get_interfaces=False)
    simulated_cm = SimulatedConnectionManager(q, bus)

    # service-activate MC; it will try to introspect the running client.
    mc = MC(q, bus, wait_for_names=False)
    get_interfaces, = mc.wait_for_names(
        EventPattern('dbus-method-call', path=client.object_path,
            interface=cs.PROPERTIES_IFACE, method='Get',
            args=[cs.CLIENT, 'Interfaces'],
            handled=False))

    # The client doesn't reply just yet; meanwhile, immediately make a channel
    # request
    account = bus.get_object(cs.MC,
            cs.tp_path_prefix +
            '/Account/fakecm/fakeprotocol/jc_2edenton_40unatco_2eint')
    cd = bus.get_object(cs.MC, cs.CD_PATH)
    cd_props = dbus.Interface(cd, cs.PROPERTIES_IFACE)

    request = dbus.Dictionary({
            cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_TEXT,
            cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT,
            cs.CHANNEL + '.TargetID': '*****@*****.**',
            }, signature='sv')
    call_async(q, cd, 'CreateChannel',
            account.object_path, request, user_action_time, client.bus_name,
            dbus_interface=cs.CD)

    ret = q.expect('dbus-return', method='CreateChannel')
    request_path = ret.value[0]

    # chat UI connects to signals and calls ChannelRequest.Proceed()
    cr = bus.get_object(cs.MC, request_path)
    request_props = cr.GetAll(cs.CR, dbus_interface=cs.PROPERTIES_IFACE)
    assert request_props['Account'] == account.object_path
    assert request_props['Requests'] == [request]
    assert request_props['UserActionTime'] == user_action_time
    assert request_props['PreferredHandler'] == client.bus_name
    assert request_props['Interfaces'] == []

    call_async(q, cr, 'Proceed', dbus_interface=cs.CR)

    e = q.expect('dbus-method-call', method='RequestConnection',
            args=['fakeprotocol', {
                'account': '*****@*****.**',
                'password': '******',
                }],
            destination=cs.tp_name_prefix + '.ConnectionManager.fakecm',
            path=cs.tp_path_prefix + '/ConnectionManager/fakecm',
            interface=cs.tp_name_prefix + '.ConnectionManager',
            handled=False)
    conn = SimulatedConnection(q, bus, 'fakecm', 'fakeprotocol', 'the_conn',
            '*****@*****.**')
    q.dbus_return(e.message, conn.bus_name, conn.object_path, signature='so')

    q.expect('dbus-method-call', method='Connect',
            path=conn.object_path, handled=True)
    conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED)

    # A channel appears spontaneously

    announcement_immutable = dbus.Dictionary(text_fixed_properties)
    announcement_immutable[cs.CHANNEL + '.TargetID'] = '*****@*****.**'
    announcement_immutable[cs.CHANNEL + '.TargetHandle'] = \
            conn.ensure_handle(cs.HT_CONTACT, '*****@*****.**')
    announcement_immutable[cs.CHANNEL + '.InitiatorHandle'] = \
        announcement_immutable[cs.CHANNEL + '.TargetHandle']
    announcement_immutable[cs.CHANNEL + '.InitiatorID'] = \
        announcement_immutable[cs.CHANNEL + '.TargetID']
    announcement_immutable[cs.CHANNEL + '.Interfaces'] = \
            dbus.Array([], signature='s')
    announcement_immutable[cs.CHANNEL + '.Requested'] = False
    announcement = SimulatedChannel(conn, announcement_immutable)
    announcement.announce()

    # Now the Client returns its info
    q.dbus_return(get_interfaces.message,
            dbus.Array([cs.HANDLER, cs.OBSERVER, cs.APPROVER,
                cs.CLIENT_IFACE_REQUESTS], signature='s'), signature='v')

    expect_client_setup(q, [client], got_interfaces_already=True)

    # Now that the dispatcher is ready to go, we start looking for channels,
    # and also make the actual request
    # Empathy observes the channel we originally requested.
    _, a, cm_request_call = q.expect_many(
            EventPattern('dbus-method-call',
                interface=cs.PROPERTIES_IFACE, method='GetAll',
                args=[cs.CONN_IFACE_REQUESTS],
                path=conn.object_path, handled=True),
            EventPattern('dbus-method-call',
                path=client.object_path,
                interface=cs.OBSERVER, method='ObserveChannels',
                handled=False),
            EventPattern('dbus-method-call',
                interface=cs.CONN_IFACE_REQUESTS, method='CreateChannel',
                path=conn.object_path, args=[request], handled=False),
            )

    assert a.args[0] == account.object_path, a.args
    assert a.args[1] == conn.object_path, a.args
    assert a.args[3] != '/', a.args         # there is a dispatch operation
    assert a.args[4] == [], a.args
    channels = a.args[2]
    assert len(channels) == 1, channels
    assert channels[0][0] == announcement.object_path, channels
    assert channels[0][1] == announcement_immutable, channels

    # Time passes. A channel is returned.

    channel_immutable = dbus.Dictionary(request)
    channel_immutable[cs.CHANNEL + '.InitiatorID'] = conn.self_ident
    channel_immutable[cs.CHANNEL + '.InitiatorHandle'] = conn.self_handle
    channel_immutable[cs.CHANNEL + '.Requested'] = True
    channel_immutable[cs.CHANNEL + '.Interfaces'] = \
        dbus.Array([], signature='s')
    channel_immutable[cs.CHANNEL + '.TargetHandle'] = \
        conn.ensure_handle(cs.HT_CONTACT, '*****@*****.**')
    channel = SimulatedChannel(conn, channel_immutable)

    q.dbus_return(cm_request_call.message,
            channel.object_path, channel.immutable, signature='oa{sv}')
    channel.announce()

    # Empathy observes the newly-created channel.
    e = q.expect('dbus-method-call',
            path=client.object_path,
            interface=cs.OBSERVER, method='ObserveChannels',
            handled=False)

    assert e.args[0] == account.object_path, e.args
    assert e.args[1] == conn.object_path, e.args
    assert e.args[3] == '/', e.args         # no dispatch operation
    assert e.args[4] == [request_path], e.args
    channels = e.args[2]
    assert len(channels) == 1, channels
    assert channels[0][0] == channel.object_path, channels
    assert channels[0][1] == channel_immutable, channels

    # Observer says "OK, go"
    q.dbus_return(a.message, signature='')
    q.dbus_return(e.message, signature='')

    # Empathy is asked to handle the channel
    e = q.expect('dbus-method-call',
            path=client.object_path,
            interface=cs.HANDLER, method='HandleChannels',
            handled=False)
    assert e.args[0] == account.object_path, e.args
    assert e.args[1] == conn.object_path, e.args
    channels = e.args[2]
    assert len(channels) == 1, channels
    assert channels[0][0] == channel.object_path, channels
    assert channels[0][1] == channel_immutable, channels
    assert e.args[3] == [request_path], e.args
    assert e.args[4] == user_action_time
    assert isinstance(e.args[5], dict)
    assert len(e.args) == 6
コード例 #4
0
def test(q, bus, unused, **kwargs):
    simulated_cm = SimulatedConnectionManager(q, bus)

    # make sure RequestConnection doesn't get called yet
    events = [EventPattern('dbus-method-call', method='RequestConnection')]
    q.forbid_events(events)

    fake_accounts_service = kwargs['fake_accounts_service']
    preseed(q, bus, fake_accounts_service)

    # Wait for MC to load
    mc = MC(q, bus)

    # Trying to make a channel on account 1 doesn't work, because it's
    # not valid

    account_path = (cs.tp_path_prefix + '/Account/' + account1_id)

    cd = bus.get_object(cs.CD, cs.CD_PATH)

    user_action_time = dbus.Int64(1238582606)
    request = dbus.Dictionary(
        {
            cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_TEXT,
            cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT,
            cs.CHANNEL + '.TargetID': 'juliet',
        },
        signature='sv')
    call_async(q,
               cd,
               'CreateChannel',
               account_path,
               request,
               user_action_time,
               "",
               dbus_interface=cs.CD)
    ret = q.expect('dbus-return', method='CreateChannel')
    request_path = ret.value[0]

    cr = bus.get_object(cs.CD, request_path)
    request_props = cr.GetAll(cs.CR, dbus_interface=cs.PROPERTIES_IFACE)
    assert request_props['Account'] == account_path
    assert request_props['Requests'] == [request]
    assert request_props['UserActionTime'] == user_action_time
    assert request_props['PreferredHandler'] == ""
    assert request_props['Interfaces'] == []

    sync_dbus(bus, q, mc)

    cr.Proceed(dbus_interface=cs.CR)

    # FIXME: error isn't specified (NotAvailable perhaps?)
    q.expect('dbus-signal',
             path=cr.object_path,
             interface=cs.CR,
             signal='Failed')

    # Make account 1 valid: it should connect automatically

    account_path = (cs.tp_path_prefix + '/Account/' + account1_id)
    account = bus.get_object(cs.MC, account_path)

    sync_dbus(bus, q, mc)
    q.unforbid_events(events)

    call_async(q,
               account,
               'UpdateParameters', {'password': '******'}, [],
               dbus_interface=cs.ACCOUNT)

    expected_params = {
        'password': '******',
        'account': '*****@*****.**'
    }

    e = q.expect('dbus-method-call',
                 method='RequestConnection',
                 args=['fakeprotocol', expected_params],
                 destination=cs.tp_name_prefix + '.ConnectionManager.fakecm',
                 path=cs.tp_path_prefix + '/ConnectionManager/fakecm',
                 interface=cs.tp_name_prefix + '.ConnectionManager',
                 handled=False)

    conn = SimulatedConnection(q,
                               bus,
                               'fakecm',
                               'fakeprotocol',
                               'account1',
                               'myself',
                               has_presence=True)

    q.dbus_return(e.message, conn.bus_name, conn.object_path, signature='so')

    q.expect('dbus-method-call',
             method='Connect',
             path=conn.object_path,
             handled=True,
             interface=cs.CONN)
    conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED)

    set_presence, e = q.expect_many(
        EventPattern('dbus-method-call',
                     path=conn.object_path,
                     interface=cs.CONN_IFACE_SIMPLE_PRESENCE,
                     method='SetPresence',
                     handled=True),
        EventPattern('dbus-signal',
                     signal='AccountPropertyChanged',
                     path=account_path,
                     interface=cs.ACCOUNT,
                     predicate=lambda e: 'CurrentPresence' in e.args[0] and e.
                     args[0]['CurrentPresence'][2] != ''),
    )

    assert e.args[0]['CurrentPresence'] == (cs.PRESENCE_AVAILABLE, 'available',
                                            'My vision is augmented')

    # Request an online presence on account 2, then make it valid

    q.forbid_events(events)

    account_path = (cs.tp_path_prefix + '/Account/' + account2_id)
    account = bus.get_object(cs.MC, account_path)

    requested_presence = dbus.Struct(
        (dbus.UInt32(cs.PRESENCE_BUSY), 'busy', 'Talking to Illuminati'))
    account.Set(cs.ACCOUNT,
                'RequestedPresence',
                dbus.Struct(requested_presence, variant_level=1),
                dbus_interface=cs.PROPERTIES_IFACE)

    sync_dbus(bus, q, mc)
    q.unforbid_events(events)

    # Make the account valid
    call_async(q,
               account,
               'UpdateParameters', {'password': '******'}, [],
               dbus_interface=cs.ACCOUNT)

    expected_params = {
        'password': '******',
        'account': '*****@*****.**'
    }

    e = q.expect('dbus-method-call',
                 method='RequestConnection',
                 args=['fakeprotocol', expected_params],
                 destination=cs.tp_name_prefix + '.ConnectionManager.fakecm',
                 path=cs.tp_path_prefix + '/ConnectionManager/fakecm',
                 interface=cs.tp_name_prefix + '.ConnectionManager',
                 handled=False)

    conn = SimulatedConnection(q,
                               bus,
                               'fakecm',
                               'fakeprotocol',
                               'account2',
                               'myself',
                               has_presence=True)

    q.dbus_return(e.message, conn.bus_name, conn.object_path, signature='so')

    q.expect('dbus-method-call',
             method='Connect',
             path=conn.object_path,
             handled=True,
             interface=cs.CONN)
    conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED)

    set_presence = q.expect('dbus-method-call',
                            path=conn.object_path,
                            interface=cs.CONN_IFACE_SIMPLE_PRESENCE,
                            method='SetPresence',
                            handled=True)

    e = q.expect('dbus-signal',
                 signal='AccountPropertyChanged',
                 path=account_path,
                 interface=cs.ACCOUNT,
                 predicate=lambda e: 'CurrentPresence' in e.args[0] and e.args[
                     0]['CurrentPresence'][1] == 'busy')

    assert e.args[0]['CurrentPresence'] == (cs.PRESENCE_BUSY, 'busy',
                                            'Talking to Illuminati')
コード例 #5
0
def test(q, bus, unused, **kwargs):
    fake_accounts_service = kwargs['fake_accounts_service']
    preseed(q, bus, fake_accounts_service)

    text_fixed_properties = dbus.Dictionary(
        {
            cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT,
            cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_TEXT,
        },
        signature='sv')

    conn = SimulatedConnection(q, bus, 'fakecm', 'fakeprotocol', 'jc',
                               '*****@*****.**')
    conn.StatusChanged(cs.CONN_STATUS_CONNECTED, 0)

    unhandled_properties = dbus.Dictionary(text_fixed_properties,
                                           signature='sv')
    unhandled_properties[cs.CHANNEL +
                         '.Interfaces'] = dbus.Array(signature='s')
    unhandled_properties[cs.CHANNEL + '.TargetID'] = '*****@*****.**'
    unhandled_properties[cs.CHANNEL + '.TargetHandle'] = \
            dbus.UInt32(conn.ensure_handle(cs.HT_CONTACT, '*****@*****.**'))
    unhandled_properties[cs.CHANNEL + '.InitiatorHandle'] = dbus.UInt32(
        conn.self_handle)
    unhandled_properties[cs.CHANNEL + '.InitiatorID'] = conn.self_ident
    unhandled_properties[cs.CHANNEL + '.Requested'] = True
    unhandled_chan = SimulatedChannel(conn, unhandled_properties)
    unhandled_chan.announce()

    handled_properties = dbus.Dictionary(text_fixed_properties, signature='sv')
    handled_properties[cs.CHANNEL + '.Interfaces'] = dbus.Array(signature='s')
    handled_properties[cs.CHANNEL + '.TargetID'] = '*****@*****.**'
    handled_properties[cs.CHANNEL + '.TargetHandle'] = \
            dbus.UInt32(conn.ensure_handle(cs.HT_CONTACT, '*****@*****.**'))
    handled_properties[cs.CHANNEL + '.InitiatorHandle'] = dbus.UInt32(
        conn.self_handle)
    handled_properties[cs.CHANNEL + '.InitiatorID'] = conn.self_ident
    handled_properties[cs.CHANNEL + '.Requested'] = True
    handled_chan = SimulatedChannel(conn, handled_properties)
    handled_chan.announce()

    client = SimulatedClient(q,
                             bus,
                             'Empathy',
                             observe=[text_fixed_properties],
                             approve=[text_fixed_properties],
                             handle=[text_fixed_properties],
                             bypass_approval=False)
    client.handled_channels.append(handled_chan.object_path)

    # Service-activate MC.
    # We're told about the other channel as an observer...
    mc = MC(q, bus, wait_for_names=False)
    e, = mc.wait_for_names(
        EventPattern('dbus-method-call',
                     path=client.object_path,
                     interface=cs.OBSERVER,
                     method='ObserveChannels',
                     handled=False), )

    assert e.args[1] == conn.object_path, e.args
    channels = e.args[2]
    assert channels[0][0] == unhandled_chan.object_path, channels
    q.dbus_return(e.message, signature='')

    # ... and as a handler
    e = q.expect('dbus-method-call',
                 path=client.object_path,
                 interface=cs.HANDLER,
                 method='HandleChannels',
                 handled=False)
    assert e.args[1] == conn.object_path, e.args
    channels = e.args[2]
    assert channels[0][0] == unhandled_chan.object_path, channels
    q.dbus_return(e.message, signature='')
コード例 #6
0
def test(q, bus, unused, **kwargs):
    simulated_cm = SimulatedConnectionManager(q, bus)

    fake_accounts_service = kwargs['fake_accounts_service']
    preseed(q, bus, fake_accounts_service)

    expected_params = {
        'account': '*****@*****.**',
        'password': r'\\ionstorm\\',
    }

    mc = MC(q, bus, wait_for_names=False)
    mc.wait_for_names(
        # Migration step: the three separate attributes get combined
        # (before the names are taken, so we need to expect it here)
        EventPattern(
            'dbus-method-call',
            interface=cs.TEST_DBUS_ACCOUNT_SERVICE_IFACE,
            method='UpdateAttributes',
            predicate=(
                lambda e: e.args[0] == account_id and e.args[1] == {
                    'AutomaticPresence':
                    (2, 'available', 'My vision is augmented')
                } and e.args[2] == {
                    'AutomaticPresence': 0
                } and  # flags
                set(e.args[3]) == set([  # no particular order
                    'AutomaticPresenceType',
                    'AutomaticPresenceStatus',
                    'AutomaticPresenceMessage',
                ]))))

    request_conn, prop_changed = q.expect_many(
        EventPattern('dbus-method-call',
                     method='RequestConnection',
                     args=['fakeprotocol', expected_params],
                     destination=cs.tp_name_prefix +
                     '.ConnectionManager.fakecm',
                     path=cs.tp_path_prefix + '/ConnectionManager/fakecm',
                     interface=cs.tp_name_prefix + '.ConnectionManager',
                     handled=False),
        EventPattern('dbus-signal',
                     signal='AccountPropertyChanged',
                     predicate=(lambda e: 'ConnectionStatus' in e.args[0])),
    )

    conn = SimulatedConnection(q,
                               bus,
                               'fakecm',
                               'fakeprotocol',
                               '_',
                               'myself',
                               has_presence=True,
                               has_aliasing=True,
                               has_avatars=True)

    assertEquals('/', prop_changed.args[0].get('Connection'))
    assertEquals('', prop_changed.args[0].get('ConnectionError'))
    assertEquals({}, prop_changed.args[0].get('ConnectionErrorDetails'))
    assertEquals(cs.CONN_STATUS_CONNECTING,
                 prop_changed.args[0].get('ConnectionStatus'))
    assertEquals(cs.CSR_REQUESTED,
                 prop_changed.args[0].get('ConnectionStatusReason'))

    q.dbus_return(request_conn.message,
                  conn.bus_name,
                  conn.object_path,
                  signature='so')

    account_path = (cs.tp_path_prefix + '/Account/' + account_id)
    account = bus.get_object(cs.tp_name_prefix + '.AccountManager',
                             account_path)

    prop_changed, _ = q.expect_many(
        EventPattern('dbus-signal',
                     signal='AccountPropertyChanged',
                     predicate=(lambda e: 'ConnectionStatus' in e.args[0])),
        EventPattern('dbus-method-call',
                     method='Connect',
                     path=conn.object_path,
                     handled=True,
                     interface=cs.CONN),
    )

    assertEquals(conn.object_path, prop_changed.args[0].get('Connection'))
    assertEquals('', prop_changed.args[0].get('ConnectionError'))
    assertEquals({}, prop_changed.args[0].get('ConnectionErrorDetails'))
    assertEquals(cs.CONN_STATUS_CONNECTING,
                 prop_changed.args[0].get('ConnectionStatus'))
    assertEquals(cs.CSR_REQUESTED,
                 prop_changed.args[0].get('ConnectionStatusReason'))

    props = account.GetAll(cs.ACCOUNT, dbus_interface=cs.PROPERTIES_IFACE)
    assert props['Connection'] == conn.object_path
    assert props['ConnectionStatus'] == cs.CONN_STATUS_CONNECTING
    assert props['ConnectionStatusReason'] == cs.CSR_REQUESTED

    print "becoming connected"
    conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED)

    set_aliases, set_presence, set_avatar, prop_changed = q.expect_many(
        EventPattern('dbus-method-call',
                     interface=cs.CONN_IFACE_ALIASING,
                     method='SetAliases',
                     args=[{
                         conn.self_handle: 'JC'
                     }],
                     handled=False),
        EventPattern('dbus-method-call',
                     path=conn.object_path,
                     interface=cs.CONN_IFACE_SIMPLE_PRESENCE,
                     method='SetPresence',
                     handled=True),
        EventPattern('dbus-method-call',
                     interface=cs.CONN_IFACE_AVATARS,
                     method='SetAvatar',
                     args=['Deus Ex', 'image/jpeg'],
                     handled=True),
        EventPattern(
            'dbus-signal',
            signal='AccountPropertyChanged',
            path=account_path,
            interface=cs.ACCOUNT,
            predicate=(lambda e: e.args[0].get('ConnectionStatus') == cs.
                       CONN_STATUS_CONNECTED),
        ),
    )

    assertEquals(conn.object_path, prop_changed.args[0].get('Connection'))
    assertEquals('', prop_changed.args[0].get('ConnectionError'))
    assertEquals({}, prop_changed.args[0].get('ConnectionErrorDetails'))
    assertEquals(cs.CONN_STATUS_CONNECTED,
                 prop_changed.args[0].get('ConnectionStatus'))
    assertEquals(cs.CSR_REQUESTED,
                 prop_changed.args[0].get('ConnectionStatusReason'))

    assert account.Get(
        cs.ACCOUNT, 'CurrentPresence',
        dbus_interface=cs.PROPERTIES_IFACE) == (cs.PRESENCE_AVAILABLE,
                                                'available',
                                                'My vision is augmented')

    q.dbus_return(set_aliases.message, signature='')
def test(q, bus, unused, **kwargs):
    simulated_cm = SimulatedConnectionManager(q, bus)

    fake_accounts_service = kwargs['fake_accounts_service']
    preseed(q, bus, fake_accounts_service)

    expected_params = {
            'account': '*****@*****.**',
            'password': r'\\ionstorm\\',
            }

    mc = MC(q, bus, wait_for_names=False)
    mc.wait_for_names(
            # Migration step: the three separate attributes get combined
            # (before the names are taken, so we need to expect it here)
            EventPattern('dbus-method-call',
                interface=cs.TEST_DBUS_ACCOUNT_SERVICE_IFACE,
                method='UpdateAttributes',
                predicate=(lambda e:
                    e.args[0] == account_id and
                    e.args[1] == {'AutomaticPresence':
                        (2, 'available', 'My vision is augmented')} and
                    e.args[2] == {'AutomaticPresence': 0} and   # flags
                    set(e.args[3]) == set([     # no particular order
                        'AutomaticPresenceType',
                        'AutomaticPresenceStatus',
                        'AutomaticPresenceMessage',
                        ])))
            )

    request_conn, prop_changed = q.expect_many(
            EventPattern('dbus-method-call', method='RequestConnection',
                args=['fakeprotocol', expected_params],
                destination=cs.tp_name_prefix + '.ConnectionManager.fakecm',
                path=cs.tp_path_prefix + '/ConnectionManager/fakecm',
                interface=cs.tp_name_prefix + '.ConnectionManager',
                handled=False),
            EventPattern('dbus-signal', signal='AccountPropertyChanged',
                predicate=(lambda e: 'ConnectionStatus' in e.args[0])),
            )

    conn = SimulatedConnection(q, bus, 'fakecm', 'fakeprotocol', '_',
            'myself', has_presence=True, has_aliasing=True, has_avatars=True)

    assertEquals('/', prop_changed.args[0].get('Connection'))
    assertEquals('', prop_changed.args[0].get('ConnectionError'))
    assertEquals({}, prop_changed.args[0].get('ConnectionErrorDetails'))
    assertEquals(cs.CONN_STATUS_CONNECTING,
        prop_changed.args[0].get('ConnectionStatus'))
    assertEquals(cs.CSR_REQUESTED,
        prop_changed.args[0].get('ConnectionStatusReason'))

    q.dbus_return(request_conn.message, conn.bus_name, conn.object_path,
        signature='so')

    account_path = (cs.tp_path_prefix + '/Account/' + account_id)
    account = bus.get_object(
        cs.tp_name_prefix + '.AccountManager',
        account_path)

    prop_changed, _ = q.expect_many(
        EventPattern('dbus-signal', signal='AccountPropertyChanged',
            predicate=(lambda e: 'ConnectionStatus' in e.args[0])),
        EventPattern('dbus-method-call', method='Connect',
            path=conn.object_path, handled=True, interface=cs.CONN),
        )

    assertEquals(conn.object_path, prop_changed.args[0].get('Connection'))
    assertEquals('', prop_changed.args[0].get('ConnectionError'))
    assertEquals({}, prop_changed.args[0].get('ConnectionErrorDetails'))
    assertEquals(cs.CONN_STATUS_CONNECTING,
        prop_changed.args[0].get('ConnectionStatus'))
    assertEquals(cs.CSR_REQUESTED,
        prop_changed.args[0].get('ConnectionStatusReason'))

    props = account.GetAll(cs.ACCOUNT, dbus_interface=cs.PROPERTIES_IFACE)
    assert props['Connection'] == conn.object_path
    assert props['ConnectionStatus'] == cs.CONN_STATUS_CONNECTING
    assert props['ConnectionStatusReason'] == cs.CSR_REQUESTED

    print "becoming connected"
    conn.StatusChanged(cs.CONN_STATUS_CONNECTED, cs.CSR_NONE_SPECIFIED)

    set_aliases, set_presence, set_avatar, prop_changed = q.expect_many(
            EventPattern('dbus-method-call',
                interface=cs.CONN_IFACE_ALIASING, method='SetAliases',
                args=[{ conn.self_handle: 'JC' }],
                handled=False),
            EventPattern('dbus-method-call', path=conn.object_path,
                interface=cs.CONN_IFACE_SIMPLE_PRESENCE, method='SetPresence',
                handled=True),
            EventPattern('dbus-method-call',
                interface=cs.CONN_IFACE_AVATARS, method='SetAvatar',
                args=['Deus Ex', 'image/jpeg'],
                handled=True),
            EventPattern('dbus-signal', signal='AccountPropertyChanged',
                path=account_path, interface=cs.ACCOUNT,
                predicate=(lambda e:
                    e.args[0].get('ConnectionStatus') ==
                        cs.CONN_STATUS_CONNECTED),
                ),
            )

    assertEquals(conn.object_path, prop_changed.args[0].get('Connection'))
    assertEquals('', prop_changed.args[0].get('ConnectionError'))
    assertEquals({}, prop_changed.args[0].get('ConnectionErrorDetails'))
    assertEquals(cs.CONN_STATUS_CONNECTED,
        prop_changed.args[0].get('ConnectionStatus'))
    assertEquals(cs.CSR_REQUESTED,
        prop_changed.args[0].get('ConnectionStatusReason'))

    assert account.Get(cs.ACCOUNT, 'CurrentPresence',
            dbus_interface=cs.PROPERTIES_IFACE) == (cs.PRESENCE_AVAILABLE,
            'available', 'My vision is augmented')

    q.dbus_return(set_aliases.message, signature='')
コード例 #8
0
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': <'*****@*****.**'>,
    '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': '*****@*****.**',
    'password': '******',
    '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': '*****@*****.**',
    'password': '******',
    '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': '*****@*****.**',
    'password': '******',
    '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': '*****@*****.**',
    'password': '******',
    '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': '*****@*****.**',
    'password': '******',
    '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() == ''

    # Because the CM exists, we can work out the correct types
    # for the 'migration' account's parameters. This triggers a commit
    # even though nothing has conceptually changed, so we have the type
    # for later. The file is copied, not moved, because XDG_DATA_DIRS are,
    # conceptually, read-only.
    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(
        "'password_in_variant_file'",
        account_store('get',
                      'variant-file',
                      'param-password',
                      account=tails['migration']))
    assertEquals(
        "uint32 42",
        account_store('get',
                      'variant-file',
                      'param-snakes',
                      account=tails['migration']))

    # Setting the password still does the right thing.
    account_ifaces['migration'].UpdateParameters({'password': '******'}, [])
    q.expect(
        'dbus-signal',
        path=account_paths['migration'],
        signal='AccountPropertyChanged',
        interface=cs.ACCOUNT,
        predicate=(lambda e: 'Parameters' in e.args[0]),
    )
    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']))

    # 'absentcm' is still only in the low-priority location: we can't
    # known the types of its parameters, so it doesn't get migrated.
    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'])
コード例 #9
0
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
[email protected]
param-password=1
DisplayName=First among equals
AutomaticPresence=2;available;;
AvatarMime=text/plain
avatar_token=hello, world

[%s]
manager=fakecm
protocol=fakeprotocol
[email protected]
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))
    # Because the CM is installed, we can work out the right types
    # for the parameters, too.
    assertEquals(
        "'*****@*****.**'",
        account_store('get', 'variant-file', 'param-account', account=a1_tail))
    assertEquals(
        "'*****@*****.**'",
        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('*****@*****.**',
                 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('*****@*****.**',
                 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'])