123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401 |
- # Copyright (c) Twisted Matrix Laboratories.
- # See LICENSE for details.
-
- """
- Tests for lots of functionality provided by L{twisted.internet}.
- """
-
-
- import os
- import sys
- import time
- from unittest import skipIf
-
- from twisted.internet import abstract, base, defer, error, interfaces, protocol, reactor
- from twisted.internet.defer import Deferred, passthru
- from twisted.internet.tcp import Connector
- from twisted.python import util
- from twisted.trial.unittest import TestCase
-
- try:
- from twisted.internet import ssl as _ssl
- except ImportError:
- ssl = None
- else:
- ssl = _ssl
-
- if ssl and not ssl.supported:
- ssl = None
-
-
- class ThreePhaseEventTests(TestCase):
- """
- Tests for the private implementation helpers for system event triggers.
- """
-
- def setUp(self):
- """
- Create a trigger, an argument, and an event to be used by tests.
- """
- self.trigger = lambda x: None
- self.arg = object()
- self.event = base._ThreePhaseEvent()
-
- def test_addInvalidPhase(self):
- """
- L{_ThreePhaseEvent.addTrigger} should raise L{KeyError} when called
- with an invalid phase.
- """
- self.assertRaises(
- KeyError, self.event.addTrigger, "xxx", self.trigger, self.arg
- )
-
- def test_addBeforeTrigger(self):
- """
- L{_ThreePhaseEvent.addTrigger} should accept C{'before'} as a phase, a
- callable, and some arguments and add the callable with the arguments to
- the before list.
- """
- self.event.addTrigger("before", self.trigger, self.arg)
- self.assertEqual(self.event.before, [(self.trigger, (self.arg,), {})])
-
- def test_addDuringTrigger(self):
- """
- L{_ThreePhaseEvent.addTrigger} should accept C{'during'} as a phase, a
- callable, and some arguments and add the callable with the arguments to
- the during list.
- """
- self.event.addTrigger("during", self.trigger, self.arg)
- self.assertEqual(self.event.during, [(self.trigger, (self.arg,), {})])
-
- def test_addAfterTrigger(self):
- """
- L{_ThreePhaseEvent.addTrigger} should accept C{'after'} as a phase, a
- callable, and some arguments and add the callable with the arguments to
- the after list.
- """
- self.event.addTrigger("after", self.trigger, self.arg)
- self.assertEqual(self.event.after, [(self.trigger, (self.arg,), {})])
-
- def test_removeTrigger(self):
- """
- L{_ThreePhaseEvent.removeTrigger} should accept an opaque object
- previously returned by L{_ThreePhaseEvent.addTrigger} and remove the
- associated trigger.
- """
- handle = self.event.addTrigger("before", self.trigger, self.arg)
- self.event.removeTrigger(handle)
- self.assertEqual(self.event.before, [])
-
- def test_removeNonexistentTrigger(self):
- """
- L{_ThreePhaseEvent.removeTrigger} should raise L{ValueError} when given
- an object not previously returned by L{_ThreePhaseEvent.addTrigger}.
- """
- self.assertRaises(ValueError, self.event.removeTrigger, object())
-
- def test_removeRemovedTrigger(self):
- """
- L{_ThreePhaseEvent.removeTrigger} should raise L{ValueError} the second
- time it is called with an object returned by
- L{_ThreePhaseEvent.addTrigger}.
- """
- handle = self.event.addTrigger("before", self.trigger, self.arg)
- self.event.removeTrigger(handle)
- self.assertRaises(ValueError, self.event.removeTrigger, handle)
-
- def test_removeAlmostValidTrigger(self):
- """
- L{_ThreePhaseEvent.removeTrigger} should raise L{ValueError} if it is
- given a trigger handle which resembles a valid trigger handle aside
- from its phase being incorrect.
- """
- self.assertRaises(
- KeyError, self.event.removeTrigger, ("xxx", self.trigger, (self.arg,), {})
- )
-
- def test_fireEvent(self):
- """
- L{_ThreePhaseEvent.fireEvent} should call I{before}, I{during}, and
- I{after} phase triggers in that order.
- """
- events = []
- self.event.addTrigger("after", events.append, ("first", "after"))
- self.event.addTrigger("during", events.append, ("first", "during"))
- self.event.addTrigger("before", events.append, ("first", "before"))
- self.event.addTrigger("before", events.append, ("second", "before"))
- self.event.addTrigger("during", events.append, ("second", "during"))
- self.event.addTrigger("after", events.append, ("second", "after"))
-
- self.assertEqual(events, [])
- self.event.fireEvent()
- self.assertEqual(
- events,
- [
- ("first", "before"),
- ("second", "before"),
- ("first", "during"),
- ("second", "during"),
- ("first", "after"),
- ("second", "after"),
- ],
- )
-
- def test_asynchronousBefore(self):
- """
- L{_ThreePhaseEvent.fireEvent} should wait for any L{Deferred} returned
- by a I{before} phase trigger before proceeding to I{during} events.
- """
- events = []
- beforeResult = Deferred()
- self.event.addTrigger("before", lambda: beforeResult)
- self.event.addTrigger("during", events.append, "during")
- self.event.addTrigger("after", events.append, "after")
-
- self.assertEqual(events, [])
- self.event.fireEvent()
- self.assertEqual(events, [])
- beforeResult.callback(None)
- self.assertEqual(events, ["during", "after"])
-
- def test_beforeTriggerException(self):
- """
- If a before-phase trigger raises a synchronous exception, it should be
- logged and the remaining triggers should be run.
- """
- events = []
-
- class DummyException(Exception):
- pass
-
- def raisingTrigger():
- raise DummyException()
-
- self.event.addTrigger("before", raisingTrigger)
- self.event.addTrigger("before", events.append, "before")
- self.event.addTrigger("during", events.append, "during")
- self.event.fireEvent()
- self.assertEqual(events, ["before", "during"])
- errors = self.flushLoggedErrors(DummyException)
- self.assertEqual(len(errors), 1)
-
- def test_duringTriggerException(self):
- """
- If a during-phase trigger raises a synchronous exception, it should be
- logged and the remaining triggers should be run.
- """
- events = []
-
- class DummyException(Exception):
- pass
-
- def raisingTrigger():
- raise DummyException()
-
- self.event.addTrigger("during", raisingTrigger)
- self.event.addTrigger("during", events.append, "during")
- self.event.addTrigger("after", events.append, "after")
- self.event.fireEvent()
- self.assertEqual(events, ["during", "after"])
- errors = self.flushLoggedErrors(DummyException)
- self.assertEqual(len(errors), 1)
-
- def test_synchronousRemoveAlreadyExecutedBefore(self):
- """
- If a before-phase trigger tries to remove another before-phase trigger
- which has already run, a warning should be emitted.
- """
- events = []
-
- def removeTrigger():
- self.event.removeTrigger(beforeHandle)
-
- beforeHandle = self.event.addTrigger(
- "before", events.append, ("first", "before")
- )
- self.event.addTrigger("before", removeTrigger)
- self.event.addTrigger("before", events.append, ("second", "before"))
- self.assertWarns(
- DeprecationWarning,
- "Removing already-fired system event triggers will raise an "
- "exception in a future version of Twisted.",
- __file__,
- self.event.fireEvent,
- )
- self.assertEqual(events, [("first", "before"), ("second", "before")])
-
- def test_synchronousRemovePendingBefore(self):
- """
- If a before-phase trigger removes another before-phase trigger which
- has not yet run, the removed trigger should not be run.
- """
- events = []
- self.event.addTrigger("before", lambda: self.event.removeTrigger(beforeHandle))
- beforeHandle = self.event.addTrigger(
- "before", events.append, ("first", "before")
- )
- self.event.addTrigger("before", events.append, ("second", "before"))
- self.event.fireEvent()
- self.assertEqual(events, [("second", "before")])
-
- def test_synchronousBeforeRemovesDuring(self):
- """
- If a before-phase trigger removes a during-phase trigger, the
- during-phase trigger should not be run.
- """
- events = []
- self.event.addTrigger("before", lambda: self.event.removeTrigger(duringHandle))
- duringHandle = self.event.addTrigger("during", events.append, "during")
- self.event.addTrigger("after", events.append, "after")
- self.event.fireEvent()
- self.assertEqual(events, ["after"])
-
- def test_asynchronousBeforeRemovesDuring(self):
- """
- If a before-phase trigger returns a L{Deferred} and later removes a
- during-phase trigger before the L{Deferred} fires, the during-phase
- trigger should not be run.
- """
- events = []
- beforeResult = Deferred()
- self.event.addTrigger("before", lambda: beforeResult)
- duringHandle = self.event.addTrigger("during", events.append, "during")
- self.event.addTrigger("after", events.append, "after")
- self.event.fireEvent()
- self.event.removeTrigger(duringHandle)
- beforeResult.callback(None)
- self.assertEqual(events, ["after"])
-
- def test_synchronousBeforeRemovesConspicuouslySimilarDuring(self):
- """
- If a before-phase trigger removes a during-phase trigger which is
- identical to an already-executed before-phase trigger aside from their
- phases, no warning should be emitted and the during-phase trigger
- should not be run.
- """
- events = []
-
- def trigger():
- events.append("trigger")
-
- self.event.addTrigger("before", trigger)
- self.event.addTrigger("before", lambda: self.event.removeTrigger(duringTrigger))
- duringTrigger = self.event.addTrigger("during", trigger)
- self.event.fireEvent()
- self.assertEqual(events, ["trigger"])
-
- def test_synchronousRemovePendingDuring(self):
- """
- If a during-phase trigger removes another during-phase trigger which
- has not yet run, the removed trigger should not be run.
- """
- events = []
- self.event.addTrigger("during", lambda: self.event.removeTrigger(duringHandle))
- duringHandle = self.event.addTrigger(
- "during", events.append, ("first", "during")
- )
- self.event.addTrigger("during", events.append, ("second", "during"))
- self.event.fireEvent()
- self.assertEqual(events, [("second", "during")])
-
- def test_triggersRunOnce(self):
- """
- A trigger should only be called on the first call to
- L{_ThreePhaseEvent.fireEvent}.
- """
- events = []
- self.event.addTrigger("before", events.append, "before")
- self.event.addTrigger("during", events.append, "during")
- self.event.addTrigger("after", events.append, "after")
- self.event.fireEvent()
- self.event.fireEvent()
- self.assertEqual(events, ["before", "during", "after"])
-
- def test_finishedBeforeTriggersCleared(self):
- """
- The temporary list L{_ThreePhaseEvent.finishedBefore} should be emptied
- and the state reset to C{'BASE'} before the first during-phase trigger
- executes.
- """
- events = []
-
- def duringTrigger():
- events.append("during")
- self.assertEqual(self.event.finishedBefore, [])
- self.assertEqual(self.event.state, "BASE")
-
- self.event.addTrigger("before", events.append, "before")
- self.event.addTrigger("during", duringTrigger)
- self.event.fireEvent()
- self.assertEqual(events, ["before", "during"])
-
-
- class SystemEventTests(TestCase):
- """
- Tests for the reactor's implementation of the C{fireSystemEvent},
- C{addSystemEventTrigger}, and C{removeSystemEventTrigger} methods of the
- L{IReactorCore} interface.
-
- @ivar triggers: A list of the handles to triggers which have been added to
- the reactor.
- """
-
- def setUp(self):
- """
- Create an empty list in which to store trigger handles.
- """
- self.triggers = []
-
- def tearDown(self):
- """
- Remove all remaining triggers from the reactor.
- """
- while self.triggers:
- trigger = self.triggers.pop()
- try:
- reactor.removeSystemEventTrigger(trigger)
- except (ValueError, KeyError):
- pass
-
- def addTrigger(self, event, phase, func):
- """
- Add a trigger to the reactor and remember it in C{self.triggers}.
- """
- t = reactor.addSystemEventTrigger(event, phase, func)
- self.triggers.append(t)
- return t
-
- def removeTrigger(self, trigger):
- """
- Remove a trigger by its handle from the reactor and from
- C{self.triggers}.
- """
- reactor.removeSystemEventTrigger(trigger)
- self.triggers.remove(trigger)
-
- def _addSystemEventTriggerTest(self, phase):
- eventType = "test"
- events = []
-
- def trigger():
- events.append(None)
-
- self.addTrigger(phase, eventType, trigger)
- self.assertEqual(events, [])
- reactor.fireSystemEvent(eventType)
- self.assertEqual(events, [None])
-
- def test_beforePhase(self):
- """
- L{IReactorCore.addSystemEventTrigger} should accept the C{'before'}
- phase and not call the given object until the right event is fired.
- """
- self._addSystemEventTriggerTest("before")
-
- def test_duringPhase(self):
- """
- L{IReactorCore.addSystemEventTrigger} should accept the C{'during'}
- phase and not call the given object until the right event is fired.
- """
- self._addSystemEventTriggerTest("during")
-
- def test_afterPhase(self):
- """
- L{IReactorCore.addSystemEventTrigger} should accept the C{'after'}
- phase and not call the given object until the right event is fired.
- """
- self._addSystemEventTriggerTest("after")
-
- def test_unknownPhase(self):
- """
- L{IReactorCore.addSystemEventTrigger} should reject phases other than
- C{'before'}, C{'during'}, or C{'after'}.
- """
- eventType = "test"
- self.assertRaises(KeyError, self.addTrigger, "xxx", eventType, lambda: None)
-
- def test_beforePreceedsDuring(self):
- """
- L{IReactorCore.addSystemEventTrigger} should call triggers added to the
- C{'before'} phase before it calls triggers added to the C{'during'}
- phase.
- """
- eventType = "test"
- events = []
-
- def beforeTrigger():
- events.append("before")
-
- def duringTrigger():
- events.append("during")
-
- self.addTrigger("before", eventType, beforeTrigger)
- self.addTrigger("during", eventType, duringTrigger)
- self.assertEqual(events, [])
- reactor.fireSystemEvent(eventType)
- self.assertEqual(events, ["before", "during"])
-
- def test_duringPreceedsAfter(self):
- """
- L{IReactorCore.addSystemEventTrigger} should call triggers added to the
- C{'during'} phase before it calls triggers added to the C{'after'}
- phase.
- """
- eventType = "test"
- events = []
-
- def duringTrigger():
- events.append("during")
-
- def afterTrigger():
- events.append("after")
-
- self.addTrigger("during", eventType, duringTrigger)
- self.addTrigger("after", eventType, afterTrigger)
- self.assertEqual(events, [])
- reactor.fireSystemEvent(eventType)
- self.assertEqual(events, ["during", "after"])
-
- def test_beforeReturnsDeferred(self):
- """
- If a trigger added to the C{'before'} phase of an event returns a
- L{Deferred}, the C{'during'} phase should be delayed until it is called
- back.
- """
- triggerDeferred = Deferred()
- eventType = "test"
- events = []
-
- def beforeTrigger():
- return triggerDeferred
-
- def duringTrigger():
- events.append("during")
-
- self.addTrigger("before", eventType, beforeTrigger)
- self.addTrigger("during", eventType, duringTrigger)
- self.assertEqual(events, [])
- reactor.fireSystemEvent(eventType)
- self.assertEqual(events, [])
- triggerDeferred.callback(None)
- self.assertEqual(events, ["during"])
-
- def test_multipleBeforeReturnDeferred(self):
- """
- If more than one trigger added to the C{'before'} phase of an event
- return L{Deferred}s, the C{'during'} phase should be delayed until they
- are all called back.
- """
- firstDeferred = Deferred()
- secondDeferred = Deferred()
- eventType = "test"
- events = []
-
- def firstBeforeTrigger():
- return firstDeferred
-
- def secondBeforeTrigger():
- return secondDeferred
-
- def duringTrigger():
- events.append("during")
-
- self.addTrigger("before", eventType, firstBeforeTrigger)
- self.addTrigger("before", eventType, secondBeforeTrigger)
- self.addTrigger("during", eventType, duringTrigger)
- self.assertEqual(events, [])
- reactor.fireSystemEvent(eventType)
- self.assertEqual(events, [])
- firstDeferred.callback(None)
- self.assertEqual(events, [])
- secondDeferred.callback(None)
- self.assertEqual(events, ["during"])
-
- def test_subsequentBeforeTriggerFiresPriorBeforeDeferred(self):
- """
- If a trigger added to the C{'before'} phase of an event calls back a
- L{Deferred} returned by an earlier trigger in the C{'before'} phase of
- the same event, the remaining C{'before'} triggers for that event
- should be run and any further L{Deferred}s waited on before proceeding
- to the C{'during'} events.
- """
- eventType = "test"
- events = []
- firstDeferred = Deferred()
- secondDeferred = Deferred()
-
- def firstBeforeTrigger():
- return firstDeferred
-
- def secondBeforeTrigger():
- firstDeferred.callback(None)
-
- def thirdBeforeTrigger():
- events.append("before")
- return secondDeferred
-
- def duringTrigger():
- events.append("during")
-
- self.addTrigger("before", eventType, firstBeforeTrigger)
- self.addTrigger("before", eventType, secondBeforeTrigger)
- self.addTrigger("before", eventType, thirdBeforeTrigger)
- self.addTrigger("during", eventType, duringTrigger)
- self.assertEqual(events, [])
- reactor.fireSystemEvent(eventType)
- self.assertEqual(events, ["before"])
- secondDeferred.callback(None)
- self.assertEqual(events, ["before", "during"])
-
- def test_removeSystemEventTrigger(self):
- """
- A trigger removed with L{IReactorCore.removeSystemEventTrigger} should
- not be called when the event fires.
- """
- eventType = "test"
- events = []
-
- def firstBeforeTrigger():
- events.append("first")
-
- def secondBeforeTrigger():
- events.append("second")
-
- self.addTrigger("before", eventType, firstBeforeTrigger)
- self.removeTrigger(self.addTrigger("before", eventType, secondBeforeTrigger))
- self.assertEqual(events, [])
- reactor.fireSystemEvent(eventType)
- self.assertEqual(events, ["first"])
-
- def test_removeNonExistentSystemEventTrigger(self):
- """
- Passing an object to L{IReactorCore.removeSystemEventTrigger} which was
- not returned by a previous call to
- L{IReactorCore.addSystemEventTrigger} or which has already been passed
- to C{removeSystemEventTrigger} should result in L{TypeError},
- L{KeyError}, or L{ValueError} being raised.
- """
- b = self.addTrigger("during", "test", lambda: None)
- self.removeTrigger(b)
- self.assertRaises(TypeError, reactor.removeSystemEventTrigger, None)
- self.assertRaises(ValueError, reactor.removeSystemEventTrigger, b)
- self.assertRaises(
- KeyError, reactor.removeSystemEventTrigger, (b[0], ("xxx",) + b[1][1:])
- )
-
- def test_interactionBetweenDifferentEvents(self):
- """
- L{IReactorCore.fireSystemEvent} should behave the same way for a
- particular system event regardless of whether Deferreds are being
- waited on for a different system event.
- """
- events = []
-
- firstEvent = "first-event"
- firstDeferred = Deferred()
-
- def beforeFirstEvent():
- events.append(("before", "first"))
- return firstDeferred
-
- def afterFirstEvent():
- events.append(("after", "first"))
-
- secondEvent = "second-event"
- secondDeferred = Deferred()
-
- def beforeSecondEvent():
- events.append(("before", "second"))
- return secondDeferred
-
- def afterSecondEvent():
- events.append(("after", "second"))
-
- self.addTrigger("before", firstEvent, beforeFirstEvent)
- self.addTrigger("after", firstEvent, afterFirstEvent)
- self.addTrigger("before", secondEvent, beforeSecondEvent)
- self.addTrigger("after", secondEvent, afterSecondEvent)
-
- self.assertEqual(events, [])
-
- # After this, firstEvent should be stuck before 'during' waiting for
- # firstDeferred.
- reactor.fireSystemEvent(firstEvent)
- self.assertEqual(events, [("before", "first")])
-
- # After this, secondEvent should be stuck before 'during' waiting for
- # secondDeferred.
- reactor.fireSystemEvent(secondEvent)
- self.assertEqual(events, [("before", "first"), ("before", "second")])
-
- # After this, firstEvent should have finished completely, but
- # secondEvent should be at the same place.
- firstDeferred.callback(None)
- self.assertEqual(
- events, [("before", "first"), ("before", "second"), ("after", "first")]
- )
-
- # After this, secondEvent should have finished completely.
- secondDeferred.callback(None)
- self.assertEqual(
- events,
- [
- ("before", "first"),
- ("before", "second"),
- ("after", "first"),
- ("after", "second"),
- ],
- )
-
-
- class TimeTests(TestCase):
- """
- Tests for the IReactorTime part of the reactor.
- """
-
- def test_seconds(self):
- """
- L{twisted.internet.reactor.seconds} should return something
- like a number.
-
- 1. This test specifically does not assert any relation to the
- "system time" as returned by L{time.time} or
- L{twisted.python.runtime.seconds}, because at some point we
- may find a better option for scheduling calls than
- wallclock-time.
- 2. This test *also* does not assert anything about the type of
- the result, because operations may not return ints or
- floats: For example, datetime-datetime == timedelta(0).
- """
- now = reactor.seconds()
- self.assertEqual(now - now + now, now)
-
- def test_callLaterUsesReactorSecondsInDelayedCall(self):
- """
- L{reactor.callLater<twisted.internet.interfaces.IReactorTime.callLater>}
- should use the reactor's seconds factory
- to produce the time at which the DelayedCall will be called.
- """
- oseconds = reactor.seconds
- reactor.seconds = lambda: 100
- try:
- call = reactor.callLater(5, lambda: None)
- self.assertEqual(call.getTime(), 105)
- finally:
- reactor.seconds = oseconds
- call.cancel()
-
- def test_callLaterUsesReactorSecondsAsDelayedCallSecondsFactory(self):
- """
- L{reactor.callLater<twisted.internet.interfaces.IReactorTime.callLater>}
- should propagate its own seconds factory
- to the DelayedCall to use as its own seconds factory.
- """
- oseconds = reactor.seconds
- reactor.seconds = lambda: 100
- try:
- call = reactor.callLater(5, lambda: None)
- self.assertEqual(call.seconds(), 100)
- finally:
- reactor.seconds = oseconds
- call.cancel()
-
- def test_callLater(self):
- """
- Test that a DelayedCall really calls the function it is
- supposed to call.
- """
- d = Deferred()
- reactor.callLater(0, d.callback, None)
- d.addCallback(self.assertEqual, None)
- return d
-
- def test_callLaterReset(self):
- """
- A L{DelayedCall} that is reset will be scheduled at the new time.
- """
- call = reactor.callLater(2, passthru, passthru)
- self.addCleanup(call.cancel)
- origTime = call.time
- call.reset(1)
- self.assertNotEqual(call.time, origTime)
-
- def test_cancelDelayedCall(self):
- """
- Test that when a DelayedCall is cancelled it does not run.
- """
- called = []
-
- def function():
- called.append(None)
-
- call = reactor.callLater(0, function)
- call.cancel()
-
- # Schedule a call in two "iterations" to check to make sure that the
- # above call never ran.
- d = Deferred()
-
- def check():
- try:
- self.assertEqual(called, [])
- except BaseException:
- d.errback()
- else:
- d.callback(None)
-
- reactor.callLater(0, reactor.callLater, 0, check)
- return d
-
- def test_cancelCancelledDelayedCall(self):
- """
- Test that cancelling a DelayedCall which has already been cancelled
- raises the appropriate exception.
- """
- call = reactor.callLater(0, lambda: None)
- call.cancel()
- self.assertRaises(error.AlreadyCancelled, call.cancel)
-
- def test_cancelCalledDelayedCallSynchronous(self):
- """
- Test that cancelling a DelayedCall in the DelayedCall's function as
- that function is being invoked by the DelayedCall raises the
- appropriate exception.
- """
- d = Deferred()
-
- def later():
- try:
- self.assertRaises(error.AlreadyCalled, call.cancel)
- except BaseException:
- d.errback()
- else:
- d.callback(None)
-
- call = reactor.callLater(0, later)
- return d
-
- def test_cancelCalledDelayedCallAsynchronous(self):
- """
- Test that cancelling a DelayedCall after it has run its function
- raises the appropriate exception.
- """
- d = Deferred()
-
- def check():
- try:
- self.assertRaises(error.AlreadyCalled, call.cancel)
- except BaseException:
- d.errback()
- else:
- d.callback(None)
-
- def later():
- reactor.callLater(0, check)
-
- call = reactor.callLater(0, later)
- return d
-
- def testCallLaterTime(self):
- d = reactor.callLater(10, lambda: None)
- try:
- self.assertTrue(d.getTime() - (time.time() + 10) < 1)
- finally:
- d.cancel()
-
- def testDelayedCallStringification(self):
- # Mostly just make sure str() isn't going to raise anything for
- # DelayedCalls within reason.
- dc = reactor.callLater(0, lambda x, y: None, "x", y=10)
- str(dc)
- dc.reset(5)
- str(dc)
- dc.cancel()
- str(dc)
-
- dc = reactor.callLater(
- 0, lambda: None, x=[({"hello": "world"}, 10j), reactor], *range(10)
- )
- str(dc)
- dc.cancel()
- str(dc)
-
- def calledBack(ignored):
- str(dc)
-
- d = Deferred().addCallback(calledBack)
- dc = reactor.callLater(0, d.callback, None)
- str(dc)
- return d
-
- def testDelayedCallSecondsOverride(self):
- """
- Test that the C{seconds} argument to DelayedCall gets used instead of
- the default timing function, if it is not None.
- """
-
- def seconds():
- return 10
-
- dc = base.DelayedCall(
- 5, lambda: None, (), {}, lambda dc: None, lambda dc: None, seconds
- )
- self.assertEqual(dc.getTime(), 5)
- dc.reset(3)
- self.assertEqual(dc.getTime(), 13)
-
-
- class CallFromThreadStopsAndWakeUpTests(TestCase):
- @skipIf(
- not interfaces.IReactorThreads(reactor, None),
- "Nothing to wake up for without thread support",
- )
- def testWakeUp(self):
- # Make sure other threads can wake up the reactor
- d = Deferred()
-
- def wake():
- time.sleep(0.1)
- # callFromThread will call wakeUp for us
- reactor.callFromThread(d.callback, None)
-
- reactor.callInThread(wake)
- return d
-
- def _stopCallFromThreadCallback(self):
- self.stopped = True
-
- def _callFromThreadCallback(self, d):
- reactor.callFromThread(self._callFromThreadCallback2, d)
- reactor.callLater(0, self._stopCallFromThreadCallback)
-
- def _callFromThreadCallback2(self, d):
- try:
- self.assertTrue(self.stopped)
- except BaseException:
- # Send the error to the deferred
- d.errback()
- else:
- d.callback(None)
-
- def testCallFromThreadStops(self):
- """
- Ensure that callFromThread from inside a callFromThread
- callback doesn't sit in an infinite loop and lets other
- things happen too.
- """
- self.stopped = False
- d = defer.Deferred()
- reactor.callFromThread(self._callFromThreadCallback, d)
- return d
-
-
- class DelayedTests(TestCase):
- def setUp(self):
- self.finished = 0
- self.counter = 0
- self.timers = {}
- self.deferred = defer.Deferred()
-
- def tearDown(self):
- for t in self.timers.values():
- t.cancel()
-
- def checkTimers(self):
- l1 = self.timers.values()
- l2 = list(reactor.getDelayedCalls())
-
- # There should be at least the calls we put in. There may be other
- # calls that are none of our business and that we should ignore,
- # though.
-
- missing = []
- for dc in l1:
- if dc not in l2:
- missing.append(dc)
- if missing:
- self.finished = 1
- self.assertFalse(
- missing,
- "Should have been missing no calls, instead "
- + "was missing "
- + repr(missing),
- )
-
- def callback(self, tag):
- del self.timers[tag]
- self.checkTimers()
-
- def addCallback(self, tag):
- self.callback(tag)
- self.addTimer(15, self.callback)
-
- def done(self, tag):
- self.finished = 1
- self.callback(tag)
- self.deferred.callback(None)
-
- def addTimer(self, when, callback):
- self.timers[self.counter] = reactor.callLater(
- when * 0.01, callback, self.counter
- )
- self.counter += 1
- self.checkTimers()
-
- def testGetDelayedCalls(self):
- if not hasattr(reactor, "getDelayedCalls"):
- return
- # This is not a race because we don't do anything which might call
- # the reactor until we have all the timers set up. If we did, this
- # test might fail on slow systems.
- self.checkTimers()
- self.addTimer(35, self.done)
- self.addTimer(20, self.callback)
- self.addTimer(30, self.callback)
- which = self.counter
- self.addTimer(29, self.callback)
- self.addTimer(25, self.addCallback)
- self.addTimer(26, self.callback)
-
- self.timers[which].cancel()
- del self.timers[which]
- self.checkTimers()
-
- self.deferred.addCallback(lambda x: self.checkTimers())
- return self.deferred
-
- def test_active(self):
- """
- L{IDelayedCall.active} returns False once the call has run.
- """
- dcall = reactor.callLater(0.01, self.deferred.callback, True)
- self.assertTrue(dcall.active())
-
- def checkDeferredCall(success):
- self.assertFalse(dcall.active())
- return success
-
- self.deferred.addCallback(checkDeferredCall)
-
- return self.deferred
-
-
- resolve_helper = """
- import %(reactor)s
- %(reactor)s.install()
- from twisted.internet import reactor
-
- class Foo:
- def __init__(self):
- reactor.callWhenRunning(self.start)
- self.timer = reactor.callLater(3, self.failed)
- def start(self):
- reactor.resolve('localhost').addBoth(self.done)
- def done(self, res):
- print('done', res)
- reactor.stop()
- def failed(self):
- print('failed')
- self.timer = None
- reactor.stop()
- f = Foo()
- reactor.run()
- """
-
-
- class ChildResolveProtocol(protocol.ProcessProtocol):
- def __init__(self, onCompletion):
- self.onCompletion = onCompletion
-
- def connectionMade(self):
- self.output = []
- self.error = []
-
- def outReceived(self, out):
- self.output.append(out)
-
- def errReceived(self, err):
- self.error.append(err)
-
- def processEnded(self, reason):
- self.onCompletion.callback((reason, self.output, self.error))
- self.onCompletion = None
-
-
- @skipIf(
- not interfaces.IReactorProcess(reactor, None),
- "cannot run test: reactor doesn't support IReactorProcess",
- )
- class ResolveTests(TestCase):
- def testChildResolve(self):
- # I've seen problems with reactor.run under gtk2reactor. Spawn a
- # child which just does reactor.resolve after the reactor has
- # started, fail if it does not complete in a timely fashion.
- helperPath = os.path.abspath(self.mktemp())
- with open(helperPath, "w") as helperFile:
-
- # Eeueuuggg
- reactorName = reactor.__module__
-
- helperFile.write(resolve_helper % {"reactor": reactorName})
-
- env = os.environ.copy()
- env["PYTHONPATH"] = os.pathsep.join(sys.path)
-
- helperDeferred = Deferred()
- helperProto = ChildResolveProtocol(helperDeferred)
-
- reactor.spawnProcess(
- helperProto, sys.executable, ("python", "-u", helperPath), env
- )
-
- def cbFinished(result):
- (reason, output, error) = result
- # If the output is "done 127.0.0.1\n" we don't really care what
- # else happened.
- output = b"".join(output)
- expected_output = b"done 127.0.0.1" + os.linesep.encode("ascii")
- if output != expected_output:
- self.fail(
- (
- "The child process failed to produce "
- "the desired results:\n"
- " Reason for termination was: {!r}\n"
- " Output stream was: {!r}\n"
- " Error stream was: {!r}\n"
- ).format(reason.getErrorMessage(), output, b"".join(error))
- )
-
- helperDeferred.addCallback(cbFinished)
- return helperDeferred
-
-
- @skipIf(
- not interfaces.IReactorThreads(reactor, None),
- "Nothing to test without thread support",
- )
- class CallFromThreadTests(TestCase):
- """
- Task scheduling from threads tests.
- """
-
- def setUp(self):
- self.counter = 0
- self.deferred = Deferred()
-
- def schedule(self, *args, **kwargs):
- """
- Override in subclasses.
- """
- reactor.callFromThread(*args, **kwargs)
-
- def test_lotsOfThreadsAreScheduledCorrectly(self):
- """
- L{IReactorThreads.callFromThread} can be used to schedule a large
- number of calls in the reactor thread.
- """
-
- def addAndMaybeFinish():
- self.counter += 1
- if self.counter == 100:
- self.deferred.callback(True)
-
- for i in range(100):
- self.schedule(addAndMaybeFinish)
-
- return self.deferred
-
- def test_threadsAreRunInScheduledOrder(self):
- """
- Callbacks should be invoked in the order they were scheduled.
- """
- order = []
-
- def check(_):
- self.assertEqual(order, [1, 2, 3])
-
- self.deferred.addCallback(check)
- self.schedule(order.append, 1)
- self.schedule(order.append, 2)
- self.schedule(order.append, 3)
- self.schedule(reactor.callFromThread, self.deferred.callback, None)
-
- return self.deferred
-
- def test_scheduledThreadsNotRunUntilReactorRuns(self):
- """
- Scheduled tasks should not be run until the reactor starts running.
- """
-
- def incAndFinish():
- self.counter = 1
- self.deferred.callback(True)
-
- self.schedule(incAndFinish)
-
- # Callback shouldn't have fired yet.
- self.assertEqual(self.counter, 0)
-
- return self.deferred
-
-
- class MyProtocol(protocol.Protocol):
- """
- Sample protocol.
- """
-
-
- class MyFactory(protocol.Factory):
- """
- Sample factory.
- """
-
- protocol = MyProtocol
-
-
- class ProtocolTests(TestCase):
- def testFactory(self):
- factory = MyFactory()
- protocol = factory.buildProtocol(None)
- self.assertEqual(protocol.factory, factory)
- self.assertIsInstance(protocol, factory.protocol)
-
-
- class DummyProducer:
- """
- Very uninteresting producer implementation used by tests to ensure the
- right methods are called by the consumer with which it is registered.
-
- @type events: C{list} of C{str}
- @ivar events: The producer/consumer related events which have happened to
- this producer. Strings in this list may be C{'resume'}, C{'stop'}, or
- C{'pause'}. Elements are added as they occur.
- """
-
- def __init__(self):
- self.events = []
-
- def resumeProducing(self):
- self.events.append("resume")
-
- def stopProducing(self):
- self.events.append("stop")
-
- def pauseProducing(self):
- self.events.append("pause")
-
-
- class SillyDescriptor(abstract.FileDescriptor):
- """
- A descriptor whose data buffer gets filled very fast.
-
- Useful for testing FileDescriptor's IConsumer interface, since
- the data buffer fills as soon as at least four characters are
- written to it, and gets emptied in a single doWrite() cycle.
- """
-
- bufferSize = 3
- connected = True
-
- def writeSomeData(self, data):
- """
- Always write all data.
- """
- return len(data)
-
- def startWriting(self):
- """
- Do nothing: bypass the reactor.
- """
-
- stopWriting = startWriting
-
-
- class ReentrantProducer(DummyProducer):
- """
- Similar to L{DummyProducer}, but with a resumeProducing method which calls
- back into an L{IConsumer} method of the consumer against which it is
- registered.
-
- @ivar consumer: The consumer with which this producer has been or will
- be registered.
-
- @ivar methodName: The name of the method to call on the consumer inside
- C{resumeProducing}.
-
- @ivar methodArgs: The arguments to pass to the consumer method invoked in
- C{resumeProducing}.
- """
-
- def __init__(self, consumer, methodName, *methodArgs):
- super().__init__()
- self.consumer = consumer
- self.methodName = methodName
- self.methodArgs = methodArgs
-
- def resumeProducing(self):
- super().resumeProducing()
- getattr(self.consumer, self.methodName)(*self.methodArgs)
-
-
- class ProducerTests(TestCase):
- """
- Test abstract.FileDescriptor's consumer interface.
- """
-
- def test_doubleProducer(self):
- """
- Verify that registering a non-streaming producer invokes its
- resumeProducing() method and that you can only register one producer
- at a time.
- """
- fd = abstract.FileDescriptor()
- fd.connected = 1
- dp = DummyProducer()
- fd.registerProducer(dp, 0)
- self.assertEqual(dp.events, ["resume"])
- self.assertRaises(RuntimeError, fd.registerProducer, DummyProducer(), 0)
-
- def test_unconnectedFileDescriptor(self):
- """
- Verify that registering a producer when the connection has already
- been closed invokes its stopProducing() method.
- """
- fd = abstract.FileDescriptor()
- fd.disconnected = 1
- dp = DummyProducer()
- fd.registerProducer(dp, 0)
- self.assertEqual(dp.events, ["stop"])
-
- def _dontPausePullConsumerTest(self, methodName):
- """
- Pull consumers don't get their C{pauseProducing} method called if the
- descriptor buffer fills up.
-
- @param _methodName: Either 'write', or 'writeSequence', indicating
- which transport method to write data to.
- """
- descriptor = SillyDescriptor()
- producer = DummyProducer()
- descriptor.registerProducer(producer, streaming=False)
- self.assertEqual(producer.events, ["resume"])
- del producer.events[:]
-
- # Fill up the descriptor's write buffer so we can observe whether or
- # not it pauses its producer in that case.
- if methodName == "writeSequence":
- descriptor.writeSequence([b"1", b"2", b"3", b"4"])
- else:
- descriptor.write(b"1234")
-
- self.assertEqual(producer.events, [])
-
- def test_dontPausePullConsumerOnWrite(self):
- """
- Verify that FileDescriptor does not call producer.pauseProducing() on a
- non-streaming pull producer in response to a L{IConsumer.write} call
- which results in a full write buffer. Issue #2286.
- """
- return self._dontPausePullConsumerTest("write")
-
- def test_dontPausePullConsumerOnWriteSequence(self):
- """
- Like L{test_dontPausePullConsumerOnWrite}, but for a call to
- C{writeSequence} rather than L{IConsumer.write}.
-
- C{writeSequence} is not part of L{IConsumer}, but
- L{abstract.FileDescriptor} has supported consumery behavior in response
- to calls to L{writeSequence} forever.
- """
- return self._dontPausePullConsumerTest("writeSequence")
-
- def _reentrantStreamingProducerTest(self, methodName):
- descriptor = SillyDescriptor()
- if methodName == "writeSequence":
- data = [b"s", b"p", b"am"]
- else:
- data = b"spam"
- producer = ReentrantProducer(descriptor, methodName, data)
- descriptor.registerProducer(producer, streaming=True)
-
- # Start things off by filling up the descriptor's buffer so it will
- # pause its producer.
- getattr(descriptor, methodName)(data)
-
- # Sanity check - make sure that worked.
- self.assertEqual(producer.events, ["pause"])
- del producer.events[:]
-
- # After one call to doWrite, the buffer has been emptied so the
- # FileDescriptor should resume its producer. That will result in an
- # immediate call to FileDescriptor.write which will again fill the
- # buffer and result in the producer being paused.
- descriptor.doWrite()
- self.assertEqual(producer.events, ["resume", "pause"])
- del producer.events[:]
-
- # After a second call to doWrite, the exact same thing should have
- # happened. Prior to the bugfix for which this test was written,
- # FileDescriptor would have incorrectly believed its producer was
- # already resumed (it was paused) and so not resume it again.
- descriptor.doWrite()
- self.assertEqual(producer.events, ["resume", "pause"])
-
- def test_reentrantStreamingProducerUsingWrite(self):
- """
- Verify that FileDescriptor tracks producer's paused state correctly.
- Issue #811, fixed in revision r12857.
- """
- return self._reentrantStreamingProducerTest("write")
-
- def test_reentrantStreamingProducerUsingWriteSequence(self):
- """
- Like L{test_reentrantStreamingProducerUsingWrite}, but for calls to
- C{writeSequence}.
-
- C{writeSequence} is B{not} part of L{IConsumer}, however
- C{abstract.FileDescriptor} has supported consumery behavior in response
- to calls to C{writeSequence} forever.
- """
- return self._reentrantStreamingProducerTest("writeSequence")
-
-
- class PortStringificationTests(TestCase):
- @skipIf(not interfaces.IReactorTCP(reactor, None), "IReactorTCP is needed")
- def testTCP(self):
- p = reactor.listenTCP(0, protocol.ServerFactory())
- portNo = p.getHost().port
- self.assertNotEqual(
- str(p).find(str(portNo)), -1, "%d not found in %s" % (portNo, p)
- )
- return p.stopListening()
-
- @skipIf(not interfaces.IReactorUDP(reactor, None), "IReactorUDP is needed")
- def testUDP(self):
- p = reactor.listenUDP(0, protocol.DatagramProtocol())
- portNo = p.getHost().port
- self.assertNotEqual(
- str(p).find(str(portNo)), -1, "%d not found in %s" % (portNo, p)
- )
- return p.stopListening()
-
- @skipIf(not interfaces.IReactorSSL(reactor, None), "IReactorSSL is needed")
- @skipIf(not ssl, "SSL support is missing")
- def testSSL(self, ssl=ssl):
- pem = util.sibpath(__file__, "server.pem")
- p = reactor.listenSSL(
- 0, protocol.ServerFactory(), ssl.DefaultOpenSSLContextFactory(pem, pem)
- )
- portNo = p.getHost().port
- self.assertNotEqual(
- str(p).find(str(portNo)), -1, "%d not found in %s" % (portNo, p)
- )
- return p.stopListening()
-
-
- class ConnectorReprTests(TestCase):
- def test_tcp_repr(self):
- c = Connector("localhost", 666, object(), 0, object())
- expect = "<twisted.internet.tcp.Connector instance at 0x%x " "disconnected %s>"
- expect = expect % (id(c), c.getDestination())
- self.assertEqual(repr(c), expect)
|