~canonical-livepatch-dependencies/canonical-livepatch-service-dependencies/twisted

« back to all changes in this revision

Viewing changes to twisted/test/test_pb.py

  • Committer: Free Ekanayaka
  • Date: 2016-07-01 12:22:33 UTC
  • Revision ID: free.ekanayaka@canonical.com-20160701122233-nh55w514zwzoz1ip
Tags: upstream-16.2.0
ImportĀ upstreamĀ versionĀ 16.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (c) Twisted Matrix Laboratories.
 
2
# See LICENSE for details.
 
3
 
 
4
"""
 
5
Tests for Perspective Broker module.
 
6
 
 
7
TODO: update protocol level tests to use new connection API, leaving
 
8
only specific tests for old API.
 
9
"""
 
10
 
 
11
# issue1195 TODOs: replace pump.pump() with something involving Deferreds.
 
12
# Clean up warning suppression.
 
13
 
 
14
import sys, os, time, gc, weakref
 
15
 
 
16
from cStringIO import StringIO
 
17
from zope.interface import implements, Interface
 
18
 
 
19
from twisted.trial import unittest
 
20
from twisted.spread import pb, util, publish, jelly
 
21
from twisted.internet import protocol, main, reactor
 
22
from twisted.internet.error import ConnectionRefusedError
 
23
from twisted.internet.defer import Deferred, gatherResults, succeed
 
24
from twisted.protocols.policies import WrappingFactory
 
25
from twisted.python import failure, log
 
26
from twisted.cred.error import UnauthorizedLogin, UnhandledCredentials
 
27
from twisted.cred import portal, checkers, credentials
 
28
 
 
29
 
 
30
class Dummy(pb.Viewable):
 
31
    def view_doNothing(self, user):
 
32
        if isinstance(user, DummyPerspective):
 
33
            return 'hello world!'
 
34
        else:
 
35
            return 'goodbye, cruel world!'
 
36
 
 
37
 
 
38
class DummyPerspective(pb.Avatar):
 
39
    """
 
40
    An L{IPerspective} avatar which will be used in some tests.
 
41
    """
 
42
    def perspective_getDummyViewPoint(self):
 
43
        return Dummy()
 
44
 
 
45
 
 
46
 
 
47
class DummyRealm(object):
 
48
    implements(portal.IRealm)
 
49
 
 
50
    def requestAvatar(self, avatarId, mind, *interfaces):
 
51
        for iface in interfaces:
 
52
            if iface is pb.IPerspective:
 
53
                return iface, DummyPerspective(avatarId), lambda: None
 
54
 
 
55
 
 
56
class IOPump:
 
57
    """
 
58
    Utility to pump data between clients and servers for protocol testing.
 
59
 
 
60
    Perhaps this is a utility worthy of being in protocol.py?
 
61
    """
 
62
    def __init__(self, client, server, clientIO, serverIO):
 
63
        self.client = client
 
64
        self.server = server
 
65
        self.clientIO = clientIO
 
66
        self.serverIO = serverIO
 
67
 
 
68
 
 
69
    def flush(self):
 
70
        """
 
71
        Pump until there is no more input or output or until L{stop} is called.
 
72
        This does not run any timers, so don't use it with any code that calls
 
73
        reactor.callLater.
 
74
        """
 
75
        # failsafe timeout
 
76
        self._stop = False
 
77
        timeout = time.time() + 5
 
78
        while not self._stop and self.pump():
 
79
            if time.time() > timeout:
 
80
                return
 
81
 
 
82
 
 
83
    def stop(self):
 
84
        """
 
85
        Stop a running L{flush} operation, even if data remains to be
 
86
        transferred.
 
87
        """
 
88
        self._stop = True
 
89
 
 
90
 
 
91
    def pump(self):
 
92
        """
 
93
        Move data back and forth.
 
94
 
 
95
        Returns whether any data was moved.
 
96
        """
 
97
        self.clientIO.seek(0)
 
98
        self.serverIO.seek(0)
 
99
        cData = self.clientIO.read()
 
100
        sData = self.serverIO.read()
 
101
        self.clientIO.seek(0)
 
102
        self.serverIO.seek(0)
 
103
        self.clientIO.truncate()
 
104
        self.serverIO.truncate()
 
105
        self.client.transport._checkProducer()
 
106
        self.server.transport._checkProducer()
 
107
        for byte in cData:
 
108
            self.server.dataReceived(byte)
 
109
        for byte in sData:
 
110
            self.client.dataReceived(byte)
 
111
        if cData or sData:
 
112
            return 1
 
113
        else:
 
114
            return 0
 
115
 
 
116
 
 
117
 
 
118
def connectedServerAndClient(realm=None):
 
119
    """
 
120
    Connect a client and server L{Broker} together with an L{IOPump}
 
121
 
 
122
    @param realm: realm to use, defaulting to a L{DummyRealm}
 
123
 
 
124
    @returns: a 3-tuple (client, server, pump).
 
125
    """
 
126
    realm = realm or DummyRealm()
 
127
    clientBroker = pb.Broker()
 
128
    checker = checkers.InMemoryUsernamePasswordDatabaseDontUse(guest='guest')
 
129
    factory = pb.PBServerFactory(portal.Portal(realm, [checker]))
 
130
    serverBroker = factory.buildProtocol(('127.0.0.1',))
 
131
 
 
132
    clientTransport = StringIO()
 
133
    serverTransport = StringIO()
 
134
    clientBroker.makeConnection(protocol.FileWrapper(clientTransport))
 
135
    serverBroker.makeConnection(protocol.FileWrapper(serverTransport))
 
136
    pump = IOPump(clientBroker, serverBroker, clientTransport, serverTransport)
 
137
    # Challenge-response authentication:
 
138
    pump.flush()
 
139
    return clientBroker, serverBroker, pump
 
140
 
 
141
 
 
142
class SimpleRemote(pb.Referenceable):
 
143
    def remote_thunk(self, arg):
 
144
        self.arg = arg
 
145
        return arg + 1
 
146
 
 
147
    def remote_knuth(self, arg):
 
148
        raise Exception()
 
149
 
 
150
 
 
151
class NestedRemote(pb.Referenceable):
 
152
    def remote_getSimple(self):
 
153
        return SimpleRemote()
 
154
 
 
155
 
 
156
class SimpleCopy(pb.Copyable):
 
157
    def __init__(self):
 
158
        self.x = 1
 
159
        self.y = {"Hello":"World"}
 
160
        self.z = ['test']
 
161
 
 
162
 
 
163
class SimpleLocalCopy(pb.RemoteCopy):
 
164
    pass
 
165
 
 
166
pb.setUnjellyableForClass(SimpleCopy, SimpleLocalCopy)
 
167
 
 
168
 
 
169
class SimpleFactoryCopy(pb.Copyable):
 
170
    """
 
171
    @cvar allIDs: hold every created instances of this class.
 
172
    @type allIDs: C{dict}
 
173
    """
 
174
    allIDs = {}
 
175
    def __init__(self, id):
 
176
        self.id = id
 
177
        SimpleFactoryCopy.allIDs[id] = self
 
178
 
 
179
 
 
180
def createFactoryCopy(state):
 
181
    """
 
182
    Factory of L{SimpleFactoryCopy}, getting a created instance given the
 
183
    C{id} found in C{state}.
 
184
    """
 
185
    stateId = state.get("id", None)
 
186
    if stateId is None:
 
187
        raise RuntimeError("factory copy state has no 'id' member %s" %
 
188
                           (repr(state),))
 
189
    if not stateId in SimpleFactoryCopy.allIDs:
 
190
        raise RuntimeError("factory class has no ID: %s" %
 
191
                           (SimpleFactoryCopy.allIDs,))
 
192
    inst = SimpleFactoryCopy.allIDs[stateId]
 
193
    if not inst:
 
194
        raise RuntimeError("factory method found no object with id")
 
195
    return inst
 
196
 
 
197
pb.setUnjellyableFactoryForClass(SimpleFactoryCopy, createFactoryCopy)
 
198
 
 
199
 
 
200
class NestedCopy(pb.Referenceable):
 
201
    def remote_getCopy(self):
 
202
        return SimpleCopy()
 
203
 
 
204
    def remote_getFactory(self, value):
 
205
        return SimpleFactoryCopy(value)
 
206
 
 
207
 
 
208
 
 
209
class SimpleCache(pb.Cacheable):
 
210
    def __init___(self):
 
211
        self.x = 1
 
212
        self.y = {"Hello":"World"}
 
213
        self.z = ['test']
 
214
 
 
215
 
 
216
class NestedComplicatedCache(pb.Referenceable):
 
217
    def __init__(self):
 
218
        self.c = VeryVeryComplicatedCacheable()
 
219
 
 
220
    def remote_getCache(self):
 
221
        return self.c
 
222
 
 
223
 
 
224
class VeryVeryComplicatedCacheable(pb.Cacheable):
 
225
    def __init__(self):
 
226
        self.x = 1
 
227
        self.y = 2
 
228
        self.foo = 3
 
229
 
 
230
    def setFoo4(self):
 
231
        self.foo = 4
 
232
        self.observer.callRemote('foo',4)
 
233
 
 
234
    def getStateToCacheAndObserveFor(self, perspective, observer):
 
235
        self.observer = observer
 
236
        return {"x": self.x,
 
237
                "y": self.y,
 
238
                "foo": self.foo}
 
239
 
 
240
    def stoppedObserving(self, perspective, observer):
 
241
        log.msg("stopped observing")
 
242
        observer.callRemote("end")
 
243
        if observer == self.observer:
 
244
            self.observer = None
 
245
 
 
246
 
 
247
class RatherBaroqueCache(pb.RemoteCache):
 
248
    def observe_foo(self, newFoo):
 
