diff options
author | Guillaume Desmottes <guillaume.desmottes@collabora.co.uk> | 2011-03-03 13:15:47 +0100 |
---|---|---|
committer | Guillaume Desmottes <guillaume.desmottes@collabora.co.uk> | 2011-03-03 13:15:47 +0100 |
commit | 55f1a0545ede0d54ce65c2a32bb87bda62a9f5b5 (patch) | |
tree | c9524642a53282e17743ab31a13f8cb7802a9078 | |
parent | ca1e743e3f5ff6726981d0b61f763629d42866d1 (diff) | |
parent | 4371e5e46ebd8d6537c9d6ba5106ee8bc703c1b8 (diff) | |
download | telepathy-mission-control-55f1a0545ede0d54ce65c2a32bb87bda62a9f5b5.tar.gz |
Merge remote branch 'jonny/delay-approvers'
-rw-r--r-- | src/mcd-client-priv.h | 2 | ||||
-rw-r--r-- | src/mcd-client.c | 19 | ||||
-rw-r--r-- | src/mcd-dispatch-operation.c | 42 | ||||
-rw-r--r-- | test/twisted/dispatcher/delay-approvers.py | 141 | ||||
-rw-r--r-- | test/twisted/dispatcher/delay-then-call-handle-with.py | 141 | ||||
-rw-r--r-- | test/twisted/dispatcher/delay-then-dont-call-approvers.py | 128 | ||||
-rw-r--r-- | test/twisted/dispatcher/some-delay-approvers.py | 157 | ||||
-rw-r--r-- | tests/twisted/Makefile.am | 4 | ||||
-rw-r--r-- | tests/twisted/mctest.py | 4 |
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) |