~facundo/magicicada-client/changes-for-xenial

« back to all changes in this revision

Viewing changes to ubuntuone/utils/tests/test_ipc.py

  • Committer: Magicicada Bot
  • Author(s): Natalia
  • Date: 2016-05-30 15:43:30 UTC
  • mfrom: (1418.1.21 no-sso-client)
  • Revision ID: magicicada_bot-20160530154330-b4his4s3wlucu7zv
[r=facundo] - Decouple client code from ubuntu-sso-client code. Copied and made an initial cleanup on the networkstate, utils and keyring modules.
- Removed completely dependencies with oauthlibs.
- Moved tests/ folder to inside ubuntuone/ proper folders.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
#
 
3
# Copyright 2011-2012 Canonical Ltd.
 
4
# Copyright 2015-2016 Chicharreros (https://launchpad.net/~chicharreros)
 
5
#
 
6
# This program is free software: you can redistribute it and/or modify it
 
7
# under the terms of the GNU General Public License version 3, as published
 
8
# by the Free Software Foundation.
 
9
#
 
10
# This program is distributed in the hope that it will be useful, but
 
11
# WITHOUT ANY WARRANTY; without even the implied warranties of
 
12
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
 
13
# PURPOSE.  See the GNU General Public License for more details.
 
14
#
 
15
# You should have received a copy of the GNU General Public License along
 
16
# with this program.  If not, see <http://www.gnu.org/licenses/>.
 
17
#
 
18
# In addition, as a special exception, the copyright holders give
 
19
# permission to link the code of portions of this program with the
 
20
# OpenSSL library under certain conditions as described in each
 
21
# individual source file, and distribute linked combinations
 
22
# including the two.
 
23
# You must obey the GNU General Public License in all respects
 
24
# for all of the code used other than OpenSSL.  If you modify
 
25
# file(s) with this exception, you may extend this exception to your
 
26
# version of the file(s), but you are not obligated to do so.  If you
 
27
# do not wish to do so, delete this exception statement from your
 
28
# version.  If you delete this exception statement from all source
 
29
# files in the program, then also delete it here.
 
30
 
 
31
"""Perspective Broker IPC test cases."""
 
32
 
 
33
import logging
 
34
 
 
35
from collections import namedtuple
 
36
 
 
37
from twisted.internet import defer
 
38
from twisted.spread.pb import (
 
39
    DeadReferenceError,
 
40
    NoSuchMethod,
 
41
)
 
42
from ubuntuone.devtools.handlers import MementoHandler
 
43
from ubuntuone.devtools.testcases import skipIfOS
 
44
from ubuntuone.devtools.testcases.txsocketserver import (
 
45
    TidyUnixServer,
 
46
    TCPPbServerTestCase,
 
47
)
 
48
 
 
49
from ubuntuone.tests import TestCase
 
50
from ubuntuone.utils import ipc
 
51
 
 
52
 
 
53
TEST_SERVICE = 'foo-service'
 
54
TEST_CMDLINE = 'foo.bin'
 
55
TEST_SERVER_DESCRIPTION = 'tcp:40404:interface=127.0.0.1'
 
56
TEST_CLIENT_DESCRIPTION = 'tcp:host=127.0.0.1:port=40404'
 
57
 
 
58
 
 
59
class RandomException(Exception):
 
60
    """A random exception."""
 
61
 
 
62
 
 
63
class FakeActivationClient(object):
 
64
    """A fake ActivationClient."""
 
65
 
 
66
    def __init__(self, config):
 
67
        """Initialize this fake instance."""
 
68
        self.config = config
 
69
 
 
70
    def get_active_client_description(self):
 
71
        """Return the description where the pb server is running."""
 
72
        return defer.succeed(self.config.description.client)
 
73
 
 
74
 
 
75
class FakeActivationInstance(object):
 
76
    """A fake ActivationInstance."""
 
77
 
 
78
    def __init__(self, config):
 
79
        """Initialize the fake instance."""
 
80
        self.config = config
 
81
 
 
82
    def get_server_description(self):
 
83
        """Return the description where the pb server is running."""
 
84
        return defer.succeed(self.config.description.server)
 