249
        self.foo = newFoo
 
250
 
 
251
    def observe_end(self):
 
252
        log.msg("the end of things")
 
253
 
 
254
pb.setUnjellyableForClass(VeryVeryComplicatedCacheable, RatherBaroqueCache)
 
255
 
 
256
 
 
257
class SimpleLocalCache(pb.RemoteCache):
 
258
    def setCopyableState(self, state):
 
259
        self.__dict__.update(state)
 
260
 
 
261
    def checkMethod(self):
 
262
        return self.check
 
263
 
 
264
    def checkSelf(self):
 
265
        return self
 
266
 
 
267
    def check(self):
 
268
        return 1
 
269
 
 
270
pb.setUnjellyableForClass(SimpleCache, SimpleLocalCache)
 
271
 
 
272
 
 
273
class NestedCache(pb.Referenceable):
 
274
    def __init__(self):
 
275
        self.x = SimpleCache()
 
276
 
 
277
    def remote_getCache(self):
 
278
        return [self.x,self.x]
 
279
 
 
280
    def remote_putCache(self, cache):
 
281
        return (self.x is cache)
 
282
 
 
283
 
 
284
class Observable(pb.Referenceable):
 
285
    def __init__(self):
 
286
        self.observers = []
 
287
 
 
288
    def remote_observe(self, obs):
 
289
        self.observers.append(obs)
 
290
 
 
291
    def remote_unobserve(self, obs):
 
292
        self.observers.remove(obs)
 
293
 
 
294
    def notify(self, obj):
 
295
        for observer in self.observers:
 
296
            observer.callRemote('notify', self, obj)
 
297
 
 
298
 
 
299
class DeferredRemote(pb.Referenceable):
 
300
    def __init__(self):
 
301
        self.run = 0
 
302
 
 
303
    def runMe(self, arg):
 
304
        self.run = arg
 
305
        return arg + 1
 
306
 
 
307
    def dontRunMe(self, arg):
 
308
        assert 0, "shouldn't have been run!"
 
309
 
 
310
    def remote_doItLater(self):
 
311
        """
 
312
        Return a L{Deferred} to be fired on client side. When fired,
 
313
        C{self.runMe} is called.
 
314
        """
 
315
        d = Deferred()
 
316
        d.addCallbacks(self.runMe, self.dontRunMe)
 
317
        self.d = d
 
318
        return d
 
319
 
 
320
 
 
321
class Observer(pb.Referenceable):
 
322
    notified = 0
 
323
    obj = None
 
324
    def remote_notify(self, other, obj):
 
325
        self.obj = obj
 
326
        self.notified = self.notified + 1
 
327
        other.callRemote('unobserve',self)
 
328
 
 
329
 
 
330
class NewStyleCopy(pb.Copyable, pb.RemoteCopy, object):
 
331
    def __init__(self, s):
 
332
        self.s = s
 
333
pb.setUnjellyableForClass(NewStyleCopy, NewStyleCopy)
 
334
 
 
335
 
 
336
class NewStyleCopy2(pb.Copyable, pb.RemoteCopy, object):
 
337
    allocated = 0
 
338
    initialized = 0
 
339
    value = 1
 
340
 
 
341
    def __new__(self):
 
342
        NewStyleCopy2.allocated += 1
 
343
        inst = object.__new__(self)
 
344
        inst.value = 2
 
345
        return inst
 
346
 
 
347
    def __init__(self):
 
348
        NewStyleCopy2.initialized += 1
 
349
 
 
350
pb.setUnjellyableForClass(NewStyleCopy2, NewStyleCopy2)
 
351
 
 
352
 
 
353
class NewStyleCacheCopy(pb.Cacheable, pb.RemoteCache, object):
 
354
    def getStateToCacheAndObserveFor(self, perspective, observer):
 
355
        return self.__dict__
 
356
 
 
357
pb.setUnjellyableForClass(NewStyleCacheCopy, NewStyleCacheCopy)
 
358
 
 
359
 
 
360
class Echoer(pb.Root):
 
361
    def remote_echo(self, st):
 
362
        return st
 
363
 
 
364
 
 
365
class CachedReturner(pb.Root):
 
366
    def __init__(self, cache):
 
367
        self.cache = cache
 
368
    def remote_giveMeCache(self, st):
 
369
        return self.cache
 
370
 
 
371
 
 
372
class NewStyleTests(unittest.TestCase):
 
373
    def setUp(self):
 
374
        """
 
375
        Create a pb server using L{Echoer} protocol and connect a client to it.
 
376
        """
 
377
        self.serverFactory = pb.PBServerFactory(Echoer())
 
378
        self.wrapper = WrappingFactory(self.serverFactory)
 
379
        self.server = reactor.listenTCP(0, self.wrapper)
 
380
        clientFactory = pb.PBClientFactory()
 
381
        reactor.connectTCP("localhost", self.server.getHost().port,
 
382
                           clientFactory)
 
383
        def gotRoot(ref):
 
384
            self.ref = ref
 
385
        return clientFactory.getRootObject().addCallback(gotRoot)
 
386
 
 
387
 
 
388
    def tearDown(self):
 
389
        """
 
390
        Close client and server connections, reset values of L{NewStyleCopy2}
 
391
        class variables.
 
392
        """
 
393
        NewStyleCopy2.allocated = 0
 
394
        NewStyleCopy2.initialized = 0
 
395
        NewStyleCopy2.value = 1
 
396
        self.ref.broker.transport.loseConnection()
 
397
        # Disconnect any server-side connections too.
 
398
        for proto in self.wrapper.protocols:
 
399
            proto.transport.loseConnection()
 
400
        return self.server.stopListening()
 
401
 
 
402
    def test_newStyle(self):
 
403
        """
 
404
        Create a new style object, send it over the wire, and check the result.
 
405
        """
 
406
        orig = NewStyleCopy("value")
 
407
        d = self.ref.callRemote("echo", orig)
 
408
        def cb(res):
 
409
            self.assertTrue(isinstance(res, NewStyleCopy))
 
410
            self.assertEqual(res.s, "value")
 
411
            self.assertFalse(res is orig) # no cheating :)
 
412
        d.addCallback(cb)
 
413
        return d
 
414
 
 
415
    def test_alloc(self):
 
416
        """
 
417
        Send a new style object and check the number of allocations.
 
418
        """
 
419
        orig = NewStyleCopy2()
 
420
        self.assertEqual(NewStyleCopy2.allocated, 1)
 
421
        self.assertEqual(NewStyleCopy2.initialized, 1)
 
422
        d = self.ref.callRemote("echo", orig)
 
423
        def cb(res):
 
424
            # receiving the response creates a third one on the way back
 
425
            self.assertTrue(isinstance(res, NewStyleCopy2))
 
426
            self.assertEqual(res.value, 2)
 
427
            self.assertEqual(NewStyleCopy2.allocated, 3)
 
428
            self.assertEqual(NewStyleCopy2.initialized, 1)
 
429
            self.assertFalse(res is orig) # no cheating :)
 
430
        # sending the object creates a second one on the far side
 
431
        d.addCallback(cb)
 
432
        return d
 
433
 
 
434
 
 
435
 
 
436
class ConnectionNotifyServerFactory(pb.PBServerFactory):
 
437
    """
 
438
    A server factory which stores the last connection and fires a
 
439
    L{Deferred} on connection made. This factory can handle only one
 
440
    client connection.
 
441
 
 
442
    @ivar protocolInstance: the last protocol instance.
 
443
    @type protocolInstance: C{pb.Broker}
 
444
 
 
445
    @ivar connectionMade: the deferred fired upon connection.
 
446
    @type connectionMade: C{Deferred}
 
447
    """
 
448
    protocolInstance = None
 
449
 
 
450
    def __init__(self, root):
 
451
        """
 
452
        Initialize the factory.
 
453
        """
 
454
        pb.PBServerFactory.__init__(self, root)
 
455
        self.connectionMade = Deferred()
 
456
 
 
457
 
 
458
    def clientConnectionMade(self, protocol):
 
459
        """
 
460
        Store the protocol and fire the connection deferred.
 
461
        """
 
462
        self.protocolInstance = protocol
 
463
        d, self.connectionMade = self.connectionMade, None
 
464
        if d is not None:
 
465
            d.callback(None)
 
466
 
 
467
 
 
468
 
 
469
class NewStyleCachedTests(unittest.TestCase):
 
470
    def setUp(self):
 
471
        """
 
472
        Create a pb server using L{CachedReturner} protocol and connect a
 
473
        client to it.
 
474
        """
 
475
        self.orig = NewStyleCacheCopy()
 
476
        self.orig.s = "value"
 
477
        self.server = reactor.listenTCP(0,
 
478
            ConnectionNotifyServerFactory(CachedReturner(self.orig)))
 
479
        clientFactory = pb.PBClientFactory()
 
480
        reactor.connectTCP("localhost", self.server.getHost().port,
 
481
                           clientFactory)
 
482
        def gotRoot(ref):
 
483
            self.ref = ref
 
484
        d1 = clientFactory.getRootObject().addCallback(gotRoot)
 
485
        d2 = self.server.factory.connectionMade
 
486
        return gatherResults([d1, d2])
 
487
 
 
488
 
 
489
    def tearDown(self):
 
490
        """
 
491
        Close client and server connections.
 
492
        """
 
493
        self.server.factory.protocolInstance.transport.loseConnection()
 
494
        self.ref.broker.transport.loseConnection()
 
495
        return self.server.stopListening()
 
496
 
 
497
 
 
498
    def test_newStyleCache(self):
 
499
        """
 
500
        A new-style cacheable object can be retrieved and re-retrieved over a
 
501
        single connection.  The value of an attribute of the cacheable can be
 
502
        accessed on the receiving side.
 
503
        """
 
