summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuillaume Desmottes <guillaume.desmottes@collabora.co.uk>2011-03-03 13:15:47 +0100
committerGuillaume Desmottes <guillaume.desmottes@collabora.co.uk>2011-03-03 13:15:47 +0100
commit55f1a0545ede0d54ce65c2a32bb87bda62a9f5b5 (patch)
treec9524642a53282e17743ab31a13f8cb7802a9078
parentca1e743e3f5ff6726981d0b61f763629d42866d1 (diff)
parent4371e5e46ebd8d6537c9d6ba5106ee8bc703c1b8 (diff)
downloadtelepathy-mission-control-55f1a0545ede0d54ce65c2a32bb87bda62a9f5b5.tar.gz
Merge remote branch 'jonny/delay-approvers'
-rw-r--r--src/mcd-client-priv.h2
-rw-r--r--src/mcd-client.c19
-rw-r--r--src/mcd-dispatch-operation.c42
-rw-r--r--test/twisted/dispatcher/delay-approvers.py141
-rw-r--r--test/twisted/dispatcher/delay-then-call-handle-with.py141
-rw-r--r--test/twisted/dispatcher/delay-then-dont-call-approvers.py128
-rw-r--r--test/twisted/dispatcher/some-delay-approvers.py157
-rw-r--r--tests/twisted/Makefile.am4
-rw-r--r--tests/twisted/mctest.py4
9 files changed, 632 insertions, 6 deletions
diff --git a/src/mcd-client-priv.h b/src/mcd-client-priv.h
index 6e1a8c85..f20f9e99 100644
--- a/src/mcd-client-priv.h
+++ b/src/mcd-client-priv.h
@@ -99,6 +99,8 @@ G_GNUC_INTERNAL gboolean _mcd_client_proxy_get_bypass_approval
(McdClientProxy *self);
G_GNUC_INTERNAL gboolean _mcd_client_proxy_get_bypass_observers
(McdClientProxy *self);
+G_GNUC_INTERNAL gboolean _mcd_client_proxy_get_delay_approvers
+ (McdClientProxy *self);
G_GNUC_INTERNAL GValueArray *_mcd_client_proxy_dup_handler_capabilities (
McdClientProxy *self);
diff --git a/src/mcd-client.c b/src/mcd-client.c
index 392edf0e..a7ff0258 100644
--- a/src/mcd-client.c
+++ b/src/mcd-client.c
@@ -77,6 +77,7 @@ struct _McdClientProxyPrivate
gboolean ready;
gboolean bypass_approval;
gboolean bypass_observers;
+ gboolean delay_approvers;
gboolean recover;
/* If a client was in the ListActivatableNames list, it must not be
@@ -411,6 +412,10 @@ parse_client_file (McdClientProxy *client,
g_key_file_get_boolean (file, TP_IFACE_CLIENT_HANDLER,
"BypassObservers", NULL);
+ client->priv->delay_approvers =
+ g_key_file_get_boolean (file, TP_IFACE_CLIENT_OBSERVER,
+ "DelayApprovers", NULL);
+
client->priv->recover =
g_key_file_get_boolean (file, TP_IFACE_CLIENT_OBSERVER,
"Recover", NULL);
@@ -777,6 +782,12 @@ _mcd_client_proxy_observer_get_all_cb (TpProxy *proxy,
/* by now, we at least know whether the client is running or not */
g_assert (self->priv->unique_name != NULL);
+ /* FALSE if DelayApprovers is invalid or missing is a good fallback */
+ self->priv->delay_approvers = tp_asv_get_boolean (
+ properties, "DelayApprovers", NULL);
+ DEBUG ("%s has DelayApprovers=%c", bus_name,
+ self->priv->delay_approvers ? 'T' : 'F');
+
filters = tp_asv_get_boxed (properties, "ObserverChannelFilter",
TP_ARRAY_TYPE_STRING_VARIANT_MAP_LIST);
@@ -1411,6 +1422,14 @@ _mcd_client_proxy_get_bypass_observers (McdClientProxy *self)
return self->priv->bypass_observers;
}
+gboolean
+_mcd_client_proxy_get_delay_approvers (McdClientProxy *self)
+{
+ g_return_val_if_fail (MCD_IS_CLIENT_PROXY (self), FALSE);
+
+ return self->priv->delay_approvers;
+}
+
static void
_mcd_client_proxy_become_incapable (McdClientProxy *self)
{
diff --git a/src/mcd-dispatch-operation.c b/src/mcd-dispatch-operation.c
index c2b03e30..05bbcb7a 100644
--- a/src/mcd-dispatch-operation.c
+++ b/src/mcd-dispatch-operation.c
@@ -233,6 +233,11 @@ struct _McdDispatchOperationPrivate
* A reference is held for each pending observer. */
gsize observers_pending;
+ /* The number of observers that are pending which have
+ * DelayApprovers=TRUE. This is used to know if
+ * AddDispatchOperation can be called yet. */
+ gsize delay_approver_observers_pending;
+
/* The number of approvers that have not yet returned from
* AddDispatchOperation. Until they have done so, we can't allow the
* dispatch operation to finish. This is a client lock.
@@ -282,7 +287,8 @@ mcd_dispatch_operation_may_signal_finished (McdDispatchOperation *self)
}
static void
-_mcd_dispatch_operation_inc_observers_pending (McdDispatchOperation *self)
+_mcd_dispatch_operation_inc_observers_pending (McdDispatchOperation *self,
+ McdClientProxy *client)
{
g_return_if_fail (self->priv->result == NULL);
@@ -292,10 +298,14 @@ _mcd_dispatch_operation_inc_observers_pending (McdDispatchOperation *self)
self->priv->observers_pending,
self->priv->observers_pending + 1);
self->priv->observers_pending++;
+
+ if (_mcd_client_proxy_get_delay_approvers (client))
+ self->priv->delay_approver_observers_pending++;
}
static void
-_mcd_dispatch_operation_dec_observers_pending (McdDispatchOperation *self)
+_mcd_dispatch_operation_dec_observers_pending (McdDispatchOperation *self,
+ McdClientProxy *client)
{
DEBUG ("%" G_GSIZE_FORMAT " -> %" G_GSIZE_FORMAT,
self->priv->observers_pending,
@@ -303,6 +313,9 @@ _mcd_dispatch_operation_dec_observers_pending (McdDispatchOperation *self)
g_return_if_fail (self->priv->observers_pending > 0);
self->priv->observers_pending--;
+ if (_mcd_client_proxy_get_delay_approvers (client))
+ self->priv->delay_approver_observers_pending--;
+
_mcd_dispatch_operation_check_finished (self);
_mcd_dispatch_operation_check_client_locks (self);
g_object_unref (self);
@@ -376,6 +389,7 @@ static void
_mcd_dispatch_operation_check_client_locks (McdDispatchOperation *self)
{
Approval *approval;
+ guint approver_event_id = 0;
if (!self->priv->invoked_observers_if_needed)
{
@@ -408,13 +422,14 @@ _mcd_dispatch_operation_check_client_locks (McdDispatchOperation *self)
* return, then run that handler, then proceed with the other handlers. */
if (!self->priv->tried_handlers_before_approval &&
!_mcd_dispatch_operation_handlers_can_bypass_approval (self)
+ && self->priv->delay_approver_observers_pending == 0
&& self->priv->channels != NULL &&
! _mcd_plugin_dispatch_operation_will_terminate (
self->priv->plugin_api))
{
self->priv->tried_handlers_before_approval = TRUE;
- g_idle_add_full (G_PRIORITY_HIGH,
+ approver_event_id = g_idle_add_full (G_PRIORITY_HIGH,
mcd_dispatch_operation_idle_run_approvers,
g_object_ref (self), g_object_unref);
}
@@ -492,8 +507,25 @@ _mcd_dispatch_operation_check_client_locks (McdDispatchOperation *self)
caller);
g_free (caller);
+ if (approver_event_id > 0)
+ {
+ DEBUG ("Cancelling call to approvers as dispatch operation has been Claimed");
+ g_source_remove (approver_event_id);
+ }
+
return;
}
+ else if (approval != NULL && approval->type == APPROVAL_TYPE_HANDLE_WITH)
+ {
+ /* We set this to TRUE so that the handlers are called. */
+ self->priv->invoked_approvers_if_needed = TRUE;
+
+ if (approver_event_id > 0)
+ {
+ DEBUG ("Cancelling call to approvers as dispatch operation has been HandledWith'd");
+ g_source_remove (approver_event_id);
+ }
+ }
if (self->priv->invoked_approvers_if_needed)
{
@@ -1831,7 +1863,7 @@ observe_channels_cb (TpClient *proxy, const GError *error,
else
DEBUG ("success from %s", tp_proxy_get_object_path (proxy));
- _mcd_dispatch_operation_dec_observers_pending (self);
+ _mcd_dispatch_operation_dec_observers_pending (self, MCD_CLIENT_PROXY (proxy));
}
/*
@@ -1955,7 +1987,7 @@ _mcd_dispatch_operation_run_observers (McdDispatchOperation *self)
dispatch_operation_path = _mcd_dispatch_operation_get_path (self);
}
- _mcd_dispatch_operation_inc_observers_pending (self);
+ _mcd_dispatch_operation_inc_observers_pending (self, client);
DEBUG ("calling ObserveChannels on %s for CDO %p",
tp_proxy_get_bus_name (client), self);
diff --git a/test/twisted/dispatcher/delay-approvers.py b/test/twisted/dispatcher/delay-approvers.py
new file mode 100644
index 00000000..a627159d
--- /dev/null
+++ b/test/twisted/dispatcher/delay-approvers.py
@@ -0,0 +1,141 @@
+# Copyright (C) 2010 Collabora Ltd.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+# 02110-1301 USA
+
+import dbus
+import dbus.bus
+import dbus.service
+
+from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \
+ call_async, sync_dbus
+from mctest import exec_test, SimulatedConnection, SimulatedClient, \
+ create_fakecm_account, enable_fakecm_account, SimulatedChannel, \
+ expect_client_setup
+import constants as cs
+
+def test(q, bus, mc):
+ params = dbus.Dictionary({"account": "someguy@example.com",
+ "password": "secrecy"}, signature='sv')
+ cm_name_ref, 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') == []
+
+if __name__ == '__main__':
+ exec_test(test, {})
diff --git a/test/twisted/dispatcher/delay-then-call-handle-with.py b/test/twisted/dispatcher/delay-then-call-handle-with.py
new file mode 100644
index 00000000..2f79b10e
--- /dev/null
+++ b/test/twisted/dispatcher/delay-then-call-handle-with.py
@@ -0,0 +1,141 @@
+# Copyright (C) 2010 Collabora Ltd.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+# 02110-1301 USA
+
+import dbus
+import dbus.bus
+import dbus.service
+
+from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \
+ call_async, sync_dbus
+from mctest import exec_test, SimulatedConnection, SimulatedClient, \
+ create_fakecm_account, enable_fakecm_account, SimulatedChannel, \
+ expect_client_setup
+import constants as cs
+
+def test(q, bus, mc):
+ params = dbus.Dictionary({"account": "someguy@example.com",
+ "password": "secrecy"}, signature='sv')
+ cm_name_ref, 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, but Empathy will call HandleWith(Kopete) on the
+ # CDO so we should ensure ADO is never called on any approver.
+ forbidden = [EventPattern('dbus-method-call',
+ 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')
+
+ # We can't call this synchronously because MC won't return until
+ # HandleChannels returns.
+ call_async(q, cdo_iface, 'HandleWith', cs.CLIENT + '.Kopete')
+
+ # Finally return from ObserveChannels.
+ q.dbus_return(o.message, bus=bus, signature='')
+
+ h = q.expect('dbus-method-call',
+ path=kopete.object_path,
+ interface=cs.HANDLER, method='HandleChannels',
+ 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')
+
+ # Return from HandleChannels.
+ q.dbus_return(h.message, bus=bus, signature='')
+
+ # MC finally returns from HandleWith.
+ q.expect('dbus-return', method='HandleWith')
+
+ 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') == []
+
+ q.unforbid_events(forbidden)
+
+if __name__ == '__main__':
+ exec_test(test, {})
diff --git a/test/twisted/dispatcher/delay-then-dont-call-approvers.py b/test/twisted/dispatcher/delay-then-dont-call-approvers.py
new file mode 100644
index 00000000..90040228
--- /dev/null
+++ b/test/twisted/dispatcher/delay-then-dont-call-approvers.py
@@ -0,0 +1,128 @@
+# Copyright (C) 2010 Collabora Ltd.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+# 02110-1301 USA
+
+import dbus
+import dbus.bus
+import dbus.service
+
+from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \
+ call_async, sync_dbus
+from mctest import exec_test, SimulatedConnection, SimulatedClient, \
+ create_fakecm_account, enable_fakecm_account, SimulatedChannel, \
+ expect_client_setup
+import constants as cs
+
+def test(q, bus, mc):
+ params = dbus.Dictionary({"account": "someguy@example.com",
+ "password": "secrecy"}, signature='sv')
+ cm_name_ref, 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, but Empathy will call Claim on the CDO so we
+ # should ensure neither ADO or HC is called on any of our clients.
+ forbidden = [EventPattern('dbus-method-call',
+ interface=cs.APPROVER, method='AddDispatchOperation'),
+ EventPattern('dbus-method-call',
+ interface=cs.HANDLER, method='HandleChannels')]
+ 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')
+
+ # We can't call this synchronously because MC won't return until
+ # ObserveChannels calls return.
+ call_async(q, cdo_iface, 'Claim')
+
+ # Finally return from ObserveChannels.
+ q.dbus_return(o.message, bus=bus, signature='')
+
+ q.expect('dbus-return', method='Claim')
+
+ 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') == []
+
+ q.unforbid_events(forbidden)
+
+if __name__ == '__main__':
+ exec_test(test, {})
diff --git a/test/twisted/dispatcher/some-delay-approvers.py b/test/twisted/dispatcher/some-delay-approvers.py
new file mode 100644
index 00000000..e06b13a6
--- /dev/null
+++ b/test/twisted/dispatcher/some-delay-approvers.py
@@ -0,0 +1,157 @@
+# Copyright (C) 2010 Collabora Ltd.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+# 02110-1301 USA
+
+import dbus
+import dbus.bus
+import dbus.service
+
+from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \
+ call_async, sync_dbus
+from mctest import exec_test, SimulatedConnection, SimulatedClient, \
+ create_fakecm_account, enable_fakecm_account, SimulatedChannel, \
+ expect_client_setup
+import constants as cs
+
+def test(q, bus, mc):
+ params = dbus.Dictionary({"account": "someguy@example.com",
+ "password": "secrecy"}, signature='sv')
+ cm_name_ref, 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') == []
+
+if __name__ == '__main__':
+ exec_test(test, {})
diff --git a/tests/twisted/Makefile.am b/tests/twisted/Makefile.am
index e84febea..dba8b36a 100644
--- a/tests/twisted/Makefile.am
+++ b/tests/twisted/Makefile.am
@@ -39,6 +39,9 @@ TWISTED_BASIC_TESTS = \
dispatcher/create-rejected-by-mini-plugin.py \
dispatcher/create-text.py \
dispatcher/created-behind-our-back.py \
+ dispatcher/delay-approvers.py \
+ dispatcher/delay-then-call-handle-with.py \
+ dispatcher/delay-then-dont-call-approvers.py \
dispatcher/dispatch-activatable.py \
dispatcher/dispatch-before-connected.py \
dispatcher/dispatch-delayed-by-mini-plugin.py \
@@ -57,6 +60,7 @@ TWISTED_BASIC_TESTS = \
dispatcher/request-disabled-account.py \
dispatcher/respawn-observers.py \
dispatcher/respawn-activatable-observers.py \
+ dispatcher/some-delay-approvers.py \
dispatcher/undispatchable.py \
dispatcher/vanishing-client.py
diff --git a/tests/twisted/mctest.py b/tests/twisted/mctest.py
index 8a37e914..6c858bac 100644
--- a/tests/twisted/mctest.py
+++ b/tests/twisted/mctest.py
@@ -659,7 +659,7 @@ class SimulatedClient(object):
observe=[], approve=[], handle=[],
cap_tokens=[], bypass_approval=False, wants_recovery=False,
request_notification=True, implement_get_interfaces=True,
- is_handler=None, bypass_observers=False):
+ is_handler=None, bypass_observers=False, delay_approvers=False):
self.q = q
self.bus = bus
self.bus_name = '.'.join([cs.tp_name_prefix, 'Client', clientname])
@@ -670,6 +670,7 @@ class SimulatedClient(object):
self.handle = aasv(handle)
self.bypass_approval = bool(bypass_approval)
self.bypass_observers = bool(bypass_observers)
+ self.delay_approvers = bool(delay_approvers)
self.wants_recovery = bool(wants_recovery)
self.request_notification = bool(request_notification)
self.handled_channels = dbus.Array([], signature='o')
@@ -760,6 +761,7 @@ class SimulatedClient(object):
self.q.dbus_return(e.message, {
'ObserverChannelFilter': self.observe,
'Recover': self.wants_recovery,
+ 'DelayApprovers': dbus.Boolean(self.delay_approvers),
},
signature='a{sv}', bus=self.bus)