Beispiel #1
0
  def test(self):
    """Test debmarshal.privops.call."""
    self.mox.StubOutWithMock(dbus, 'SystemBus', use_mock_anything=True)
    bus = self.mox.CreateMock(dbus.bus.BusConnection)
    proxy = self.mox.CreateMockAnything()
    method = self.mox.CreateMockAnything()

    dbus.SystemBus().AndReturn(bus)
    bus.get_object(privops.DBUS_BUS_NAME, privops.DBUS_OBJECT_PATH).AndReturn(
      proxy)
    proxy.get_dbus_method(
        'createNetwork', dbus_interface=privops.DBUS_INTERFACE).AndReturn(
        method)
    method(['www.company.com', 'login.company.com'])

    self.mox.ReplayAll()

    privops.call('createNetwork', ['www.company.com', 'login.company.com'])
    def test(self):
        """Test debmarshal.privops.call."""
        self.mox.StubOutWithMock(dbus, 'SystemBus', use_mock_anything=True)
        bus = self.mox.CreateMock(dbus.bus.BusConnection)
        proxy = self.mox.CreateMockAnything()
        method = self.mox.CreateMockAnything()

        dbus.SystemBus().AndReturn(bus)
        bus.get_object(privops.DBUS_BUS_NAME,
                       privops.DBUS_OBJECT_PATH).AndReturn(proxy)
        proxy.get_dbus_method(
            'createNetwork',
            dbus_interface=privops.DBUS_INTERFACE).AndReturn(method)
        method(['www.company.com', 'login.company.com'])

        self.mox.ReplayAll()

        privops.call('createNetwork', ['www.company.com', 'login.company.com'])
Beispiel #3
0
def setup(app):
    """Request and return a dbus interface for controlling the MeldApp."""
    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
    bus = dbus.SessionBus()
    request = bus.request_name(DBUS_NAME, dbus.bus.NAME_FLAG_DO_NOT_QUEUE)
    already_running = request == dbus.bus.REQUEST_NAME_REPLY_EXISTS
    if already_running:
        obj = dbus.Interface(bus.get_object(DBUS_NAME, DBUS_PATH), DBUS_NAME)
    else:
        obj = DBusProvider(bus, DBUS_NAME, DBUS_PATH, app)
    return already_running, obj
Beispiel #4
0
def setup(app):
    """Request and return a dbus interface for controlling the MeldApp."""
    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
    bus = dbus.SessionBus()
    request = bus.request_name(DBUS_NAME, dbus.bus.NAME_FLAG_DO_NOT_QUEUE)
    already_running = request == dbus.bus.REQUEST_NAME_REPLY_EXISTS
    if already_running:
        obj = dbus.Interface(bus.get_object(DBUS_NAME, DBUS_PATH), DBUS_NAME)
    else:
        obj = DBusProvider(bus, DBUS_NAME, DBUS_PATH, app)
    return already_running, obj
Beispiel #5
0
def setup_dbus(gtk_app, bus_interface="org.ojuba.Monajat"):
    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
    bus = dbus.SessionBus()
    request = bus.request_name(bus_interface, dbus.bus.NAME_FLAG_DO_NOT_QUEUE)

    if request != dbus.bus.REQUEST_NAME_REPLY_EXISTS:
        app = OjDBus(gtk_app, bus, '/', bus_interface)
    else:
        print "Exiting: Application already running..."
        object = bus.get_object(bus_interface, "/")
        app = dbus.Interface(object, bus_interface)
        app.start()
        exit(-1)
Beispiel #6
0
def setup_dbus(gtk_app, bus_interface="org.ojuba.Monajat"):
    dbus.mainloop.glib.DBusGMainLoop (set_as_default=True)
    bus = dbus.SessionBus ()
    request = bus.request_name (bus_interface, dbus.bus.NAME_FLAG_DO_NOT_QUEUE)
    
    if request != dbus.bus.REQUEST_NAME_REPLY_EXISTS:
        app = OjDBus(gtk_app, bus, '/', bus_interface)
    else:
        print "Exiting: Application already running..."
        object = bus.get_object (bus_interface, "/")
        app = dbus.Interface (object, bus_interface)
        app.start()
        exit(-1)
Beispiel #7
0
def main():
    """The main entry point, compatible with setuptools entry points."""
    from optparse import OptionParser
    parser = OptionParser(version="%%prog v%s" % __version__,
                          usage="%prog [options] <gogdownloader URI> ...",
                          description=__doc__.replace(
                              '\r\n', '\n').split('\n--snip--\n')[0])
    parser.add_option(
        '-v',
        '--verbose',
        action="count",
        dest="verbose",
        default=2,
        help="Increase the verbosity. Use twice for extra effect")
    parser.add_option(
        '-q',
        '--quiet',
        action="count",
        dest="quiet",
        default=0,
        help="Decrease the verbosity. Use twice for extra effect")
    # Reminder: %default can be used in help strings.

    # Allow pre-formatted descriptions
    parser.formatter.format_description = lambda description: description

    opts, args = parser.parse_args()

    # Set up clean logging to stderr
    log_levels = [
        logging.CRITICAL, logging.ERROR, logging.WARNING, logging.INFO,
        logging.DEBUG
    ]
    opts.verbose = min(opts.verbose - opts.quiet, len(log_levels) - 1)
    opts.verbose = max(opts.verbose, 0)
    logging.basicConfig(level=log_levels[opts.verbose],
                        format='%(levelname)s: %(message)s')

    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
    bus = dbus.SessionBus()
    request = bus.request_name(SVC_NAME, dbus.bus.NAME_FLAG_DO_NOT_QUEUE)
    if request != dbus.bus.REQUEST_NAME_REPLY_EXISTS:
        app = Application(bus, '/', SVC_NAME)
    else:
        obj = bus.get_object(SVC_NAME, "/")
        app = dbus.Interface(obj, SVC_NAME)

    app.start(vars(opts), args, int(time.time()))
    if app.is_running():
        gtk.gdk.notify_startup_complete()