85
 
 
86
 
 
87
class FakeDescriptionFactory(object):
 
88
    """A fake description factory."""
 
89
 
 
90
    def __init__(self, server_description, client_description):
 
91
        """Create a new instace."""
 
92
        self.server = server_description
 
93
        self.client = client_description
 
94
 
 
95
 
 
96
class FakeReactor(object):
 
97
    """A fake reactor."""
 
98
 
 
99
    def __init__(self):
 
100
        """Initialize this faker."""
 
101
        self.connection_class = namedtuple("Connection",
 
102
                                         "host port factory backlog")
 
103
        self.connections = []
 
104
 
 
105
    def connectTCP(self, host, port, factory, timeout=None, bindAddress=None):
 
106
        """Store the connected factory."""
 
107
        connection = self.connection_class(host, port, factory, None)
 
108
        self.connections.append(connection)
 
109
 
 
110
    def listenTCP(self, port, factory, interface=None, backlog=None):
 
111
        """Store the listenning factory."""
 
112
        connection = self.connection_class(interface, port, factory, backlog)
 
113
        self.connections.append(connection)
 
114
 
 
115
 
 
116
class FakeTCP4ClientEndpoint(object):
 
117
    """A fake tcp4 client."""
 
118
 
 
119
    def __init__(self, protocol):
 
120
        """Create a new instance."""
 
121
        self.protocol = protocol
 
122
 
 
123
    def connect(self, *args, **kwargs):
 
124
        """Return the client."""
 
125
        return defer.succeed(self.protocol)
 
126
 
 
127
 
 
128
class FakeRemoteClient(object):
 
129
    """A fake RemoteClient."""
 
130
 
 
131
    missing_signal = "missing"
 
132
    failing_signal = "failing"
 
133
    dead_remote = False
 
134
    random_exception = RandomException()
 
135
 
 
136
    def __init__(self, dead_remote=False):
 
137
        self.called = False
 
138
        self.dead_remote = dead_remote
 
139
 
 
140
    def callRemote(self, signal_name, *a, **kw):
 
141
        """Fake a call to a given remote method."""
 
142
        if self.dead_remote:
 
143
            raise DeadReferenceError("Calling Stale Broker")
 
144
 
 
145
        if signal_name == self.missing_signal:
 
146
            return defer.fail(NoSuchMethod())
 
147
        if signal_name == self.failing_signal:
 
148
            return defer.fail(self.random_exception)
 
149
 
 
150
        self.called = (a, kw)
 
151
        return defer.succeed(None)
 
152
 
 
153
 
 
154
class DummyRemoteService(ipc.RemoteService):
 
155
    """Represent a dummy IPC object."""
 
156
 
 
157
    remote_calls = ['foo', 'bar']
 
158
    next_result = None
 
159
 
 
160
    def foo(self):
 
161
        """Dummy foo."""
 
162
        self.Success(self.next_result)
 
163
 
 
164
    def bar(self, error):
 
165
        """Dummy bar."""
 
166
        self.NotSuccess(error)
 
167
 
 
168
    @ipc.signal
 
169
    def Success(self, param):
 
170
        """Fire a signal to notify a success."""
 
171
 
 
172
    @ipc.signal
 
173
    def NotSuccess(self, error):
 
174
        """Fire a signal to notify a not-success."""
 
175
 
 
176
    @ipc.signal
 
177
    def NoArgs(self):
 
178
        """Get no args passed."""
 
179
 
 
180
    @ipc.signal
 
181
    def JustArgs(self, *args):
 
182
        """Just get args."""
 
183
 
 
184
    @ipc.signal
 
185
    def JustKwargs(self, **kwargs):
 
186
        """Just get kwargs."""
 
187
 
 
188
    @ipc.signal
 
189
    def BothArgsAndKwargs(self, *args, **kwargs):
 
190
        """Both args and kwargs."""
 
191
 
 
192
 
 
193
class DummyService(ipc.BaseService):
 
194
    """Represent a dummy root service."""
 
195
 
 
196
    services = {'dummy': DummyRemoteService}
 
197
    name = 'Dummy Service'
 
198
    description = TEST_CLIENT_DESCRIPTION
 