504
        d = self.ref.callRemote("giveMeCache", self.orig)
 
505
        def cb(res, again):
 
506
            self.assertIsInstance(res, NewStyleCacheCopy)
 
507
            self.assertEqual("value", res.s)
 
508
            # no cheating :)
 
509
            self.assertNotIdentical(self.orig, res)
 
510
 
 
511
            if again:
 
512
                # Save a reference so it stays alive for the rest of this test
 
513
                self.res = res
 
514
                # And ask for it again to exercise the special re-jelly logic in
 
515
                # Cacheable.
 
516
                return self.ref.callRemote("giveMeCache", self.orig)
 
517
        d.addCallback(cb, True)
 
518
        d.addCallback(cb, False)
 
519
        return d
 
520
 
 
521
 
 
522
 
 
523
class BrokerTests(unittest.TestCase):
 
524
    thunkResult = None
 
525
 
 
526
    def tearDown(self):
 
527
        try:
 
528
            # from RemotePublished.getFileName
 
529
            os.unlink('None-None-TESTING.pub')
 
530
        except OSError:
 
531
            pass
 
532
 
 
533
    def thunkErrorBad(self, error):
 
534
        self.fail("This should cause a return value, not %s" % (error,))
 
535
 
 
536
    def thunkResultGood(self, result):
 
537
        self.thunkResult = result
 
538
 
 
539
    def thunkErrorGood(self, tb):
 
540
        pass
 
541
 
 
542
    def thunkResultBad(self, result):
 
543
        self.fail("This should cause an error, not %s" % (result,))
 
544
 
 
545
    def test_reference(self):
 
546
        c, s, pump = connectedServerAndClient()
 
547
 
 
548
        class X(pb.Referenceable):
 
549
            def remote_catch(self,arg):
 
550
                self.caught = arg
 
551
 
 
552
        class Y(pb.Referenceable):
 
553
            def remote_throw(self, a, b):
 
554
                a.callRemote('catch', b)
 
555
 
 
556
        s.setNameForLocal("y", Y())
 
557
        y = c.remoteForName("y")
 
558
        x = X()
 
559
        z = X()
 
560
        y.callRemote('throw', x, z)
 
561
        pump.pump()
 
562
        pump.pump()
 
563
        pump.pump()
 
564
        self.assertIdentical(x.caught, z, "X should have caught Z")
 
565
 
 
566
        # make sure references to remote methods are equals
 
567
        self.assertEqual(y.remoteMethod('throw'), y.remoteMethod('throw'))
 
568
 
 
569
    def test_result(self):
 
570
        c, s, pump = connectedServerAndClient()
 
571
        for x, y in (c, s), (s, c):
 
572
            # test reflexivity
 
573
            foo = SimpleRemote()
 
574
            x.setNameForLocal("foo", foo)
 
575
            bar = y.remoteForName("foo")
 
576
            self.expectedThunkResult = 8
 
577
            bar.callRemote('thunk',self.expectedThunkResult - 1
 
578
                ).addCallbacks(self.thunkResultGood, self.thunkErrorBad)
 
579
            # Send question.
 
580
            pump.pump()
 
581
            # Send response.
 
582
            pump.pump()
 
583
            # Shouldn't require any more pumping than that...
 
584
            self.assertEqual(self.thunkResult, self.expectedThunkResult,
 
585
                              "result wasn't received.")
 
586
 
 
587
    def refcountResult(self, result):
 
588
        self.nestedRemote = result
 
589
 
 
590
    def test_tooManyRefs(self):
 
591
        l = []
 
592
        e = []
 
593
        c, s, pump = connectedServerAndClient()
 
594
        foo = NestedRemote()
 
595
        s.setNameForLocal("foo", foo)
 
596
        x = c.remoteForName("foo")
 
597
        for igno in xrange(pb.MAX_BROKER_REFS + 10):
 
598
            if s.transport.closed or c.transport.closed:
 
599
                break
 
600
            x.callRemote("getSimple").addCallbacks(l.append, e.append)
 
601
            pump.pump()
 
602
        expected = (pb.MAX_BROKER_REFS - 1)
 
603
        self.assertTrue(s.transport.closed, "transport was not closed")
 
604
        self.assertEqual(len(l), expected,
 
605
                          "expected %s got %s" % (expected, len(l)))
 
606
 
 
607
    def test_copy(self):
 
608
        c, s, pump = connectedServerAndClient()
 
609
        foo = NestedCopy()
 
610
        s.setNameForLocal("foo", foo)
 
611
        x = c.remoteForName("foo")
 
612
        x.callRemote('getCopy'
 
613
            ).addCallbacks(self.thunkResultGood, self.thunkErrorBad)
 
614
        pump.pump()
 
615
        pump.pump()
 
616
        self.assertEqual(self.thunkResult.x, 1)
 
617
        self.assertEqual(self.thunkResult.y['Hello'], 'World')
 
618
        self.assertEqual(self.thunkResult.z[0], 'test')
 
619
 
 
620
    def test_observe(self):
 
621
        c, s, pump = connectedServerAndClient()
 
622
 
 
623
        # this is really testing the comparison between remote objects, to make
 
624
        # sure that you can *UN*observe when you have an observer architecture.
 
625
        a = Observable()
 
626
        b = Observer()
 
627
        s.setNameForLocal("a", a)
 
628
        ra = c.remoteForName("a")
 
629
        ra.callRemote('observe',b)
 
630
        pump.pump()
 
631
        a.notify(1)
 
632
        pump.pump()
 
633
        pump.pump()
 
634
        a.notify(10)
 
635
        pump.pump()
 
636
        pump.pump()
 
637
        self.assertNotIdentical(b.obj, None, "didn't notify")
 
638
        self.assertEqual(b.obj, 1, 'notified too much')
 
639
 
 
640
    def test_defer(self):
 
641
        c, s, pump = connectedServerAndClient()
 
642
        d = DeferredRemote()
 
643
        s.setNameForLocal("d", d)
 
644
        e = c.remoteForName("d")
 
645
        pump.pump(); pump.pump()
 
646
        results = []
 
647
        e.callRemote('doItLater').addCallback(results.append)
 
648
        pump.pump(); pump.pump()
 
649
        self.assertFalse(d.run, "Deferred method run too early.")
 
650
        d.d.callback(5)
 
651
        self.assertEqual(d.run, 5, "Deferred method run too late.")
 
652
        pump.pump(); pump.pump()
 
653
        self.assertEqual(results[0], 6, "Incorrect result.")
 
654
 
 
655
 
 
656
    def test_refcount(self):
 
657
        c, s, pump = connectedServerAndClient()
 
658
        foo = NestedRemote()
 
659
        s.setNameForLocal("foo", foo)
 
660
        bar = c.remoteForName("foo")
 
661
        bar.callRemote('getSimple'
 
662
            ).addCallbacks(self.refcountResult, self.thunkErrorBad)
 
663
 
 
664
        # send question
 
665
        pump.pump()
 
666
        # send response
 
667
        pump.pump()
 
668
 
 
669
        # delving into internal structures here, because GC is sort of
 
670
        # inherently internal.
 
671
        rluid = self.nestedRemote.luid
 
672
        self.assertIn(rluid, s.localObjects)
 
673
        del self.nestedRemote
 
674
        # nudge the gc
 
675
        if sys.hexversion >= 0x2000000:
 
676
            gc.collect()
 
677
        # try to nudge the GC even if we can't really
 
678
        pump.pump()
 
679
        pump.pump()
 
680
        pump.pump()
 
681
        self.assertNotIn(rluid, s.localObjects)
 
682
 
 
683
    def test_cache(self):
 
684
        c, s, pump = connectedServerAndClient()
 
685
        obj = NestedCache()
 
686
        obj2 = NestedComplicatedCache()
 
687
        vcc = obj2.c
 
688
        s.setNameForLocal("obj", obj)
 
689
        s.setNameForLocal("xxx", obj2)
 
690
        o2 = c.remoteForName("obj")
 
691
        o3 = c.remoteForName("xxx")
 
692
        coll = []
 
693
        o2.callRemote("getCache"
 
694
            ).addCallback(coll.append).addErrback(coll.append)
 
695
        o2.callRemote("getCache"
 
696
            ).addCallback(coll.append).addErrback(coll.append)
 
697
        complex = []
 
698
        o3.callRemote("getCache").addCallback(complex.append)
 
699
        o3.callRemote("getCache").addCallback(complex.append)
 
700
        pump.flush()
 
701
        # `worst things first'
 
702
        self.assertEqual(complex[0].x, 1)
 
703
        self.assertEqual(complex[0].y, 2)
 
704
        self.assertEqual(complex[0].foo, 3)
 
705
 
 
706
        vcc.setFoo4()
 
707
        pump.flush()
 
708
        self.assertEqual(complex[0].foo, 4)
 
709
        self.assertEqual(len(coll), 2)
 
710
        cp = coll[0][0]
 
711
        self.assertIdentical(cp.checkMethod().im_self, cp,
 
712
                             "potential refcounting issue")
 
713
        self.assertIdentical(cp.checkSelf(), cp,
 
714
                             "other potential refcounting issue")
 
715
        col2 = []
 
716
        o2.callRemote('putCache',cp).addCallback(col2.append)
 
717
        pump.flush()
 
718
        # The objects were the same (testing lcache identity)
 
719
        self.assertTrue(col2[0])
 
720
        # test equality of references to methods
 
721
        self.assertEqual(o2.remoteMethod("getCache"),
 
722
                          o2.remoteMethod("getCache"))
 
723
 
 
724
        # now, refcounting (similiar to testRefCount)
 
725
        luid = cp.luid
 