Beispiel #8
0
def main():
    """The main entry point, compatible with setuptools entry points."""
    from optparse import OptionParser
    parser = OptionParser(version="%%prog v%s" % __version__,
            usage="%prog [options] <gogdownloader URI> ...",
            description=__doc__.replace('\r\n', '\n').split('\n--snip--\n')[0])
    parser.add_option('-v', '--verbose', action="count", dest="verbose",
        default=2, help="Increase the verbosity. Use twice for extra effect")
    parser.add_option('-q', '--quiet', action="count", dest="quiet",
        default=0, help="Decrease the verbosity. Use twice for extra effect")
    # Reminder: %default can be used in help strings.

    # Allow pre-formatted descriptions
    parser.formatter.format_description = lambda description: description

    opts, args = parser.parse_args()

    # Set up clean logging to stderr
    log_levels = [logging.CRITICAL, logging.ERROR, logging.WARNING,
                  logging.INFO, logging.DEBUG]
    opts.verbose = min(opts.verbose - opts.quiet, len(log_levels) - 1)
    opts.verbose = max(opts.verbose, 0)
    logging.basicConfig(level=log_levels[opts.verbose],
                        format='%(levelname)s: %(message)s')

    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
    bus = dbus.SessionBus()
    request = bus.request_name(SVC_NAME, dbus.bus.NAME_FLAG_DO_NOT_QUEUE)
    if request != dbus.bus.REQUEST_NAME_REPLY_EXISTS:
        app = Application(bus, '/', SVC_NAME)
    else:
        obj = bus.get_object(SVC_NAME, "/")
        app = dbus.Interface(obj, SVC_NAME)

    app.start(vars(opts), args, int(time.time()))
    if app.is_running():
        gtk.gdk.notify_startup_complete()
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 + '.ChannelType': cs.CHANNEL_TYPE_TEXT,
            cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT,
        },
        signature='sv')

    # 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') == []

    client = SimulatedClient(q,
                             bus,
                             'Client',
                             observe=[text_fixed_properties],
                             approve=[text_fixed_properties],
                             handle=[],
                             bypass_approval=False)

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

    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')

    forbidden = [
        EventPattern('dbus-method-call', method='AddDispatchOperation'),
    ]
    q.forbid_events(forbidden)

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

    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'][:]
    assertEquals([], handlers)

    e = q.expect('dbus-method-call',
                 path=client.object_path,
                 interface=cs.OBSERVER,
                 method='ObserveChannels',
                 handled=False)

    sync_dbus(bus, q, mc)

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

    # now (but only now) MC kills the channel
    q.expect_many(
        EventPattern('dbus-signal', path=cdo_path, signal='Finished'),
        EventPattern('dbus-method-call',
                     path=chan.object_path,
                     method='Close',
                     handled=True),
    )

    # There are no active channel dispatch operations
    assert cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations') == []
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')

    empathy_bus = dbus.bus.BusConnection()
    q.attach_to_bus(empathy_bus)
    empathy = SimulatedClient(q, empathy_bus, 'Empathy',
            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])

    # 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'][:]
    assert handlers == [cs.tp_name_prefix + '.Client.Empathy'], 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)
    cdo_props_iface = dbus.Interface(cdo, cs.PROPERTIES_IFACE)

    assert cdo_props_iface.Get(cs.CDO, 'Interfaces') == \
            cdo_properties[cs.CDO + '.Interfaces']
    assert cdo_props_iface.Get(cs.CDO, 'Connection') == conn.object_path
    assert cdo_props_iface.Get(cs.CDO, 'Account') == account.object_path
    assert cdo_props_iface.Get(cs.CDO, 'Channels') == [(chan.object_path,
        channel_properties)]
    assert cdo_props_iface.Get(cs.CDO, 'PossibleHandlers') == \
            cdo_properties[cs.CDO + '.PossibleHandlers']

    e = q.expect('dbus-method-call',
            path=empathy.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] == cdo_path, e.args
    assert e.args[4] == [], e.args      # no requests satisfied
    channels = e.args[2]
    assert len(channels) == 1, channels
    assert channels[0][0] == chan.object_path, channels
    assert channels[0][1] == channel_properties, channels

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

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

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

    # Empathy rejects the channels
    q.dbus_raise(e.message, cs.NOT_AVAILABLE, 'Blind drunk', bus=empathy_bus)

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

    # Empathy rejects the channels
    q.dbus_raise(e.message, cs.NOT_AVAILABLE, 'Still drunk', bus=empathy_bus)

    q.expect_many(
            EventPattern('dbus-method-call', path=chan.object_path,
                interface=cs.CHANNEL, method='Close', handled=True),
            EventPattern('dbus-signal', path=cdo.object_path,
                interface=cs.CDO, signal='Finished'),
            EventPattern('dbus-signal', path=cd.object_path,
                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') == []
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')

    # Empathy is an observer for text channels with
    # DelayApprovers=TRUE.
    empathy = SimulatedClient(q, bus, 'Empathy',
        observe=[text_fixed_properties], approve=[],
        handle=[], delay_approvers=True)

    # Loggy is an observer for text channels with
    # DelayApprovers=FALSE.
    loggy = SimulatedClient(q, bus, 'Loggy',
        observe=[text_fixed_properties], approve=[],
        handle=[], delay_approvers=False)

    # Kopete is an approver and handler for text channels.
    kopete = SimulatedClient(q, bus, 'Kopete',
        observe=[], approve=[text_fixed_properties],
        handle=[text_fixed_properties])

    expect_client_setup(q, [empathy, loggy, 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') == []

    # A text channel appears!
    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()

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

    cdo_path = e.args[0]
    cdo = bus.get_object(cs.CD, cdo_path)
    cdo_iface = dbus.Interface(cdo, cs.CDO)
    cdo_props_iface = dbus.Interface(cdo, cs.PROPERTIES_IFACE)

    # Empathy, the observer, gets the channel to observe. Because it
    # has DelayApprovers=TRUE, Kopete should not have
    # AddDispatchOperation called on it until Empathy returns from
    # ObserveChannels. Because Loggy has DelayApprovers=False,
    # however, ADO can be called on Kopete before Loggy returns, but
    # again, only after Empathy returns.
    forbidden = [EventPattern('dbus-method-call',
            path=kopete.object_path,
            interface=cs.APPROVER, method='AddDispatchOperation')]
    q.forbid_events(forbidden)

    e, l = q.expect_many(EventPattern('dbus-method-call',
                 path=empathy.object_path,
                 interface=cs.OBSERVER, method='ObserveChannels',
                 handled=False),
             EventPattern('dbus-method-call',
                 path=loggy.object_path,
                 interface=cs.OBSERVER, method='ObserveChannels',
                 handled=False),
             )

    # Waste a little time here and there.  We can't call sync_dbus
    # here because it calls Ping and libdbus returns from Ping
    # synchronously and doesn't turn the main loop handle enough.
    call_async(q, cd_props, 'Get', cs.CD_IFACE_OP_LIST, 'DispatchOperations')
    event = q.expect('dbus-return', method='Get')

    # Finally return from ObserveChannels from Empathy, so now we
    # expect ADO to be called on Kopete.
    q.dbus_return(e.message, bus=bus, signature='')
    q.unforbid_events(forbidden)

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

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

    # Return from loggy's ObserveChannels.
    q.dbus_return(l.message, bus=bus, signature='')

    # The user responds to Kopete
    call_async(q, cdo_iface, 'HandleWith',
            cs.tp_name_prefix + '.Client.Kopete')

    # Kopete is asked to handle the channels
    k = q.expect('dbus-method-call',
            path=kopete.object_path,
            interface=cs.HANDLER, method='HandleChannels',
            handled=False)

    # Kopete accepts the channels
    q.dbus_return(k.message, bus=bus, 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') == []
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 + '.ChannelType': cs.CHANNEL_TYPE_TEXT,
        cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT,
        }, signature='sv')

    # 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') == []

    client = SimulatedClient(q, bus, 'Client',
            observe=[text_fixed_properties],
            approve=[text_fixed_properties],
            handle=[], bypass_approval=False)

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

    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')

    forbidden = [
            EventPattern('dbus-method-call', method='AddDispatchOperation'),
            ]
    q.forbid_events(forbidden)

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

    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'][:]
    assertEquals([], handlers)

    e = q.expect('dbus-method-call',
            path=client.object_path,
            interface=cs.OBSERVER, method='ObserveChannels',
            handled=False)

    sync_dbus(bus, q, mc)

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

    # now (but only now) MC kills the channel
    q.expect_many(
            EventPattern('dbus-signal', path=cdo_path, signal='Finished'),
            EventPattern('dbus-method-call', path=chan.object_path,
                method='Close', handled=True),
            )

    # There are no active channel dispatch operations
    assert cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations') == []
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')

    # Empathy is an observer for text channels with
    # DelayApprovers=TRUE.
    empathy = SimulatedClient(q, bus, 'Empathy',
        observe=[text_fixed_properties], approve=[],
        handle=[], delay_approvers=True)

    # Kopete is an approver and handler for text channels.
    kopete = SimulatedClient(q, bus, 'Kopete',
        observe=[], approve=[text_fixed_properties],
        handle=[text_fixed_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') == []

    # A text channel appears!
    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()

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

    cdo_path = e.args[0]
    cdo = bus.get_object(cs.CD, cdo_path)
    cdo_iface = dbus.Interface(cdo, cs.CDO)
    cdo_props_iface = dbus.Interface(cdo, cs.PROPERTIES_IFACE)

    # Empathy, the observer, gets the channel to observe. Because it
    # has DelayApprovers=TRUE, Kopete should not have
    # AddDispatchOperation called on it until Empathy returns from
    # ObserveChannels.
    forbidden = [EventPattern('dbus-method-call',
            path=kopete.object_path,
            interface=cs.APPROVER, method='AddDispatchOperation')]
    q.forbid_events(forbidden)

    o = q.expect('dbus-method-call',
             path=empathy.object_path,
             interface=cs.OBSERVER, method='ObserveChannels',
             handled=False)

    # Waste a little time here and there.  We can't call sync_dbus
    # here because it calls Ping and libdbus returns from Ping
    # synchronously and doesn't turn the main loop handle enough.
    call_async(q, cd_props, 'Get', cs.CD_IFACE_OP_LIST, 'DispatchOperations')
    event = q.expect('dbus-return', method='Get')

    # Finally return from ObserveChannels, so now we expect ADO to be
    # called on Kopete.
    q.dbus_return(o.message, bus=bus, signature='')
    q.unforbid_events(forbidden)

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

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

    # The user responds to Kopete
    call_async(q, cdo_iface, 'HandleWith',
            cs.tp_name_prefix + '.Client.Kopete')

    # Kopete is asked to handle the channels
    k = q.expect('dbus-method-call',
            path=kopete.object_path,
            interface=cs.HANDLER, method='HandleChannels',
            handled=False)

    # Kopete accepts the channels
    q.dbus_return(k.message, bus=bus, 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') == []
        self.main_window.show_all()

    @dbus.service.method(DBUS_NAME, in_signature='', out_signature='b')
    def is_running(self):
        return self.running

    @dbus.service.method(DBUS_NAME, in_signature='a{sv}i', out_signature='')
    def start(self, options, timestamp):
        if self.is_running():
            self.main_window.present_with_time(timestamp)
        else:
            self.running = True
            Gtk.main()
            self.running = False


dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
bus = dbus.SessionBus()
request = bus.request_name(DBUS_NAME, dbus.bus.NAME_FLAG_DO_NOT_QUEUE)
if request != dbus.bus.REQUEST_NAME_REPLY_EXISTS:
    app = DBusDaemon(bus, '/', DBUS_NAME)
else:
    obj = bus.get_object(DBUS_NAME, "/")
    app = dbus.Interface(obj, DBUS_NAME)

# Get your options from the command line, e.g. with OptionParser
options = {'option1': 'value1'}
app.start(options, int(time.time()))
if app.is_running():
    Gdk.notify_startup_complete()
Beispiel #15
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')

    # Empathy is an Observers who will crash
    empathy_bus = dbus.bus.BusConnection()
    empathy_bus.set_exit_on_disconnect(False)  # we'll disconnect later

    # Kopete is an Approver, Handler and will not crash
    kopete_bus = dbus.bus.BusConnection()
    q.attach_to_bus(empathy_bus)
    q.attach_to_bus(kopete_bus)

    # Two clients want to observe, approve and handle channels
    empathy = SimulatedClient(q,
                              empathy_bus,
                              'Empathy',
                              observe=[text_fixed_properties],
                              approve=[],
                              handle=[],
                              wants_recovery=True)
    kopete = SimulatedClient(q,
                             kopete_bus,
                             'Kopete',
                             observe=[],
                             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.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)
    cdo_props_iface = dbus.Interface(cdo, cs.PROPERTIES_IFACE)

    assert cdo_props_iface.Get(cs.CDO, 'Interfaces') == \
            cdo_properties[cs.CDO + '.Interfaces']
    assert cdo_props_iface.Get(cs.CDO, 'Connection') == conn.object_path
    assert cdo_props_iface.Get(cs.CDO, 'Account') == account.object_path
    assert cdo_props_iface.Get(cs.CDO, 'Channels') == [(chan.object_path,
                                                        channel_properties)]
    assert cdo_props_iface.Get(cs.CDO, 'PossibleHandlers') == \
            cdo_properties[cs.CDO + '.PossibleHandlers']

    # The Observer (Empathy) is told about the new channel

    e = q.expect('dbus-method-call',
                 path=empathy.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] == cdo_path, e.args
    assert e.args[4] == [], e.args  # no requests satisfied
    channels = e.args[2]
    assert len(channels) == 1, channels
    assert channels[0][0] == chan.object_path, channels
    assert channels[0][1] == channel_properties, channels

    # Empathy indicates that it is ready to proceed
    q.dbus_return(e.message, bus=empathy_bus, signature='')

    # The Approver (Kopete) is next

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

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

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

    # The user responds to Kopete
    call_async(q, cdo_iface, 'HandleWith',
               cs.tp_name_prefix + '.Client.Kopete')

    # Kopete is asked to handle the channels
    k = q.expect('dbus-method-call',
                 path=kopete.object_path,
                 interface=cs.HANDLER,
                 method='HandleChannels',
                 handled=False)

    # Kopete accepts the channels
    q.dbus_return(k.message, bus=kopete_bus, 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') == []

    # Another channel: this one will remain unapproved

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

    chan2 = SimulatedChannel(conn, channel2_properties)
    chan2.announce()

    # A channel dispatch operation is created

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

    cdo2_path = e.args[0]
    cdo2_properties = e.args[1]

    assert cdo2_properties[cs.CDO + '.Account'] == account.object_path
    assert cdo2_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.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') ==\
            [(cdo2_path, cdo2_properties)]

    cdo2 = bus.get_object(cs.CD, cdo2_path)
    cdo2_iface = dbus.Interface(cdo2, cs.CDO)
    cdo2_props_iface = dbus.Interface(cdo2, cs.PROPERTIES_IFACE)

    assert cdo2_props_iface.Get(cs.CDO, 'Interfaces') == \
            cdo2_properties[cs.CDO + '.Interfaces']
    assert cdo2_props_iface.Get(cs.CDO, 'Connection') == conn.object_path
    assert cdo2_props_iface.Get(cs.CDO, 'Account') == account.object_path
    assert cdo2_props_iface.Get(cs.CDO, 'Channels') == [(chan2.object_path,
                                                         channel2_properties)]
    assert cdo2_props_iface.Get(cs.CDO, 'PossibleHandlers') == \
            cdo2_properties[cs.CDO + '.PossibleHandlers']

    # The Observer (Empathy) is told about the new channel

    e = q.expect('dbus-method-call',
                 path=empathy.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] == cdo2_path, e.args
    assert e.args[4] == [], e.args  # no requests satisfied
    channels = e.args[2]
    assert len(channels) == 1, channels
    assert channels[0][0] == chan2.object_path, channels
    assert channels[0][1] == channel2_properties, channels

    # Empathy indicates that it is ready to proceed
    q.dbus_return(e.message, bus=empathy_bus, signature='')

    # The Approver (Kopete) is next; this time, we don't approve

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

    assert k.args == [[(chan2.object_path, channel2_properties)], cdo2_path,
                      cdo2_properties]

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

    # Empathy crashes
    empathy.release_name()

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

    empathy_bus.flush()

    # Empathy gets restarted
    empathy.reacquire_name()

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

    e1, e2 = q.expect_many(
        EventPattern('dbus-method-call',
                     path=empathy.object_path,
                     interface=cs.OBSERVER,
                     method='ObserveChannels',
                     predicate=lambda e: e.args[2][0][0] == 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] == chan2.object_path,
                     handled=False),
    )

    assert e1.args[0] == account.object_path, e1.args
    assert e1.args[1] == conn.object_path, e1.args
    assert e1.args[4] == [], e1.args  # no requests satisfied
    assert e1.args[5]['recovering'] == 1, e1.args  # due to observer recovery
    channels = e1.args[2]
    assert len(channels) == 1, channels
    assert channels[0][0] == chan.object_path, channels
    assert channels[0][1] == channel_properties, channels

    assert e2.args[0] == account.object_path, e2.args
    assert e2.args[1] == conn.object_path, e2.args
    assert e2.args[4] == [], e2.args  # no requests satisfied
    assert e2.args[5]['recovering'] == 1, e2.args  # due to observer recovery
    channels = e2.args[2]
    assert len(channels) == 1, channels
    assert channels[0][0] == chan2.object_path, channels
    assert channels[0][1] == channel2_properties, channels

    # Empathy indicates that it is ready to proceed
    q.dbus_return(e1.message, bus=empathy_bus, signature='')
    q.dbus_return(e2.message, bus=empathy_bus, signature='')

    sync_dbus(bus, q, mc)
Beispiel #16
0
        self.main_window.connect('destroy', Gtk.main_quit)
        self.main_window.show_all()

    @dbus.service.method(DBUS_NAME, in_signature='', out_signature='b')
    def is_running(self):
        return self.running

    @dbus.service.method(DBUS_NAME, in_signature='a{sv}i', out_signature='')
    def start(self, options, timestamp):
        if self.is_running():
            self.main_window.present_with_time(timestamp)
        else:
            self.running = True
            Gtk.main()
            self.running = False

dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
bus = dbus.SessionBus()
request = bus.request_name(DBUS_NAME, dbus.bus.NAME_FLAG_DO_NOT_QUEUE)
if request != dbus.bus.REQUEST_NAME_REPLY_EXISTS:
    app = DBusDaemon(bus, '/', DBUS_NAME)
else:
    obj = bus.get_object(DBUS_NAME, "/")
    app = dbus.Interface(obj, DBUS_NAME)

# Get your options from the command line, e.g. with OptionParser
options = {'option1': 'value1'}
app.start(options, int(time.time()))
if app.is_running():
    Gdk.notify_startup_complete()
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)

    # One client (Kopete) has less specific filters than the other (Empathy),
    # to make sure that the dispatcher would normally prefer Empathy; this
    # means that when we use Kopete as the preferred handler, we know that
    # if Kopete is invoked, then preferring the preferred handler correctly
    # took precedence over the normal logic.
    vague_fixed_properties = dbus.Dictionary({
        cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_TEXT,
        }, signature='sv')
    text_fixed_properties = dbus.Dictionary({
        cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT,
        cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_TEXT,
        }, signature='sv')

    empathy_bus = dbus.bus.BusConnection()
    kopete_bus = dbus.bus.BusConnection()
    q.attach_to_bus(empathy_bus)
    q.attach_to_bus(kopete_bus)
    # Two clients want to observe, approve and handle channels
    empathy = SimulatedClient(q, empathy_bus, 'Empathy',
            observe=[text_fixed_properties], approve=[text_fixed_properties],
            handle=[text_fixed_properties], bypass_approval=False)
    kopete = SimulatedClient(q, kopete_bus, 'Kopete',
            observe=[vague_fixed_properties], approve=[vague_fixed_properties],
            handle=[vague_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'][:]
    # Empathy has a more specific filter, so it comes first
    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)
    cdo_props_iface = dbus.Interface(cdo, cs.PROPERTIES_IFACE)

    assert cdo_props_iface.Get(cs.CDO, 'Interfaces') == \
            cdo_properties[cs.CDO + '.Interfaces']
    assert cdo_props_iface.Get(cs.CDO, 'Connection') == conn.object_path
    assert cdo_props_iface.Get(cs.CDO, 'Account') == account.object_path
    assert cdo_props_iface.Get(cs.CDO, 'Channels') == [(chan.object_path,
        channel_properties)]
    assert cdo_props_iface.Get(cs.CDO, 'PossibleHandlers') == \
            cdo_properties[cs.CDO + '.PossibleHandlers']

    # 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
    assert e.args[3] == cdo_path, e.args
    assert e.args[4] == [], e.args      # no requests satisfied
    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, bus=empathy_bus, signature='')
    q.dbus_return(e.message, bus=kopete_bus, 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, bus=empathy_bus, signature='')
    q.dbus_return(k.message, bus=kopete_bus, signature='')

    # Both Approvers now have a flashing icon or something, trying to get the
    # user's attention. However, the user is busy looking through the address
    # book, and independently decides to talk to Juliet. The address book
    # is from KDE so wants Kopete to be used.

    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, 'EnsureChannel',
            account.object_path, request, user_action_time, kopete.bus_name,
            dbus_interface=cs.CD)
    ret, add_request_call = q.expect_many(
            EventPattern('dbus-return', method='EnsureChannel'),
            EventPattern('dbus-method-call', handled=False,
                interface=cs.CLIENT_IFACE_REQUESTS,
                method='AddRequest', path=kopete.object_path),
            )
    request_path = ret.value[0]

    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'] == kopete.bus_name
    assert request_props[cs.CR + '.Interfaces'] == []

    # 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'] == kopete.bus_name
    assert request_props['Interfaces'] == []

    cr.Proceed(dbus_interface=cs.CR)

    cm_request_call = q.expect('dbus-method-call',
            interface=cs.CONN_IFACE_REQUESTS,
            method='EnsureChannel',
            path=conn.object_path, args=[request], handled=False)

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

    # Time passes. The CM returns the existing channel

    q.dbus_return(cm_request_call.message, False,
            chan.object_path, chan.immutable, signature='boa{sv}')

    # EnsureChannel constitutes approval, so Kopete is told to handle the
    # channel

    e = q.expect('dbus-method-call',
            path=kopete.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] == chan.object_path, channels
    assert channels[0][1] == chan.immutable, channels
    assert e.args[3] == [request_path], e.args
    assert e.args[4] == user_action_time, (e.args[4], user_action_time)
    assert isinstance(e.args[5], dict)
    assert len(e.args) == 6

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

    q.expect_many(
            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') == []
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.RespawnObservers'
        }, signature='sv')

    # Logger is an activatable Observer who will crash
    logger_bus = dbus.bus.BusConnection()
    logger_bus.set_exit_on_disconnect(False)   # we'll disconnect later

    # Kopete is an Approver, Handler and will not crash
    kopete_bus = dbus.bus.BusConnection()
    q.attach_to_bus(logger_bus)
    q.attach_to_bus(kopete_bus)

    kopete = SimulatedClient(q, kopete_bus, 'Kopete',
            observe=[], approve=[text_fixed_properties],
            handle=[text_fixed_properties], bypass_approval=False)

    # wait for MC to download the properties
    expect_client_setup(q, [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.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)
    cdo_props_iface = dbus.Interface(cdo, cs.PROPERTIES_IFACE)

    assert cdo_props_iface.Get(cs.CDO, 'Interfaces') == \
            cdo_properties[cs.CDO + '.Interfaces']
    assert cdo_props_iface.Get(cs.CDO, 'Connection') == conn.object_path
    assert cdo_props_iface.Get(cs.CDO, 'Account') == account.object_path
    assert cdo_props_iface.Get(cs.CDO, 'Channels') == [(chan.object_path,
        channel_properties)]
    assert cdo_props_iface.Get(cs.CDO, 'PossibleHandlers') == \
            cdo_properties[cs.CDO + '.PossibleHandlers']

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

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

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


    # The fake Logger implementation is run
    e = q.expect('dbus-signal',
            path=cs.tp_path_prefix + '/RegressionTests',
            interface=cs.tp_name_prefix + '.RegressionTests',
            signal='FakeStartup',
            args=[cs.tp_name_prefix + '.Client.Logger'],
            )
    # We take on its identity to be able to continue with the test
    logger = SimulatedClient(q, bus, 'Logger',
            observe=[text_fixed_properties])

    # Logger is told about the new channel
    e = q.expect('dbus-method-call',
                path=logger.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] == cdo_path, e.args
    assert e.args[4] == [], e.args      # no requests satisfied
    channels = e.args[2]
    assert len(channels) == 1, channels
    assert channels[0][0] == chan.object_path, channels
    assert channels[0][1] == channel_properties, channels

    # Logger indicates that it is ready to proceed
    q.dbus_return(e.message, bus=logger_bus, signature='')

    # The Approver (Kopete) is next

    # The user responds to Kopete
    call_async(q, cdo_iface, 'HandleWith',
            cs.tp_name_prefix + '.Client.Kopete')

    # Kopete is asked to handle the channels
    k = q.expect('dbus-method-call',
            path=kopete.object_path,
            interface=cs.HANDLER, method='HandleChannels',
            handled=False)

    # Kopete accepts the channels
    q.dbus_return(k.message, bus=kopete_bus, 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') == []

    # Logger crashes
    logger.release_name()

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

    logger_bus.flush()

    # Logger gets restart request
    e = q.expect('dbus-signal',
            path=cs.tp_path_prefix + '/RegressionTests',
            interface=cs.tp_name_prefix + '.RegressionTests',
            signal='FakeStartup',
            args=[cs.tp_name_prefix + '.Client.Logger'],
            )

    # 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_unique_name = e.args[2]

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

    # FIXME: assert the same things as before, except CDO (which we don't
    # have) and account path (which we don't know how to get); also check
    # that the recovering observer info key is set
    assert e.args[0] == account.object_path, e.args
    assert e.args[1] == conn.object_path, e.args
    assert e.args[4] == [], e.args      # no requests satisfied
    assert e.args[5]['recovering'] == 1, e.args # due to observer recovery
    channels = e.args[2]
    assert len(channels) == 1, channels
    assert channels[0][0] == chan.object_path, channels
    assert channels[0][1] == channel_properties, channels

    # Logger indicates that it is ready to proceed
    q.dbus_return(e.message, bus=logger_bus, signature='')

    sync_dbus(bus, q, mc)
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')
    vague_fixed_properties = dbus.Dictionary({
        cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_TEXT,
        }, signature='sv')

    empathy_bus = dbus.bus.BusConnection()
    q.attach_to_bus(empathy_bus)
    empathy = SimulatedClient(q, empathy_bus, 'Empathy',
            observe=[text_fixed_properties], approve=[text_fixed_properties],
            handle=[text_fixed_properties], bypass_approval=False)

    # Kopete's filter is less specific than Empathy's, so we'll prefer Empathy
    kopete_bus = dbus.bus.BusConnection()
    q.attach_to_bus(kopete_bus)
    kopete = SimulatedClient(q, kopete_bus, 'Kopete',
            observe=[], approve=[],
            handle=[vague_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

    # In this test Empathy's filter has more things in it than Kopete's, so
    # MC will prefer Empathy
    handlers = cdo_properties[cs.CDO + '.PossibleHandlers'][:]
    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)
    cdo_props_iface = dbus.Interface(cdo, cs.PROPERTIES_IFACE)

    assert cdo_props_iface.Get(cs.CDO, 'Interfaces') == \
            cdo_properties[cs.CDO + '.Interfaces']
    assert cdo_props_iface.Get(cs.CDO, 'Connection') == conn.object_path
    assert cdo_props_iface.Get(cs.CDO, 'Account') == account.object_path
    assert cdo_props_iface.Get(cs.CDO, 'Channels') == [(chan.object_path,
        channel_properties)]
    assert cdo_props_iface.Get(cs.CDO, 'PossibleHandlers') == \
            cdo_properties[cs.CDO + '.PossibleHandlers']

    e = q.expect('dbus-method-call',
            path=empathy.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] == cdo_path, e.args
    assert e.args[4] == [], e.args      # no requests satisfied
    channels = e.args[2]
    assert len(channels) == 1, channels
    assert channels[0][0] == chan.object_path, channels
    assert channels[0][1] == channel_properties, channels

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

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

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

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

    call_async(q, cdo_iface, 'HandleWith',
            cs.tp_name_prefix + '.Client.Empathy')

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

    # Empathy rejects the channels
    q.dbus_raise(e.message, cs.NOT_AVAILABLE, 'Blind drunk', bus=empathy_bus)

    e = q.expect('dbus-error', method='HandleWith')
    assert e.error.get_dbus_name() == cs.NOT_AVAILABLE
    assert e.error.get_dbus_message() == 'Blind drunk'

    # The channels no longer count as having been approved. Check that MC
    # doesn't carry on regardless
    forbidden = [EventPattern('dbus-method-call', method='HandleChannels')]
    q.forbid_events(forbidden)
    sync_dbus(bus, q, mc)
    q.unforbid_events(forbidden)

    # I'm Feeling Lucky. It might work if I try again? Maybe?
    call_async(q, cdo_iface, 'HandleWith',
            cs.tp_name_prefix + '.Client.Empathy')

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

    # Empathy rejects the channels, again
    q.dbus_raise(e.message, cs.NOT_CAPABLE, 'Still drunk', bus=empathy_bus)

    e = q.expect('dbus-error', method='HandleWith')
    assert e.error.get_dbus_name() == cs.NOT_CAPABLE
    assert e.error.get_dbus_message() == 'Still drunk'

    # OK, OK, is anyone else competent enough to handle them?
    # (Also, assert that MC doesn't offer them back to Empathy, knowing that
    # it already tried and failed)
    forbidden = [EventPattern('dbus-method-call', method='HandleChannels',
        path=empathy.object_path)]
    q.forbid_events(forbidden)
    call_async(q, cdo_iface, 'HandleWith', '')

    # Kopete is asked to handle the channels
    k = q.expect('dbus-method-call',
                path=kopete.object_path,
                interface=cs.HANDLER, method='HandleChannels',
                handled=False)

    # Kopete rejects the channels too
    q.dbus_raise(k.message, cs.NOT_AVAILABLE, 'Also blind drunk',
            bus=kopete_bus)

    e = q.expect('dbus-error', method='HandleWith')

    assert e.error.get_dbus_name() == cs.NOT_AVAILABLE
    assert e.error.get_dbus_message() == 'Also blind drunk'

    # MC gives up and closes the channel. This is the end of the CDO.
    q.expect_many(
            EventPattern('dbus-method-call', path=chan.object_path,
                interface=cs.CHANNEL, method='Close', args=[]),
            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') == []
Beispiel #20
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')

    empathy_bus = dbus.bus.BusConnection()
    empathy_bus.set_exit_on_disconnect(False)  # we'll disconnect later
    kopete_bus = dbus.bus.BusConnection()
    q.attach_to_bus(empathy_bus)
    q.attach_to_bus(kopete_bus)
    # Two clients want to observe, approve and handle channels
    empathy = SimulatedClient(q,
                              empathy_bus,
                              'Empathy',
                              observe=[text_fixed_properties],
                              approve=[text_fixed_properties],
                              handle=[text_fixed_properties],
                              bypass_approval=False)
    kopete = SimulatedClient(q,
                             kopete_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)
    cdo_props_iface = dbus.Interface(cdo, cs.PROPERTIES_IFACE)

    assert cdo_props_iface.Get(cs.CDO, 'Interfaces') == \
            cdo_properties[cs.CDO + '.Interfaces']
    assert cdo_props_iface.Get(cs.CDO, 'Connection') == conn.object_path
    assert cdo_props_iface.Get(cs.CDO, 'Account') == account.object_path
    assert cdo_props_iface.Get(cs.CDO, 'Channels') == [(chan.object_path,
                                                        channel_properties)]
    assert cdo_props_iface.Get(cs.CDO, 'PossibleHandlers') == \
            cdo_properties[cs.CDO + '.PossibleHandlers']

    # 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
    assert e.args[3] == cdo_path, e.args
    assert e.args[4] == [], e.args  # no requests satisfied
    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, bus=empathy_bus, signature='')
    q.dbus_return(e.message, bus=kopete_bus, 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, bus=empathy_bus, signature='')
    q.dbus_return(k.message, bus=kopete_bus, signature='')

    # Both Approvers now have a flashing icon or something, trying to get the
    # user's attention

    # Using an invalid Handler name should fail
    call_async(q, cdo_iface, 'HandleWith',
               cs.tp_name_prefix + '.The.Moon.On.A.Stick')
    q.expect('dbus-error', method='HandleWith')
    call_async(q, cdo_iface, 'HandleWith',
               cs.tp_name_prefix + '.Client.the moon on a stick')
    q.expect('dbus-error', method='HandleWith')

    # The user responds to Empathy first
    call_async(q, cdo_iface, 'HandleWith',
               cs.tp_name_prefix + '.Client.Empathy')

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

    # Empathy accepts the channels
    q.dbus_return(e.message, bus=empathy_bus, 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') == []

    # Approve another channel using HandleWithTime()
    channel_properties = dbus.Dictionary(text_fixed_properties, signature='sv')
    channel_properties[cs.CHANNEL + '.TargetID'] = 'lucien'
    channel_properties[cs.CHANNEL + '.TargetHandle'] = \
            conn.ensure_handle(cs.HT_CONTACT, 'lucien')
    channel_properties[cs.CHANNEL + '.InitiatorID'] = 'lucien'
    channel_properties[cs.CHANNEL + '.InitiatorHandle'] = \
            conn.ensure_handle(cs.HT_CONTACT, 'lucien')
    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)
    cdo_props_iface = dbus.Interface(cdo, cs.PROPERTIES_IFACE)

    assert cdo_props_iface.Get(cs.CDO, 'Interfaces') == \
            cdo_properties[cs.CDO + '.Interfaces']
    assert cdo_props_iface.Get(cs.CDO, 'Connection') == conn.object_path
    assert cdo_props_iface.Get(cs.CDO, 'Account') == account.object_path
    assert cdo_props_iface.Get(cs.CDO, 'Channels') == [(chan.object_path,
                                                        channel_properties)]
    assert cdo_props_iface.Get(cs.CDO, 'PossibleHandlers') == \
            cdo_properties[cs.CDO + '.PossibleHandlers']

    # 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
    assert e.args[3] == cdo_path, e.args
    assert e.args[4] == [], e.args  # no requests satisfied
    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, bus=empathy_bus, signature='')
    q.dbus_return(e.message, bus=kopete_bus, 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, bus=empathy_bus, signature='')
    q.dbus_return(k.message, bus=kopete_bus, signature='')

    # Both Approvers now have a flashing icon or something, trying to get the
    # user's attention

    # The user responds to Empathy first
    call_async(q, cdo_iface, 'HandleWithTime',
               cs.tp_name_prefix + '.Client.Empathy', 13)

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

    account_path, conn_path, channels, requests, action_time, info = e.args
    assert action_time == 13

    # Empathy accepts the channels
    q.dbus_return(e.message, bus=empathy_bus, signature='')

    q.expect_many(
        EventPattern('dbus-return', method='HandleWithTime'),
        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') == []

    # From now on, it is an error to get HandleChannels (because we're
    # testing Claim())
    forbidden = [
        EventPattern('dbus-method-call', method='HandleChannels'),
    ]
    q.forbid_events(forbidden)

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

    claimed_chan = SimulatedChannel(conn, channel_properties)
    claimed_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
    assert e.args[3] == cdo_path, e.args
    assert e.args[4] == [], e.args  # no requests satisfied
    channels = e.args[2]
    assert len(channels) == 1, channels
    assert channels[0][0] == claimed_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, bus=kopete_bus, signature='')
    q.dbus_return(e.message, bus=empathy_bus, 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 == [[(claimed_chan.object_path, channel_properties)],
                      cdo_path, cdo_properties]
    assert k.args == e.args

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

    # Both Approvers now have a flashing icon or something, trying to get the
    # user's attention

    # The user responds to Empathy first, and Empathy decides it wants the
    # channel for itself
    empathy_cdo = empathy_bus.get_object(cdo.bus_name, cdo.object_path)
    empathy_cdo_iface = dbus.Interface(empathy_cdo, cs.CDO)
    call_async(q, empathy_cdo_iface, 'Claim')

    q.expect_many(
        EventPattern('dbus-signal', path=cdo_path, signal='Finished'),
        EventPattern('dbus-signal',
                     path=cs.CD_PATH,
                     signal='DispatchOperationFinished',
                     args=[cdo_path]),
        EventPattern('dbus-return', method='Claim'),
    )

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

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

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

    # third_chan should not be closed
    q.unforbid_events(forbidden)
    forbidden.append(
        EventPattern('dbus-method-call',
                     method='Close',
                     path=third_chan.object_path))
    q.forbid_events(forbidden)

    # 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]

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

    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),
    )
    q.dbus_return(k.message, bus=kopete_bus, signature='')
    q.dbus_return(e.message, bus=empathy_bus, signature='')

    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),
    )
    q.dbus_return(e.message, bus=empathy_bus, signature='')
    q.dbus_return(k.message, bus=kopete_bus, signature='')

    # Kopete closes this one
    kopete_cdo = kopete_bus.get_object(cdo.bus_name, cdo.object_path)
    kopete_cdo_iface = dbus.Interface(kopete_cdo, cs.CDO)
    call_async(q, kopete_cdo_iface, 'Claim')

    q.expect_many(
        EventPattern('dbus-signal', path=cdo_path, signal='Finished'),
        EventPattern('dbus-signal',
                     path=cs.CD_PATH,
                     signal='DispatchOperationFinished',
                     args=[cdo_path]),
        EventPattern('dbus-return', method='Claim'),
    )

    # Empathy crashes
    empathy.release_name()

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

    empathy_bus.flush()
    empathy_bus.close()

    # In response, the channels that were being handled by Empathy are closed.
    # Kopete's channel is *not* closed.
    q.expect_many(
        EventPattern('dbus-signal',
                     signal='NameOwnerChanged',
                     predicate=(lambda e: e.args[0] == empathy_unique_name and
                                e.args[2] == '')),
        EventPattern('dbus-method-call', path=chan.object_path,
                     method='Close'),
        EventPattern('dbus-method-call',
                     path=claimed_chan.object_path,
                     method='Close'),
    )

    sync_dbus(bus, q, mc)
    q.unforbid_events(forbidden)
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)

    # One client (Kopete) has less specific filters than the other (Empathy),
    # to make sure that the dispatcher would normally prefer Empathy; this
    # means that when we use Kopete as the preferred handler, we know that
    # if Kopete is invoked, then preferring the preferred handler correctly
    # took precedence over the normal logic.
    vague_fixed_properties = dbus.Dictionary(
        {
            cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_TEXT,
        }, signature='sv')
    text_fixed_properties = dbus.Dictionary(
        {
            cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT,
            cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_TEXT,
        },
        signature='sv')

    empathy_bus = dbus.bus.BusConnection()
    kopete_bus = dbus.bus.BusConnection()
    q.attach_to_bus(empathy_bus)
    q.attach_to_bus(kopete_bus)
    # Two clients want to observe, approve and handle channels
    empathy = SimulatedClient(q,
                              empathy_bus,
                              'Empathy',
                              observe=[text_fixed_properties],
                              approve=[text_fixed_properties],
                              handle=[text_fixed_properties],
                              bypass_approval=False)
    kopete = SimulatedClient(q,
                             kopete_bus,
                             'Kopete',
                             observe=[vague_fixed_properties],
                             approve=[vague_fixed_properties],
                             handle=[vague_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'][:]
    # Empathy has a more specific filter, so it comes first
    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)
    cdo_props_iface = dbus.Interface(cdo, cs.PROPERTIES_IFACE)

    assert cdo_props_iface.Get(cs.CDO, 'Interfaces') == \
            cdo_properties[cs.CDO + '.Interfaces']
    assert cdo_props_iface.Get(cs.CDO, 'Connection') == conn.object_path
    assert cdo_props_iface.Get(cs.CDO, 'Account') == account.object_path
    assert cdo_props_iface.Get(cs.CDO, 'Channels') == [(chan.object_path,
                                                        channel_properties)]
    assert cdo_props_iface.Get(cs.CDO, 'PossibleHandlers') == \
            cdo_properties[cs.CDO + '.PossibleHandlers']

    # 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
    assert e.args[3] == cdo_path, e.args
    assert e.args[4] == [], e.args  # no requests satisfied
    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, bus=empathy_bus, signature='')
    q.dbus_return(e.message, bus=kopete_bus, 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, bus=empathy_bus, signature='')
    q.dbus_return(k.message, bus=kopete_bus, signature='')

    # Both Approvers now have a flashing icon or something, trying to get the
    # user's attention. However, the user is busy looking through the address
    # book, and independently decides to talk to Juliet. The address book
    # is from KDE so wants Kopete to be used.

    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,
               'EnsureChannel',
               account.object_path,
               request,
               user_action_time,
               kopete.bus_name,
               dbus_interface=cs.CD)
    ret, add_request_call = q.expect_many(
        EventPattern('dbus-return', method='EnsureChannel'),
        EventPattern('dbus-method-call',
                     handled=False,
                     interface=cs.CLIENT_IFACE_REQUESTS,
                     method='AddRequest',
                     path=kopete.object_path),
    )
    request_path = ret.value[0]

    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'] == kopete.bus_name
    assert request_props[cs.CR + '.Interfaces'] == []

    # 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'] == kopete.bus_name
    assert request_props['Interfaces'] == []

    cr.Proceed(dbus_interface=cs.CR)

    cm_request_call = q.expect('dbus-method-call',
                               interface=cs.CONN_IFACE_REQUESTS,
                               method='EnsureChannel',
                               path=conn.object_path,
                               args=[request],
                               handled=False)

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

    # Time passes. The CM returns the existing channel

    q.dbus_return(cm_request_call.message,
                  False,
                  chan.object_path,
                  chan.immutable,
                  signature='boa{sv}')

    # EnsureChannel constitutes approval, so Kopete is told to handle the
    # channel

    e = q.expect('dbus-method-call',
                 path=kopete.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] == chan.object_path, channels
    assert channels[0][1] == chan.immutable, channels
    assert e.args[3] == [request_path], e.args
    assert e.args[4] == user_action_time, (e.args[4], user_action_time)
    assert isinstance(e.args[5], dict)
    assert len(e.args) == 6

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

    q.expect_many(
        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') == []
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')

    empathy_bus = dbus.bus.BusConnection()
    empathy_bus.set_exit_on_disconnect(False)   # we'll disconnect later
    kopete_bus = dbus.bus.BusConnection()
    q.attach_to_bus(empathy_bus)
    q.attach_to_bus(kopete_bus)
    # Two clients want to observe, approve and handle channels
    empathy = SimulatedClient(q, empathy_bus, 'Empathy',
            observe=[text_fixed_properties], approve=[text_fixed_properties],
            handle=[text_fixed_properties], bypass_approval=False)
    kopete = SimulatedClient(q, kopete_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)
    cdo_props_iface = dbus.Interface(cdo, cs.PROPERTIES_IFACE)

    assert cdo_props_iface.Get(cs.CDO, 'Interfaces') == \
            cdo_properties[cs.CDO + '.Interfaces']
    assert cdo_props_iface.Get(cs.CDO, 'Connection') == conn.object_path
    assert cdo_props_iface.Get(cs.CDO, 'Account') == account.object_path
    assert cdo_props_iface.Get(cs.CDO, 'Channels') == [(chan.object_path,
        channel_properties)]
    assert cdo_props_iface.Get(cs.CDO, 'PossibleHandlers') == \
            cdo_properties[cs.CDO + '.PossibleHandlers']

    # 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
    assert e.args[3] == cdo_path, e.args
    assert e.args[4] == [], e.args      # no requests satisfied
    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, bus=empathy_bus, signature='')
    q.dbus_return(e.message, bus=kopete_bus, 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, bus=empathy_bus, signature='')
    q.dbus_return(k.message, bus=kopete_bus, signature='')

    # Both Approvers now have a flashing icon or something, trying to get the
    # user's attention

    # Using an invalid Handler name should fail
    call_async(q, cdo_iface, 'HandleWith',
            cs.tp_name_prefix + '.The.Moon.On.A.Stick')
    q.expect('dbus-error', method='HandleWith')
    call_async(q, cdo_iface, 'HandleWith',
            cs.tp_name_prefix + '.Client.the moon on a stick')
    q.expect('dbus-error', method='HandleWith')

    # The user responds to Empathy first
    call_async(q, cdo_iface, 'HandleWith',
            cs.tp_name_prefix + '.Client.Empathy')

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

    # Empathy accepts the channels
    q.dbus_return(e.message, bus=empathy_bus, 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') == []

    # Approve another channel using HandleWithTime()
    channel_properties = dbus.Dictionary(text_fixed_properties,
            signature='sv')
    channel_properties[cs.CHANNEL + '.TargetID'] = 'lucien'
    channel_properties[cs.CHANNEL + '.TargetHandle'] = \
            conn.ensure_handle(cs.HT_CONTACT, 'lucien')
    channel_properties[cs.CHANNEL + '.InitiatorID'] = 'lucien'
    channel_properties[cs.CHANNEL + '.InitiatorHandle'] = \
            conn.ensure_handle(cs.HT_CONTACT, 'lucien')
    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)
    cdo_props_iface = dbus.Interface(cdo, cs.PROPERTIES_IFACE)

    assert cdo_props_iface.Get(cs.CDO, 'Interfaces') == \
            cdo_properties[cs.CDO + '.Interfaces']
    assert cdo_props_iface.Get(cs.CDO, 'Connection') == conn.object_path
    assert cdo_props_iface.Get(cs.CDO, 'Account') == account.object_path
    assert cdo_props_iface.Get(cs.CDO, 'Channels') == [(chan.object_path,
        channel_properties)]
    assert cdo_props_iface.Get(cs.CDO, 'PossibleHandlers') == \
            cdo_properties[cs.CDO + '.PossibleHandlers']

    # 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
    assert e.args[3] == cdo_path, e.args
    assert e.args[4] == [], e.args      # no requests satisfied
    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, bus=empathy_bus, signature='')
    q.dbus_return(e.message, bus=kopete_bus, 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, bus=empathy_bus, signature='')
    q.dbus_return(k.message, bus=kopete_bus, signature='')

    # Both Approvers now have a flashing icon or something, trying to get the
    # user's attention

    # The user responds to Empathy first
    call_async(q, cdo_iface, 'HandleWithTime',
            cs.tp_name_prefix + '.Client.Empathy', 13)

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

    account_path, conn_path, channels, requests, action_time, info = e.args
    assert action_time == 13

    # Empathy accepts the channels
    q.dbus_return(e.message, bus=empathy_bus, signature='')

    q.expect_many(
            EventPattern('dbus-return', method='HandleWithTime'),
            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') == []

    # From now on, it is an error to get HandleChannels (because we're
    # testing Claim())
    forbidden = [
            EventPattern('dbus-method-call', method='HandleChannels'),
            ]
    q.forbid_events(forbidden)

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

    claimed_chan = SimulatedChannel(conn, channel_properties)
    claimed_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
    assert e.args[3] == cdo_path, e.args
    assert e.args[4] == [], e.args      # no requests satisfied
    channels = e.args[2]
    assert len(channels) == 1, channels
    assert channels[0][0] == claimed_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, bus=kopete_bus, signature='')
    q.dbus_return(e.message, bus=empathy_bus, 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 == [[(claimed_chan.object_path, channel_properties)],
            cdo_path, cdo_properties]
    assert k.args == e.args

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

    # Both Approvers now have a flashing icon or something, trying to get the
    # user's attention

    # The user responds to Empathy first, and Empathy decides it wants the
    # channel for itself
    empathy_cdo = empathy_bus.get_object(cdo.bus_name, cdo.object_path)
    empathy_cdo_iface = dbus.Interface(empathy_cdo, cs.CDO)
    call_async(q, empathy_cdo_iface, 'Claim')

    q.expect_many(
            EventPattern('dbus-signal', path=cdo_path, signal='Finished'),
            EventPattern('dbus-signal', path=cs.CD_PATH,
                signal='DispatchOperationFinished', args=[cdo_path]),
            EventPattern('dbus-return', method='Claim'),
            )

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

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

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

    # third_chan should not be closed
    q.unforbid_events(forbidden)
    forbidden.append(EventPattern('dbus-method-call', method='Close',
        path=third_chan.object_path))
    q.forbid_events(forbidden)

    # 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]

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

    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),
            )
    q.dbus_return(k.message, bus=kopete_bus, signature='')
    q.dbus_return(e.message, bus=empathy_bus, signature='')

    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),
            )
    q.dbus_return(e.message, bus=empathy_bus, signature='')
    q.dbus_return(k.message, bus=kopete_bus, signature='')

    # Kopete closes this one
    kopete_cdo = kopete_bus.get_object(cdo.bus_name, cdo.object_path)
    kopete_cdo_iface = dbus.Interface(kopete_cdo, cs.CDO)
    call_async(q, kopete_cdo_iface, 'Claim')

    q.expect_many(
            EventPattern('dbus-signal', path=cdo_path, signal='Finished'),
            EventPattern('dbus-signal', path=cs.CD_PATH,
                signal='DispatchOperationFinished', args=[cdo_path]),
            EventPattern('dbus-return', method='Claim'),
            )

    # Empathy crashes
    empathy.release_name()

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

    empathy_bus.flush()
    empathy_bus.close()

    # In response, the channels that were being handled by Empathy are closed.
    # Kopete's channel is *not* closed.
    q.expect_many(
            EventPattern('dbus-signal',
                signal='NameOwnerChanged',
                predicate=(lambda e:
                    e.args[0] == empathy_unique_name and e.args[2] == '')),
            EventPattern('dbus-method-call',
                path=chan.object_path, method='Close'),
            EventPattern('dbus-method-call',
                path=claimed_chan.object_path, method='Close'),
            )

    sync_dbus(bus, q, mc)
    q.unforbid_events(forbidden)
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.RespawnObservers'
        },
        signature='sv')

    # Logger is an activatable Observer who will crash
    logger_bus = dbus.bus.BusConnection()
    logger_bus.set_exit_on_disconnect(False)  # we'll disconnect later

    # Kopete is an Approver, Handler and will not crash
    kopete_bus = dbus.bus.BusConnection()
    q.attach_to_bus(logger_bus)
    q.attach_to_bus(kopete_bus)

    kopete = SimulatedClient(q,
                             kopete_bus,
                             'Kopete',
                             observe=[],
                             approve=[text_fixed_properties],
                             handle=[text_fixed_properties],
                             bypass_approval=False)

    # wait for MC to download the properties
    expect_client_setup(q, [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.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)
    cdo_props_iface = dbus.Interface(cdo, cs.PROPERTIES_IFACE)

    assert cdo_props_iface.Get(cs.CDO, 'Interfaces') == \
            cdo_properties[cs.CDO + '.Interfaces']
    assert cdo_props_iface.Get(cs.CDO, 'Connection') == conn.object_path
    assert cdo_props_iface.Get(cs.CDO, 'Account') == account.object_path
    assert cdo_props_iface.Get(cs.CDO, 'Channels') == [(chan.object_path,
                                                        channel_properties)]
    assert cdo_props_iface.Get(cs.CDO, 'PossibleHandlers') == \
            cdo_properties[cs.CDO + '.PossibleHandlers']

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

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

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

    # The fake Logger implementation is run
    e = q.expect(
        'dbus-signal',
        path=cs.tp_path_prefix + '/RegressionTests',
        interface=cs.tp_name_prefix + '.RegressionTests',
        signal='FakeStartup',
        args=[cs.tp_name_prefix + '.Client.Logger'],
    )
    # We take on its identity to be able to continue with the test
    logger = SimulatedClient(q, bus, 'Logger', observe=[text_fixed_properties])

    # Logger is told about the new channel
    e = q.expect('dbus-method-call',
                 path=logger.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] == cdo_path, e.args
    assert e.args[4] == [], e.args  # no requests satisfied
    channels = e.args[2]
    assert len(channels) == 1, channels
    assert channels[0][0] == chan.object_path, channels
    assert channels[0][1] == channel_properties, channels

    # Logger indicates that it is ready to proceed
    q.dbus_return(e.message, bus=logger_bus, signature='')

    # The Approver (Kopete) is next

    # The user responds to Kopete
    call_async(q, cdo_iface, 'HandleWith',
               cs.tp_name_prefix + '.Client.Kopete')

    # Kopete is asked to handle the channels
    k = q.expect('dbus-method-call',
                 path=kopete.object_path,
                 interface=cs.HANDLER,
                 method='HandleChannels',
                 handled=False)

    # Kopete accepts the channels
    q.dbus_return(k.message, bus=kopete_bus, 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') == []

    # Logger crashes
    logger.release_name()

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

    logger_bus.flush()

    # Logger gets restart request
    e = q.expect(
        'dbus-signal',
        path=cs.tp_path_prefix + '/RegressionTests',
        interface=cs.tp_name_prefix + '.RegressionTests',
        signal='FakeStartup',
        args=[cs.tp_name_prefix + '.Client.Logger'],
    )

    # 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_unique_name = e.args[2]

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

    # FIXME: assert the same things as before, except CDO (which we don't
    # have) and account path (which we don't know how to get); also check
    # that the recovering observer info key is set
    assert e.args[0] == account.object_path, e.args
    assert e.args[1] == conn.object_path, e.args
    assert e.args[4] == [], e.args  # no requests satisfied
    assert e.args[5]['recovering'] == 1, e.args  # due to observer recovery
    channels = e.args[2]
    assert len(channels) == 1, channels
    assert channels[0][0] == chan.object_path, channels
    assert channels[0][1] == channel_properties, channels

    # Logger indicates that it is ready to proceed
    q.dbus_return(e.message, bus=logger_bus, signature='')

    sync_dbus(bus, q, mc)