199
    cmdline = 'yadda yo'
 
200
 
 
201
 
 
202
class DummyRemoteClient(ipc.RemoteClient):
 
203
    """Represent a dummy remote client."""
 
204
 
 
205
    call_remote_functions = DummyRemoteService.remote_calls
 
206
    signal_handlers = ['Success', 'NotSuccess']
 
207
 
 
208
 
 
209
class DummyClient(ipc.BaseClient):
 
210
    """Represent a dummy base client."""
 
211
 
 
212
    clients = {'dummy': DummyRemoteClient}
 
213
    service_name = DummyService.name
 
214
    service_port = TEST_SERVER_DESCRIPTION
 
215
    service_cmdline = DummyService.cmdline
 
216
 
 
217
 
 
218
class DummyDescription(object):
 
219
    """Return the descriptions accordingly."""
 
220
 
 
221
    def __init__(self, client, server):
 
222
        """Create a new instance."""
 
223
        self.client = client
 
224
        self.server = server
 
225
 
 
226
 
 
227
class BaseIPCTestCase(TCPPbServerTestCase, TestCase):
 
228
    """Set the ipc to a random port for this instance."""
 
229
 
 
230
    timeout = 5
 
231
 
 
232
    client_class = None  # the BaseClient instance
 
233
    service_class = None  # the BaseService instance
 
234
 
 
235
    remote_client_name = None  # the name of the remote client in the client
 
236
    remote_service_name = None  # the name of the remote service in the service
 
237
 
 
238
    method_mapping = []
 
239
    signal_mapping = []
 
240
 
 
241
    @defer.inlineCallbacks
 
242
    def setUp(self):
 
243
        yield super(BaseIPCTestCase, self).setUp()
 
244
 
 
245
        self.service = None
 
246
        self.client = None
 
247
 
 
248
        if self.service_class is not None:
 
249
 
 
250
            self.service = self.service_class()
 
251
            self.client = self.client_class()
 
252
 
 
253
            # patch server connection and client connection to ensure that
 
254
            # we have clean connections
 
255
 
 
256
            @defer.inlineCallbacks
 
257
            def server_listen(server_factory, service_name, activation_cmd,
 
258
                              description, reactor=None):
 
259
                """Connect to the local running service."""
 
260
                yield self.listen_server(self.service)
 
261
                defer.returnValue(self.listener)
 
262
 
 
263
            self.patch(ipc, 'server_listen', server_listen)
 
264
 
 
265
            @defer.inlineCallbacks
 
266
            def client_connect(client_factory, service_name,
 
267
                               activation_cmdline, description, reactor=None):
 
268
                """Connect the local running client."""
 
269
                yield self.connect_client()
 
270
                self.client.factory = self.client_factory
 
271
                defer.returnValue(self.connector)
 
272
 
 
273
            self.patch(ipc, 'client_connect', client_connect)
 
274
 
 
275
            yield self.service.start()
 
276
            yield self.client.connect()
 
277
 
 
278
    @property
 
279
    def remote_service(self):
 
280
        """Get the service named 'service_name'."""
 
281
        return getattr(self.service, self.remote_service_name)
 
282
 
 
283
    @property
 
284
    def remote_client(self):
 
285
        """Get the client named 'remote_client_name'."""
 
286
        return getattr(self.client, self.remote_client_name)
 
287
 
 
288
    @defer.inlineCallbacks
 
289
    def assert_method_called(self, service, method, result, *args, **kwargs):
 
290
        """Check that calling 'method(*args, **kwargs)' should query 'service'.
 
291
 
 
292
        The returned result from calling 'method(*args, **kwargs)' should be
 
293
        equal to the given parameter 'result'. If 'result' is a deferred, its
 
294
        result attribute will be used as expected result (ergo the deferred
 
295
        should be already called).
 
296
 
 
297
        """
 
298
        client = self.remote_client
 
299
 
 
300
        # hack to handle service methods returning a deferred with result
 
301
        if isinstance(result, defer.Deferred):
 
302
            real_result = result.result
 
303
        else:
 
304
            real_result = result
 
305
 
 
306
        self.patch(service, method, lambda *a, **kw: result)
 
