コード例 #1
0
def test_channel_creation(q,
                          bus,
                          account,
                          client,
                          conn,
                          ensure=False,
                          prefer=None,
                          channel_type=cs.CHANNEL_TYPE_TEXT):
    user_action_time = dbus.Int64(1238582606)

    if prefer is None:
        prefer = client

    cd = bus.get_object(cs.CD, cs.CD_PATH)
    cd_props = dbus.Interface(cd, cs.PROPERTIES_IFACE)

    # chat UI calls ChannelDispatcher.EnsureChannel or CreateChannel
    request = dbus.Dictionary(
        {
            cs.CHANNEL + '.ChannelType': channel_type,
            cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT,
            cs.CHANNEL + '.TargetID': 'juliet',
        },
        signature='sv')
    call_async(q,
               cd, (ensure and 'EnsureChannel' or 'CreateChannel'),
               account.object_path,
               request,
               user_action_time,
               prefer.bus_name,
               dbus_interface=cs.CD)
    ret = q.expect('dbus-return',
                   method=(ensure and 'EnsureChannel' or 'CreateChannel'))
    request_path = ret.value[0]

    # chat UI connects to signals and calls ChannelRequest.Proceed()

    cr = bus.get_object(cs.AM, 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'] == prefer.bus_name
    assert request_props['Interfaces'] == []

    cr.Proceed(dbus_interface=cs.CR)

    # FIXME: should the EnsureChannel/CreateChannel call, and the AddRequest
    # call, be in a defined order? Probably not though, since CMs and Clients
    # aren't meant to be the same process!

    cm_request_call, add_request_call = q.expect_many(
        EventPattern('dbus-method-call',
                     interface=cs.CONN_IFACE_REQUESTS,
                     method=(ensure and 'EnsureChannel' or 'CreateChannel'),
                     path=conn.object_path,
                     args=[request],
                     handled=False),
        EventPattern('dbus-method-call',
                     handled=False,
                     interface=cs.CLIENT_IFACE_REQUESTS,
                     method='AddRequest'),
    )

    assert add_request_call.args[0] == request_path
    assert add_request_call.path == prefer.object_path
    request_props = add_request_call.args[1]
    assert request_props[cs.CR + '.Account'] == account.object_path
    assert request_props[cs.CR + '.Requests'] == [request]
    assert request_props[cs.CR + '.UserActionTime'] == user_action_time
    assert request_props[cs.CR + '.PreferredHandler'] == prefer.bus_name
    assert request_props[cs.CR + '.Interfaces'] == []

    q.dbus_return(add_request_call.message, signature='')

    # 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, 'juliet')
    channel = SimulatedChannel(conn, channel_immutable)

    # this order of events is guaranteed by telepathy-spec (since 0.17.14)
    if ensure:
        q.dbus_return(
            cm_request_call.message,
            True,  # <- Yours
            channel.object_path,
            channel.immutable,
            signature='boa{sv}')
    else:  # Create
        q.dbus_return(cm_request_call.message,
                      channel.object_path,
                      channel.immutable,
                      signature='oa{sv}')
    channel.announce()

    if channel_type == cs.CHANNEL_TYPE_TEXT:
        # Observer should get told, processing waits for it
        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(e.message, signature='')

    # Handler is next
    e = q.expect('dbus-method-call',
                 path=prefer.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)
    assertContains('request-properties', e.args[5])
    assertContains(request_path, e.args[5]['request-properties'])
    assertLength(1, e.args[5]['request-properties'])
    assertEquals(request_props, e.args[5]['request-properties'][request_path])
    assert len(e.args) == 6

    # Handler accepts the Channels
    q.dbus_return(e.message, signature='')

    # SucceededWithChannel is fired first
    e = q.expect('dbus-signal',
                 path=request_path,
                 interface=cs.CR,
                 signal='SucceededWithChannel')

    assertEquals(conn.object_path, e.args[0])
    assert isinstance(e.args[1], dict), e.args[1]
    assertEquals(channel.object_path, e.args[2])
    assertEquals(channel_immutable, e.args[3])

    # CR emits Succeeded
    q.expect('dbus-signal',
             path=request_path,
             interface=cs.CR,
             signal='Succeeded')

    # Close the channel
    channel.close()
コード例 #2
0
def test(q, bus, mc):
    params = dbus.Dictionary({"account": "*****@*****.**",
        "password": "******"}, signature='sv')
    simulated_cm, account = create_fakecm_account(q, bus, mc, params)
    conn = enable_fakecm_account(q, bus, mc, account, params)

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

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

    misc_fixed_properties = dbus.Dictionary({
        cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT,
        cs.CHANNEL + '.ChannelType': 'com.example.Extension',
        }, signature='sv')

    # Two clients want to observe, approve and handle channels. Empathy handles
    # VoIP, Kopete does not.
    empathy = SimulatedClient(q, bus, 'org.gnome.Empathy',
            observe=[text_fixed_properties, media_fixed_properties],
            approve=[text_fixed_properties, media_fixed_properties],
            handle=[text_fixed_properties, media_fixed_properties],
            bypass_approval=False)

    kopete = SimulatedClient(q, bus, 'org.kde.Kopete',
            observe=[text_fixed_properties], approve=[text_fixed_properties],
            handle=[text_fixed_properties], bypass_approval=False)

    # wait for MC to download the properties
    expect_client_setup(q, [empathy, kopete])

    # subscribe to the OperationList interface (MC assumes that until this
    # property has been retrieved once, nobody cares)

    cd = bus.get_object(cs.CD, cs.CD_PATH)
    cd_props = dbus.Interface(cd, cs.PROPERTIES_IFACE)
    assert cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations') == []

    # Part 1. A bundle that Empathy, but not Kopete, can handle

    text_channel_properties = dbus.Dictionary(text_fixed_properties,
            signature='sv')
    text_channel_properties[cs.CHANNEL + '.TargetID'] = 'juliet'
    text_channel_properties[cs.CHANNEL + '.TargetHandle'] = \
            conn.ensure_handle(cs.HT_CONTACT, 'juliet')
    text_channel_properties[cs.CHANNEL + '.InitiatorID'] = 'juliet'
    text_channel_properties[cs.CHANNEL + '.InitiatorHandle'] = \
            conn.ensure_handle(cs.HT_CONTACT, 'juliet')
    text_channel_properties[cs.CHANNEL + '.Requested'] = False
    text_channel_properties[cs.CHANNEL + '.Interfaces'] = dbus.Array(
            [cs.CHANNEL_IFACE_DESTROYABLE], signature='s')

    text_chan = SimulatedChannel(conn, text_channel_properties,
            destroyable=True)

    media_channel_properties = dbus.Dictionary(media_fixed_properties,
            signature='sv')
    media_channel_properties[cs.CHANNEL + '.TargetID'] = 'juliet'
    media_channel_properties[cs.CHANNEL + '.TargetHandle'] = \
            conn.ensure_handle(cs.HT_CONTACT, 'juliet')
    media_channel_properties[cs.CHANNEL + '.InitiatorID'] = 'juliet'
    media_channel_properties[cs.CHANNEL + '.InitiatorHandle'] = \
            conn.ensure_handle(cs.HT_CONTACT, 'juliet')
    media_channel_properties[cs.CHANNEL + '.Requested'] = False
    media_channel_properties[cs.CHANNEL + '.Interfaces'] = dbus.Array(
            signature='s')

    media_chan = SimulatedChannel(conn, media_channel_properties,
            destroyable=False)

    conn.NewChannels([text_chan, media_chan])

    # A channel dispatch operation is created for the Text channel first.

    e = q.expect('dbus-signal',
            path=cs.CD_PATH,
            interface=cs.CD_IFACE_OP_LIST,
            signal='NewDispatchOperation')

    text_cdo_path = e.args[0]
    text_cdo_properties = e.args[1]

    assert text_cdo_properties[cs.CDO + '.Account'] == account.object_path
    assert text_cdo_properties[cs.CDO + '.Connection'] == conn.object_path

    handlers = text_cdo_properties[cs.CDO + '.PossibleHandlers'][:]
    assert (sorted(handlers) ==
        [cs.tp_name_prefix + '.Client.org.gnome.Empathy',
            cs.tp_name_prefix + '.Client.org.kde.Kopete']), handlers

    text_cdo = bus.get_object(cs.CD, text_cdo_path)
    text_cdo_iface = dbus.Interface(text_cdo, cs.CDO)

    # Both Observers are told about the new Text channel

    e_observe_text, k_observe_text = q.expect_many(
            EventPattern('dbus-method-call',
                path=empathy.object_path,
                interface=cs.OBSERVER, method='ObserveChannels',
                handled=False),
            EventPattern('dbus-method-call',
                path=kopete.object_path,
                interface=cs.OBSERVER, method='ObserveChannels',
                handled=False),
            )
    assert e_observe_text.args[0] == account.object_path, e_observe_text.args
    assert e_observe_text.args[1] == conn.object_path, e_observe_text.args
    assert e_observe_text.args[3] == text_cdo_path, e_observe_text.args
    assert e_observe_text.args[4] == [], e_observe_text.args
    channels = e_observe_text.args[2]
    assert len(channels) == 1, channels
    assert (text_chan.object_path, text_channel_properties) in channels

    assert k_observe_text.args[0] == e_observe_text.args[0], k_observe_text.args
    assert k_observe_text.args[1] == e_observe_text.args[1], k_observe_text.args
    assert (k_observe_text.args[2] ==
            [(text_chan.object_path, text_channel_properties)])

    # Now a separate CDO is created for the media channel.

    e = q.expect('dbus-signal',
            path=cs.CD_PATH,
            interface=cs.CD_IFACE_OP_LIST,
            signal='NewDispatchOperation')

    media_cdo_path = e.args[0]
    media_cdo_properties = e.args[1]

    assert media_cdo_properties[cs.CDO + '.Account'] == account.object_path
    assert media_cdo_properties[cs.CDO + '.Connection'] == conn.object_path

    handlers = media_cdo_properties[cs.CDO + '.PossibleHandlers'][:]
    # only Empathy can handle it
    assert (sorted(handlers) ==
        [cs.tp_name_prefix + '.Client.org.gnome.Empathy']), handlers

    assert cs.CD_IFACE_OP_LIST in cd_props.Get(cs.CD, 'Interfaces')
    assert (sorted(cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations')) ==
            [(text_cdo_path, text_cdo_properties),
                (media_cdo_path, media_cdo_properties)])

    media_cdo = bus.get_object(cs.CD, media_cdo_path)
    media_cdo_iface = dbus.Interface(media_cdo, cs.CDO)

    # Only Empathy is told about the new media channel

    e_observe_media = q.expect('dbus-method-call',
            path=empathy.object_path,
            interface=cs.OBSERVER, method='ObserveChannels',
            handled=False)
    assert e_observe_media.args[0] == account.object_path, e_observe_media.args
    assert e_observe_media.args[1] == conn.object_path, e_observe_media.args
    assert e_observe_media.args[3] == media_cdo_path, e_observe_media.args
    assert e_observe_media.args[4] == [], e_observe_media.args
    channels = e_observe_media.args[2]
    assert len(channels) == 1, channels
    assert (media_chan.object_path, media_channel_properties) in channels

    # All Observers reply.

    q.dbus_return(e_observe_text.message, signature='')
    q.dbus_return(k_observe_text.message, signature='')
    q.dbus_return(e_observe_media.message, signature='')

    # The Approvers are next
    e_approve_text, k_approve_text, e_approve_media = q.expect_many(
            EventPattern('dbus-method-call',
                path=empathy.object_path,
                interface=cs.APPROVER, method='AddDispatchOperation',
                predicate=lambda e: e.args[1] == text_cdo_path,
                handled=False),
            EventPattern('dbus-method-call',
                path=kopete.object_path,
                interface=cs.APPROVER, method='AddDispatchOperation',
                handled=False),
            EventPattern('dbus-method-call',
                path=empathy.object_path,
                interface=cs.APPROVER, method='AddDispatchOperation',
                predicate=lambda e: e.args[1] == media_cdo_path,
                handled=False)
            )
    assert len(e_approve_text.args[0]) == 1
    assert ((text_chan.object_path, text_channel_properties) in
            e_approve_text.args[0])
    assert e_approve_text.args[1:] == [text_cdo_path, text_cdo_properties]
    assert k_approve_text.args == e_approve_text.args

    assert len(e_approve_media.args[0]) == 1
    assert ((media_chan.object_path, media_channel_properties) in
            e_approve_media.args[0])
    assert e_approve_media.args[1:] == [media_cdo_path, media_cdo_properties]

    q.dbus_return(e_approve_text.message, signature='')
    q.dbus_return(k_approve_text.message, signature='')
    q.dbus_return(e_approve_media.message, signature='')

    # Both Approvers now have a flashing icon or something, trying to get the
    # user's attention. The user clicks on Empathy
    call_async(q, text_cdo_iface, 'HandleWith',
        cs.tp_name_prefix + '.Client.org.gnome.Empathy')

    # Empathy is asked to handle the channel
    e = q.expect('dbus-method-call',
            path=empathy.object_path,
            interface=cs.HANDLER, method='HandleChannels',
            handled=False)

    # Empathy accepts the channel
    q.dbus_return(e.message, signature='')

    q.expect_many(
            EventPattern('dbus-return', method='HandleWith'),
            EventPattern('dbus-signal', interface=cs.CDO, signal='Finished'),
            EventPattern('dbus-signal', interface=cs.CD_IFACE_OP_LIST,
                signal='DispatchOperationFinished'),
            )

    # The user doesn't care which client will handle the channel - because
    # Empathy is the only possibility, it will be chosen (this is also a
    # regression test for the ability to leave the handler unspecified).
    call_async(q, media_cdo_iface, 'HandleWith', '')

    # Empathy is asked to handle the channel
    e = q.expect('dbus-method-call',
            path=empathy.object_path,
            interface=cs.HANDLER, method='HandleChannels',
            handled=False)

    # Empathy accepts the channel
    q.dbus_return(e.message, signature='')

    q.expect_many(
            EventPattern('dbus-return', method='HandleWith'),
            EventPattern('dbus-signal', interface=cs.CDO, signal='Finished'),
            EventPattern('dbus-signal', interface=cs.CD_IFACE_OP_LIST,
                signal='DispatchOperationFinished'),
            )

    # Now there are no more active channel dispatch operations
    assert cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations') == []

    text_chan.close()
    media_chan.close()

    # Part 2. A bundle that neither client can handle in its entirety

    respawning_channel_properties = dbus.Dictionary(misc_fixed_properties,
            signature='sv')
    respawning_channel_properties[cs.CHANNEL + '.TargetID'] = 'juliet'
    respawning_channel_properties[cs.CHANNEL + '.TargetHandle'] = \
            conn.ensure_handle(cs.HT_CONTACT, 'juliet')
    respawning_channel_properties[cs.CHANNEL + '.InitiatorID'] = 'juliet'
    respawning_channel_properties[cs.CHANNEL + '.InitiatorHandle'] = \
            conn.ensure_handle(cs.HT_CONTACT, 'juliet')
    respawning_channel_properties[cs.CHANNEL + '.Requested'] = False
    respawning_channel_properties[cs.CHANNEL + '.Interfaces'] = dbus.Array(
            [cs.CHANNEL_IFACE_DESTROYABLE], signature='s')

    ext_channel_properties = dbus.Dictionary(misc_fixed_properties,
            signature='sv')
    ext_channel_properties[cs.CHANNEL + '.TargetID'] = 'juliet'
    ext_channel_properties[cs.CHANNEL + '.TargetHandle'] = \
            conn.ensure_handle(cs.HT_CONTACT, 'juliet')
    ext_channel_properties[cs.CHANNEL + '.InitiatorID'] = 'juliet'
    ext_channel_properties[cs.CHANNEL + '.InitiatorHandle'] = \
            conn.ensure_handle(cs.HT_CONTACT, 'juliet')
    ext_channel_properties[cs.CHANNEL + '.Requested'] = False
    ext_channel_properties[cs.CHANNEL + '.Interfaces'] = dbus.Array(
            signature='s')

    text_chan = SimulatedChannel(conn, text_channel_properties,
            destroyable=True)
    media_chan = SimulatedChannel(conn, media_channel_properties,
            destroyable=False)
    respawning_chan = SimulatedChannel(conn, respawning_channel_properties,
            destroyable=True)
    ext_chan = SimulatedChannel(conn, ext_channel_properties,
            destroyable=False)

    conn.NewChannels([text_chan, media_chan, ext_chan, respawning_chan])

    # No client can handle all four channels, so the bundle explodes into
    # two dispatch operations and two failures. We can only match the first
    # CDO here - we look at the others later.
    e_observe_media, e_observe_text, k_observe_text, \
    e_approve_media, e_approve_text, k_approve_text, \
    _, _, _ = q.expect_many(
            EventPattern('dbus-method-call',
                path=empathy.object_path,
                interface=cs.OBSERVER, method='ObserveChannels',
                predicate=(lambda e: e.args[2][0][0] == media_chan.object_path),
                handled=False),
            EventPattern('dbus-method-call',
                path=empathy.object_path,
                interface=cs.OBSERVER, method='ObserveChannels',
                predicate=(lambda e: e.args[2][0][0] == text_chan.object_path),
                handled=False),
            EventPattern('dbus-method-call',
                path=kopete.object_path,
                interface=cs.OBSERVER, method='ObserveChannels',
                predicate=(lambda e: e.args[2][0][0] == text_chan.object_path),
                handled=False),
            EventPattern('dbus-method-call',
                path=empathy.object_path,
                interface=cs.APPROVER, method='AddDispatchOperation',
                predicate=(lambda e:
                    e.args[0][0][0] ==
                    media_chan.object_path),
                handled=False),
            EventPattern('dbus-method-call',
                path=empathy.object_path,
                interface=cs.APPROVER, method='AddDispatchOperation',
                predicate=(lambda e:
                    e.args[0][0][0] ==
                    text_chan.object_path),
                handled=False),
            EventPattern('dbus-method-call',
                path=kopete.object_path,
                interface=cs.APPROVER, method='AddDispatchOperation',
                predicate=(lambda e:
                    e.args[0][0][0] ==
                    text_chan.object_path),
                handled=False),
            EventPattern('dbus-method-call',
                interface=cs.CHANNEL_IFACE_DESTROYABLE,
                method='Destroy',
                path=respawning_chan.object_path,
                handled=True),
            EventPattern('dbus-method-call',
                interface=cs.CHANNEL,
                method='Close',
                path=ext_chan.object_path,
                handled=True),
            # we can't distinguish between the two NewDispatchOperation signals
            # since we no longer see the Channels property (it's mutable)
            EventPattern('dbus-signal',
                path=cs.CD_PATH,
                interface=cs.CD_IFACE_OP_LIST,
                signal='NewDispatchOperation'),
            )

    q.dbus_return(e_observe_media.message, signature='')
    q.dbus_return(e_observe_text.message, signature='')
    q.dbus_return(k_observe_text.message, signature='')
    q.dbus_return(e_approve_media.message, signature='')
    q.dbus_return(e_approve_text.message, signature='')
    q.dbus_return(k_approve_text.message, signature='')
コード例 #3
0
def test(q, bus, mc):
    params = dbus.Dictionary(
        {
            "account": "*****@*****.**",
            "password": "******"
        },
        signature='sv')
    simulated_cm, account = create_fakecm_account(q, bus, mc, params)
    conn = enable_fakecm_account(q, bus, mc, account, params)

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

    # Throughout this entire test, we should never be asked to approve or
    # handle a channel.
    forbidden = [
        EventPattern('dbus-method-call', method='AddDispatchOperation'),
        EventPattern('dbus-method-call', method='HandleChannels'),
    ]
    q.forbid_events(forbidden)

    # Two clients want to observe, approve and handle channels
    empathy = SimulatedClient(q,
                              bus,
                              'Empathy',
                              observe=[text_fixed_properties],
                              approve=[text_fixed_properties],
                              handle=[text_fixed_properties],
                              bypass_approval=False)
    kopete = SimulatedClient(q,
                             bus,
                             'Kopete',
                             observe=[text_fixed_properties],
                             approve=[text_fixed_properties],
                             handle=[text_fixed_properties],
                             bypass_approval=False)

    # wait for MC to download the properties
    expect_client_setup(q, [empathy, kopete])

    # subscribe to the OperationList interface (MC assumes that until this
    # property has been retrieved once, nobody cares)

    cd = bus.get_object(cs.CD, cs.CD_PATH)
    cd_props = dbus.Interface(cd, cs.PROPERTIES_IFACE)
    assert cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations') == []

    # This ID is special-cased by the mcp-plugin plugin, which rejects
    # channels to or from it by destroying them, without waiting for observers
    # to return
    target = '*****@*****.**'
    channel_properties = dbus.Dictionary(text_fixed_properties, signature='sv')
    channel_properties[cs.CHANNEL + '.TargetID'] = target
    channel_properties[cs.CHANNEL + '.TargetHandle'] = \
            conn.ensure_handle(cs.HT_CONTACT, target)
    channel_properties[cs.CHANNEL + '.InitiatorID'] = target
    channel_properties[cs.CHANNEL + '.InitiatorHandle'] = \
            conn.ensure_handle(cs.HT_CONTACT, target)
    channel_properties[cs.CHANNEL + '.Requested'] = False
    channel_properties[cs.CHANNEL + '.Interfaces'] = \
            dbus.Array([cs.CHANNEL_IFACE_DESTROYABLE, cs.CHANNEL_IFACE_GROUP,
                ],signature='s')

    chan = SimulatedChannel(conn, channel_properties, group=True)
    chan.announce()

    # A channel dispatch operation is created

    e = q.expect('dbus-signal',
                 path=cs.CD_PATH,
                 interface=cs.CD_IFACE_OP_LIST,
                 signal='NewDispatchOperation')

    cdo_path = e.args[0]
    cdo_properties = e.args[1]

    assert cdo_properties[cs.CDO + '.Account'] == account.object_path
    assert cdo_properties[cs.CDO + '.Connection'] == conn.object_path
    assert cs.CDO + '.Interfaces' in cdo_properties

    handlers = cdo_properties[cs.CDO + '.PossibleHandlers'][:]
    handlers.sort()
    assert handlers == [
        cs.tp_name_prefix + '.Client.Empathy',
        cs.tp_name_prefix + '.Client.Kopete'
    ], handlers

    # The plugin realises we've been rickrolled, and responds. It calls Destroy
    # even though neither Empathy nor Kopete has returned from ObserveChannels
    # yet
    destruction, e, k = q.expect_many(
        EventPattern('dbus-method-call',
                     path=chan.object_path,
                     interface=cs.CHANNEL_IFACE_DESTROYABLE,
                     method='Destroy',
                     args=[],
                     handled=False),
        EventPattern('dbus-method-call',
                     path=empathy.object_path,
                     interface=cs.OBSERVER,
                     method='ObserveChannels',
                     handled=False),
        EventPattern('dbus-method-call',
                     path=kopete.object_path,
                     interface=cs.OBSERVER,
                     method='ObserveChannels',
                     handled=False),
    )
    # treat the destruction like Close
    chan.Close(destruction)

    # Both Observers indicate that they are ready to proceed (somewhat late)
    q.dbus_return(k.message, signature='')
    q.dbus_return(e.message, signature='')

    # When the Observers have returned, the CDO finishes
    q.expect_many(
        EventPattern('dbus-signal',
                     path=cdo_path,
                     interface=cs.CDO,
                     signal='Finished'),
        EventPattern('dbus-signal',
                     path=cs.CD_PATH,
                     interface=cs.CD_IFACE_OP_LIST,
                     signal='DispatchOperationFinished',
                     args=[cdo_path]),
    )

    # This ID is also special-cased
    target = '*****@*****.**'
    channel_properties = dbus.Dictionary(text_fixed_properties, signature='sv')
    channel_properties[cs.CHANNEL + '.TargetID'] = target
    channel_properties[cs.CHANNEL + '.TargetHandle'] = \
            conn.ensure_handle(cs.HT_CONTACT, target)
    channel_properties[cs.CHANNEL + '.InitiatorID'] = target
    channel_properties[cs.CHANNEL + '.InitiatorHandle'] = \
            conn.ensure_handle(cs.HT_CONTACT, target)
    channel_properties[cs.CHANNEL + '.Requested'] = False
    channel_properties[cs.CHANNEL + '.Interfaces'] = \
            dbus.Array([cs.CHANNEL_IFACE_DESTROYABLE, cs.CHANNEL_IFACE_GROUP,
                ],signature='s')

    chan = SimulatedChannel(conn, channel_properties, group=True)
    chan.announce()

    # A channel dispatch operation is created

    e = q.expect('dbus-signal',
                 path=cs.CD_PATH,
                 interface=cs.CD_IFACE_OP_LIST,
                 signal='NewDispatchOperation')

    cdo_path = e.args[0]
    cdo_properties = e.args[1]

    assert cdo_properties[cs.CDO + '.Account'] == account.object_path
    assert cdo_properties[cs.CDO + '.Connection'] == conn.object_path
    assert cs.CDO + '.Interfaces' in cdo_properties

    handlers = cdo_properties[cs.CDO + '.PossibleHandlers'][:]
    handlers.sort()
    assert handlers == [
        cs.tp_name_prefix + '.Client.Empathy',
        cs.tp_name_prefix + '.Client.Kopete'
    ], handlers

    # The plugin realises it's MC Hammer, and responds, but its response waits
    # for the observers to return
    e, k = q.expect_many(
        EventPattern('dbus-method-call',
                     path=empathy.object_path,
                     interface=cs.OBSERVER,
                     method='ObserveChannels',
                     handled=False),
        EventPattern('dbus-method-call',
                     path=kopete.object_path,
                     interface=cs.OBSERVER,
                     method='ObserveChannels',
                     handled=False),
    )

    sync_dbus(bus, q, account)

    # Both Observers indicate that they are ready to proceed
    q.dbus_return(k.message, signature='')
    q.dbus_return(e.message, signature='')

    _, _, e = q.expect_many(
        EventPattern('dbus-signal',
                     path=cdo_path,
                     interface=cs.CDO,
                     signal='Finished'),
        EventPattern('dbus-signal',
                     path=cs.CD_PATH,
                     interface=cs.CD_IFACE_OP_LIST,
                     signal='DispatchOperationFinished',
                     args=[cdo_path]),
        EventPattern('dbus-method-call',
                     path=chan.object_path,
                     interface=cs.CHANNEL_IFACE_DESTROYABLE,
                     method='Destroy',
                     handled=False),
    )
    q.dbus_return(e.message, signature='')
    chan.close()
def test_channel_creation(q, bus, account, client, conn, ensure):
    user_action_time = dbus.Int64(1238582606)

    cd = bus.get_object(cs.CD, cs.CD_PATH)
    cd_props = dbus.Interface(cd, cs.PROPERTIES_IFACE)

    # chat UI calls ChannelDispatcher.EnsureChannel or CreateChannel
    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,
            (ensure and 'EnsureChannel' or 'CreateChannel'),
            account.object_path, request, user_action_time, "",
            dbus_interface=cs.CD)
    ret = q.expect('dbus-return',
            method=(ensure and 'EnsureChannel' or 'CreateChannel'))
    request_path = ret.value[0]

    # chat UI connects to signals and calls ChannelRequest.Proceed()

    cr = bus.get_object(cs.AM, 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'] == ""
    assert request_props['Interfaces'] == []

    cr.Proceed(dbus_interface=cs.CR)

    # FIXME: should the EnsureChannel/CreateChannel call, and the AddRequest
    # call, be in a defined order? Probably not though, since CMs and Clients
    # aren't meant to be the same process!

    cm_request_call, add_request_call = q.expect_many(
            EventPattern('dbus-method-call',
                interface=cs.CONN_IFACE_REQUESTS,
                method=(ensure and 'EnsureChannel' or 'CreateChannel'),
                path=conn.object_path, args=[request], handled=False),
            EventPattern('dbus-method-call', handled=False,
                interface=cs.CLIENT_IFACE_REQUESTS,
                method='AddRequest', path=client.object_path),
            )

    assert add_request_call.args[0] == request_path
    request_props = add_request_call.args[1]
    assert request_props[cs.CR + '.Account'] == account.object_path
    assert request_props[cs.CR + '.Requests'] == [request]
    assert request_props[cs.CR + '.UserActionTime'] == user_action_time
    assert request_props[cs.CR + '.PreferredHandler'] == ""
    assert request_props[cs.CR + '.Interfaces'] == []

    q.dbus_return(add_request_call.message, signature='')

    # 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, 'juliet')
    channel = SimulatedChannel(conn, channel_immutable)

    # this order of events is guaranteed by telepathy-spec (since 0.17.14)
    if ensure:
        q.dbus_return(cm_request_call.message, True, # <- Yours
                channel.object_path, channel.immutable, signature='boa{sv}')
    else:   # Create
        q.dbus_return(cm_request_call.message,
                channel.object_path, channel.immutable, signature='oa{sv}')
    channel.announce()

    # Observer should get told, processing waits for it
    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(e.message, signature='')

    # Handler is next
    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

    # Handler accepts the Channels
    q.dbus_return(e.message, signature='')

    # CR emits Succeeded
    q.expect('dbus-signal', path=request_path,
                interface=cs.CR, signal='Succeeded')

    # Close the channel
    channel.close()
def test(q, bus, mc):
    params = dbus.Dictionary({"account": "*****@*****.**",
        "password": "******"}, signature='sv')
    simulated_cm, account = create_fakecm_account(q, bus, mc, params)
    conn = enable_fakecm_account(q, bus, mc, account, params)

    text_fixed_properties = dbus.Dictionary({
        cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
        cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
        }, signature='sv')

    # Logger is a text observer wanting recovery
    logger = SimulatedClient(q, bus, 'LoggerChat',
            observe=[text_fixed_properties],
            bypass_approval=False,
            wants_recovery=True)

    # gnome-shell is a text approver and handler
    gs = SimulatedClient(q, bus, 'GnomeShell',
            approve=[text_fixed_properties],
            handle=[text_fixed_properties],
            bypass_approval=False)

    # wait for MC to download the properties
    expect_client_setup(q, [logger, gs])

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

    # incoming text channel
    channel_properties = dbus.Dictionary(text_fixed_properties,
            signature='sv')
    channel_properties[cs.CHANNEL + '.TargetID'] = 'juliet'
    channel_properties[cs.CHANNEL + '.TargetHandle'] = \
            conn.ensure_handle(cs.HT_CONTACT, 'juliet')
    channel_properties[cs.CHANNEL + '.InitiatorID'] = 'juliet'
    channel_properties[cs.CHANNEL + '.InitiatorHandle'] = \
            conn.ensure_handle(cs.HT_CONTACT, 'juliet')
    channel_properties[cs.CHANNEL + '.Requested'] = False
    channel_properties[cs.CHANNEL + '.Interfaces'] = dbus.Array(signature='s')

    chan = SimulatedChannel(conn, channel_properties)
    chan.announce()

    # gnome-shell's approver and logger's observer are notified
    e, k = q.expect_many(
            EventPattern('dbus-method-call',
                path=logger.object_path,
                interface=cs.OBSERVER, method='ObserveChannels',
                handled=False),
            EventPattern('dbus-method-call',
                path=gs.object_path,
                interface=cs.APPROVER, method='AddDispatchOperation',
                handled=False),
            )

    channels, cdo_path, props = k.args

    cdo = bus.get_object(cs.CD, cdo_path)
    cdo_iface = dbus.Interface(cdo, cs.CDO)

    q.dbus_return(e.message, signature='')
    q.dbus_return(k.message, signature='')

    # gnome-shell claims the channel
    call_async(q, cdo_iface, 'Claim')
    e = q.expect('dbus-return', method='Claim')

    # Logger crash
    logger.release_name()

    e = q.expect('dbus-signal',
            signal='NameOwnerChanged',
            predicate=(lambda e:
                e.args[0] == logger.bus_name and e.args[2] == ''),
            )

    bus.flush()

    # Logger gets restarted
    logger.reacquire_name()

    e = q.expect('dbus-signal',
            signal='NameOwnerChanged',
            predicate=(lambda e:
                e.args[0] == logger.bus_name and e.args[1] == ''),
            )

    # Logger recovers the channel
    e = q.expect('dbus-method-call',
                path=logger.object_path,
                interface=cs.OBSERVER, method='ObserveChannels',
                handled=False)

    # gnome-shell which is handling the channel asks to re-ensure it
    cd_iface = dbus.Interface(cd, cs.CD)
    call_async(q, cd_iface, 'PresentChannel',
        chan.object_path, 0)

    # gnome-shell is asked to re-handle the channel
    e = q.expect('dbus-method-call',
            path=gs.object_path,
            interface=cs.HANDLER, method='HandleChannels',
            handled=False)

    q.dbus_return(e.message, signature='')

    q.expect('dbus-return', method='PresentChannel')

    chan.close()
コード例 #6
0
def test(q, bus, mc):
    params = dbus.Dictionary(
        {
            "account": "*****@*****.**",
            "password": "******"
        },
        signature='sv')
    simulated_cm, account = create_fakecm_account(q, bus, mc, params)
    conn = enable_fakecm_account(q, bus, mc, account, params)

    text_fixed_properties = dbus.Dictionary(
        {
            cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
            cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT,
        },
        signature='sv')

    # Logger is a text observer wanting recovery
    logger = SimulatedClient(q,
                             bus,
                             'LoggerChat',
                             observe=[text_fixed_properties],
                             bypass_approval=False,
                             wants_recovery=True)

    # gnome-shell is a text approver and handler
    gs = SimulatedClient(q,
                         bus,
                         'GnomeShell',
                         approve=[text_fixed_properties],
                         handle=[text_fixed_properties],
                         bypass_approval=False)

    # wait for MC to download the properties
    expect_client_setup(q, [logger, gs])

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

    # incoming text channel
    channel_properties = dbus.Dictionary(text_fixed_properties, signature='sv')
    channel_properties[cs.CHANNEL + '.TargetID'] = 'juliet'
    channel_properties[cs.CHANNEL + '.TargetHandle'] = \
            conn.ensure_handle(cs.HT_CONTACT, 'juliet')
    channel_properties[cs.CHANNEL + '.InitiatorID'] = 'juliet'
    channel_properties[cs.CHANNEL + '.InitiatorHandle'] = \
            conn.ensure_handle(cs.HT_CONTACT, 'juliet')
    channel_properties[cs.CHANNEL + '.Requested'] = False
    channel_properties[cs.CHANNEL + '.Interfaces'] = dbus.Array(signature='s')

    chan = SimulatedChannel(conn, channel_properties)
    chan.announce()

    # gnome-shell's approver and logger's observer are notified
    e, k = q.expect_many(
        EventPattern('dbus-method-call',
                     path=logger.object_path,
                     interface=cs.OBSERVER,
                     method='ObserveChannels',
                     handled=False),
        EventPattern('dbus-method-call',
                     path=gs.object_path,
                     interface=cs.APPROVER,
                     method='AddDispatchOperation',
                     handled=False),
    )

    channels, cdo_path, props = k.args

    cdo = bus.get_object(cs.CD, cdo_path)
    cdo_iface = dbus.Interface(cdo, cs.CDO)

    q.dbus_return(e.message, signature='')
    q.dbus_return(k.message, signature='')

    # gnome-shell claims the channel
    call_async(q, cdo_iface, 'Claim')
    e = q.expect('dbus-return', method='Claim')

    # Logger crash
    logger.release_name()

    e = q.expect(
        'dbus-signal',
        signal='NameOwnerChanged',
        predicate=(lambda e: e.args[0] == logger.bus_name and e.args[2] == ''),
    )

    bus.flush()

    # Logger gets restarted
    logger.reacquire_name()

    e = q.expect(
        'dbus-signal',
        signal='NameOwnerChanged',
        predicate=(lambda e: e.args[0] == logger.bus_name and e.args[1] == ''),
    )

    # Logger recovers the channel
    e = q.expect('dbus-method-call',
                 path=logger.object_path,
                 interface=cs.OBSERVER,
                 method='ObserveChannels',
                 handled=False)

    # gnome-shell which is handling the channel asks to re-ensure it
    cd_iface = dbus.Interface(cd, cs.CD)
    call_async(q, cd_iface, 'PresentChannel', chan.object_path, 0)

    # gnome-shell is asked to re-handle the channel
    e = q.expect('dbus-method-call',
                 path=gs.object_path,
                 interface=cs.HANDLER,
                 method='HandleChannels',
                 handled=False)

    q.dbus_return(e.message, signature='')

    q.expect('dbus-return', method='PresentChannel')

    chan.close()
コード例 #7
0
def test(q, bus, mc):
    params = dbus.Dictionary(
        {
            "account": "*****@*****.**",
            "password": "******"
        },
        signature='sv')
    simulated_cm, account = create_fakecm_account(q, bus, mc, params)
    conn = enable_fakecm_account(q, bus, mc, account, params)

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

    # Two clients want to observe, approve and handle channels
    empathy = SimulatedClient(q,
                              bus,
                              'Empathy',
                              observe=[text_fixed_properties],
                              approve=[text_fixed_properties],
                              handle=[text_fixed_properties],
                              bypass_approval=False)
    kopete = SimulatedClient(q,
                             bus,
                             'Kopete',
                             observe=[text_fixed_properties],
                             approve=[text_fixed_properties],
                             handle=[text_fixed_properties],
                             bypass_approval=False)

    # wait for MC to download the properties
    expect_client_setup(q, [empathy, kopete])

    # subscribe to the OperationList interface (MC assumes that until this
    # property has been retrieved once, nobody cares)

    cd = bus.get_object(cs.CD, cs.CD_PATH)
    cd_props = dbus.Interface(cd, cs.PROPERTIES_IFACE)
    assert cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations') == []

    channel_properties = dbus.Dictionary(text_fixed_properties, signature='sv')
    channel_properties[cs.CHANNEL + '.TargetID'] = 'juliet'
    channel_properties[cs.CHANNEL + '.TargetHandle'] = \
            conn.ensure_handle(cs.HT_CONTACT, 'juliet')
    channel_properties[cs.CHANNEL + '.InitiatorID'] = 'juliet'
    channel_properties[cs.CHANNEL + '.InitiatorHandle'] = \
            conn.ensure_handle(cs.HT_CONTACT, 'juliet')
    channel_properties[cs.CHANNEL + '.Requested'] = False
    channel_properties[cs.CHANNEL + '.Interfaces'] = dbus.Array(signature='s')

    chan = SimulatedChannel(conn, channel_properties)
    chan.announce()

    # A channel dispatch operation is created

    e = q.expect('dbus-signal',
                 path=cs.CD_PATH,
                 interface=cs.CD_IFACE_OP_LIST,
                 signal='NewDispatchOperation')

    cdo_path = e.args[0]
    cdo_properties = e.args[1]

    assert cdo_properties[cs.CDO + '.Account'] == account.object_path
    assert cdo_properties[cs.CDO + '.Connection'] == conn.object_path
    assert cs.CDO + '.Interfaces' in cdo_properties

    handlers = cdo_properties[cs.CDO + '.PossibleHandlers'][:]
    handlers.sort()
    assert handlers == [
        cs.tp_name_prefix + '.Client.Empathy',
        cs.tp_name_prefix + '.Client.Kopete'
    ], handlers

    assert cs.CD_IFACE_OP_LIST in cd_props.Get(cs.CD, 'Interfaces')
    assert cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations') ==\
            [(cdo_path, cdo_properties)]

    cdo = bus.get_object(cs.CD, cdo_path)
    cdo_iface = dbus.Interface(cdo, cs.CDO)

    # Both Observers are told about the new channel

    e, k = q.expect_many(
        EventPattern('dbus-method-call',
                     path=empathy.object_path,
                     interface=cs.OBSERVER,
                     method='ObserveChannels',
                     handled=False),
        EventPattern('dbus-method-call',
                     path=kopete.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
    channels = e.args[2]
    assert len(channels) == 1, channels
    assert channels[0][0] == chan.object_path, channels
    assert channels[0][1] == channel_properties, channels

    assert k.args == e.args

    # Both Observers indicate that they are ready to proceed
    q.dbus_return(k.message, signature='')
    q.dbus_return(e.message, signature='')

    # The Approvers are next

    e, k = q.expect_many(
        EventPattern('dbus-method-call',
                     path=empathy.object_path,
                     interface=cs.APPROVER,
                     method='AddDispatchOperation',
                     handled=False),
        EventPattern('dbus-method-call',
                     path=kopete.object_path,
                     interface=cs.APPROVER,
                     method='AddDispatchOperation',
                     handled=False),
    )

    assert e.args == [[(chan.object_path, channel_properties)], cdo_path,
                      cdo_properties]
    assert k.args == e.args

    q.dbus_return(e.message, signature='')

    # The channel closes before Kopete has said yes. As a result, MC isn't
    # allowed to emit ChannelLost or Finished yet.
    chan.close()

    # Empathy wants to handle the channel, but is too late
    call_async(q, cdo_iface, 'HandleWith',
               cs.tp_name_prefix + '.Client.Empathy')
    e = q.expect('dbus-error')
    # FIXME: e.error.get_dbus_name() == [...Disconnected] which doesn't
    #   seem like the most appropriate thing for MC to do (but at least it's
    # consistent with ChannelLost)

    # *Now* Kopete is happy...

    q.dbus_return(k.message, signature='')

    # ... and in response, the channel dispatch operation finishes

    e = q.expect('dbus-signal', path=cdo_path, signal='ChannelLost')
    assert e.args[0] == chan.object_path
    # FIXME: e.args[1:] == [...Disconnected, 'Channel aborted'] which doesn't
    #   seem like the most appropriate thing for MC to do

    q.expect('dbus-signal', path=cdo_path, signal='Finished')
    q.expect('dbus-signal',
             path=cs.CD_PATH,
             signal='DispatchOperationFinished',
             args=[cdo_path])

    # Now there are no more active channel dispatch operations
    assert cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations') == []
コード例 #8
0
def test_channel_creation(q, bus, account, client, conn, ensure):
    user_action_time = dbus.Int64(1238582606)

    # chat UI calls ChannelDispatcher.EnsureChannel or CreateChannel
    cd = ChannelDispatcher(bus)
    request = dbus.Dictionary({
            cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_TEXT,
            cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT,
            cs.CHANNEL + '.TargetID': 'juliet',
            }, signature='sv')

    if ensure:
        method = cd.EnsureChannel
    else:
        method = cd.CreateChannel

    request_path = method(account.object_path, request,
        user_action_time, client.bus_name)

    cr = bus.get_object(cs.CD, 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

    add_request = q.expect('dbus-method-call', handled=False,
        interface=cs.CLIENT_IFACE_REQUESTS, method='AddRequest',
        path=client.object_path)

    assert add_request.args[0] == request_path
    request_props = add_request.args[1]
    assert request_props[cs.CR + '.Account'] == account.object_path
    assert request_props[cs.CR + '.Requests'] == [request]
    assert request_props[cs.CR + '.UserActionTime'] == user_action_time
    assert request_props[cs.CR + '.PreferredHandler'] == client.bus_name

    q.dbus_return(add_request.message, signature='')

    # chat UI connects to signals and calls ChannelRequest.Proceed()
    cr.Proceed()
    cm_request_call = q.expect('dbus-method-call',
                interface=cs.CONN_IFACE_REQUESTS,
                method=(ensure and 'EnsureChannel' or 'CreateChannel'),
                path=conn.object_path, args=[request], handled=False)

    # 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, 'juliet')
    channel = SimulatedChannel(conn, channel_immutable)

    # this order of events is guaranteed by telepathy-spec (since 0.17.14)
    if ensure:
        q.dbus_return(cm_request_call.message, True, # <- Yours
                channel.object_path, channel.immutable, signature='boa{sv}')
    else:   # Create
        q.dbus_return(cm_request_call.message,
                channel.object_path, channel.immutable, signature='oa{sv}')
    channel.announce()

    # Observer should get told, processing waits for it
    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(e.message, signature='')

    # Handler is next
    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

    # Handler accepts the Channels
    q.dbus_return(e.message, signature='')

    # CR emits Succeeded
    q.expect('dbus-signal', path=request_path,
                interface=cs.CR, signal='Succeeded')

    # Close the channel
    channel.close()
def test(q, bus, mc):
    params = dbus.Dictionary({"account": "*****@*****.**",
        "password": "******"}, signature='sv')
    simulated_cm, account = create_fakecm_account(q, bus, mc, params)
    conn = enable_fakecm_account(q, bus, mc, account, params)

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

    # Two clients want to observe, approve and handle channels
    empathy = SimulatedClient(q, bus, 'Empathy',
            observe=[text_fixed_properties], approve=[text_fixed_properties],
            handle=[text_fixed_properties], bypass_approval=False)
    kopete = SimulatedClient(q, bus, 'Kopete',
            observe=[text_fixed_properties], approve=[text_fixed_properties],
            handle=[text_fixed_properties], bypass_approval=False)

    # wait for MC to download the properties
    expect_client_setup(q, [empathy, kopete])

    # subscribe to the OperationList interface (MC assumes that until this
    # property has been retrieved once, nobody cares)

    cd = bus.get_object(cs.CD, cs.CD_PATH)
    cd_props = dbus.Interface(cd, cs.PROPERTIES_IFACE)
    assert cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations') == []

    channel_properties = dbus.Dictionary(text_fixed_properties,
            signature='sv')
    channel_properties[cs.CHANNEL + '.TargetID'] = 'juliet'
    channel_properties[cs.CHANNEL + '.TargetHandle'] = \
            conn.ensure_handle(cs.HT_CONTACT, 'juliet')
    channel_properties[cs.CHANNEL + '.InitiatorID'] = 'juliet'
    channel_properties[cs.CHANNEL + '.InitiatorHandle'] = \
            conn.ensure_handle(cs.HT_CONTACT, 'juliet')
    channel_properties[cs.CHANNEL + '.Requested'] = False
    channel_properties[cs.CHANNEL + '.Interfaces'] = dbus.Array(signature='s')

    chan = SimulatedChannel(conn, channel_properties)
    chan.announce()

    # A channel dispatch operation is created

    e = q.expect('dbus-signal',
            path=cs.CD_PATH,
            interface=cs.CD_IFACE_OP_LIST,
            signal='NewDispatchOperation')

    cdo_path = e.args[0]
    cdo_properties = e.args[1]

    assert cdo_properties[cs.CDO + '.Account'] == account.object_path
    assert cdo_properties[cs.CDO + '.Connection'] == conn.object_path
    assert cs.CDO + '.Interfaces' in cdo_properties

    handlers = cdo_properties[cs.CDO + '.PossibleHandlers'][:]
    handlers.sort()
    assert handlers == [cs.tp_name_prefix + '.Client.Empathy',
            cs.tp_name_prefix + '.Client.Kopete'], handlers

    assert cs.CD_IFACE_OP_LIST in cd_props.Get(cs.CD, 'Interfaces')
    assert cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations') ==\
            [(cdo_path, cdo_properties)]

    cdo = bus.get_object(cs.CD, cdo_path)
    cdo_iface = dbus.Interface(cdo, cs.CDO)

    # Both Observers are told about the new channel

    e, k = q.expect_many(
            EventPattern('dbus-method-call',
                path=empathy.object_path,
                interface=cs.OBSERVER, method='ObserveChannels',
                handled=False),
            EventPattern('dbus-method-call',
                path=kopete.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
    channels = e.args[2]
    assert len(channels) == 1, channels
    assert channels[0][0] == chan.object_path, channels
    assert channels[0][1] == channel_properties, channels

    assert k.args == e.args

    # Both Observers indicate that they are ready to proceed
    q.dbus_return(k.message, signature='')
    q.dbus_return(e.message, signature='')

    # The Approvers are next

    e, k = q.expect_many(
            EventPattern('dbus-method-call',
                path=empathy.object_path,
                interface=cs.APPROVER, method='AddDispatchOperation',
                handled=False),
            EventPattern('dbus-method-call',
                path=kopete.object_path,
                interface=cs.APPROVER, method='AddDispatchOperation',
                handled=False),
            )

    assert e.args == [[(chan.object_path, channel_properties)],
            cdo_path, cdo_properties]
    assert k.args == e.args

    q.dbus_return(e.message, signature='')

    # The channel closes before Kopete has said yes. As a result, MC isn't
    # allowed to emit ChannelLost or Finished yet.
    chan.close()

    # Empathy wants to handle the channel, but is too late
    call_async(q, cdo_iface, 'HandleWith',
            cs.tp_name_prefix + '.Client.Empathy')
    e = q.expect('dbus-error')
    # FIXME: e.error.get_dbus_name() == [...Disconnected] which doesn't
    #   seem like the most appropriate thing for MC to do (but at least it's
    # consistent with ChannelLost)

    # *Now* Kopete is happy...

    q.dbus_return(k.message, signature='')

    # ... and in response, the channel dispatch operation finishes

    e = q.expect('dbus-signal', path=cdo_path, signal='ChannelLost')
    assert e.args[0] == chan.object_path
    # FIXME: e.args[1:] == [...Disconnected, 'Channel aborted'] which doesn't
    #   seem like the most appropriate thing for MC to do

    q.expect('dbus-signal', path=cdo_path, signal='Finished')
    q.expect('dbus-signal', path=cs.CD_PATH,
        signal='DispatchOperationFinished', args=[cdo_path])

    # Now there are no more active channel dispatch operations
    assert cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations') == []