726
        baroqueLuid = complex[0].luid
 
727
        self.assertIn(luid, s.remotelyCachedObjects,
 
728
                      "remote cache doesn't have it")
 
729
        del coll
 
730
        del cp
 
731
        pump.flush()
 
732
        del complex
 
733
        del col2
 
734
        # extra nudge...
 
735
        pump.flush()
 
736
        # del vcc.observer
 
737
        # nudge the gc
 
738
        if sys.hexversion >= 0x2000000:
 
739
            gc.collect()
 
740
        # try to nudge the GC even if we can't really
 
741
        pump.flush()
 
742
        # The GC is done with it.
 
743
        self.assertNotIn(luid, s.remotelyCachedObjects,
 
744
                         "Server still had it after GC")
 
745
        self.assertNotIn(luid, c.locallyCachedObjects,
 
746
                         "Client still had it after GC")
 
747
        self.assertNotIn(baroqueLuid, s.remotelyCachedObjects,
 
748
                         "Server still had complex after GC")
 
749
        self.assertNotIn(baroqueLuid, c.locallyCachedObjects,
 
750
                         "Client still had complex after GC")
 
751
        self.assertIdentical(vcc.observer, None, "observer was not removed")
 
752
 
 
753
    def test_publishable(self):
 
754
        try:
 
755
            os.unlink('None-None-TESTING.pub') # from RemotePublished.getFileName
 
756
        except OSError:
 
757
            pass # Sometimes it's not there.
 
758
        c, s, pump = connectedServerAndClient()
 
759
        foo = GetPublisher()
 
760
        # foo.pub.timestamp = 1.0
 
761
        s.setNameForLocal("foo", foo)
 
762
        bar = c.remoteForName("foo")
 
763
        accum = []
 
764
        bar.callRemote('getPub').addCallbacks(accum.append, self.thunkErrorBad)
 
765
        pump.flush()
 
766
        obj = accum.pop()
 
767
        self.assertEqual(obj.activateCalled, 1)
 
768
        self.assertEqual(obj.isActivated, 1)
 
769
        self.assertEqual(obj.yayIGotPublished, 1)
 
770
        # timestamp's dirty, we don't have a cache file
 
771
        self.assertEqual(obj._wasCleanWhenLoaded, 0)
 
772
        c, s, pump = connectedServerAndClient()
 
773
        s.setNameForLocal("foo", foo)
 
774
        bar = c.remoteForName("foo")
 
775
        bar.callRemote('getPub').addCallbacks(accum.append, self.thunkErrorBad)
 
776
        pump.flush()
 
777
        obj = accum.pop()
 
778
        # timestamp's clean, our cache file is up-to-date
 
779
        self.assertEqual(obj._wasCleanWhenLoaded, 1)
 
780
 
 
781
    def gotCopy(self, val):
 
782
        self.thunkResult = val.id
 
783
 
 
784
 
 
785
    def test_factoryCopy(self):
 
786
        c, s, pump = connectedServerAndClient()
 
787
        ID = 99
 
788
        obj = NestedCopy()
 
789
        s.setNameForLocal("foo", obj)
 
790
        x = c.remoteForName("foo")
 
791
        x.callRemote('getFactory', ID
 
792
            ).addCallbacks(self.gotCopy, self.thunkResultBad)
 
793
        pump.pump()
 
794
        pump.pump()
 
795
        pump.pump()
 
796
        self.assertEqual(self.thunkResult, ID,
 
797
            "ID not correct on factory object %s" % (self.thunkResult,))
 
798
 
 
799
 
 
800
bigString = "helloworld" * 50
 
801
 
 
802
callbackArgs = None
 
803
callbackKeyword = None
 
804
 
 
805
def finishedCallback(*args, **kw):
 
806
    global callbackArgs, callbackKeyword
 
807
    callbackArgs = args
 
808
    callbackKeyword = kw
 
809
 
 
810
 
 
811
class Pagerizer(pb.Referenceable):
 
812
    def __init__(self, callback, *args, **kw):
 
813
        self.callback, self.args, self.kw = callback, args, kw
 
814
 
 
815
    def remote_getPages(self, collector):
 
816
        util.StringPager(collector, bigString, 100,
 
817
                         self.callback, *self.args, **self.kw)
 
818
        self.args = self.kw = None
 
819
 
 
820
 
 
821
class FilePagerizer(pb.Referenceable):
 
822
    pager = None
 
823
 
 
824
    def __init__(self, filename, callback, *args, **kw):
 
825
        self.filename = filename
 
826
        self.callback, self.args, self.kw = callback, args, kw
 
827
 
 
828
    def remote_getPages(self, collector):
 
829
        self.pager = util.FilePager(collector, file(self.filename),
 
830
                                    self.callback, *self.args, **self.kw)
 
831
        self.args = self.kw = None
 
832
 
 
833
 
 
834
 
 
835
class PagingTests(unittest.TestCase):
 
836
    """
 
837
    Test pb objects sending data by pages.
 
838
    """
 
839
 
 
840
    def setUp(self):
 
841
        """
 
842
        Create a file used to test L{util.FilePager}.
 
843
        """
 
844
        self.filename = self.mktemp()
 
845
        fd = file(self.filename, 'w')
 
846
        fd.write(bigString)
 
847
        fd.close()
 
848
 
 
849
 
 
850
    def test_pagingWithCallback(self):
 
851
        """
 
852
        Test L{util.StringPager}, passing a callback to fire when all pages
 
853
        are sent.
 
854
        """
 
855
        c, s, pump = connectedServerAndClient()
 
856
        s.setNameForLocal("foo", Pagerizer(finishedCallback, 'hello', value=10))
 
857
        x = c.remoteForName("foo")
 
858
        l = []
 
859
        util.getAllPages(x, "getPages").addCallback(l.append)
 
860
        while not l:
 
861
            pump.pump()
 
862
        self.assertEqual(''.join(l[0]), bigString,
 
863
                          "Pages received not equal to pages sent!")
 
864
        self.assertEqual(callbackArgs, ('hello',),
 
865
                          "Completed callback not invoked")
 
866
        self.assertEqual(callbackKeyword, {'value': 10},
 
867
                          "Completed callback not invoked")
 
868
 
 
869
 
 
870
    def test_pagingWithoutCallback(self):
 
871
        """
 
872
        Test L{util.StringPager} without a callback.
 
873
        """
 
874
        c, s, pump = connectedServerAndClient()
 
875
        s.setNameForLocal("foo", Pagerizer(None))
 
876
        x = c.remoteForName("foo")
 
877
        l = []
 
878
        util.getAllPages(x, "getPages").addCallback(l.append)
 
879
        while not l:
 
880
            pump.pump()
 
881
        self.assertEqual(''.join(l[0]), bigString,
 
882
                          "Pages received not equal to pages sent!")
 
883
 
 
884
 
 
885
    def test_emptyFilePaging(self):
 
886
        """
 
887
        Test L{util.FilePager}, sending an empty file.
 
888
        """
 
889
        filenameEmpty = self.mktemp()
 
890
        fd = file(filenameEmpty, 'w')
 
891
        fd.close()
 
892
        c, s, pump = connectedServerAndClient()
 
893
        pagerizer = FilePagerizer(filenameEmpty, None)
 
894
        s.setNameForLocal("bar", pagerizer)
 
895
        x = c.remoteForName("bar")
 
896
        l = []
 
897
        util.getAllPages(x, "getPages").addCallback(l.append)
 
898
        ttl = 10
 
899
        while not l and ttl > 0:
 
900
            pump.pump()
 
901
            ttl -= 1
 
902
        if not ttl:
 
903
            self.fail('getAllPages timed out')
 
904
        self.assertEqual(''.join(l[0]), '',
 
905
                          "Pages received not equal to pages sent!")
 
906
 
 
907
 
 
908
    def test_filePagingWithCallback(self):
 
909
        """
 
910
        Test L{util.FilePager}, passing a callback to fire when all pages
 
911
        are sent, and verify that the pager doesn't keep chunks in memory.
 
912
        """
 
913
        c, s, pump = connectedServerAndClient()
 
914
        pagerizer = FilePagerizer(self.filename, finishedCallback,
 
915
                                  'frodo', value = 9)
 
916
        s.setNameForLocal("bar", pagerizer)
 
917
        x = c.remoteForName("bar")
 
918
        l = []
 
919
        util.getAllPages(x, "getPages").addCallback(l.append)
 
920
        while not l:
 
921
            pump.pump()
 
922
        self.assertEqual(''.join(l[0]), bigString,
 
923
                          "Pages received not equal to pages sent!")
 
924
        self.assertEqual(callbackArgs, ('frodo',),
 
925
                          "Completed callback not invoked")
 
926
        self.assertEqual(callbackKeyword, {'value': 9},
 
927
                          "Completed callback not invoked")
 
928
        self.assertEqual(pagerizer.pager.chunks, [])
 
929
 
 
930
 
 
931
    def test_filePagingWithoutCallback(self):
 
932
        """
 
933
        Test L{util.FilePager} without a callback.
 
934
        """
 
935
        c, s, pump = connectedServerAndClient()
 
936
        pagerizer = FilePagerizer(self.filename, None)
 
937
        s.setNameForLocal("bar", pagerizer)
 
938
        x = c.remoteForName("bar")
 
939
        l = []
 
940
        util.getAllPages(x, "getPages").addCallback(l.append)
 
941
        while not l:
 
942
            pump.pump()
 
943
        self.assertEqual(''.join(l[0]), bigString,
 
944
                          "Pages received not equal to pages sent!")
 
945
        self.assertEqual(pagerizer.pager.chunks, [])
 
946
 
 
947
 
 
948
 
 
949
class DumbPublishable(publish.Publishable):
 
950
    def getStateToPublish(self):
 
