1 # Copyright (c) Twisted Matrix Laboratories.
2 # See LICENSE for details.
5 Tests for implementations of L{IReactorCore}.
14 from twisted.internet.abstract import FileDescriptor
15 from twisted.internet.error import ReactorAlreadyRunning, ReactorNotRestartable
16 from twisted.internet.defer import Deferred
17 from twisted.internet.test.reactormixins import ReactorBuilder
20 class ObjectModelIntegrationMixin(object):
22 Helpers for tests about the object model of reactor-related objects.
24 def assertFullyNewStyle(self, instance):
26 Assert that the given object is an instance of a new-style class and
27 that there are no classic classes in the inheritance hierarchy of
30 This is a beneficial condition because PyPy is better able to
31 optimize attribute lookup on such classes.
33 self.assertIsInstance(instance, object)
34 mro = inspect.getmro(type(instance))
37 issubclass(subclass, object),
38 "%r is not new-style" % (subclass,))
42 class ObjectModelIntegrationTest(ReactorBuilder, ObjectModelIntegrationMixin):
44 Test details of object model integration against all reactors.
47 def test_newstyleReactor(self):
49 Checks that all reactors on a platform have method resolution order
50 containing only new style classes.
52 reactor = self.buildReactor()
53 self.assertFullyNewStyle(reactor)
57 class SystemEventTestsBuilder(ReactorBuilder):
59 Builder defining tests relating to L{IReactorCore.addSystemEventTrigger}
60 and L{IReactorCore.fireSystemEvent}.
62 def test_stopWhenNotStarted(self):
64 C{reactor.stop()} raises L{RuntimeError} when called when the reactor
67 reactor = self.buildReactor()
68 self.assertRaises(RuntimeError, reactor.stop)
71 def test_stopWhenAlreadyStopped(self):
73 C{reactor.stop()} raises L{RuntimeError} when called after the reactor
76 reactor = self.buildReactor()
77 reactor.callWhenRunning(reactor.stop)
78 self.runReactor(reactor)
79 self.assertRaises(RuntimeError, reactor.stop)
82 def test_callWhenRunningOrder(self):
84 Functions are run in the order that they were passed to
85 L{reactor.callWhenRunning}.
87 reactor = self.buildReactor()
89 reactor.callWhenRunning(events.append, "first")
90 reactor.callWhenRunning(events.append, "second")
91 reactor.callWhenRunning(reactor.stop)
92 self.runReactor(reactor)
93 self.assertEqual(events, ["first", "second"])
96 def test_runningForStartupEvents(self):
98 The reactor is not running when C{"before"} C{"startup"} triggers are
99 called and is running when C{"during"} and C{"after"} C{"startup"}
102 reactor = self.buildReactor()
105 state['before'] = reactor.running
107 state['during'] = reactor.running
109 state['after'] = reactor.running
110 reactor.addSystemEventTrigger("before", "startup", beforeStartup)
111 reactor.addSystemEventTrigger("during", "startup", duringStartup)
112 reactor.addSystemEventTrigger("after", "startup", afterStartup)
113 reactor.callWhenRunning(reactor.stop)
114 self.assertEqual(state, {})
115 self.runReactor(reactor)
123 def test_signalHandlersInstalledDuringStartup(self):
125 Signal handlers are installed in responsed to the C{"during"}
128 reactor = self.buildReactor()
134 reactor.addSystemEventTrigger("before", "startup", beforeStartup)
135 reactor.addSystemEventTrigger("after", "startup", afterStartup)
138 def fakeSignal(signum, action):
139 sawPhase.append(phase[0])
140 self.patch(signal, 'signal', fakeSignal)
141 reactor.callWhenRunning(reactor.stop)
142 self.assertEqual(phase[0], None)
143 self.assertEqual(sawPhase, [])
144 self.runReactor(reactor)
145 self.assertIn("before", sawPhase)
146 self.assertEqual(phase[0], "after")
149 def test_stopShutDownEvents(self):
151 C{reactor.stop()} fires all three phases of shutdown event triggers
152 before it makes C{reactor.run()} return.
154 reactor = self.buildReactor()
156 reactor.addSystemEventTrigger(
157 "before", "shutdown",
158 lambda: events.append(("before", "shutdown")))
159 reactor.addSystemEventTrigger(
160 "during", "shutdown",
161 lambda: events.append(("during", "shutdown")))
162 reactor.addSystemEventTrigger(
164 lambda: events.append(("after", "shutdown")))
165 reactor.callWhenRunning(reactor.stop)
166 self.runReactor(reactor)
167 self.assertEqual(events, [("before", "shutdown"),
168 ("during", "shutdown"),
169 ("after", "shutdown")])
172 def test_shutdownFiresTriggersAsynchronously(self):
174 C{"before"} C{"shutdown"} triggers are not run synchronously from
177 reactor = self.buildReactor()
179 reactor.addSystemEventTrigger(
180 "before", "shutdown", events.append, "before shutdown")
183 events.append("stopped")
184 reactor.callWhenRunning(stopIt)
185 self.assertEqual(events, [])
186 self.runReactor(reactor)
187 self.assertEqual(events, ["stopped", "before shutdown"])
190 def test_shutdownDisconnectsCleanly(self):
192 A L{IFileDescriptor.connectionLost} implementation which raises an
193 exception does not prevent the remaining L{IFileDescriptor}s from
194 having their C{connectionLost} method called.
198 # Subclass FileDescriptor to get logPrefix
199 class ProblematicFileDescriptor(FileDescriptor):
200 def connectionLost(self, reason):
201 raise RuntimeError("simulated connectionLost error")
203 class OKFileDescriptor(FileDescriptor):
204 def connectionLost(self, reason):
207 reactor = self.buildReactor()
209 # Unfortunately, it is necessary to patch removeAll to directly control
210 # the order of the returned values. The test is only valid if
211 # ProblematicFileDescriptor comes first. Also, return these
212 # descriptors only the first time removeAll is called so that if it is
213 # called again the file descriptors aren't re-disconnected.
214 fds = iter([ProblematicFileDescriptor(), OKFileDescriptor()])
215 reactor.removeAll = lambda: fds
216 reactor.callWhenRunning(reactor.stop)
217 self.runReactor(reactor)
218 self.assertEqual(len(self.flushLoggedErrors(RuntimeError)), 1)
219 self.assertTrue(lostOK[0])
222 def test_multipleRun(self):
224 C{reactor.run()} raises L{ReactorAlreadyRunning} when called when
225 the reactor is already running.
229 self.assertRaises(ReactorAlreadyRunning, reactor.run)
230 events.append("tested")
231 reactor = self.buildReactor()
232 reactor.callWhenRunning(reentrantRun)
233 reactor.callWhenRunning(reactor.stop)
234 self.runReactor(reactor)
235 self.assertEqual(events, ["tested"])
238 def test_runWithAsynchronousBeforeStartupTrigger(self):
240 When there is a C{'before'} C{'startup'} trigger which returns an
241 unfired L{Deferred}, C{reactor.run()} starts the reactor and does not
242 return until after C{reactor.stop()} is called
246 events.append('trigger')
248 d.addCallback(callback)
249 reactor.callLater(0, d.callback, None)
251 def callback(ignored):
252 events.append('callback')
254 reactor = self.buildReactor()
255 reactor.addSystemEventTrigger('before', 'startup', trigger)
256 self.runReactor(reactor)
257 self.assertEqual(events, ['trigger', 'callback'])
260 def test_iterate(self):
262 C{reactor.iterate()} does not block.
264 reactor = self.buildReactor()
265 t = reactor.callLater(5, reactor.crash)
268 reactor.iterate(0) # Shouldn't block
269 elapsed = time.time() - start
271 self.failUnless(elapsed < 2)
275 def test_crash(self):
277 C{reactor.crash()} stops the reactor and does not fire shutdown
280 reactor = self.buildReactor()
282 reactor.addSystemEventTrigger(
283 "before", "shutdown",
284 lambda: events.append(("before", "shutdown")))
285 reactor.callWhenRunning(reactor.callLater, 0, reactor.crash)
286 self.runReactor(reactor)
287 self.assertFalse(reactor.running)
290 "Shutdown triggers invoked but they should not have been.")
293 def test_runAfterCrash(self):
295 C{reactor.run()} restarts the reactor after it has been stopped by
300 events.append('crash')
302 reactor = self.buildReactor()
303 reactor.callWhenRunning(crash)
304 self.runReactor(reactor)
306 events.append(('stop', reactor.running))
308 reactor.callWhenRunning(stop)
309 self.runReactor(reactor)
310 self.assertEqual(events, ['crash', ('stop', True)])
313 def test_runAfterStop(self):
315 C{reactor.run()} raises L{ReactorNotRestartable} when called when
316 the reactor is being run after getting stopped priorly.
320 self.assertRaises(ReactorNotRestartable, reactor.run)
321 events.append('tested')
322 reactor = self.buildReactor()
323 reactor.callWhenRunning(reactor.stop)
324 reactor.addSystemEventTrigger('after', 'shutdown', restart)
325 self.runReactor(reactor)
326 self.assertEqual(events, ['tested'])
330 globals().update(SystemEventTestsBuilder.makeTestCaseClasses())
331 globals().update(ObjectModelIntegrationTest.makeTestCaseClasses())