~ubuntu-branches/ubuntu/precise/telepathy-mission-control-5/precise

« back to all changes in this revision

Viewing changes to tests/twisted/dispatcher/ensure-and-redispatch.py

  • Committer: Bazaar Package Importer
  • Author(s): Jonny Lamb
  • Date: 2011-01-27 17:54:12 UTC
  • mto: (0.12.1 upstream) (7.1.4 maverick)
  • mto: This revision was merged to the branch mainline in revision 8.
  • Revision ID: james.westby@ubuntu.com-20110127175412-cijhp5z0763s11cy
Tags: upstream-5.7.2
ImportĀ upstreamĀ versionĀ 5.7.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
19
"""Feature test ensuring that MC deals correctly with EnsureChannel returning
 
20
a channel that has already been dispatched to a handler.
 
21
"""
 
22
 
 
23
import dbus
 
24
import dbus.service
 
25
 
 
26
from servicetest import (EventPattern, tp_name_prefix, tp_path_prefix,
 
27
        call_async, assertContains, assertLength, assertEquals)
 
28
from mctest import exec_test, SimulatedConnection, SimulatedClient, \
 
29
        create_fakecm_account, enable_fakecm_account, SimulatedChannel, \
 
30
        expect_client_setup
 
31
import constants as cs
 
32
 
 
33
def test(q, bus, mc):
 
34
    params = dbus.Dictionary({"account": "someguy@example.com",
 
35
        "password": "secrecy"}, signature='sv')
 
36
    cm_name_ref, account = create_fakecm_account(q, bus, mc, params)
 
37
    conn = enable_fakecm_account(q, bus, mc, account, params)
 
38
 
 
39
    text_fixed_properties = dbus.Dictionary({
 
40
        cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT,
 
41
        cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_TEXT,
 
42
        }, signature='sv')
 
43
 
 
44
    # We have more than one Client "head" on the same unique name, to test
 
45
    # fd.o #24645 - we want the same "head" to be reinvoked if at all possible
 
46
 
 
47
    # This one is first in alphabetical order, is discovered first, and can
 
48
    # handle all channels, but is not the one we want
 
49
    empathy_worse_match = SimulatedClient(q, bus, 'A.Temporary.Handler',
 
50
            observe=[], approve=[],
 
51
            handle=[[]], bypass_approval=False)
 
52
 
 
53
    # This is the one we actually want
 
54
    empathy = SimulatedClient(q, bus, 'Empathy',
 
55
            observe=[text_fixed_properties], approve=[text_fixed_properties],
 
56
            handle=[text_fixed_properties], bypass_approval=False)
 
57
 
 
58
    # this one is a closer match, but not the preferred handler
 
59
    closer_match = dbus.Dictionary(text_fixed_properties)
 
60
    closer_match[cs.CHANNEL + '.TargetID'] = 'juliet'
 
61
    empathy_better_match = SimulatedClient(q, bus, 'Empathy.BetterMatch',
 
62
            observe=[], approve=[],
 
63
            handle=[closer_match], bypass_approval=False)
 
64
 
 
65
    # wait for MC to download the properties
 
66
    expect_client_setup(q, [empathy_worse_match, empathy,
 
67
        empathy_better_match])
 
68
 
 
69
    channel = test_channel_creation(q, bus, account, empathy, conn)
 
70
 
 
71
    # After the channel has been dispatched, a handler that would normally
 
72
    # be a closer match turns up. Regardless, we should not redispatch to it.
 
73
    # For the better client to be treated as if it's in a different process,
 
74
    # it needs its own D-Bus connection.
 
75
    closer_match = dbus.Dictionary(text_fixed_properties)
 
76
    closer_match[cs.CHANNEL + '.TargetID'] = 'juliet'
 
77
    closer_match[cs.CHANNEL + '.InitiatorID'] = conn.self_ident
 
78
    better_bus = dbus.bus.BusConnection()
 
79
    q.attach_to_bus(better_bus)
 
80
    better = SimulatedClient(q, better_bus, 'BetterMatch',
 
81
            observe=[], approve=[],
 
82
            handle=[closer_match], bypass_approval=False)
 
83
    expect_client_setup(q, [better])
 
84
 
 
85
    test_channel_redispatch(q, bus, account, empathy, conn, channel)
 
86
    test_channel_redispatch(q, bus, account, empathy, conn, channel,
 
87
            ungrateful_handler=True)
 