307
        actual = yield client.call_method(method, *args, **kwargs)
 
308
        self.assertEqual(real_result, actual)
 
309
        self.assertEqual(service.called, {method: [(args, kwargs)]})
 
310
 
 
311
    def assert_remote_method(self, method_name, *args, **kwargs):
 
312
        """Assert that 'method_name' is a remote method.
 
313
 
 
314
        The parameters args and kwargs will be passed as such to the method
 
315
        itself, to exercise it.
 
316
 
 
317
        """
 
318
        self.assertIn(method_name, self.remote_service.remote_calls)
 
319
        method = getattr(self.remote_service, method_name)
 
320
        method(*args, **kwargs)
 
321
 
 
322
    def assert_remote_signal(self, signal_name, *args, **kwargs):
 
323
        """Assert that 'signal' is a remote signal.
 
324
 
 
325
        The parameters args and kwargs will be passed as such to the signal
 
326
        itself, to exercise it.
 
327
 
 
328
        """
 
329
        self.patch(self.remote_service, 'emit_signal', self._set_called)
 
330
        signal = getattr(self.remote_service, signal_name)
 
331
        signal(*args, **kwargs)
 
332
 
 
333
        expected = (signal_name,) + args
 
334
        self.assertEqual(self._called, (expected, kwargs))
 
335
 
 
336
    def test_remote_methods(self):
 
337
        """Check every method defined in self.method_mapping.
 
338
 
 
339
        Assert that every method is a remote method and that it has the
 
340
        expected signature.
 
341
 
 
342
        """
 
343
        for method, args, kwargs in self.method_mapping:
 
344
            self.assert_remote_method(method, *args, **kwargs)
 
345
 
 
346
    def test_remote_signals(self):
 
347
        """Check every signal defined in self.signal_mapping.
 
348
 
 
349
        Assert that every signal is a remote signal and that it has the
 
350
        expected signature.
 
351
 
 
352
        """
 
353
        for signal_name, args, kwargs in self.signal_mapping:
 
354
            self.assert_remote_signal(signal_name, *args, **kwargs)
 
355
 
 
356
 
 
357
class TCPListenConnectTestCase(BaseIPCTestCase):
 
358
    """Test suite for the server_listen and client_connect methods."""
 
359
 
 
360
    @defer.inlineCallbacks
 
361
    def setUp(self):
 
362
        yield super(TCPListenConnectTestCase, self).setUp()
 
363
        self.fake_reactor = FakeReactor()
 
364
 
 
365
    @defer.inlineCallbacks
 
366
    def test_server_listen(self):
 
367
        """Test the server_listen function."""
 
368
        self.patch(ipc, "ActivationInstance", FakeActivationInstance)
 
369
 
 
370
        description_factory = FakeDescriptionFactory(TEST_SERVER_DESCRIPTION,
 
371
                                                     TEST_CLIENT_DESCRIPTION)
 
372
 
 
373
        fake_factory = object()
 
374
        yield ipc.server_listen(fake_factory, TEST_SERVICE,
 
375
                                TEST_CMDLINE, description_factory,
 
376
                                reactor=self.fake_reactor)
 
377
 
 
378
        self.assertEqual(len(self.fake_reactor.connections), 1)
 
379
        conn = self.fake_reactor.connections[0]
 
380
        self.assertEqual(conn.factory, fake_factory)
 
381
        self.assertEqual(conn.host, ipc.LOCALHOST)
 
382
 
 
383
    @defer.inlineCallbacks
 
384
    def test_client_connect(self):
 
385
        """Test the client_connect function."""
 
386
        called = []
 
387
        self.patch(ipc, "ActivationClient", FakeActivationClient)
 
388
 
 
389
        protocol = 'protocol'
 
390
        client = FakeTCP4ClientEndpoint(protocol)
 
391
 
 
392
        def client_from_string(reactor, description):
 
393
            """Create a client from the given string."""
 
394
            called.append(('clientFromString', reactor, description))
 
395
            return client
 
396
 
 
397
        self.patch(ipc.endpoints, 'clientFromString', client_from_string)
 
398
 
 
399
        description_factory = FakeDescriptionFactory(TEST_SERVER_DESCRIPTION,
 
400
                                                     TEST_CLIENT_DESCRIPTION)
 
