1# Copyright (C) 2009 Nokia Corporation 2# Copyright (C) 2009 Collabora Ltd. 3# 4# This library is free software; you can redistribute it and/or 5# modify it under the terms of the GNU Lesser General Public 6# License as published by the Free Software Foundation; either 7# version 2.1 of the License, or (at your option) any later version. 8# 9# This library is distributed in the hope that it will be useful, but 10# WITHOUT ANY WARRANTY; without even the implied warranty of 11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12# Lesser General Public License for more details. 13# 14# You should have received a copy of the GNU Lesser General Public 15# License along with this library; if not, write to the Free Software 16# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 17# 02110-1301 USA 18 19import dbus 20"""Regression test for dispatching an incoming Text channel with bypassed 21approval. 22""" 23 24import dbus 25import dbus.service 26 27from servicetest import EventPattern, tp_name_prefix, tp_path_prefix, \ 28 call_async, sync_dbus 29from mctest import exec_test, SimulatedConnection, SimulatedClient, \ 30 create_fakecm_account, enable_fakecm_account, SimulatedChannel, \ 31 expect_client_setup 32import constants as cs 33 34text_fixed_properties = dbus.Dictionary({ 35 cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_TEXT, 36 }, signature='sv') 37contact_text_fixed_properties = dbus.Dictionary({ 38 cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT, 39 cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_TEXT, 40 }, signature='sv') 41urgent_fixed_properties = dbus.Dictionary({ 42 cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT, 43 cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_TEXT, 44 'com.example.Urgency.Urgent': True, 45 }, signature='sv') 46 47def announce_common(q, bus, empathy, kopete, account, conn, cd_props, 48 urgent=False): 49 if urgent: 50 jid = 'friar.lawrence' 51 else: 52 jid = 'juliet' 53 54 channel_properties = dbus.Dictionary(contact_text_fixed_properties, 55 signature='sv') 56 channel_properties[cs.CHANNEL + '.TargetID'] = jid 57 channel_properties[cs.CHANNEL + '.TargetHandle'] = \ 58 conn.ensure_handle(cs.HT_CONTACT, jid) 59 channel_properties[cs.CHANNEL + '.InitiatorID'] = jid 60 channel_properties[cs.CHANNEL + '.InitiatorHandle'] = \ 61 conn.ensure_handle(cs.HT_CONTACT, jid) 62 channel_properties[cs.CHANNEL + '.Requested'] = False 63 channel_properties[cs.CHANNEL + '.Interfaces'] = dbus.Array(signature='s') 64 65 if urgent: 66 channel_properties['com.example.Urgency.Urgent'] = True 67 68 chan = SimulatedChannel(conn, channel_properties) 69 chan.announce() 70 71 # A channel dispatch operation is created 72 73 e = q.expect('dbus-signal', 74 path=cs.CD_PATH, 75 interface=cs.CD_IFACE_OP_LIST, 76 signal='NewDispatchOperation') 77 78 cdo_path = e.args[0] 79 cdo_properties = e.args[1] 80 81 assert cdo_properties[cs.CDO + '.Account'] == account.object_path 82 assert cdo_properties[cs.CDO + '.Connection'] == conn.object_path 83 assert cs.CDO + '.Interfaces' in cdo_properties 84 85 handlers = cdo_properties[cs.CDO + '.PossibleHandlers'][:] 86 87 if urgent: 88 # The handler with BypassApproval is first 89 assert handlers[0] == cs.tp_name_prefix + '.Client.Kopete.BypassApproval' 90 # Kopete's filter is more specific than Empathy's, so it comes next 91 assert handlers[1] == cs.tp_name_prefix + '.Client.Kopete' 92 # Empathy's filter is the least specific, so it's last 93 assert handlers[2] == cs.tp_name_prefix + '.Client.Empathy' 94 assert len(handlers) == 3 95 else: 96 handlers.sort() 97 assert handlers == [cs.tp_name_prefix + '.Client.Empathy', 98 cs.tp_name_prefix + '.Client.Kopete'], handlers 99 100 assert cs.CD_IFACE_OP_LIST in cd_props.Get(cs.CD, 'Interfaces') 101 assert cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations') ==\ 102 [(cdo_path, cdo_properties)] 103 104 cdo = bus.get_object(cs.CD, cdo_path) 105 cdo_iface = dbus.Interface(cdo, cs.CDO) 106 107 # Both Observers are told about the new channel 108 109 e, k = q.expect_many( 110 EventPattern('dbus-method-call', 111 path=empathy.object_path, 112 interface=cs.OBSERVER, method='ObserveChannels', 113 handled=False), 114 EventPattern('dbus-method-call', 115 path=kopete.object_path, 116 interface=cs.OBSERVER, method='ObserveChannels', 117 handled=False), 118 ) 119 assert e.args[0] == account.object_path, e.args 120 assert e.args[1] == conn.object_path, e.args 121 assert e.args[3] == cdo_path, e.args 122 assert e.args[4] == [], e.args # no requests satisfied 123 channels = e.args[2] 124 assert len(channels) == 1, channels 125 assert channels[0][0] == chan.object_path, channels 126 assert channels[0][1] == channel_properties, channels 127 128 assert k.args == e.args 129 130 return cdo_iface, chan, channel_properties, [e, k] 131 132def expect_and_exercise_approval(q, bus, chan, channel_properties, 133 empathy, kopete, cdo_iface, cd_props): 134 # The Approvers are next 135 136 e, k = q.expect_many( 137 EventPattern('dbus-method-call', 138 path=empathy.object_path, 139 interface=cs.APPROVER, method='AddDispatchOperation', 140 handled=False), 141 EventPattern('dbus-method-call', 142 path=kopete.object_path, 143 interface=cs.APPROVER, method='AddDispatchOperation', 144 handled=False), 145 ) 146 147 assert e.args[0] == [(chan.object_path, channel_properties)] 148 assert k.args == e.args 149 150 # Both Approvers indicate that they are ready to proceed 151 q.dbus_return(e.message, signature='') 152 q.dbus_return(k.message, signature='') 153 154 # Both Approvers now have a flashing icon or something, trying to get the 155 # user's attention 156 157 # The user responds to Empathy first 158 call_async(q, cdo_iface, 'HandleWith', 159 cs.tp_name_prefix + '.Client.Empathy') 160 161 # Empathy is asked to handle the channels 162 e = q.expect('dbus-method-call', 163 path=empathy.object_path, 164 interface=cs.HANDLER, method='HandleChannels', 165 handled=False) 166 167 # Empathy accepts the channels 168 q.dbus_return(e.message, signature='') 169 170 q.expect_many( 171 EventPattern('dbus-return', method='HandleWith'), 172 EventPattern('dbus-signal', interface=cs.CDO, signal='Finished'), 173 EventPattern('dbus-signal', interface=cs.CD_IFACE_OP_LIST, 174 signal='DispatchOperationFinished'), 175 ) 176 177 # Now there are no more active channel dispatch operations 178 assert cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations') == [] 179 180 181def test(q, bus, mc): 182 params = dbus.Dictionary({"account": "someguy@example.com", 183 "password": "secrecy"}, signature='sv') 184 cm_name_ref, account = create_fakecm_account(q, bus, mc, params) 185 conn = enable_fakecm_account(q, bus, mc, account, params) 186 187 # Two clients want to observe, approve and handle channels. Additionally, 188 # Kopete recognises an "Urgent" flag on certain incoming channels, and 189 # wants to bypass approval for them. 190 empathy = SimulatedClient(q, bus, 'Empathy', 191 observe=[text_fixed_properties], approve=[text_fixed_properties], 192 handle=[text_fixed_properties], bypass_approval=False) 193 kopete = SimulatedClient(q, bus, 'Kopete', 194 observe=[contact_text_fixed_properties], 195 approve=[contact_text_fixed_properties], 196 handle=[contact_text_fixed_properties], bypass_approval=False) 197 bypass = SimulatedClient(q, bus, 'Kopete.BypassApproval', 198 observe=[], approve=[], 199 handle=[urgent_fixed_properties], bypass_approval=True) 200 201 # wait for MC to download the properties 202 expect_client_setup(q, [empathy, kopete, bypass]) 203 204 # subscribe to the OperationList interface (MC assumes that until this 205 # property has been retrieved once, nobody cares) 206 207 cd = bus.get_object(cs.CD, cs.CD_PATH) 208 cd_props = dbus.Interface(cd, cs.PROPERTIES_IFACE) 209 assert cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations') == [] 210 211 # First, a non-urgent channel is created 212 213 cdo_iface, chan, channel_properties, observe_events = announce_common(q, 214 bus, empathy, kopete, account, conn, cd_props, False) 215 216 # Both Observers indicate that they are ready to proceed 217 for e in observe_events: 218 q.dbus_return(e.message, signature='') 219 220 expect_and_exercise_approval(q, bus, chan, channel_properties, 221 empathy, kopete, cdo_iface, cd_props) 222 223 # Now a channel that bypasses approval comes in. During this process, 224 # we should never be asked to approve anything. 225 226 approval = [ 227 EventPattern('dbus-method-call', method='AddDispatchOperation'), 228 ] 229 230 q.forbid_events(approval) 231 232 cdo_iface, chan, channel_properties, observe_events = announce_common(q, 233 bus, empathy, kopete, account, conn, cd_props, True) 234 235 # Both Observers indicate that they are ready to proceed 236 for e in observe_events: 237 q.dbus_return(e.message, signature='') 238 239 # Kopete's BypassApproval part is asked to handle the channels 240 e = q.expect('dbus-method-call', 241 path=bypass.object_path, 242 interface=cs.HANDLER, method='HandleChannels', 243 handled=False) 244 # Kopete accepts the channels 245 q.dbus_return(e.message, signature='') 246 247 q.unforbid_events(approval) 248 249 # Regression test for fd.o #22670 250 251 closure = [ 252 EventPattern('dbus-method-call', method='Close'), 253 ] 254 255 q.forbid_events(closure) 256 257 bypass.release_name() 258 sync_dbus(bus, q, mc) 259 260 q.unforbid_events(closure) 261 262 # Bring back that handler 263 del bypass 264 bypass = SimulatedClient(q, bus, 'Kopete.BypassApproval', 265 observe=[], approve=[], 266 handle=[urgent_fixed_properties], bypass_approval=True) 267 expect_client_setup(q, [bypass]) 268 269 # Another channel that bypasses approval comes in, but the handler that 270 # bypasses approval fails. 271 272 cdo_iface, chan, channel_properties, observe_events = announce_common(q, 273 bus, empathy, kopete, account, conn, cd_props, True) 274 275 # Both Observers indicate that they are ready to proceed 276 for e in observe_events: 277 q.dbus_return(e.message, signature='') 278 279 # Kopete's BypassApproval part is asked to handle the channels 280 e = q.expect('dbus-method-call', 281 path=bypass.object_path, 282 interface=cs.HANDLER, method='HandleChannels', 283 handled=False) 284 # Kopete's BypassApproval part fails to accept the channels 285 q.dbus_raise(e.message, 'com.example.Broken', 'No way') 286 287 # MC recovers by running the approvers and doing what they say 288 expect_and_exercise_approval(q, bus, chan, channel_properties, 289 empathy, kopete, cdo_iface, cd_props) 290 291if __name__ == '__main__': 292 exec_test(test, {}) 293 294