951
        return {"yayIGotPublished": 1}
 
952
 
 
953
 
 
954
class DumbPub(publish.RemotePublished):
 
955
    def activated(self):
 
956
        self.activateCalled = 1
 
957
 
 
958
 
 
959
class GetPublisher(pb.Referenceable):
 
960
    def __init__(self):
 
961
        self.pub = DumbPublishable("TESTING")
 
962
 
 
963
    def remote_getPub(self):
 
964
        return self.pub
 
965
 
 
966
 
 
967
pb.setUnjellyableForClass(DumbPublishable, DumbPub)
 
968
 
 
969
class DisconnectionTests(unittest.TestCase):
 
970
    """
 
971
    Test disconnection callbacks.
 
972
    """
 
973
 
 
974
    def error(self, *args):
 
975
        raise RuntimeError("I shouldn't have been called: %s" % (args,))
 
976
 
 
977
 
 
978
    def gotDisconnected(self):
 
979
        """
 
980
        Called on broker disconnect.
 
981
        """
 
982
        self.gotCallback = 1
 
983
 
 
984
    def objectDisconnected(self, o):
 
985
        """
 
986
        Called on RemoteReference disconnect.
 
987
        """
 
988
        self.assertEqual(o, self.remoteObject)
 
989
        self.objectCallback = 1
 
990
 
 
991
    def test_badSerialization(self):
 
992
        c, s, pump = connectedServerAndClient()
 
993
        pump.pump()
 
994
        s.setNameForLocal("o", BadCopySet())
 
995
        g = c.remoteForName("o")
 
996
        l = []
 
997
        g.callRemote("setBadCopy", BadCopyable()).addErrback(l.append)
 
998
        pump.flush()
 
999
        self.assertEqual(len(l), 1)
 
1000
 
 
1001
    def test_disconnection(self):
 
1002
        c, s, pump = connectedServerAndClient()
 
1003
        pump.pump()
 
1004
        s.setNameForLocal("o", SimpleRemote())
 
1005
 
 
1006
        # get a client reference to server object
 
1007
        r = c.remoteForName("o")
 
1008
        pump.pump()
 
1009
        pump.pump()
 
1010
        pump.pump()
 
1011
 
 
1012
        # register and then unregister disconnect callbacks
 
1013
        # making sure they get unregistered
 
1014
        c.notifyOnDisconnect(self.error)
 
1015
        self.assertIn(self.error, c.disconnects)
 
1016
        c.dontNotifyOnDisconnect(self.error)
 
1017
        self.assertNotIn(self.error, c.disconnects)
 
1018
 
 
1019
        r.notifyOnDisconnect(self.error)
 
1020
        self.assertIn(r._disconnected, c.disconnects)
 
1021
        self.assertIn(self.error, r.disconnectCallbacks)
 
1022
        r.dontNotifyOnDisconnect(self.error)
 
1023
        self.assertNotIn(r._disconnected, c.disconnects)
 
1024
        self.assertNotIn(self.error, r.disconnectCallbacks)
 
1025
 
 
1026
        # register disconnect callbacks
 
1027
        c.notifyOnDisconnect(self.gotDisconnected)
 
1028
        r.notifyOnDisconnect(self.objectDisconnected)
 
1029
        self.remoteObject = r
 
1030
 
 
1031
        # disconnect
 
1032
        c.connectionLost(failure.Failure(main.CONNECTION_DONE))
 
1033
        self.assertTrue(self.gotCallback)
 
1034
        self.assertTrue(self.objectCallback)
 
1035
 
 
1036
 
 
1037
class FreakOut(Exception):
 
1038
    pass
 
1039
 
 
1040
 
 
1041
class BadCopyable(pb.Copyable):
 
1042
    def getStateToCopyFor(self, p):
 
1043
        raise FreakOut()
 
1044
 
 
1045
 
 
1046
class BadCopySet(pb.Referenceable):
 
1047
    def remote_setBadCopy(self, bc):
 
1048
        return None
 
1049
 
 
1050
 
 
1051
class LocalRemoteTest(util.LocalAsRemote):
 
1052
    reportAllTracebacks = 0
 
1053
 
 
1054
    def sync_add1(self, x):
 
1055
        return x + 1
 
1056
 
 
1057
    def async_add(self, x=0, y=1):
 
1058
        return x + y
 
1059
 
 
1060
    def async_fail(self):
 
1061
        raise RuntimeError()
 
1062
 
 
1063
 
 
1064
 
 
1065
class MyPerspective(pb.Avatar):
 
1066
    """
 
1067
    @ivar loggedIn: set to C{True} when the avatar is logged in.
 
1068
    @type loggedIn: C{bool}
 
1069
 
 
1070
    @ivar loggedOut: set to C{True} when the avatar is logged out.
 
1071
    @type loggedOut: C{bool}
 
1072
    """
 
1073
    implements(pb.IPerspective)
 
1074
 
 
1075
    loggedIn = loggedOut = False
 
1076
 
 
1077
    def __init__(self, avatarId):
 
1078
        self.avatarId = avatarId
 
1079
 
 
1080
 
 
1081
    def perspective_getAvatarId(self):
 
1082
        """
 
1083
        Return the avatar identifier which was used to access this avatar.
 
1084
        """
 
1085
        return self.avatarId
 
1086
 
 
1087
 
 
1088
    def perspective_getViewPoint(self):
 
1089
        return MyView()
 
1090
 
 
1091
 
 
1092
    def perspective_add(self, a, b):
 
1093
        """
 
1094
        Add the given objects and return the result.  This is a method
 
1095
        unavailable on L{Echoer}, so it can only be invoked by authenticated
 
1096
        users who received their avatar from L{TestRealm}.
 
1097
        """
 
1098
        return a + b
 
1099
 
 
1100
 
 
1101
    def logout(self):
 
1102
        self.loggedOut = True
 
1103
 
 
1104
 
 
1105
 
 
1106
class TestRealm(object):
 
1107
    """
 
1108
    A realm which repeatedly gives out a single instance of L{MyPerspective}
 
1109
    for non-anonymous logins and which gives out a new instance of L{Echoer}
 
1110
    for each anonymous login.
 
1111
 
 
1112
    @ivar lastPerspective: The L{MyPerspective} most recently created and
 
1113
        returned from C{requestAvatar}.
 
1114
 
 
1115
    @ivar perspectiveFactory: A one-argument callable which will be used to
 
1116
        create avatars to be returned from C{requestAvatar}.
 
1117
    """
 
1118
    perspectiveFactory = MyPerspective
 
1119
 
 
1120
    lastPerspective = None
 
1121
 
 
1122
    def requestAvatar(self, avatarId, mind, interface):
 
1123
        """
 
1124
        Verify that the mind and interface supplied have the expected values
 
1125
        (this should really be done somewhere else, like inside a test method)
 
1126
        and return an avatar appropriate for the given identifier.
 
1127
        """
 
1128
        assert interface == pb.IPerspective
 
1129
        assert mind == "BRAINS!"
 
1130
        if avatarId is checkers.ANONYMOUS:
 
1131
            return pb.IPerspective, Echoer(), lambda: None
 
1132
        else:
 
1133
            self.lastPerspective = self.perspectiveFactory(avatarId)
 
1134
            self.lastPerspective.loggedIn = True
 
1135
            return (
 
1136
                pb.IPerspective, self.lastPerspective,
 
1137
                self.lastPerspective.logout)
 
1138
 
 
1139
 
 
1140
 
 
1141
class MyView(pb.Viewable):
 
1142
 
 
1143
    def view_check(self, user):
 
1144
        return isinstance(user, MyPerspective)
 
1145
 
 
1146
 
 
1147
 
 
1148
class LeakyRealm(TestRealm):
 
1149
    """
 
1150
    A realm which hangs onto a reference to the mind object in its logout
 
1151
    function.
 
1152
    """
 
1153
    def __init__(self, mindEater):
 
1154
        """
 
1155
        Create a L{LeakyRealm}.
 
1156
 
 
1157
        @param mindEater: a callable that will be called with the C{mind}
 
1158
        object when it is available
 
1159
        """
 
1160
        self._mindEater = mindEater
 
1161
 
 
1162
 
 
1163
    def requestAvatar(self, avatarId, mind, interface):
 
1164
        self._mindEater(mind)
 
1165
        persp = self.perspectiveFactory(avatarId)
 
1166
        return (pb.IPerspective, persp, lambda : (mind, persp.logout()))
 
1167
 
 
1168
 
 
1169
 
 
1170
class NewCredLeakTests(unittest.TestCase):
 
1171
    """
 
1172
    Tests to try to trigger memory leaks.
 
1173
    """
 
1174
    def test_logoutLeak(self):
 
1175
        """
 
1176
        The server does not leak a reference when the client disconnects
 
1177
        suddenly, even if the cred logout function forms a reference cycle with
 
1178
        the perspective.
 
1179
        """
 
1180
        # keep a weak reference to the mind object, which we can verify later
 
1181
        # evaluates to None, thereby ensuring the reference leak is fixed.
 
1182
        self.mindRef = None
 
1183
        def setMindRef(mind):
 
1184
            self.mindRef = weakref.ref(mind)
 
1185
 
 
1186
        clientBroker, serverBroker, pump = connectedServerAndClient(
 
1187
                LeakyRealm(setMindRef))
 
1188
 
 
1189
        # log in from the client
 
1190
        connectionBroken = []
 
1191
        root = clientBroker.remoteForName("root")
 
1192
        d = root.callRemote("login", 'guest')
 
1193
        def cbResponse((challenge, challenger)):
 
1194
            mind = SimpleRemote()
 
1195
            return challenger.callRemote("respond",
 
1196
                    pb.respond(challenge, 'guest'), mind)
 