401
 
 
402
        fake_factory = object()
 
403
        returned_protocol = yield ipc.client_connect(fake_factory,
 
404
                                              TEST_SERVICE,
 
405
                                              TEST_CMDLINE,
 
406
                                              description_factory,
 
407
                                              reactor=self.fake_reactor)
 
408
 
 
409
        self.assertIn(('clientFromString', self.fake_reactor,
 
410
                          description_factory.client), called)
 
411
        self.assertEqual(protocol, returned_protocol)
 
412
 
 
413
 
 
414
@skipIfOS('win32', 'Unix domain sockets not supported on windows.')
 
415
class DomainListenConnectTestCase(TCPListenConnectTestCase):
 
416
    """Test suite for the server_listen and client_connect methods."""
 
417
 
 
418
    def get_server(self):
 
419
        """Return the server to be used to run the tests."""
 
420
        return TidyUnixServer()
 
421
 
 
422
 
 
423
class TCPDummyClientTestCase(BaseIPCTestCase):
 
424
    """Test the status client class."""
 
425
 
 
426
    client_class = DummyClient
 
427
    service_class = DummyService
 
428
 
 
429
    remote_client_name = remote_service_name = 'dummy'
 
430
 
 
431
    method_mapping = [
 
432
        ('foo', (), {}),
 
433
        ('bar', (object(),), {}),
 
434
    ]
 
435
    signal_mapping = [
 
436
        ('Success', ('test',), {}),
 
437
        ('NotSuccess', ('yadda',), {}),
 
438
        ('NoArgs', (), {}),
 
439
        ('JustArgs', (object(), 'foo'), {}),
 
440
        ('JustKwargs', (), {'foo': 'bar'}),
 
441
        ('BothArgsAndKwargs', ('zaraza', 8), {'foo': -42}),
 
442
    ]
 
443
 
 
444
    @defer.inlineCallbacks
 
445
    def test_deprecated_siganl_is_also_sent(self):
 
446
        """Old-style, deprecated signals handler are also called."""
 
447
        d1 = defer.Deferred()
 
448
        d2 = defer.Deferred()
 
449
 
 
450
        self.remote_service.next_result = 'yadda'
 
451
 
 
452
        # old, deprecated way
 
453
        self.remote_client.connect_to_signal('Success', d1.callback)
 
454
        self.remote_client.on_success_cb = d2.callback
 
455
 
 
456
        self.remote_client.foo()
 
457
 
 
458
        result = yield defer.gatherResults([d1, d2])
 
459
 
 
460
        self.assertEqual(result, ['yadda', 'yadda'])
 
461
 
 
462
    @defer.inlineCallbacks
 
463
    def test_register_to_signals(self):
 
464
        """Test the register_to_signals method."""
 
465
        yield self.remote_client.register_to_signals()
 
466
        self.addCleanup(self.remote_client.unregister_to_signals)
 
467
 
 
468
        for signal in self.remote_client.signal_handlers:
 
469
            self.assertIn(signal, self.service.dummy.clients_per_signal)
 
470
 
 
471
    @defer.inlineCallbacks
 
472
    def test_unregister_to_signals(self):
 
473
        """Test the register_to_signals method."""
 
474
        yield self.remote_client.register_to_signals()
 
475
        yield self.remote_client.unregister_to_signals()
 
476
 
 
477
        for signal in self.remote_client.signal_handlers:
 
478
            actual = len(self.remote_service.clients_per_signal[signal])
 
479
            self.assertEqual(0, actual)
 
480
 
 
481
 
 
482
@skipIfOS('win32', 'Unix domain sockets not supported on windows.')
 
483
class DomainDummyClientTestCase(TCPDummyClientTestCase):
 
484
    """Test the status client class."""
 
485
 
 
486
    def get_server(self):
 
487
        """Return the server to be used to run the tests."""
 
488
        return TidyUnixServer()
 
489
 
 
490
 
 
491
class RemoteMetaTestCase(TestCase):
 
492
    """Tests for the RemoteMeta metaclass."""
 
493
 
 
494
    def test_remote_calls_renamed(self):
 