88
    empathy.release_name()
 
89
    empathy_better_match.release_name()
 
90
    empathy_worse_match.release_name()
 
91
    test_channel_redispatch(q, bus, account, empathy, conn, channel,
 
92
            client_gone=True)
 
93
    channel.close()
 
94
 
 
95
def test_channel_creation(q, bus, account, client, conn):
 
96
    user_action_time = dbus.Int64(1238582606)
 
97
 
 
98
    cd = bus.get_object(cs.CD, cs.CD_PATH)
 
99
    cd_props = dbus.Interface(cd, cs.PROPERTIES_IFACE)
 
100
 
 
101
    # chat UI calls ChannelDispatcher.EnsureChannel
 
102
    request = dbus.Dictionary({
 
103
            cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_TEXT,
 
104
            cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT,
 
105
            cs.CHANNEL + '.TargetID': 'juliet',
 
106
            }, signature='sv')
 
107
    account_requests = dbus.Interface(account,
 
108
            cs.ACCOUNT_IFACE_NOKIA_REQUESTS)
 
109
    call_async(q, cd, 'EnsureChannel',
 
110
            account.object_path, request, user_action_time, client.bus_name,
 
111
            dbus_interface=cs.CD)
 
112
    ret = q.expect('dbus-return', method='EnsureChannel')
 
113
    request_path = ret.value[0]
 
114
 
 
115
    # chat UI connects to signals and calls ChannelRequest.Proceed()
 
116
 
 
117
    cr = bus.get_object(cs.AM, request_path)
 
118
    request_props = cr.GetAll(cs.CR, dbus_interface=cs.PROPERTIES_IFACE)
 
119
    assert request_props['Account'] == account.object_path
 
120
    assert request_props['Requests'] == [request]
 
121
    assert request_props['UserActionTime'] == user_action_time
 
122
    assert request_props['PreferredHandler'] == client.bus_name
 
123
    assert request_props['Interfaces'] == []
 
124
 
 
125
    cr.Proceed(dbus_interface=cs.CR)
 
126
 
 
127
    cm_request_call, add_request_call = q.expect_many(
 
128
            EventPattern('dbus-method-call',
 
129
                interface=cs.CONN_IFACE_REQUESTS,
 
130
                method='EnsureChannel',
 
131
                path=conn.object_path, args=[request], handled=False),
 
132
            EventPattern('dbus-method-call', handled=False,
 
133
                interface=cs.CLIENT_IFACE_REQUESTS,
 
134
                method='AddRequest', path=client.object_path),
 
135
            )
 
136
 
 
137
    assert add_request_call.args[0] == request_path
 
138
    request_props = add_request_call.args[1]
 
139
    assert request_props[cs.CR + '.Account'] == account.object_path
 
140
    assert request_props[cs.CR + '.Requests'] == [request]
 
141
    assert request_props[cs.CR + '.UserActionTime'] == user_action_time
 
142
    assert request_props[cs.CR + '.PreferredHandler'] == client.bus_name
 
143
    assert request_props[cs.CR + '.Interfaces'] == []
 
144
 
 
145
    q.dbus_return(add_request_call.message, signature='')
 
146
 
 
147
    # Time passes. A channel is returned.
 
148
 
 
149
    channel_immutable = dbus.Dictionary(request)
 
150
    channel_immutable[cs.CHANNEL + '.InitiatorID'] = conn.self_ident
 
151
    channel_immutable[cs.CHANNEL + '.InitiatorHandle'] = conn.self_handle
 
152
    channel_immutable[cs.CHANNEL + '.Requested'] = True
 
153
    channel_immutable[cs.CHANNEL + '.Interfaces'] = \
 
154
        dbus.Array([], signature='s')
 
155
    channel_immutable[cs.CHANNEL + '.TargetHandle'] = \
 
156
        conn.ensure_handle(cs.HT_CONTACT, 'juliet')
 
157
    channel = SimulatedChannel(conn, channel_immutable)
 
158
 
 
159
    # this order of events is guaranteed by telepathy-spec (since 0.17.14)
 
160
    q.dbus_return(cm_request_call.message, True, # <- Yours
 
161
            channel.object_path, channel.immutable, signature='boa{sv}')
 
162
    channel.announce()
 
163
 
 
164
    # Observer should get told, processing waits for it
 
