~justin-fathomdb/nova/justinsb-openstack-api-volumes

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/test/test_pb.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

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