495
        """The remote_calls are renamed."""
 
496
        test_token = object()
 
497
 
 
498
        class TestClass(ipc.meta_base(ipc.RemoteMeta)):
 
499
            """A class for testing."""
 
500
 
 
501
            remote_calls = ['test_method']
 
502
 
 
503
            def test_method(self):
 
504
                """Fake call."""
 
505
                return test_token
 
506
 
 
507
        tc = TestClass()
 
508
        self.assertEquals(tc.test_method(), test_token)
 
509
        self.assertEquals(tc.remote_test_method(), test_token)
 
510
 
 
511
 
 
512
class SignalBroadcasterTestCase(TestCase):
 
513
    """Test the signal broadcaster code."""
 
514
 
 
515
    @defer.inlineCallbacks
 
516
    def setUp(self):
 
517
        yield super(SignalBroadcasterTestCase, self).setUp()
 
518
        self.client = FakeRemoteClient()
 
519
        self.sb = ipc.SignalBroadcaster()
 
520
 
 
521
        self.memento = MementoHandler()
 
522
        ipc.logger.addHandler(self.memento)
 
523
        ipc.logger.setLevel(logging.DEBUG)
 
524
        self.addCleanup(ipc.logger.removeHandler, self.memento)
 
525
 
 
526
    def test_remote_register_to_signals(self):
 
527
        """Assert that the client was added."""
 
528
        signals = ["demo_signal1", "demo_signal2"]
 
529
        self.sb.remote_register_to_signals(self.client, signals)
 
530
        for signal in signals:
 
531
            clients = self.sb.clients_per_signal[signal]
 
532
            self.assertTrue(self.client in clients)
 
533
 
 
534
    def test_emit_signal(self):
 
535
        """Assert that the client method was called."""
 
536
        first = 1
 
537
        second = 2
 
538
        word = 'word'
 
539
        signal_name = 'on_test'
 
540
 
 
541
        self.client.callRemote(signal_name, first, second, word=word)
 
542
 
 
543
        signals = [signal_name]
 
544
        self.sb.remote_register_to_signals(self.client, signals)
 
545
        self.sb.emit_signal(signal_name, first, second, foo=word)
 
546
 
 
547
        self.assertEqual(self.client.called, ((first, second), dict(foo=word)))
 
548
 
 
549
    def test_emit_signal_dead_reference(self):
 
550
        """Test dead reference while emitting a signal."""
 
551
        sample_signal = "sample_signal"
 
552
        fake_remote_client = FakeRemoteClient(dead_remote=True)
 
553
 
 
554
        self.sb.remote_register_to_signals(fake_remote_client, [sample_signal])
 
555
        self.assertIn(fake_remote_client,
 
556
                      self.sb.clients_per_signal[sample_signal])
 
557
 
 
558
        self.sb.emit_signal(sample_signal)
 
559
        self.assertNotIn(fake_remote_client,
 
560
                         self.sb.clients_per_signal[sample_signal])
 
561
 
 
562
    def test_emit_signal_some_dead_some_not(self):
 
563
        """Test a clean reference after a dead one."""
 
564
        sample_signal = "sample_signal"
 
565
        fake_dead_remote = FakeRemoteClient(dead_remote=True)
 
566
        fake_alive_remote = FakeRemoteClient()
 
567
 
 
568
        self.sb.remote_register_to_signals(fake_dead_remote, [sample_signal])
 
569
        self.sb.remote_register_to_signals(fake_alive_remote, [sample_signal])
 
570
        self.sb.emit_signal(sample_signal)
 
571
 
 
572
        self.assertTrue(fake_alive_remote.called, "The alive must be called.")
 
573
 
 
574
    def test_emit_signal_ignore_missing_handlers(self):
 
575
        """A missing signal handler should just log a debug line."""
 
576
        fake_remote_client = FakeRemoteClient()
 
577
 
 
578
        signal = fake_remote_client.missing_signal
 
579
        signals = [signal]
 
580
        self.sb.remote_register_to_signals(fake_remote_client, signals)
 
581
        sb_clients = self.sb.clients_per_signal[signal]
 
582
        self.assertIn(fake_remote_client, sb_clients)
 
