1
# Copyright (c) Twisted Matrix Laboratories.
2
# See LICENSE for details.
5
Tests for Perspective Broker module.
7
TODO: update protocol level tests to use new connection API, leaving
8
only specific tests for old API.
11
# issue1195 TODOs: replace pump.pump() with something involving Deferreds.
12
# Clean up warning suppression.
14
import sys, os, time, gc, weakref
16
from cStringIO import StringIO
17
from zope.interface import implements, Interface
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
30
class Dummy(pb.Viewable):
31
def view_doNothing(self, user):
32
if isinstance(user, DummyPerspective):
35
return 'goodbye, cruel world!'
38
class DummyPerspective(pb.Avatar):
40
An L{IPerspective} avatar which will be used in some tests.
42
def perspective_getDummyViewPoint(self):
47
class DummyRealm(object):
48
implements(portal.IRealm)
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
58
Utility to pump data between clients and servers for protocol testing.
60
Perhaps this is a utility worthy of being in protocol.py?
62
def __init__(self, client, server, clientIO, serverIO):
65
self.clientIO = clientIO
66
self.serverIO = serverIO
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
77
timeout = time.time() + 5
78
while not self._stop and self.pump():
79
if time.time() > timeout:
85
Stop a running L{flush} operation, even if data remains to be
93
Move data back and forth.
95
Returns whether any data was moved.
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()
108
self.server.dataReceived(byte)
110
self.client.dataReceived(byte)
118
def connectedServerAndClient(realm=None):
120
Connect a client and server L{Broker} together with an L{IOPump}
122
@param realm: realm to use, defaulting to a L{DummyRealm}
124
@returns: a 3-tuple (client, server, pump).
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',))
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:
139
return clientBroker, serverBroker, pump
142
class SimpleRemote(pb.Referenceable):
143
def remote_thunk(self, arg):
147
def remote_knuth(self, arg):
151
class NestedRemote(pb.Referenceable):
152
def remote_getSimple(self):
153
return SimpleRemote()
156
class SimpleCopy(pb.Copyable):
159
self.y = {"Hello":"World"}
163
class SimpleLocalCopy(pb.RemoteCopy):
166
pb.setUnjellyableForClass(SimpleCopy, SimpleLocalCopy)
169
class SimpleFactoryCopy(pb.Copyable):
171
@cvar allIDs: hold every created instances of this class.
172
@type allIDs: C{dict}
175
def __init__(self, id):
177
SimpleFactoryCopy.allIDs[id] = self
180
def createFactoryCopy(state):
182
Factory of L{SimpleFactoryCopy}, getting a created instance given the
183
C{id} found in C{state}.
185
stateId = state.get("id", None)
187
raise RuntimeError("factory copy state has no 'id' member %s" %
189
if not stateId in SimpleFactoryCopy.allIDs:
190
raise RuntimeError("factory class has no ID: %s" %
191
(SimpleFactoryCopy.allIDs,))
192
inst = SimpleFactoryCopy.allIDs[stateId]
194
raise RuntimeError("factory method found no object with id")
197
pb.setUnjellyableFactoryForClass(SimpleFactoryCopy, createFactoryCopy)
200
class NestedCopy(pb.Referenceable):
201
def remote_getCopy(self):
204
def remote_getFactory(self, value):
205
return SimpleFactoryCopy(value)
209
class SimpleCache(pb.Cacheable):
212
self.y = {"Hello":"World"}
216
class NestedComplicatedCache(pb.Referenceable):
218
self.c = VeryVeryComplicatedCacheable()
220
def remote_getCache(self):
224
class VeryVeryComplicatedCacheable(pb.Cacheable):
232
self.observer.callRemote('foo',4)
234
def getStateToCacheAndObserveFor(self, perspective, observer):
235
self.observer = observer
240
def stoppedObserving(self, perspective, observer):
241
log.msg("stopped observing")
242
observer.callRemote("end")
243
if observer == self.observer:
247
class RatherBaroqueCache(pb.RemoteCache):
248
def observe_foo(self, newFoo):
251
def observe_end(self):
252
log.msg("the end of things")
254
pb.setUnjellyableForClass(VeryVeryComplicatedCacheable, RatherBaroqueCache)
257
class SimpleLocalCache(pb.RemoteCache):
258
def setCopyableState(self, state):
259
self.__dict__.update(state)
261
def checkMethod(self):
270
pb.setUnjellyableForClass(SimpleCache, SimpleLocalCache)
273
class NestedCache(pb.Referenceable):
275
self.x = SimpleCache()
277
def remote_getCache(self):
278
return [self.x,self.x]
280
def remote_putCache(self, cache):
281
return (self.x is cache)
284
class Observable(pb.Referenceable):
288
def remote_observe(self, obs):
289
self.observers.append(obs)
291
def remote_unobserve(self, obs):
292
self.observers.remove(obs)
294
def notify(self, obj):
295
for observer in self.observers:
296
observer.callRemote('notify', self, obj)
299
class DeferredRemote(pb.Referenceable):
303
def runMe(self, arg):
307
def dontRunMe(self, arg):
308
assert 0, "shouldn't have been run!"
310
def remote_doItLater(self):
312
Return a L{Deferred} to be fired on client side. When fired,
313
C{self.runMe} is called.
316
d.addCallbacks(self.runMe, self.dontRunMe)
321
class Observer(pb.Referenceable):
324
def remote_notify(self, other, obj):
326
self.notified = self.notified + 1
327
other.callRemote('unobserve',self)
330
class NewStyleCopy(pb.Copyable, pb.RemoteCopy, object):
331
def __init__(self, s):
333
pb.setUnjellyableForClass(NewStyleCopy, NewStyleCopy)
336
class NewStyleCopy2(pb.Copyable, pb.RemoteCopy, object):
342
NewStyleCopy2.allocated += 1
343
inst = object.__new__(self)
348
NewStyleCopy2.initialized += 1
350
pb.setUnjellyableForClass(NewStyleCopy2, NewStyleCopy2)
353
class NewStyleCacheCopy(pb.Cacheable, pb.RemoteCache, object):
354
def getStateToCacheAndObserveFor(self, perspective, observer):
357
pb.setUnjellyableForClass(NewStyleCacheCopy, NewStyleCacheCopy)
360
class Echoer(pb.Root):
361
def remote_echo(self, st):
365
class CachedReturner(pb.Root):
366
def __init__(self, cache):
368
def remote_giveMeCache(self, st):
372
class NewStyleTests(unittest.TestCase):
375
Create a pb server using L{Echoer} protocol and connect a client to it.
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,
385
return clientFactory.getRootObject().addCallback(gotRoot)
390
Close client and server connections, reset values of L{NewStyleCopy2}
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()
402
def test_newStyle(self):
404
Create a new style object, send it over the wire, and check the result.
406
orig = NewStyleCopy("value")
407
d = self.ref.callRemote("echo", orig)
409
self.assertTrue(isinstance(res, NewStyleCopy))
410
self.assertEqual(res.s, "value")
411
self.assertFalse(res is orig) # no cheating :)
415
def test_alloc(self):
417
Send a new style object and check the number of allocations.
419
orig = NewStyleCopy2()
420
self.assertEqual(NewStyleCopy2.allocated, 1)
421
self.assertEqual(NewStyleCopy2.initialized, 1)
422
d = self.ref.callRemote("echo", orig)
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
436
class ConnectionNotifyServerFactory(pb.PBServerFactory):
438
A server factory which stores the last connection and fires a
439
L{Deferred} on connection made. This factory can handle only one
442
@ivar protocolInstance: the last protocol instance.
443
@type protocolInstance: C{pb.Broker}
445
@ivar connectionMade: the deferred fired upon connection.
446
@type connectionMade: C{Deferred}
448
protocolInstance = None
450
def __init__(self, root):
452
Initialize the factory.
454
pb.PBServerFactory.__init__(self, root)
455
self.connectionMade = Deferred()
458
def clientConnectionMade(self, protocol):
460
Store the protocol and fire the connection deferred.
462
self.protocolInstance = protocol
463
d, self.connectionMade = self.connectionMade, None
469
class NewStyleCachedTests(unittest.TestCase):
472
Create a pb server using L{CachedReturner} protocol and connect a
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,
484
d1 = clientFactory.getRootObject().addCallback(gotRoot)
485
d2 = self.server.factory.connectionMade
486
return gatherResults([d1, d2])
491
Close client and server connections.
493
self.server.factory.protocolInstance.transport.loseConnection()
494
self.ref.broker.transport.loseConnection()
495
return self.server.stopListening()
498
def test_newStyleCache(self):
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.
504
d = self.ref.callRemote("giveMeCache", self.orig)
506
self.assertIsInstance(res, NewStyleCacheCopy)
507
self.assertEqual("value", res.s)
509
self.assertNotIdentical(self.orig, res)
512
# Save a reference so it stays alive for the rest of this test
514
# And ask for it again to exercise the special re-jelly logic in
516
return self.ref.callRemote("giveMeCache", self.orig)
517
d.addCallback(cb, True)
518
d.addCallback(cb, False)
523
class BrokerTests(unittest.TestCase):
528
# from RemotePublished.getFileName
529
os.unlink('None-None-TESTING.pub')
533
def thunkErrorBad(self, error):
534
self.fail("This should cause a return value, not %s" % (error,))
536
def thunkResultGood(self, result):
537
self.thunkResult = result
539
def thunkErrorGood(self, tb):
542
def thunkResultBad(self, result):
543
self.fail("This should cause an error, not %s" % (result,))
545
def test_reference(self):
546
c, s, pump = connectedServerAndClient()
548
class X(pb.Referenceable):
549
def remote_catch(self,arg):
552
class Y(pb.Referenceable):
553
def remote_throw(self, a, b):
554
a.callRemote('catch', b)
556
s.setNameForLocal("y", Y())
557
y = c.remoteForName("y")
560
y.callRemote('throw', x, z)
564
self.assertIdentical(x.caught, z, "X should have caught Z")
566
# make sure references to remote methods are equals
567
self.assertEqual(y.remoteMethod('throw'), y.remoteMethod('throw'))
569
def test_result(self):
570
c, s, pump = connectedServerAndClient()
571
for x, y in (c, s), (s, c):
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)
583
# Shouldn't require any more pumping than that...
584
self.assertEqual(self.thunkResult, self.expectedThunkResult,
585
"result wasn't received.")
587
def refcountResult(self, result):
588
self.nestedRemote = result
590
def test_tooManyRefs(self):
593
c, s, pump = connectedServerAndClient()
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:
600
x.callRemote("getSimple").addCallbacks(l.append, e.append)
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)))
608
c, s, pump = connectedServerAndClient()
610
s.setNameForLocal("foo", foo)
611
x = c.remoteForName("foo")
612
x.callRemote('getCopy'
613
).addCallbacks(self.thunkResultGood, self.thunkErrorBad)
616
self.assertEqual(self.thunkResult.x, 1)
617
self.assertEqual(self.thunkResult.y['Hello'], 'World')
618
self.assertEqual(self.thunkResult.z[0], 'test')
620
def test_observe(self):
621
c, s, pump = connectedServerAndClient()
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.
627
s.setNameForLocal("a", a)
628
ra = c.remoteForName("a")
629
ra.callRemote('observe',b)
637
self.assertNotIdentical(b.obj, None, "didn't notify")
638
self.assertEqual(b.obj, 1, 'notified too much')
640
def test_defer(self):
641
c, s, pump = connectedServerAndClient()
643
s.setNameForLocal("d", d)
644
e = c.remoteForName("d")
645
pump.pump(); pump.pump()
647
e.callRemote('doItLater').addCallback(results.append)
648
pump.pump(); pump.pump()
649
self.assertFalse(d.run, "Deferred method run too early.")
651
self.assertEqual(d.run, 5, "Deferred method run too late.")
652
pump.pump(); pump.pump()
653
self.assertEqual(results[0], 6, "Incorrect result.")
656
def test_refcount(self):
657
c, s, pump = connectedServerAndClient()
659
s.setNameForLocal("foo", foo)
660
bar = c.remoteForName("foo")
661
bar.callRemote('getSimple'
662
).addCallbacks(self.refcountResult, self.thunkErrorBad)
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
675
if sys.hexversion >= 0x2000000:
677
# try to nudge the GC even if we can't really
681
self.assertNotIn(rluid, s.localObjects)
683
def test_cache(self):
684
c, s, pump = connectedServerAndClient()
686
obj2 = NestedComplicatedCache()
688
s.setNameForLocal("obj", obj)
689
s.setNameForLocal("xxx", obj2)
690
o2 = c.remoteForName("obj")
691
o3 = c.remoteForName("xxx")
693
o2.callRemote("getCache"
694
).addCallback(coll.append).addErrback(coll.append)
695
o2.callRemote("getCache"
696
).addCallback(coll.append).addErrback(coll.append)
698
o3.callRemote("getCache").addCallback(complex.append)
699
o3.callRemote("getCache").addCallback(complex.append)
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)
708
self.assertEqual(complex[0].foo, 4)
709
self.assertEqual(len(coll), 2)
711
self.assertIdentical(cp.checkMethod().im_self, cp,
712
"potential refcounting issue")
713
self.assertIdentical(cp.checkSelf(), cp,
714
"other potential refcounting issue")
716
o2.callRemote('putCache',cp).addCallback(col2.append)
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"))
724
# now, refcounting (similiar to testRefCount)
726
baroqueLuid = complex[0].luid
727
self.assertIn(luid, s.remotelyCachedObjects,
728
"remote cache doesn't have it")
738
if sys.hexversion >= 0x2000000:
740
# try to nudge the GC even if we can't really
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")
753
def test_publishable(self):
755
os.unlink('None-None-TESTING.pub') # from RemotePublished.getFileName
757
pass # Sometimes it's not there.
758
c, s, pump = connectedServerAndClient()
760
# foo.pub.timestamp = 1.0
761
s.setNameForLocal("foo", foo)
762
bar = c.remoteForName("foo")
764
bar.callRemote('getPub').addCallbacks(accum.append, self.thunkErrorBad)
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)
778
# timestamp's clean, our cache file is up-to-date
779
self.assertEqual(obj._wasCleanWhenLoaded, 1)
781
def gotCopy(self, val):
782
self.thunkResult = val.id
785
def test_factoryCopy(self):
786
c, s, pump = connectedServerAndClient()
789
s.setNameForLocal("foo", obj)
790
x = c.remoteForName("foo")
791
x.callRemote('getFactory', ID
792
).addCallbacks(self.gotCopy, self.thunkResultBad)
796
self.assertEqual(self.thunkResult, ID,
797
"ID not correct on factory object %s" % (self.thunkResult,))
800
bigString = "helloworld" * 50
803
callbackKeyword = None
805
def finishedCallback(*args, **kw):
806
global callbackArgs, callbackKeyword
811
class Pagerizer(pb.Referenceable):
812
def __init__(self, callback, *args, **kw):
813
self.callback, self.args, self.kw = callback, args, kw
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
821
class FilePagerizer(pb.Referenceable):
824
def __init__(self, filename, callback, *args, **kw):
825
self.filename = filename
826
self.callback, self.args, self.kw = callback, args, kw
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
835
class PagingTests(unittest.TestCase):
837
Test pb objects sending data by pages.
842
Create a file used to test L{util.FilePager}.
844
self.filename = self.mktemp()
845
fd = file(self.filename, 'w')
850
def test_pagingWithCallback(self):
852
Test L{util.StringPager}, passing a callback to fire when all pages
855
c, s, pump = connectedServerAndClient()
856
s.setNameForLocal("foo", Pagerizer(finishedCallback, 'hello', value=10))
857
x = c.remoteForName("foo")
859
util.getAllPages(x, "getPages").addCallback(l.append)
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")
870
def test_pagingWithoutCallback(self):
872
Test L{util.StringPager} without a callback.
874
c, s, pump = connectedServerAndClient()
875
s.setNameForLocal("foo", Pagerizer(None))
876
x = c.remoteForName("foo")
878
util.getAllPages(x, "getPages").addCallback(l.append)
881
self.assertEqual(''.join(l[0]), bigString,
882
"Pages received not equal to pages sent!")
885
def test_emptyFilePaging(self):
887
Test L{util.FilePager}, sending an empty file.
889
filenameEmpty = self.mktemp()
890
fd = file(filenameEmpty, 'w')
892
c, s, pump = connectedServerAndClient()
893
pagerizer = FilePagerizer(filenameEmpty, None)
894
s.setNameForLocal("bar", pagerizer)
895
x = c.remoteForName("bar")
897
util.getAllPages(x, "getPages").addCallback(l.append)
899
while not l and ttl > 0:
903
self.fail('getAllPages timed out')
904
self.assertEqual(''.join(l[0]), '',
905
"Pages received not equal to pages sent!")
908
def test_filePagingWithCallback(self):
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.
913
c, s, pump = connectedServerAndClient()
914
pagerizer = FilePagerizer(self.filename, finishedCallback,
916
s.setNameForLocal("bar", pagerizer)
917
x = c.remoteForName("bar")
919
util.getAllPages(x, "getPages").addCallback(l.append)
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, [])
931
def test_filePagingWithoutCallback(self):
933
Test L{util.FilePager} without a callback.
935
c, s, pump = connectedServerAndClient()
936
pagerizer = FilePagerizer(self.filename, None)
937
s.setNameForLocal("bar", pagerizer)
938
x = c.remoteForName("bar")
940
util.getAllPages(x, "getPages").addCallback(l.append)
943
self.assertEqual(''.join(l[0]), bigString,
944
"Pages received not equal to pages sent!")
945
self.assertEqual(pagerizer.pager.chunks, [])
949
class DumbPublishable(publish.Publishable):
950
def getStateToPublish(self):
951
return {"yayIGotPublished": 1}
954
class DumbPub(publish.RemotePublished):
956
self.activateCalled = 1
959
class GetPublisher(pb.Referenceable):
961
self.pub = DumbPublishable("TESTING")
963
def remote_getPub(self):
967
pb.setUnjellyableForClass(DumbPublishable, DumbPub)
969
class DisconnectionTests(unittest.TestCase):
971
Test disconnection callbacks.
974
def error(self, *args):
975
raise RuntimeError("I shouldn't have been called: %s" % (args,))
978
def gotDisconnected(self):
980
Called on broker disconnect.
984
def objectDisconnected(self, o):
986
Called on RemoteReference disconnect.
988
self.assertEqual(o, self.remoteObject)
989
self.objectCallback = 1
991
def test_badSerialization(self):
992
c, s, pump = connectedServerAndClient()
994
s.setNameForLocal("o", BadCopySet())
995
g = c.remoteForName("o")
997
g.callRemote("setBadCopy", BadCopyable()).addErrback(l.append)
999
self.assertEqual(len(l), 1)
1001
def test_disconnection(self):
1002
c, s, pump = connectedServerAndClient()
1004
s.setNameForLocal("o", SimpleRemote())
1006
# get a client reference to server object
1007
r = c.remoteForName("o")
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)
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)
1026
# register disconnect callbacks
1027
c.notifyOnDisconnect(self.gotDisconnected)
1028
r.notifyOnDisconnect(self.objectDisconnected)
1029
self.remoteObject = r
1032
c.connectionLost(failure.Failure(main.CONNECTION_DONE))
1033
self.assertTrue(self.gotCallback)
1034
self.assertTrue(self.objectCallback)
1037
class FreakOut(Exception):
1041
class BadCopyable(pb.Copyable):
1042
def getStateToCopyFor(self, p):
1046
class BadCopySet(pb.Referenceable):
1047
def remote_setBadCopy(self, bc):
1051
class LocalRemoteTest(util.LocalAsRemote):
1052
reportAllTracebacks = 0
1054
def sync_add1(self, x):
1057
def async_add(self, x=0, y=1):
1060
def async_fail(self):
1061
raise RuntimeError()
1065
class MyPerspective(pb.Avatar):
1067
@ivar loggedIn: set to C{True} when the avatar is logged in.
1068
@type loggedIn: C{bool}
1070
@ivar loggedOut: set to C{True} when the avatar is logged out.
1071
@type loggedOut: C{bool}
1073
implements(pb.IPerspective)
1075
loggedIn = loggedOut = False
1077
def __init__(self, avatarId):
1078
self.avatarId = avatarId
1081
def perspective_getAvatarId(self):
1083
Return the avatar identifier which was used to access this avatar.
1085
return self.avatarId
1088
def perspective_getViewPoint(self):
1092
def perspective_add(self, a, b):
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}.
1102
self.loggedOut = True
1106
class TestRealm(object):
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.
1112
@ivar lastPerspective: The L{MyPerspective} most recently created and
1113
returned from C{requestAvatar}.
1115
@ivar perspectiveFactory: A one-argument callable which will be used to
1116
create avatars to be returned from C{requestAvatar}.
1118
perspectiveFactory = MyPerspective
1120
lastPerspective = None
1122
def requestAvatar(self, avatarId, mind, interface):
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.
1128
assert interface == pb.IPerspective
1129
assert mind == "BRAINS!"
1130
if avatarId is checkers.ANONYMOUS:
1131
return pb.IPerspective, Echoer(), lambda: None
1133
self.lastPerspective = self.perspectiveFactory(avatarId)
1134
self.lastPerspective.loggedIn = True
1136
pb.IPerspective, self.lastPerspective,
1137
self.lastPerspective.logout)
1141
class MyView(pb.Viewable):
1143
def view_check(self, user):
1144
return isinstance(user, MyPerspective)
1148
class LeakyRealm(TestRealm):
1150
A realm which hangs onto a reference to the mind object in its logout
1153
def __init__(self, mindEater):
1155
Create a L{LeakyRealm}.
1157
@param mindEater: a callable that will be called with the C{mind}
1158
object when it is available
1160
self._mindEater = mindEater
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()))
1170
class NewCredLeakTests(unittest.TestCase):
1172
Tests to try to trigger memory leaks.
1174
def test_logoutLeak(self):
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
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.
1183
def setMindRef(mind):
1184
self.mindRef = weakref.ref(mind)
1186
clientBroker, serverBroker, pump = connectedServerAndClient(
1187
LeakyRealm(setMindRef))
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)
1204
# flush out the response and connectionLost
1206
self.assertEqual(connectionBroken, [1])
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
1212
self.assertEqual(self.mindRef(), None)
1216
class NewCredTests(unittest.TestCase):
1218
Tests related to the L{twisted.cred} support in PB.
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
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
1235
Shut down the TCP port created by L{setUp}.
1237
return self.port.stopListening()
1240
def getFactoryAndRootObject(self, clientFactory=pb.PBClientFactory):
1242
Create a connection to the test server.
1244
@param clientFactory: the factory class used to create the connection.
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.
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
1257
def test_getRootObject(self):
1259
Assert only that L{PBClientFactory.getRootObject}'s Deferred fires with
1260
a L{RemoteReference}.
1262
factory, rootObjDeferred = self.getFactoryAndRootObject()
1264
def gotRootObject(rootObj):
1265
self.assertIsInstance(rootObj, pb.RemoteReference)
1266
disconnectedDeferred = Deferred()
1267
rootObj.notifyOnDisconnect(disconnectedDeferred.callback)
1268
factory.disconnect()
1269
return disconnectedDeferred
1271
return rootObjDeferred.addCallback(gotRootObject)
1274
def test_deadReferenceError(self):
1276
Test that when a connection is lost, calling a method on a
1277
RemoteReference obtained from it raises DeadReferenceError.
1279
factory, rootObjDeferred = self.getFactoryAndRootObject()
1281
def gotRootObject(rootObj):
1282
disconnectedDeferred = Deferred()
1283
rootObj.notifyOnDisconnect(disconnectedDeferred.callback)
1285
def lostConnection(ign):
1287
pb.DeadReferenceError,
1288
rootObj.callRemote, 'method')
1290
disconnectedDeferred.addCallback(lostConnection)
1291
factory.disconnect()
1292
return disconnectedDeferred
1294
return rootObjDeferred.addCallback(gotRootObject)
1297
def test_clientConnectionLost(self):
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.
1303
class ReconnectOnce(pb.PBClientFactory):
1304
reconnectedAlready = False
1305
def clientConnectionLost(self, connector, reason):
1306
reconnecting = not self.reconnectedAlready
1307
self.reconnectedAlready = True
1310
return pb.PBClientFactory.clientConnectionLost(
1311
self, connector, reason, reconnecting)
1313
factory, rootObjDeferred = self.getFactoryAndRootObject(ReconnectOnce)
1315
def gotRootObject(rootObj):
1316
self.assertIsInstance(rootObj, pb.RemoteReference)
1319
rootObj.notifyOnDisconnect(d.callback)
1320
factory.disconnect()
1322
def disconnected(ign):
1323
d = factory.getRootObject()
1325
def gotAnotherRootObject(anotherRootObj):
1326
self.assertIsInstance(anotherRootObj, pb.RemoteReference)
1329
anotherRootObj.notifyOnDisconnect(d.callback)
1330
factory.disconnect()
1332
return d.addCallback(gotAnotherRootObject)
1333
return d.addCallback(disconnected)
1334
return rootObjDeferred.addCallback(gotRootObject)
1337
def test_immediateClose(self):
1339
Test that if a Broker loses its connection without receiving any bytes,
1340
it doesn't raise any exceptions or log any errors.
1342
serverProto = self.factory.buildProtocol(('127.0.0.1', 12345))
1343
serverProto.makeConnection(protocol.FileWrapper(StringIO()))
1344
serverProto.connectionLost(failure.Failure(main.CONNECTION_DONE))
1347
def test_loginConnectionRefused(self):
1349
L{PBClientFactory.login} returns a L{Deferred} which is errbacked
1350
with the L{ConnectionRefusedError} if the underlying connection is
1353
clientFactory = pb.PBClientFactory()
1354
loginDeferred = clientFactory.login(
1355
credentials.UsernamePassword("foo", "bar"))
1356
clientFactory.clientConnectionFailed(
1359
ConnectionRefusedError("Test simulated refused connection")))
1360
return self.assertFailure(loginDeferred, ConnectionRefusedError)
1363
def _disconnect(self, ignore, factory):
1365
Helper method disconnecting the given client factory and returning a
1366
C{Deferred} that will fire when the server connection has noticed the
1369
disconnectedDeferred = Deferred()
1370
self.factory.protocolInstance.notifyOnDisconnect(
1371
lambda: disconnectedDeferred.callback(None))
1372
factory.disconnect()
1373
return disconnectedDeferred
1376
def test_loginLogout(self):
1378
Test that login can be performed with IUsernamePassword credentials and
1379
that when the connection is dropped the avatar is logged out.
1381
self.portal.registerChecker(
1382
checkers.InMemoryUsernamePasswordDatabaseDontUse(user='pass'))
1383
factory = pb.PBClientFactory()
1384
creds = credentials.UsernamePassword("user", "pass")
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,
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)
1400
def cbLogout(ignored):
1401
self.assertTrue(self.realm.lastPerspective.loggedOut)
1402
d.addCallback(cbLogout)
1404
connector = reactor.connectTCP("127.0.0.1", self.portno, factory)
1405
self.addCleanup(connector.disconnect)
1409
def test_logoutAfterDecref(self):
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.
1415
loggedOut = Deferred()
1417
class EventPerspective(pb.Avatar):
1419
An avatar which fires a Deferred when it is logged out.
1421
def __init__(self, avatarId):
1425
loggedOut.callback(None)
1427
self.realm.perspectiveFactory = EventPerspective
1429
self.portal.registerChecker(
1430
checkers.InMemoryUsernamePasswordDatabaseDontUse(foo='bar'))
1431
factory = pb.PBClientFactory()
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.
1438
d.addCallback(cbLoggedIn)
1439
def cbLoggedOut(ignored):
1440
# Verify that the server broker's _localCleanup dict isn't growing
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)
1450
def test_concurrentLogin(self):
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.
1455
self.portal.registerChecker(
1456
checkers.InMemoryUsernamePasswordDatabaseDontUse(
1457
foo='bar', baz='quux'))
1458
factory = pb.PBClientFactory()
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)
1476
connector = reactor.connectTCP('127.0.0.1', self.portno, factory)
1477
self.addCleanup(connector.disconnect)
1481
def test_badUsernamePasswordLogin(self):
1483
Test that a login attempt with an invalid user or invalid password
1484
fails in the appropriate way.
1486
self.portal.registerChecker(
1487
checkers.InMemoryUsernamePasswordDatabaseDontUse(user='pass'))
1488
factory = pb.PBClientFactory()
1490
firstLogin = factory.login(
1491
credentials.UsernamePassword('nosuchuser', 'pass'))
1492
secondLogin = factory.login(
1493
credentials.UsernamePassword('user', 'wrongpass'))
1495
self.assertFailure(firstLogin, UnauthorizedLogin)
1496
self.assertFailure(secondLogin, UnauthorizedLogin)
1497
d = gatherResults([firstLogin, secondLogin])
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)
1505
connector = reactor.connectTCP("127.0.0.1", self.portno, factory)
1506
self.addCleanup(connector.disconnect)
1510
def test_anonymousLogin(self):
1512
Verify that a PB server using a portal configured with an checker which
1513
allows IAnonymous credentials can be logged into using IAnonymous
1516
self.portal.registerChecker(checkers.AllowAnonymousAccess())
1517
factory = pb.PBClientFactory()
1518
d = factory.login(credentials.Anonymous(), "BRAINS!")
1520
def cbLoggedIn(perspective):
1521
return perspective.callRemote('echo', 123)
1522
d.addCallback(cbLoggedIn)
1524
d.addCallback(self.assertEqual, 123)
1526
d.addCallback(self._disconnect, factory)
1528
connector = reactor.connectTCP("127.0.0.1", self.portno, factory)
1529
self.addCleanup(connector.disconnect)
1533
def test_anonymousLoginNotPermitted(self):
1535
Verify that without an anonymous checker set up, anonymous login is
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)
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)
1550
connector = reactor.connectTCP('127.0.0.1', self.portno, factory)
1551
self.addCleanup(connector.disconnect)
1555
def test_anonymousLoginWithMultipleCheckers(self):
1557
Like L{test_anonymousLogin} but against a portal with a checker for
1558
both IAnonymous and IUsernamePassword.
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!")
1566
def cbLogin(perspective):
1567
return perspective.callRemote('echo', 123)
1568
d.addCallback(cbLogin)
1570
d.addCallback(self.assertEqual, 123)
1572
d.addCallback(self._disconnect, factory)
1574
connector = reactor.connectTCP('127.0.0.1', self.portno, factory)
1575
self.addCleanup(connector.disconnect)
1579
def test_authenticatedLoginWithMultipleCheckers(self):
1581
Like L{test_anonymousLoginWithMultipleCheckers} but check that
1582
username/password authentication works.
1584
self.portal.registerChecker(checkers.AllowAnonymousAccess())
1585
self.portal.registerChecker(
1586
checkers.InMemoryUsernamePasswordDatabaseDontUse(user='pass'))
1587
factory = pb.PBClientFactory()
1589
credentials.UsernamePassword('user', 'pass'), "BRAINS!")
1591
def cbLogin(perspective):
1592
return perspective.callRemote('add', 100, 23)
1593
d.addCallback(cbLogin)
1595
d.addCallback(self.assertEqual, 123)
1597
d.addCallback(self._disconnect, factory)
1599
connector = reactor.connectTCP('127.0.0.1', self.portno, factory)
1600
self.addCleanup(connector.disconnect)
1604
def test_view(self):
1606
Verify that a viewpoint can be retrieved after authenticating with
1609
self.portal.registerChecker(
1610
checkers.InMemoryUsernamePasswordDatabaseDontUse(user='pass'))
1611
factory = pb.PBClientFactory()
1613
credentials.UsernamePassword("user", "pass"), "BRAINS!")
1615
def cbLogin(perspective):
1616
return perspective.callRemote("getViewPoint")
1617
d.addCallback(cbLogin)
1619
def cbView(viewpoint):
1620
return viewpoint.callRemote("check")
1621
d.addCallback(cbView)
1623
d.addCallback(self.assertTrue)
1625
d.addCallback(self._disconnect, factory)
1627
connector = reactor.connectTCP("127.0.0.1", self.portno, factory)
1628
self.addCleanup(connector.disconnect)
1633
class NonSubclassingPerspective:
1634
implements(pb.IPerspective)
1636
def __init__(self, avatarId):
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))
1645
# Methods required by TestRealm
1647
self.loggedOut = True
1651
class NSPTests(unittest.TestCase):
1653
Tests for authentication against a realm where the L{IPerspective}
1654
implementation is not a subclass of L{Avatar}.
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
1671
An L{IPerspective} implementation which does not subclass
1672
L{Avatar} can expose remote methods for the client to call.
1674
factory = pb.PBClientFactory()
1675
d = factory.login(credentials.UsernamePassword('user', 'pass'),
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)
1690
class IForwarded(Interface):
1692
Interface used for testing L{util.LocalAsyncForwarder}.
1697
Simple synchronous method.
1700
def forwardDeferred():
1702
Simple asynchronous method.
1708
Test implementation of L{IForwarded}.
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}
1715
implements(IForwarded)
1719
def forwardMe(self):
1721
Set a local flag to test afterwards.
1723
self.forwarded = True
1725
def dontForwardMe(self):
1727
Set a local flag to test afterwards. This should not be called as it's
1728
not in the interface.
1730
self.unforwarded = True
1732
def forwardDeferred(self):
1734
Asynchronously return C{True}.
1736
return succeed(True)
1739
class SpreadUtilTests(unittest.TestCase):
1741
Tests for L{twisted.spread.util}.
1744
def test_sync(self):
1746
Call a synchronous method of a L{util.LocalAsRemote} object and check
1749
o = LocalRemoteTest()
1750
self.assertEqual(o.callRemote("add1", 2), 3)
1752
def test_async(self):
1754
Call an asynchronous method of a L{util.LocalAsRemote} object and check
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)
1764
def test_asyncFail(self):
1766
Test a asynchronous failure on a remote method call.
1768
o = LocalRemoteTest()
1769
d = o.callRemote("fail")
1771
self.assertTrue(isinstance(f, failure.Failure))
1772
f.trap(RuntimeError)
1773
d.addCallbacks(lambda res: self.fail("supposed to fail"), eb)
1776
def test_remoteMethod(self):
1778
Test the C{remoteMethod} facility of L{util.LocalAsRemote}.
1780
o = LocalRemoteTest()
1781
m = o.remoteMethod("add1")
1782
self.assertEqual(m(3), 4)
1784
def test_localAsyncForwarder(self):
1786
Test a call to L{util.LocalAsyncForwarder} using L{Forwarded} local
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")
1797
rr.addCallback(l.append)
1798
self.assertEqual(l[0], 1)
1802
class PBWithSecurityOptionsTests(unittest.TestCase):
1804
Test security customization.
1807
def test_clientDefaultSecurityOptions(self):
1809
By default, client broker should use C{jelly.globalSecurity} as
1812
factory = pb.PBClientFactory()
1813
broker = factory.buildProtocol(None)
1814
self.assertIdentical(broker.security, jelly.globalSecurity)
1817
def test_serverDefaultSecurityOptions(self):
1819
By default, server broker should use C{jelly.globalSecurity} as
1822
factory = pb.PBServerFactory(Echoer())
1823
broker = factory.buildProtocol(None)
1824
self.assertIdentical(broker.security, jelly.globalSecurity)
1827
def test_clientSecurityCustomization(self):
1829
Check that the security settings are passed from the client factory to
1832
security = jelly.SecurityOptions()
1833
factory = pb.PBClientFactory(security=security)
1834
broker = factory.buildProtocol(None)
1835
self.assertIdentical(broker.security, security)
1838
def test_serverSecurityCustomization(self):
1840
Check that the security settings are passed from the server factory to
1843
security = jelly.SecurityOptions()
1844
factory = pb.PBServerFactory(Echoer(), security=security)
1845
broker = factory.buildProtocol(None)
1846
self.assertIdentical(broker.security, security)