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