1197
        d.addCallback(cbResponse)
 
1198
        def connectionLost(_):
 
1199
            pump.stop() # don't try to pump data anymore - it won't work
 
1200
            connectionBroken.append(1)
 
1201
            serverBroker.connectionLost(failure.Failure(RuntimeError("boom")))
 
1202
        d.addCallback(connectionLost)
 
1203
 
 
1204
        # flush out the response and connectionLost
 
1205
        pump.flush()
 
1206
        self.assertEqual(connectionBroken, [1])
 
1207
 
 
1208
        # and check for lingering references - requestAvatar sets mindRef
 
1209
        # to a weakref to the mind; this object should be gc'd, and thus
 
1210
        # the ref should return None
 
1211
        gc.collect()
 
1212
        self.assertEqual(self.mindRef(), None)
 
1213
 
 
1214
 
 
1215
 
 
1216
class NewCredTests(unittest.TestCase):
 
1217
    """
 
1218
    Tests related to the L{twisted.cred} support in PB.
 
1219
    """
 
1220
    def setUp(self):
 
1221
        """
 
1222
        Create a portal with no checkers and wrap it around a simple test
 
1223
        realm.  Set up a PB server on a TCP port which serves perspectives
 
1224
        using that portal.
 
1225
        """
 
1226
        self.realm = TestRealm()
 
1227
        self.portal = portal.Portal(self.realm)
 
1228
        self.factory = ConnectionNotifyServerFactory(self.portal)
 
1229
        self.port = reactor.listenTCP(0, self.factory, interface="127.0.0.1")
 
1230
        self.portno = self.port.getHost().port
 
1231
 
 
1232
 
 
1233
    def tearDown(self):
 
1234
        """
 
1235
        Shut down the TCP port created by L{setUp}.
 
1236
        """
 
1237
        return self.port.stopListening()
 
1238
 
 
1239
 
 
1240
    def getFactoryAndRootObject(self, clientFactory=pb.PBClientFactory):
 
1241
        """
 
1242
        Create a connection to the test server.
 
1243
 
 
1244
        @param clientFactory: the factory class used to create the connection.
 
1245
 
 
1246
        @return: a tuple (C{factory}, C{deferred}), where factory is an
 
1247
            instance of C{clientFactory} and C{deferred} the L{Deferred} firing
 
1248
            with the PB root object.
 
1249
        """
 
1250
        factory = clientFactory()
 
1251
        rootObjDeferred = factory.getRootObject()
 
1252
        connector = reactor.connectTCP('127.0.0.1', self.portno, factory)
 
1253
        self.addCleanup(connector.disconnect)
 
1254
        return factory, rootObjDeferred
 
1255
 
 
1256
 
 
1257
    def test_getRootObject(self):
 
1258
        """
 
1259
        Assert only that L{PBClientFactory.getRootObject}'s Deferred fires with
 
1260
        a L{RemoteReference}.
 
1261
        """
 
1262
        factory, rootObjDeferred = self.getFactoryAndRootObject()
 
1263
 
 
1264
        def gotRootObject(rootObj):
 
1265
            self.assertIsInstance(rootObj, pb.RemoteReference)
 
1266
            disconnectedDeferred = Deferred()
 
1267
            rootObj.notifyOnDisconnect(disconnectedDeferred.callback)
 
1268
            factory.disconnect()
 
1269
            return disconnectedDeferred
 
1270
 
 
1271
        return rootObjDeferred.addCallback(gotRootObject)
 
1272
 
 
1273
 
 
1274
    def test_deadReferenceError(self):
 
1275
        """
 
1276
        Test that when a connection is lost, calling a method on a
 
1277
        RemoteReference obtained from it raises DeadReferenceError.
 
1278
        """
 
1279
        factory, rootObjDeferred = self.getFactoryAndRootObject()
 
1280
 
 
1281
        def gotRootObject(rootObj):
 
1282
            disconnectedDeferred = Deferred()
 
1283
            rootObj.notifyOnDisconnect(disconnectedDeferred.callback)
 
1284
 
 
1285
            def lostConnection(ign):
 
1286
                self.assertRaises(
 
1287
                    pb.DeadReferenceError,
 
1288
                    rootObj.callRemote, 'method')
 
1289
 
 
1290
            disconnectedDeferred.addCallback(lostConnection)
 
1291
            factory.disconnect()
 
1292
            return disconnectedDeferred
 
1293
 
 
1294
        return rootObjDeferred.addCallback(gotRootObject)
 
1295
 
 
1296
 
 
1297
    def test_clientConnectionLost(self):
 
1298
        """
 
1299
        Test that if the L{reconnecting} flag is passed with a True value then
 
1300
        a remote call made from a disconnection notification callback gets a
 
1301
        result successfully.
 
1302
        """
 
1303
        class ReconnectOnce(pb.PBClientFactory):
 
1304
            reconnectedAlready = False
 
1305
            def clientConnectionLost(self, connector, reason):
 
1306
                reconnecting = not self.reconnectedAlready
 
1307
                self.reconnectedAlready = True
 
1308
                if reconnecting:
 
1309
                    connector.connect()
 
1310
                return pb.PBClientFactory.clientConnectionLost(
 
1311
                    self, connector, reason, reconnecting)
 
1312
 
 
1313
        factory, rootObjDeferred = self.getFactoryAndRootObject(ReconnectOnce)
 
1314
 
 
1315
        def gotRootObject(rootObj):
 
1316
            self.assertIsInstance(rootObj, pb.RemoteReference)
 
1317
 
 
1318
            d = Deferred()
 
1319
            rootObj.notifyOnDisconnect(d.callback)
 
1320
            factory.disconnect()
 
1321
 
 
1322
            def disconnected(ign):
 
1323
                d = factory.getRootObject()
 
1324
 
 
1325
                def gotAnotherRootObject(anotherRootObj):
 
1326
                    self.assertIsInstance(anotherRootObj, pb.RemoteReference)
 
1327
 
 
1328
                    d = Deferred()
 
1329
                    anotherRootObj.notifyOnDisconnect(d.callback)
 
1330
                    factory.disconnect()
 
1331
                    return d
 
1332
                return d.addCallback(gotAnotherRootObject)
 
1333
            return d.addCallback(disconnected)
 
1334
        return rootObjDeferred.addCallback(gotRootObject)
 
1335
 
 
1336
 
 
1337
    def test_immediateClose(self):
 
1338
        """
 
1339
        Test that if a Broker loses its connection without receiving any bytes,
 
1340
        it doesn't raise any exceptions or log any errors.
 
1341
        """
 
1342
        serverProto = self.factory.buildProtocol(('127.0.0.1', 12345))
 
1343
        serverProto.makeConnection(protocol.FileWrapper(StringIO()))
 
1344
        serverProto.connectionLost(failure.Failure(main.CONNECTION_DONE))
 
1345
 
 
1346
 
 
1347
    def test_loginConnectionRefused(self):
 
1348
        """
 
1349
        L{PBClientFactory.login} returns a L{Deferred} which is errbacked
 
1350
        with the L{ConnectionRefusedError} if the underlying connection is
 
1351
        refused.
 
1352
        """
 
1353
        clientFactory = pb.PBClientFactory()
 
1354
        loginDeferred = clientFactory.login(
 
1355
            credentials.UsernamePassword("foo", "bar"))
 
1356
        clientFactory.clientConnectionFailed(
 
1357
            None,
 
1358
            failure.Failure(
 
1359
                ConnectionRefusedError("Test simulated refused connection")))
 
1360
        return self.assertFailure(loginDeferred, ConnectionRefusedError)
 
1361
 
 
1362
 
 
1363
    def _disconnect(self, ignore, factory):
 
1364
        """
 
1365
        Helper method disconnecting the given client factory and returning a
 
1366
        C{Deferred} that will fire when the server connection has noticed the
 
1367
        disconnection.
 
1368
        """
 
1369
        disconnectedDeferred = Deferred()
 
1370
        self.factory.protocolInstance.notifyOnDisconnect(
 
1371
            lambda: disconnectedDeferred.callback(None))
 
1372
        factory.disconnect()
 
1373
        return disconnectedDeferred
 
1374
 
 
1375
 
 
1376
    def test_loginLogout(self):
 
1377
        """
 
1378
        Test that login can be performed with IUsernamePassword credentials and
 
1379
        that when the connection is dropped the avatar is logged out.
 
1380
        """
 
1381
        self.portal.registerChecker(
 
1382
            checkers.InMemoryUsernamePasswordDatabaseDontUse(user='pass'))
 
1383
        factory = pb.PBClientFactory()
 
1384
        creds = credentials.UsernamePassword("user", "pass")
 
1385
 
 
1386
        # NOTE: real code probably won't need anything where we have the
 
1387
        # "BRAINS!" argument, passing None is fine. We just do it here to
 
1388
        # test that it is being passed. It is used to give additional info to
 
1389
        # the realm to aid perspective creation, if you don't need that,
 
1390
        # ignore it.
 
1391
        mind = "BRAINS!"
 
1392
 
 
1393
        d = factory.login(creds, mind)
 
1394
        def cbLogin(perspective):
 
1395
            self.assertTrue(self.realm.lastPerspective.loggedIn)
 
1396
            self.assertIsInstance(perspective, pb.RemoteReference)
 
1397
            return self._disconnect(None, factory)
 
1398
        d.addCallback(cbLogin)
 
1399
 
 
1400
        def cbLogout(ignored):
 
1401
            self.assertTrue(self.realm.lastPerspective.loggedOut)
 
1402
        d.addCallback(cbLogout)
 
1403
 
 
1404
        connector = reactor.connectTCP("127.0.0.1", self.portno, factory)
 
1405
        self.addCleanup(connector.disconnect)
 
