1
# Copyright (c) 2008-2009 Twisted Matrix Laboratories.
2
# See LICENSE for details.
5
Tests for implementations of L{IReactorTime}.
12
from twisted.internet.defer import TimeoutError
13
from twisted.trial.unittest import TestCase, SkipTest
14
from twisted.python.runtime import platformType
15
from twisted.python.reflect import namedAny
16
from twisted.python import log
17
from twisted.python.failure import Failure
19
# Access private APIs.
20
if platformType == 'posix':
21
from twisted.internet import process
28
L{TestCase} mixin which provides a reactor-creation API. This mixin
29
defines C{setUp} and C{tearDown}, so mix it in before L{TestCase} or call
30
its methods from the overridden ones in the subclass.
32
@cvar skippedReactors: A dict mapping FQPN strings of reactors for
33
which the tests defined by this class will be skipped to strings
34
giving the skip message.
35
@cvar requiredInterfaces: A C{list} of interfaces which the reactor must
36
provide or these tests will be skipped. The default, C{None}, means
37
that no interfaces are required.
38
@ivar reactorFactory: A no-argument callable which returns the reactor to
40
@ivar originalHandler: The SIGCHLD handler which was installed when setUp
41
ran and which will be re-installed when tearDown runs.
42
@ivar _reactors: A list of FQPN strings giving the reactors for which
43
TestCases will be created.
46
_reactors = ["twisted.internet.selectreactor.SelectReactor",
47
"twisted.internet.pollreactor.PollReactor",
48
"twisted.internet.epollreactor.EPollReactor",
49
"twisted.internet.glib2reactor.Glib2Reactor",
50
"twisted.internet.gtk2reactor.Gtk2Reactor",
51
"twisted.internet.kqueuereactor.KQueueReactor",
52
"twisted.internet.win32eventreactor.Win32Reactor",
53
"twisted.internet.iocpreactor.reactor.IOCPReactor"]
56
originalHandler = None
57
requiredInterface = None
62
Clear the SIGCHLD handler, if there is one, to ensure an environment
63
like the one which exists prior to a call to L{reactor.run}.
65
if platformType == 'posix':
66
self.originalHandler = signal.signal(signal.SIGCHLD, signal.SIG_DFL)
71
Restore the original SIGCHLD handler and reap processes as long as
72
there seem to be any remaining.
74
if self.originalHandler is not None:
75
signal.signal(signal.SIGCHLD, self.originalHandler)
76
if process is not None:
77
while process.reapProcessHandlers:
79
"ReactorBuilder.tearDown reaping some processes %r" % (
80
process.reapProcessHandlers,))
81
process.reapAllProcesses()
84
def unbuildReactor(self, reactor):
86
Clean up any resources which may have been allocated for the given
87
reactor by its creation or by a test which used it.
91
# XXX These explicit calls to clean up the waker (and any other
92
# internal readers) should become obsolete when bug #3063 is
93
# fixed. -radix, 2008-02-29. Fortunately it should probably cause an
94
# error when bug #3063 is fixed, so it should be removed in the same
95
# branch that fixes it.
98
if getattr(reactor, '_internalReaders', None) is not None:
99
for reader in reactor._internalReaders:
100
reactor.removeReader(reader)
101
reader.connectionLost(None)
102
reactor._internalReaders.clear()
104
# Here's an extra thing unrelated to wakers but necessary for
105
# cleaning up after the reactors we make. -exarkun
106
reactor.disconnectAll()
108
# It would also be bad if any timed calls left over were allowed to
110
calls = reactor.getDelayedCalls()
115
def buildReactor(self):
117
Create and return a reactor using C{self.reactorFactory}.
120
reactor = self.reactorFactory()
122
# Unfortunately, not all errors which result in a reactor being
123
# unusable are detectable without actually instantiating the
124
# reactor. So we catch some more here and skip the test if
126
raise SkipTest(Failure().getErrorMessage())
128
if self.requiredInterface is not None:
129
if not self.requiredInterface.providedBy(reactor):
130
self.unbuildReactor(reactor)
132
"%r does not provide %r" % (
133
reactor, self.requiredInterface))
134
self.addCleanup(self.unbuildReactor, reactor)
138
def runReactor(self, reactor, timeout=None):
140
Run the reactor for at most the given amount of time.
142
@param reactor: The reactor to run.
144
@type timeout: C{int} or C{float}
145
@param timeout: The maximum amount of time, specified in seconds, to
146
allow the reactor to run. If the reactor is still running after
147
this much time has elapsed, it will be stopped and an exception
148
raised. If C{None}, the default test method timeout imposed by
149
Trial will be used. This depends on the L{IReactorTime}
150
implementation of C{reactor} for correct operation.
152
@raise TimeoutError: If the reactor is still running after C{timeout}
156
timeout = self.getTimeout()
160
timedOut.append(None)
163
reactor.callLater(timeout, stop)
167
"reactor still running after %s seconds" % (timeout,))
170
def makeTestCaseClasses(cls):
172
Create a L{TestCase} subclass which mixes in C{cls} for each known
173
reactor and return a dict mapping their names to them.
176
for reactor in cls._reactors:
177
shortReactorName = reactor.split(".")[-1]
178
name = (cls.__name__ + "." + shortReactorName).replace(".", "_")
179
class testcase(cls, TestCase):
180
__module__ = cls.__module__
181
if reactor in cls.skippedReactors:
182
skip = cls.skippedReactors[reactor]
184
reactorFactory = namedAny(reactor)
186
skip = Failure().getErrorMessage()
187
testcase.__name__ = name
188
classes[testcase.__name__] = testcase
190
makeTestCaseClasses = classmethod(makeTestCaseClasses)
193
__all__ = ['ReactorBuilder']