def testConnectAuthenticated(self): tub = self.services[0] target = HelperTarget("bob") target.obj = "unset" url = tub.registerReference(target) # can we connect to a reference on our own Tub? d = tub.getReference(url) def _connected(ref): return ref.callRemote("set", 12) d.addCallback(_connected) def _check(res): self.assertEqual(target.obj, 12) d.addCallback(_check) def _connect_again(res): target.obj = None return tub.getReference(url) d.addCallback(_connect_again) d.addCallback(_connected) d.addCallback(_check) return d
def create_constrained_characters(self): self.alice = HelperTarget("alice") self.bob = ConstrainedHelper("bob") self.bob_url = self.tubB.registerReference(self.bob, "bob") self.carol = HelperTarget("carol") self.carol_url = self.tubC.registerReference(self.carol, "carol") self.dave = HelperTarget("dave") self.dave_url = self.tubD.registerReference(self.dave, "dave")
def testNameLookup(self): t1 = HelperTarget() t2 = HelperTarget() self.names["foo"] = t1 self.names2["bar"] = t2 self.names2["baz"] = t2 self.tubB.registerNameLookupHandler(self.lookup) self.tubB.registerNameLookupHandler(self.lookup2) # hack up a new furl pointing at the same tub but with a name that # hasn't been registered. s = SturdyRef(self.url_on_b) s.name = "foo" d = self.tubA.getReference(s) def _check(res): self.assertTrue(isinstance(res, RemoteReference)) self.assertEqual(self.lookups, ["foo"]) # the first lookup should short-circuit the process self.assertEqual(self.lookups2, []) self.lookups = []; self.lookups2 = [] s.name = "bar" return self.tubA.getReference(s) d.addCallback(_check) def _check2(res): self.assertTrue(isinstance(res, RemoteReference)) # if the first lookup fails, the second handler should be asked self.assertEqual(self.lookups, ["bar"]) self.assertEqual(self.lookups2, ["bar"]) self.lookups = []; self.lookups2 = [] # make sure that loopbacks use this too return self.tubB.getReference(s) d.addCallback(_check2) def _check3(res): self.assertTrue(isinstance(res, RemoteReference)) self.assertEqual(self.lookups, ["bar"]) self.assertEqual(self.lookups2, ["bar"]) self.lookups = []; self.lookups2 = [] # and make sure we can de-register handlers self.tubB.unregisterNameLookupHandler(self.lookup) s.name = "baz" return self.tubA.getReference(s) d.addCallback(_check3) def _check4(res): self.assertTrue(isinstance(res, RemoteReference)) self.assertEqual(self.lookups, []) self.assertEqual(self.lookups2, ["baz"]) self.lookups = []; self.lookups2 = [] d.addCallback(_check4) return d
def setUp(self): TargetMixin.setUp(self) (self.tubB, ) = self.makeTubs(1) self.barry = HelperTarget("barry") self.barry_url = self.tubB.registerReference(self.barry) self.bill = HelperTarget("bill") self.bill_url = self.tubB.registerReference(self.bill) self.bob = HelperTarget("bob") self.bob_url = self.tubB.registerReference(self.bob)
def test_callRemote(self): t = HelperTarget() t.obj = None rref = IRemoteReference(t) marker = rref.notifyOnDisconnect(self.ignored, "args", kwargs="foo") rref.dontNotifyOnDisconnect(marker) d = rref.callRemote("set", 12) # the callRemote should be put behind an eventual-send self.assertEqual(t.obj, None) def _check(res): self.assertEqual(t.obj, 12) self.assertEqual(res, True) d.addCallback(_check) return d
def test_callRemote(self): t = HelperTarget() t.obj = None rref = IRemoteReference(t) marker = rref.notifyOnDisconnect(self.ignored, "args", kwargs="foo") rref.dontNotifyOnDisconnect(marker) d = rref.callRemote("set", 12) # the callRemote should be put behind an eventual-send self.failUnlessEqual(t.obj, None) def _check(res): self.failUnlessEqual(t.obj, 12) self.failUnlessEqual(res, True) d.addCallback(_check) return d
def test_connection_lost_is_deadref(self): rr, target = self.setupTarget(HelperTarget()) d1 = rr.callRemote("hang") def get_d(): return d1 rr.tracker.broker.transport.loseConnection(Failure(CONNECTION_LOST)) d = self.shouldFail(DeadReferenceError, "lost_is_deadref.1", "Connection was lost", get_d) def _examine_error((f, )): # the (to tubid=XXX) part will see "tub=call", which is an # abbreviation of "callingBroker" as created in # TargetMixin.setupBrokers self.failUnlessIn("(to tubid=call)", str(f.value)) self.failUnlessIn("(during method=None:hang)", str(f.value)) d.addCallback(_examine_error) # and once the connection is down, we should get a DeadReferenceError # for new messages d.addCallback(lambda res: self.shouldFail( DeadReferenceError, "lost_is_deadref.2", "Calling Stale Broker", rr .callRemote, "hang")) return d
def test_connection_done_is_deadref(self): rr, target = self.setupTarget(HelperTarget()) d = rr.callRemote("hang") rr.tracker.broker.transport.loseConnection(Failure(CONNECTION_DONE)) d.addCallbacks(lambda res: self.fail("should have failed"), lambda why: why.trap(DeadReferenceError) and None) return d
def test_negotiate_fails_and_retry(self): connects = [] target = HelperTarget("bob") url = self.tubB.registerReference(target, "target") hint = referenceable.SturdyRef(url).getTubRef().getLocations()[0] l = self.tubB.getListeners()[0] l._negotiationClass = AlwaysFailNegotiation portb = self.tub_ports[1] d1 = defer.Deferred() notifiers = [d1] rc = self.tubA.connectTo(url, self._connected, notifiers, connects) yield self.poll(lambda: rc.getReconnectionInfo().state == "waiting") # the reconnector should have failed once or twice, since the # negotiation would always fail. self.failUnlessEqual(len(connects), 0) ci = rc.getReconnectionInfo().connectionInfo cs = ci.connectorStatuses self.assertEqual(cs, {hint: "negotiation failed: I always fail"}) # Now we fix tubB. We only touched the Listener, so re-doing the # listenOn should clear it. yield self.tubB.stopListeningOn(l) self.tubB.listenOn("tcp:%d:interface=127.0.0.1" % portb) # the next time the reconnector tries, it should succeed yield d1 self.failUnlessEqual(len(connects), 1) rc.stopConnecting()
def test_stop_trying(self): connects = [] target = HelperTarget("bob") url = self.tubB.registerReference(target, "target") self.services.remove(self.tubB) # this will fail, since tubB is not listening anymore yield self.tubB.stopService() rc = self.tubA.connectTo(url, self._connected, [], connects) rc.verbose = True # get better code coverage # wait for at least one retry yield self.poll(lambda: rc.getReconnectionInfo().state == "waiting") # and a bit more, for good measure yield self.stall(2) self.failUnlessEqual(len(connects), 0) f = rc.getLastFailure() self.failUnless(f.check(error.ConnectionRefusedError)) delay = rc.getDelayUntilNextAttempt() self.failUnless(delay > 0, delay) self.failUnless(delay < 60, delay) rc.reset() delay = rc.getDelayUntilNextAttempt() self.failUnless(delay < 2) # this stopConnecting occurs while the reconnector's timer is # active rc.stopConnecting() self.failUnlessEqual(rc.getDelayUntilNextAttempt(), None)
def test_bad_hints(self): self.tubA = Tub() self.tubA.startService() self.services.append(self.tubA) self.tubB = Tub() self.tubB.startService() self.services.append(self.tubB) portnum = allocate_tcp_port() self.tubB.listenOn("tcp:%d:interface=127.0.0.1" % portnum) bad1 = "no-colon" bad2 = "unknown:foo" bad3 = "tcp:300.300.300.300:333" self.tubB.setLocation(bad1, bad2, bad3) target = HelperTarget("bob") url = self.tubB.registerReference(target) rc = self.tubA.connectTo(url, None) ri = rc.getReconnectionInfo() self.assertEqual(ri.state, "connecting") yield self.poll(lambda: rc.getReconnectionInfo().state != "connecting") # now look at the details ri = rc.getReconnectionInfo() self.assertEqual(ri.state, "waiting") ci = ri.connectionInfo self.assertEqual(ci.connected, False) self.assertEqual(ci.winningHint, None) s = ci.connectorStatuses self.assertEqual(set(s.keys()), set([bad1, bad2, bad3])) self.assertEqual(s[bad1], "bad hint: no colon") self.assertEqual(s[bad2], "bad hint: no handler registered") self.assertIn("DNS lookup failed", s[bad3]) ch = ci.connectionHandlers self.assertEqual(ch, {bad2: None, bad3: "tcp"})
def test_stop_trying(self): connects = [] target = HelperTarget("bob") url = self.tubB.registerReference(target, "target") d1 = defer.Deferred() self.services.remove(self.tubB) d = self.tubB.stopService() def _start_connecting(res): # this will fail, since tubB is not listening anymore self.rc = self.tubA.connectTo(url, self._connected, d1, connects) self.rc.verbose = True # get better code coverage # give it a few tries, then tell it to stop trying return self.stall(2) d.addCallback(_start_connecting) def _stop_trying(res): self.failUnlessEqual(len(connects), 0) f = self.rc.getLastFailure() self.failUnless(f.check(error.ConnectionRefusedError)) delay = self.rc.getDelayUntilNextAttempt() self.failUnless(delay > 0, delay) self.failUnless(delay < 60, delay) self.rc.reset() delay = self.rc.getDelayUntilNextAttempt() self.failUnless(delay < 2) # this stopConnecting occurs while the reconnector's timer is # active self.rc.stopConnecting() self.failUnlessEqual(self.rc.getDelayUntilNextAttempt(), None) d.addCallback(_stop_trying) # if it keeps trying, we'll see a dirty reactor return d
def testMegaSchema(self): # try to exercise all our constraints at once rr, target = self.setupTarget(HelperTarget()) t = ( set([1, 2, 3]), b'bytes', True, 12, 19.3, None, 'string', b'bytestring', 'any', 14.3, 15, 'a' * 95, '1234567890', ) obj1 = {'key': [t]} obj2 = (set([1, 2, 3]), [1, 2, 3], {1: 'two'}) d = rr.callRemote('megaschema', obj1, obj2) d.addCallback(lambda res: self.assertEqual(res, None)) return d
def test_retry(self): tubC = Tub(certData=self.tubB.getCertData()) connects = [] target = HelperTarget("bob") url = self.tubB.registerReference(target, "target") portb = self.tub_ports[1] d1 = defer.Deferred() notifiers = [d1] self.services.remove(self.tubB) # This will fail, since tubB is not listening anymore. Wait until it's # moved to the "waiting" state. yield self.tubB.stopService() rc = self.tubA.connectTo(url, self._connected, notifiers, connects) yield self.poll(lambda: rc.getReconnectionInfo().state == "waiting") self.failUnlessEqual(len(connects), 0) # now start tubC listening on the same port that tubB used to, which # should allow the connection to complete (since they both use the same # certData) self.services.append(tubC) tubC.startService() tubC.listenOn("tcp:%d:interface=127.0.0.1" % portb) tubC.setLocation("tcp:127.0.0.1:%d" % portb) url2 = tubC.registerReference(target, "target") assert url2 == url yield d1 self.failUnlessEqual(len(connects), 1) rc.stopConnecting()
def testDisconnect_during_call(self): rr, target = self.setupTarget(HelperTarget()) d = rr.callRemote("hang") e = RuntimeError("lost connection") rr.tracker.broker.transport.loseConnection(Failure(e)) d.addCallbacks(lambda res: self.fail("should have failed"), lambda why: why.trap(RuntimeError) and None) return d
def testMega3(self): # exercise a specific bug: shared references don't pass schemas t = (0,1) obj = [t, t] rr, target = self.setupTarget(HelperTarget()) d = rr.callRemote("mega3", obj) d.addCallback(lambda res: self.failUnlessEqual(res, None)) return d
def createCharacters(self): self.alice = HelperTarget("alice") self.bob = HelperTarget("bob") self.bob_url = self.tubB.registerReference(self.bob, "bob") self.carol = HelperTarget("carol") self.carol_url = self.tubC.registerReference(self.carol, "carol") # cindy is Carol's little sister. She doesn't have a phone, but # Carol might talk about her anyway. self.cindy = HelperTarget("cindy") # more sisters. Alice knows them, and she introduces Bob to them. self.charlene = HelperTarget("charlene") self.christine = HelperTarget("christine") self.clarisse = HelperTarget("clarisse") self.colette = HelperTarget("colette") self.courtney = HelperTarget("courtney") self.dave = HelperTarget("dave") self.dave_url = self.tubD.registerReference(self.dave, "dave")
def send(self, arg): rr, target = self.setupTarget(HelperTarget()) d = rr.callRemote("set", obj=arg) d.addCallback(self.failUnless) # some of these tests require that we return a Failure object, so we # have to wrap this in a tuple to survive the Deferred. d.addCallback(lambda res: (target.obj, )) return d
def testAnswer2(self): # but objects returned by separate method calls should be distinct rr, target = self.setupTarget(HelperTarget()) r = [1, 2] target.obj = r d = rr.callRemote("get") d.addCallback(self._testAnswer2_1, rr, target) return d
def setUp(self): TargetMixin.setUp(self) self.tubB = GoodEnoughTub() self.services = [self.tubB] for s in self.services: s.startService() l = s.listenOn("tcp:0:interface=127.0.0.1") s.setLocation("127.0.0.1:%d" % l.getPortnum()) self.barry = HelperTarget("barry") self.barry_url = self.tubB.registerReference(self.barry) self.bill = HelperTarget("bill") self.bill_url = self.tubB.registerReference(self.bill) self.bob = HelperTarget("bob") self.bob_url = self.tubB.registerReference(self.bob)
def testAnswer1(self): # also, shared objects in a return value should be shared r = [1, 2] rr, target = self.setupTarget(HelperTarget()) target.obj = (r, r) d = rr.callRemote("get") d.addCallback(lambda res: self.assertIs(res[0], res[1])) return d
def testArgs2(self): # but sending them as multiple arguments of the *same* method call # results in identical objects r = [1, 2] rr, target = self.setupTarget(HelperTarget()) d = rr.callRemote("set2", obj1=r, obj2=r) d.addCallback(self._testArgs2_1, rr, target) return d
def testRef4(self): # sending the same Referenceable in multiple calls will result in # equivalent RRs r = Target() rr, target = self.setupTarget(HelperTarget()) d = rr.callRemote("set", obj=r) d.addCallback(self._testRef4_1, rr, r, target) return d
def test_reference_counting(self): self.source_tub.setOption("expose-remote-exception-types", True) target = HelperTarget() d = self.setupTarget(target) def _stash(rref): # to exercise bug #104, we need to trigger remote Violations, so # we tell the sending side to not use a RemoteInterface. We do # this by reaching inside the RemoteReference and making it # forget rref.tracker.interfaceName = None rref.tracker.interface = None self.rref = rref d.addCallback(_stash) # the first call causes an error, which discards all remaining # tokens, including the OPEN tokens for the arguments. The #104 bug # is that this causes the open-count to get out of sync, by -2 (one # for the arguments sequence, one for the list inside it). d.addCallback( lambda ign: self.shouldFail(Violation, "one", None, self.rref. callRemote, "bogus", ["one list"])) #d.addCallback(lambda ign: # self.rref.callRemote("set", ["one list"])) # a method call that has no arguments (specifically no REFERENCE # sequences) won't notice the loss of sync d.addCallback(lambda ign: self.rref.callRemote("set", 42)) def _check_42(ign): self.failUnlessEqual(target.obj, 42) d.addCallback(_check_42) # but when the call takes shared arguments, sync matters l = ["list", 1, 2] s = set([3, 4]) t = ("tuple", 5, 6) d.addCallback(lambda ign: self.rref.callRemote("set", [t, l, s, t])) def _check_shared(ign): # the off-by-two bug would cause the second tuple shared-ref to # point at the set instead of the first tuple self.failUnlessEqual(type(target.obj), list) one, two, three, four = target.obj self.failUnlessEqual(type(one), tuple) self.failUnlessEqual(one, t) self.failUnlessEqual(type(two), list) self.failUnlessEqual(two, l) self.failUnlessEqual(type(three), set) self.failUnlessEqual(three, s) self.failUnlessEqual(type(four), tuple) # this is where it fails self.failUnlessEqual(four, t) self.failUnlessIdentical(one, four) d.addCallback(_check_shared) return d
def test_try(self): self.count = 0 self.attached = False self.done = defer.Deferred() target = HelperTarget("bob") url = self.tubB.registerReference(target) rc = self.tubA.connectTo(url, self._got_ref, "arg", kw="kwarg") # at least make sure the stopConnecting method is present, even if we # don't have a real test for it yet self.failUnless(rc.stopConnecting) return self.done
def testConnectAuthenticated(self): tub = self.services[0] target = HelperTarget("bob") target.obj = "unset" url = tub.registerReference(target) # can we connect to a reference on our own Tub? d = tub.getReference(url) def _connected(ref): return ref.callRemote("set", 12) d.addCallback(_connected) def _check(res): self.failUnlessEqual(target.obj, 12) d.addCallback(_check) def _connect_again(res): target.obj = None return tub.getReference(url) d.addCallback(_connect_again) d.addCallback(_connected) d.addCallback(_check) return d
def testStallOrdering(self): # if the first message hangs (it returns a Deferred that doesn't fire # for a while), that shouldn't stall the second message. rr, target = self.setupTarget(HelperTarget()) d0 = rr.callRemote("hang") d = rr.callRemote("echo", 1) d.addCallback(lambda res: self.failUnlessEqual(res, 1)) def _done(res): target.d.callback(2) return d0 d.addCallback(_done) return d
def test_duplicate(self): basedir = "test_registration" os.makedirs(basedir) ff = os.path.join(basedir, "duplicate.furl") t1 = HelperTarget() tub = UnauthenticatedTub() tub.setLocation("bogus:1234567") u1 = tub.registerReference(t1, "name", furlFile=ff) u2 = tub.registerReference(t1, "name", furlFile=ff) self.failUnlessEqual(u1, u2) self.failUnlessRaises(WrongNameError, tub.registerReference, t1, "newname", furlFile=ff)
def testArgs1(self): # sending the same non-Referenceable object in multiple calls results # in distinct objects, because the serialization scope is bounded by # each method call r = [1, 2] rr, target = self.setupTarget(HelperTarget()) d = rr.callRemote("set", obj=r) d.addCallback(self._testArgs1_1, rr, r, target) # TODO: also make sure the original list goes out of scope once the # method call has finished, to guard against a leaky # reference-tracking implementation. return d
def testNotifyOnDisconnect_args(self): rr, target = self.setupTarget(HelperTarget()) self.lost = 0 rr.notifyOnDisconnect(self.disconnected, "arg", foo="kwarg") rr.tracker.broker.transport.loseConnection(Failure(CONNECTION_LOST)) d = flushEventualQueue() def _check(res): self.failUnless(self.lost) self.failUnlessEqual(self.lost_args, (("arg",), {"foo": "kwarg"})) d.addCallback(_check) return d
def testConnectAuthenticated(self): self.requireCrypto() tub = Tub() self.startTub(tub) target = HelperTarget("bob") target.obj = "unset" url = tub.registerReference(target) # can we connect to a reference on our own Tub? d = tub.getReference(url) def _connected(ref): return ref.callRemote("set", 12) d.addCallback(_connected) def _check(res): self.failUnlessEqual(target.obj, 12) d.addCallback(_check) def _connect_again(res): target.obj = None return tub.getReference(url) d.addCallback(_connect_again) d.addCallback(_connected) d.addCallback(_check) return d
def testNotifyOnDisconnect_unregister(self): rr, target = self.setupTarget(HelperTarget()) self.lost = 0 m = rr.notifyOnDisconnect(self.disconnected) rr.dontNotifyOnDisconnect(m) # dontNotifyOnDisconnect is supposed to be tolerant of duplicate # unregisters, because otherwise it is hard to avoid race conditions. # Validate that we can unregister something multiple times. rr.dontNotifyOnDisconnect(m) rr.tracker.broker.transport.loseConnection(Failure(CONNECTION_LOST)) d = flushEventualQueue() d.addCallback(lambda res: self.failIf(self.lost)) return d
def test_callRemoteOnly(self): t = HelperTarget() t.obj = None rref = IRemoteReference(t) rc = rref.callRemoteOnly("set", 12) self.failUnlessEqual(rc, None)