165
    e = q.expect('dbus-method-call',
 
166
            path=client.object_path,
 
167
            interface=cs.OBSERVER, method='ObserveChannels',
 
168
            handled=False)
 
169
    assert e.args[0] == account.object_path, e.args
 
170
    assert e.args[1] == conn.object_path, e.args
 
171
    assert e.args[3] == '/', e.args         # no dispatch operation
 
172
    assert e.args[4] == [request_path], e.args
 
173
    channels = e.args[2]
 
174
    assert len(channels) == 1, channels
 
175
    assert channels[0][0] == channel.object_path, channels
 
176
    assert channels[0][1] == channel_immutable, channels
 
177
 
 
178
    # Observer says "OK, go"
 
179
    q.dbus_return(e.message, signature='')
 
180
 
 
181
    # Handler is next
 
182
    e = q.expect('dbus-method-call',
 
183
            path=client.object_path,
 
184
            interface=cs.HANDLER, method='HandleChannels',
 
185
            handled=False)
 
186
    assert e.args[0] == account.object_path, e.args
 
187
    assert e.args[1] == conn.object_path, e.args
 
188
    channels = e.args[2]
 
189
    assert len(channels) == 1, channels
 
190
    assert channels[0][0] == channel.object_path, channels
 
191
    assert channels[0][1] == channel_immutable, channels
 
192
    assert e.args[3] == [request_path], e.args
 
193
    assert e.args[4] == user_action_time
 
194
    assert isinstance(e.args[5], dict)
 
195
    assert len(e.args) == 6
 
196
 
 
197
    # Handler accepts the Channels
 
198
    q.dbus_return(e.message, signature='')
 
199
 
 
200
    # CR emits Succeeded (or in Mardy's version, Account emits Succeeded)
 
201
    q.expect_many(
 
202
            EventPattern('dbus-signal', path=account.object_path,
 
203
                interface=cs.ACCOUNT_IFACE_NOKIA_REQUESTS, signal='Succeeded',
 
204
                args=[request_path]),
 
205
            EventPattern('dbus-signal', path=request_path,
 
206
                interface=cs.CR, signal='Succeeded'),
 
207
            )
 
208
 
 
209
    return channel
 
210
 
 
211
def test_channel_redispatch(q, bus, account, client, conn, channel,
 
212
        ungrateful_handler=False, client_gone=False):
 
213
 
 
214
    user_action_time = dbus.Int64(1244444444)
 
215
 
 
216
    forbidden = [
 
217
            # Because we create no new channels, nothing should be observed.
 
218
            EventPattern('dbus-method-call', method='ObserveChannels'),
 
219
            # Even though there is a better handler on a different unique
 
220
            # name, the channels must not be re-dispatched to it.
 
221
            EventPattern('dbus-method-call', method='HandleChannels',
 
222
                predicate=lambda e: e.path != client.object_path),
 
223
            # If the handler rejects the re-handle call, the channel must not
 
224
            # be closed.
 
225
            EventPattern('dbus-method-call', method='Close'),
 
226
            ]
 
227
 
 
228
    if client_gone:
 
229
        # There's nothing to call these methods on any more.
 
230
        forbidden.append(EventPattern('dbus-method-call',
 
231
            method='HandleChannels'))
 
232
        forbidden.append(EventPattern('dbus-method-call',
 
233
            method='AddRequest'))
 
234
 
 
235
    q.forbid_events(forbidden)
 
236
 
 
237
    cd = bus.get_object(cs.CD, cs.CD_PATH)
 
238
    cd_props = dbus.Interface(cd, cs.PROPERTIES_IFACE)
 
239
 
 
240
    # UI calls ChannelDispatcher.EnsureChannel again
 
241
    request = dbus.Dictionary({
 
242
            cs.CHANNEL + '.ChannelType': cs.CHANNEL_TYPE_TEXT,
 
243
            cs.CHANNEL + '.TargetHandleType': cs.HT_CONTACT,
 
244
            cs.CHANNEL + '.TargetID': 'juliet',
 
245
            }, signature='sv')
 
246
    call_async(q, cd, 'EnsureChannel',
 
247
            account.object_path, request, user_action_time, client.bus_name,
 
248
            dbus_interface=cs.CD)
 
249
    ret = q.expect('dbus-return', method='EnsureChannel')
 
250
    request_path = ret.value[0]
 
251
 
 
252
    # UI connects to signals and calls ChannelRequest.Proceed()
 