583
        self.sb.emit_signal(signal)
 
584
 
 
585
        expected = ipc.SignalBroadcaster.MSG_NO_SIGNAL_HANDLER % (
 
586
            signal,
 
587
            fake_remote_client,
 
588
        )
 
589
        self.assertTrue(self.memento.check_debug(*expected))
 
590
 
 
591
    def test_emit_signal_log_other_errors(self):
 
592
        """Other errors should be logged as warnings."""
 
593
        fake_remote_client = FakeRemoteClient()
 
594
 
 
595
        signal = fake_remote_client.failing_signal
 
596
        signals = [signal]
 
597
        self.sb.remote_register_to_signals(fake_remote_client, signals)
 
598
        sb_clients = self.sb.clients_per_signal[signal]
 
599
        self.assertIn(fake_remote_client, sb_clients)
 
600
        self.sb.emit_signal(signal)
 
601
 
 
602
        expected = ipc.SignalBroadcaster.MSG_COULD_NOT_EMIT_SIGNAL % (
 
603
            signal,
 
604
            fake_remote_client,
 
605
            fake_remote_client.random_exception,
 
606
        )
 
607
        self.assertTrue(self.memento.check_warning(*expected))
 
608
 
 
609
 
 
610
class FakeRootObject(object):
 
611
    """A fake root object."""
 
612
 
 
613
    def __init__(self, called, remote_obj):
 
614
        """Create a new instance."""
 
615
        self.called = called
 
616
        self.remote_obj = remote_obj
 
617
 
 
618
    def callRemote(self, method_name):
 
619
        """A fake call remove method."""
 
620
        self.called.append(method_name)
 
621
        return defer.succeed(self.remote_obj)
 
622
 
 
623
 
 
624
class FakeWorkingRemoteClient(object):
 
625
    """A fake remote client."""
 
626
 
 
627
    def __init__(self, called):
 
628
        """Create a new instance."""
 
629
        self.remote = None
 
630
        self.called = called
 
631
 
 
632
    def register_to_signals(self):
 
633
        """Register to signals."""
 
634
        self.called.append('register_to_signals')
 
635
        return defer.succeed(True)
 
636
 
 
637
 
 
638
class ReconnectTestCase(TestCase):
 
639
    """Test the reconnection when service is dead."""
 
640
 
 
641
    @defer.inlineCallbacks
 
642
    def setUp(self):
 
643
        """Set the different tests."""
 
644
        yield super(ReconnectTestCase, self).setUp()
 
645
        self.called = []
 
646
        self.remote_obj = 'remote'
 
647
        self.root_obj = FakeRootObject(self.called, self.remote_obj)
 
648
 
 
649
        def fake_get_root_object():
 
650
            """Fake getting the root object."""
 
651
            self.called.append('getRootObject')
 
652
            return defer.succeed(self.root_obj)
 
653
 
 
654
        def fake_client_connect(factory, service_name, cmd, description):
 
655
            """Fake the client connect."""
 
656
            self.called.append('client_connect')
 
657
            self.patch(factory, 'getRootObject', fake_get_root_object)
 
658
            return defer.succeed(True)
 
659
 
 
660
        self.patch(ipc, 'client_connect', fake_client_connect)
 
661
 
 
662
    @defer.inlineCallbacks
 
663
    def test_reconnect_method(self):
 
664
        """Test the execcution of the reconnect method."""
 
665
        clients = dict(first=FakeWorkingRemoteClient(self.called),
 
666
                       second=FakeWorkingRemoteClient(self.called))
 
667
 
 
668
        base_client = ipc.BaseClient()
 
669
        base_client.clients = clients
 
670
        for name, client in clients.items():
 
671
            setattr(base_client, name, client)
 
672
 
 
673
        yield base_client.reconnect()
 
674
        # assert that we did call the correct methods
 
675
        self.assertIn('client_connect', self.called)
 
676
        self.assertIn('getRootObject', self.called)
 
677
 
 
678
        for name in clients:
 
679
            self.assertIn('get_%s' % name, self.called)
 
680
 
 
681
        self.assertEqual(len(clients),
 
682
                self.called.count('register_to_signals'))