1406
        return d
 
1407
 
 
1408
 
 
1409
    def test_logoutAfterDecref(self):
 
1410
        """
 
1411
        If a L{RemoteReference} to an L{IPerspective} avatar is decrefed and
 
1412
        there remain no other references to the avatar on the server, the
 
1413
        avatar is garbage collected and the logout method called.
 
1414
        """
 
1415
        loggedOut = Deferred()
 
1416
 
 
1417
        class EventPerspective(pb.Avatar):
 
1418
            """
 
1419
            An avatar which fires a Deferred when it is logged out.
 
1420
            """
 
1421
            def __init__(self, avatarId):
 
1422
                pass
 
1423
 
 
1424
            def logout(self):
 
1425
                loggedOut.callback(None)
 
1426
 
 
1427
        self.realm.perspectiveFactory = EventPerspective
 
1428
 
 
1429
        self.portal.registerChecker(
 
1430
            checkers.InMemoryUsernamePasswordDatabaseDontUse(foo='bar'))
 
1431
        factory = pb.PBClientFactory()
 
1432
        d = factory.login(
 
1433
            credentials.UsernamePassword('foo', 'bar'), "BRAINS!")
 
1434
        def cbLoggedIn(avatar):
 
1435
            # Just wait for the logout to happen, as it should since the
 
1436
            # reference to the avatar will shortly no longer exists.
 
1437
            return loggedOut
 
1438
        d.addCallback(cbLoggedIn)
 
1439
        def cbLoggedOut(ignored):
 
1440
            # Verify that the server broker's _localCleanup dict isn't growing
 
1441
            # without bound.
 
1442
            self.assertEqual(self.factory.protocolInstance._localCleanup, {})
 
1443
        d.addCallback(cbLoggedOut)
 
1444
        d.addCallback(self._disconnect, factory)
 
1445
        connector = reactor.connectTCP("127.0.0.1", self.portno, factory)
 
1446
        self.addCleanup(connector.disconnect)
 
1447
        return d
 
1448
 
 
1449
 
 
1450
    def test_concurrentLogin(self):
 
1451
        """
 
1452
        Two different correct login attempts can be made on the same root
 
1453
        object at the same time and produce two different resulting avatars.
 
1454
        """
 
1455
        self.portal.registerChecker(
 
1456
            checkers.InMemoryUsernamePasswordDatabaseDontUse(
 
1457
                foo='bar', baz='quux'))
 
1458
        factory = pb.PBClientFactory()
 
1459
 
 
1460
        firstLogin = factory.login(
 
1461
            credentials.UsernamePassword('foo', 'bar'), "BRAINS!")
 
1462
        secondLogin = factory.login(
 
1463
            credentials.UsernamePassword('baz', 'quux'), "BRAINS!")
 
1464
        d = gatherResults([firstLogin, secondLogin])
 
1465
        def cbLoggedIn((first, second)):
 
1466
            return gatherResults([
 
1467
                    first.callRemote('getAvatarId'),
 
1468
                    second.callRemote('getAvatarId')])
 
1469
        d.addCallback(cbLoggedIn)
 
1470
        def cbAvatarIds((first, second)):
 
1471
            self.assertEqual(first, 'foo')
 
1472
            self.assertEqual(second, 'baz')
 
1473
        d.addCallback(cbAvatarIds)
 
1474
        d.addCallback(self._disconnect, factory)
 
1475
 
 
1476
        connector = reactor.connectTCP('127.0.0.1', self.portno, factory)
 
1477
        self.addCleanup(connector.disconnect)
 
1478
        return d
 
1479
 
 
1480
 
 
1481
    def test_badUsernamePasswordLogin(self):
 
1482
        """
 
1483
        Test that a login attempt with an invalid user or invalid password
 
1484
        fails in the appropriate way.
 
1485
        """
 
1486
        self.portal.registerChecker(
 
1487
            checkers.InMemoryUsernamePasswordDatabaseDontUse(user='pass'))
 
1488
        factory = pb.PBClientFactory()
 
1489
 
 
1490
        firstLogin = factory.login(
 
1491
            credentials.UsernamePassword('nosuchuser', 'pass'))
 
1492
        secondLogin = factory.login(
 
1493
            credentials.UsernamePassword('user', 'wrongpass'))
 
1494
 
 
1495
        self.assertFailure(firstLogin, UnauthorizedLogin)
 
1496
        self.assertFailure(secondLogin, UnauthorizedLogin)
 
1497
        d = gatherResults([firstLogin, secondLogin])
 
1498
 
 
1499
        def cleanup(ignore):
 
1500
            errors = self.flushLoggedErrors(UnauthorizedLogin)
 
1501
            self.assertEqual(len(errors), 2)
 
1502
            return self._disconnect(None, factory)
 
1503
        d.addCallback(cleanup)
 
1504
 
 
1505
        connector = reactor.connectTCP("127.0.0.1", self.portno, factory)
 
1506
        self.addCleanup(connector.disconnect)
 
1507
        return d
 
1508
 
 
1509
 
 
1510
    def test_anonymousLogin(self):
 
1511
        """
 
1512
        Verify that a PB server using a portal configured with an checker which
 
1513
        allows IAnonymous credentials can be logged into using IAnonymous
 
1514
        credentials.
 
1515
        """
 
1516
        self.portal.registerChecker(checkers.AllowAnonymousAccess())
 
1517
        factory = pb.PBClientFactory()
 
1518
        d = factory.login(credentials.Anonymous(), "BRAINS!")
 
1519
 
 
1520
        def cbLoggedIn(perspective):
 
1521
            return perspective.callRemote('echo', 123)
 
1522
        d.addCallback(cbLoggedIn)
 
1523
 
 
1524
        d.addCallback(self.assertEqual, 123)
 
1525
 
 
1526
        d.addCallback(self._disconnect, factory)
 
1527
 
 
1528
        connector = reactor.connectTCP("127.0.0.1", self.portno, factory)
 
1529
        self.addCleanup(connector.disconnect)
 
1530
        return d
 
1531
 
 
1532
 
 
1533
    def test_anonymousLoginNotPermitted(self):
 
1534
        """
 
1535
        Verify that without an anonymous checker set up, anonymous login is
 
1536
        rejected.
 
1537
        """
 
1538
        self.portal.registerChecker(
 
1539
            checkers.InMemoryUsernamePasswordDatabaseDontUse(user='pass'))
 
1540
        factory = pb.PBClientFactory()
 
1541
        d = factory.login(credentials.Anonymous(), "BRAINS!")
 
1542
        self.assertFailure(d, UnhandledCredentials)
 
1543
 
 
1544
        def cleanup(ignore):
 
1545
            errors = self.flushLoggedErrors(UnhandledCredentials)
 
1546
            self.assertEqual(len(errors), 1)
 
1547
            return self._disconnect(None, factory)
 
1548
        d.addCallback(cleanup)
 
1549
 
 
1550
        connector = reactor.connectTCP('127.0.0.1', self.portno, factory)
 
1551
        self.addCleanup(connector.disconnect)
 
1552
        return d
 
1553
 
 
1554
 
 
1555
    def test_anonymousLoginWithMultipleCheckers(self):
 
1556
        """
 
1557
        Like L{test_anonymousLogin} but against a portal with a checker for
 
1558
        both IAnonymous and IUsernamePassword.
 
1559
        """
 
1560
        self.portal.registerChecker(checkers.AllowAnonymousAccess())
 
1561
        self.portal.registerChecker(
 
1562
            checkers.InMemoryUsernamePasswordDatabaseDontUse(user='pass'))
 
1563
        factory = pb.PBClientFactory()
 
1564
        d = factory.login(credentials.Anonymous(), "BRAINS!")
 
1565
 
 
1566
        def cbLogin(perspective):
 
1567
            return perspective.callRemote('echo', 123)
 
1568
        d.addCallback(cbLogin)
 
1569
 
 
1570
        d.addCallback(self.assertEqual, 123)
 
1571
 
 
1572
        d.addCallback(self._disconnect, factory)
 
1573
 
 
1574
        connector = reactor.connectTCP('127.0.0.1', self.portno, factory)
 
1575
        self.addCleanup(connector.disconnect)
 
1576
        return d
 
1577
 
 
1578
 
 
1579
    def test_authenticatedLoginWithMultipleCheckers(self):
 
1580
        """
 
1581
        Like L{test_anonymousLoginWithMultipleCheckers} but check that
 
1582
        username/password authentication works.
 
1583
        """
 
1584
        self.portal.registerChecker(checkers.AllowAnonymousAccess())
 
1585
        self.portal.registerChecker(
 
1586
            checkers.InMemoryUsernamePasswordDatabaseDontUse(user='pass'))
 
1587
        factory = pb.PBClientFactory()
 
1588
        d = factory.login(
 
1589
            credentials.UsernamePassword('user', 'pass'), "BRAINS!")
 
1590
 
 
1591
        def cbLogin(perspective):
 
1592
            return perspective.callRemote('add', 100, 23)
 
1593
        d.addCallback(cbLogin)
 
1594
 
 
1595
        d.addCallback(self.assertEqual, 123)
 
1596
 
 
1597
        d.addCallback(self._disconnect, factory)
 
1598
 
 
1599
        connector = reactor.connectTCP('127.0.0.1', self.portno, factory)
 
1600
        self.addCleanup(connector.disconnect)
 
1601
        return d
 
1602
 
 
1603
 
 
1604
    def test_view(self):
 
1605
        """
 
1606
        Verify that a viewpoint can be retrieved after authenticating with
 
1607
        cred.
 
1608
        """
 
1609
        self.portal.registerChecker(
 
1610
            checkers.InMemoryUsernamePasswordDatabaseDontUse(user='pass'))
 