253
 
 
254
    cr = bus.get_object(cs.AM, request_path)
 
255
    request_props = cr.GetAll(cs.CR, dbus_interface=cs.PROPERTIES_IFACE)
 
256
    assert request_props['Account'] == account.object_path
 
257
    assert request_props['Requests'] == [request]
 
258
    assert request_props['UserActionTime'] == user_action_time
 
259
    assert request_props['PreferredHandler'] == client.bus_name
 
260
    assert request_props['Interfaces'] == []
 
261
 
 
262
    cr.Proceed(dbus_interface=cs.CR)
 
263
 
 
264
    cm_request_pattern = EventPattern('dbus-method-call',
 
265
        interface=cs.CONN_IFACE_REQUESTS,
 
266
        method='EnsureChannel',
 
267
        path=conn.object_path, args=[request], handled=False)
 
268
 
 
269
    if client_gone:
 
270
        (cm_request_call,) = q.expect_many(cm_request_pattern)
 
271
        add_request_call = None
 
272
    else:
 
273
        (cm_request_call, add_request_call) = q.expect_many(
 
274
                cm_request_pattern,
 
275
                EventPattern('dbus-method-call', handled=False,
 
276
                    interface=cs.CLIENT_IFACE_REQUESTS,
 
277
                    method='AddRequest', path=client.object_path),
 
278
                )
 
279
 
 
280
    if add_request_call is not None:
 
281
        assert add_request_call.args[0] == request_path
 
282
        request_props = add_request_call.args[1]
 
283
        assert request_props[cs.CR + '.Account'] == account.object_path
 
284
        assert request_props[cs.CR + '.Requests'] == [request]
 
285
        assert request_props[cs.CR + '.UserActionTime'] == user_action_time
 
286
        assert request_props[cs.CR + '.PreferredHandler'] == client.bus_name
 
287
        assert request_props[cs.CR + '.Interfaces'] == []
 
288
 
 
289
        q.dbus_return(add_request_call.message, signature='')
 
290
 
 
291
    # Time passes. The same channel is returned.
 
292
    q.dbus_return(cm_request_call.message, False, # <- Yours
 
293
            channel.object_path, channel.immutable, signature='boa{sv}')
 
294
 
 
295
    if not client_gone:
 
296
        # Handler is re-invoked. This HandleChannels call is only said to
 
297
        # satisfy the new request, because the earlier request has already
 
298
        # been satisfied.
 
299
        e = q.expect('dbus-method-call',
 
300
                path=client.object_path,
 
301
                interface=cs.HANDLER, method='HandleChannels',
 
302
                handled=False)
 
303
        assert e.args[0] == account.object_path, e.args
 
304
        assert e.args[1] == conn.object_path, e.args
 
305
        channels = e.args[2]
 
306
        assert len(channels) == 1, channels
 
307
        assert channels[0][0] == channel.object_path, channels
 
308
        assert channels[0][1] == channel.immutable, channels
 
309
        assert e.args[3] == [request_path], e.args
 
310
        assert e.args[4] == user_action_time
 
311
        assert isinstance(e.args[5], dict)
 
312
        assertContains('request-properties', e.args[5])
 
313
        assertContains(request_path, e.args[5]['request-properties'])
 
314
        assertLength(1, e.args[5]['request-properties'])
 
315
        assertEquals(request_props,
 
316
                e.args[5]['request-properties'][request_path])
 
317
        assert len(e.args) == 6
 
318
 
 
319
        if ungrateful_handler:
 
320
            q.dbus_raise(e.message, cs.INVALID_ARGUMENT,
 
321
                    'I am very strict in my misunderstanding of telepathy-spec')
 
322
        else:
 
323
            # Handler accepts the Channels
 
324
            q.dbus_return(e.message, signature='')
 
325
 
 
326
    # CR emits Succeeded (or in Mardy's version, Account emits Succeeded)
 
327
    q.expect_many(
 
328
            EventPattern('dbus-signal', path=account.object_path,
 
329
                interface=cs.ACCOUNT_IFACE_NOKIA_REQUESTS, signal='Succeeded',
 
330
                args=[request_path]),
 
331
            EventPattern('dbus-signal', path=request_path,
 
332
                interface=cs.CR, signal='Succeeded'),
 
333
            )
 
334
 
 
335
    q.unforbid_events(forbidden)
 
336
 
 
337
if __name__ == '__main__':
 
338
    exec_test(test, {})