1611
        factory = pb.PBClientFactory()
 
1612
        d = factory.login(
 
1613
            credentials.UsernamePassword("user", "pass"), "BRAINS!")
 
1614
 
 
1615
        def cbLogin(perspective):
 
1616
            return perspective.callRemote("getViewPoint")
 
1617
        d.addCallback(cbLogin)
 
1618
 
 
1619
        def cbView(viewpoint):
 
1620
            return viewpoint.callRemote("check")
 
1621
        d.addCallback(cbView)
 
1622
 
 
1623
        d.addCallback(self.assertTrue)
 
1624
 
 
1625
        d.addCallback(self._disconnect, factory)
 
1626
 
 
1627
        connector = reactor.connectTCP("127.0.0.1", self.portno, factory)
 
1628
        self.addCleanup(connector.disconnect)
 
1629
        return d
 
1630
 
 
1631
 
 
1632
 
 
1633
class NonSubclassingPerspective:
 
1634
    implements(pb.IPerspective)
 
1635
 
 
1636
    def __init__(self, avatarId):
 
1637
        pass
 
1638
 
 
1639
    # IPerspective implementation
 
1640
    def perspectiveMessageReceived(self, broker, message, args, kwargs):
 
1641
        args = broker.unserialize(args, self)
 
1642
        kwargs = broker.unserialize(kwargs, self)
 
1643
        return broker.serialize((message, args, kwargs))
 
1644
 
 
1645
    # Methods required by TestRealm
 
1646
    def logout(self):
 
1647
        self.loggedOut = True
 
1648
 
 
1649
 
 
1650
 
 
1651
class NSPTests(unittest.TestCase):
 
1652
    """
 
1653
    Tests for authentication against a realm where the L{IPerspective}
 
1654
    implementation is not a subclass of L{Avatar}.
 
1655
    """
 
1656
    def setUp(self):
 
1657
        self.realm = TestRealm()
 
1658
        self.realm.perspectiveFactory = NonSubclassingPerspective
 
1659
        self.portal = portal.Portal(self.realm)
 
1660
        self.checker = checkers.InMemoryUsernamePasswordDatabaseDontUse()
 
1661
        self.checker.addUser("user", "pass")
 
1662
        self.portal.registerChecker(self.checker)
 
1663
        self.factory = WrappingFactory(pb.PBServerFactory(self.portal))
 
1664
        self.port = reactor.listenTCP(0, self.factory, interface="127.0.0.1")
 
1665
        self.addCleanup(self.port.stopListening)
 
1666
        self.portno = self.port.getHost().port
 
1667
 
 
1668
 
 
1669
    def test_NSP(self):
 
1670
        """
 
1671
        An L{IPerspective} implementation which does not subclass
 
1672
        L{Avatar} can expose remote methods for the client to call.
 
1673
        """
 
1674
        factory = pb.PBClientFactory()
 
1675
        d = factory.login(credentials.UsernamePassword('user', 'pass'),
 
1676
                          "BRAINS!")
 
1677
        reactor.connectTCP('127.0.0.1', self.portno, factory)
 
1678
        d.addCallback(lambda p: p.callRemote('ANYTHING', 'here', bar='baz'))
 
1679
        d.addCallback(self.assertEqual,
 
1680
                      ('ANYTHING', ('here',), {'bar': 'baz'}))
 
1681
        def cleanup(ignored):
 
1682
            factory.disconnect()
 
1683
            for p in self.factory.protocols:
 
1684
                p.transport.loseConnection()
 
1685
        d.addCallback(cleanup)
 
1686
        return d
 
1687
 
 
1688
 
 
1689
 
 
1690
class IForwarded(Interface):
 
1691
    """
 
1692
    Interface used for testing L{util.LocalAsyncForwarder}.
 
1693
    """
 
1694
 
 
1695
    def forwardMe():
 
1696
        """
 
1697
        Simple synchronous method.
 
1698
        """
 
1699
 
 
1700
    def forwardDeferred():
 
1701
        """
 
1702
        Simple asynchronous method.
 
1703
        """
 
1704
 
 
1705
 
 
1706
class Forwarded:
 
1707
    """
 
1708
    Test implementation of L{IForwarded}.
 
1709
 
 
1710
    @ivar forwarded: set if C{forwardMe} is called.
 
1711
    @type forwarded: C{bool}
 
1712
    @ivar unforwarded: set if C{dontForwardMe} is called.
 
1713
    @type unforwarded: C{bool}
 
1714
    """
 
1715
    implements(IForwarded)
 
1716
    forwarded = False
 
1717
    unforwarded = False
 
1718
 
 
1719
    def forwardMe(self):
 
1720
        """
 
1721
        Set a local flag to test afterwards.
 
1722
        """
 
1723
        self.forwarded = True
 
1724
 
 
1725
    def dontForwardMe(self):
 
1726
        """
 
1727
        Set a local flag to test afterwards. This should not be called as it's
 
1728
        not in the interface.
 
1729
        """
 
1730
        self.unforwarded = True
 
1731
 
 
1732
    def forwardDeferred(self):
 
1733
        """
 
1734
        Asynchronously return C{True}.
 
1735
        """
 
1736
        return succeed(True)
 
1737
 
 
1738
 
 
1739
class SpreadUtilTests(unittest.TestCase):
 
1740
    """
 
1741
    Tests for L{twisted.spread.util}.
 
1742
    """
 
1743
 
 
1744
    def test_sync(self):
 
1745
        """
 
1746
        Call a synchronous method of a L{util.LocalAsRemote} object and check
 
1747
        the result.
 
1748
        """
 
1749
        o = LocalRemoteTest()
 
1750
        self.assertEqual(o.callRemote("add1", 2), 3)
 
1751
 
 
1752
    def test_async(self):
 
1753
        """
 
1754
        Call an asynchronous method of a L{util.LocalAsRemote} object and check
 
1755
        the result.
 
1756
        """
 
1757
        o = LocalRemoteTest()
 
1758
        o = LocalRemoteTest()
 
1759
        d = o.callRemote("add", 2, y=4)
 
1760
        self.assertIsInstance(d, Deferred)
 
1761
        d.addCallback(self.assertEqual, 6)
 
1762
        return d
 
1763
 
 
1764
    def test_asyncFail(self):
 
1765
        """
 
1766
        Test a asynchronous failure on a remote method call.
 
1767
        """
 
1768
        o = LocalRemoteTest()
 
1769
        d = o.callRemote("fail")
 
1770
        def eb(f):
 
1771
            self.assertTrue(isinstance(f, failure.Failure))
 
1772
            f.trap(RuntimeError)
 
1773
        d.addCallbacks(lambda res: self.fail("supposed to fail"), eb)
 
1774
        return d
 
1775
 
 
1776
    def test_remoteMethod(self):
 
1777
        """
 
1778
        Test the C{remoteMethod} facility of L{util.LocalAsRemote}.
 
1779
        """
 
1780
        o = LocalRemoteTest()
 
1781
        m = o.remoteMethod("add1")
 
1782
        self.assertEqual(m(3), 4)
 
1783
 
 
1784
    def test_localAsyncForwarder(self):
 
1785
        """
 
1786
        Test a call to L{util.LocalAsyncForwarder} using L{Forwarded} local
 
1787
        object.
 
1788
        """
 
1789
        f = Forwarded()
 
1790
        lf = util.LocalAsyncForwarder(f, IForwarded)
 
1791
        lf.callRemote("forwardMe")
 
1792
        self.assertTrue(f.forwarded)
 
1793
        lf.callRemote("dontForwardMe")
 
1794
        self.assertFalse(f.unforwarded)
 
1795
        rr = lf.callRemote("forwardDeferred")
 
1796
        l = []
 
1797
        rr.addCallback(l.append)
 
1798
        self.assertEqual(l[0], 1)
 
1799
 
 
1800
 
 
1801
 
 
1802
class PBWithSecurityOptionsTests(unittest.TestCase):
 
1803
    """
 
1804
    Test security customization.
 
1805
    """
 
1806
 
 
1807
    def test_clientDefaultSecurityOptions(self):
 
1808
        """
 
1809
        By default, client broker should use C{jelly.globalSecurity} as
 
1810
        security settings.
 
1811
        """
 
1812
        factory = pb.PBClientFactory()
 
1813
        broker = factory.buildProtocol(None)
 
1814
        self.assertIdentical(broker.security, jelly.globalSecurity)
 
1815
 
 
1816
 
 
1817
    def test_serverDefaultSecurityOptions(self):
 
1818
        """
 
1819
        By default, server broker should use C{jelly.globalSecurity} as
 
1820
        security settings.
 
1821
        """
 
1822
        factory = pb.PBServerFactory(Echoer())
 
1823
        broker = factory.buildProtocol(None)
 
1824
        self.assertIdentical(broker.security, jelly.globalSecurity)
 
1825
 
 
1826
 
 
1827
    def test_clientSecurityCustomization(self):
 
1828
        """
 
1829
        Check that the security settings are passed from the client factory to
 
1830
        the broker object.
 
1831
        """
 
1832
        security = jelly.SecurityOptions()
 
1833
        factory = pb.PBClientFactory(security=security)
 
1834
        broker = factory.buildProtocol(None)
 
1835
        self.assertIdentical(broker.security, security)
 
1836
 
 
1837
 
 
1838
    def test_serverSecurityCustomization(self):
 
1839
        """
 
1840
        Check that the security settings are passed from the server factory to
 
1841
        the broker object.
 
1842
        """
 
1843
        security = jelly.SecurityOptions()
 
1844
        factory = pb.PBServerFactory(Echoer(), security=security)
 
1845
        broker = factory.buildProtocol(None)
 
1846
        self.assertIdentical(broker.security, security)