1 # Copyright (c) Twisted Matrix Laboratories.
2 # See LICENSE for details.
5 Tests for implementations of L{IReactorWin32Events}.
8 from thread import get_ident
15 from zope.interface.verify import verifyObject
17 from twisted.python.threadable import isInIOThread
18 from twisted.internet.interfaces import IReactorWin32Events
19 from twisted.internet.defer import Deferred
20 from twisted.internet.test.reactormixins import ReactorBuilder
23 class Listener(object):
25 L{Listener} is an object that can be added to a L{IReactorWin32Events}
26 reactor to receive callback notification when a Windows event is set. It
27 records what thread its callback is invoked in and fires a Deferred.
29 @ivar success: A flag which is set to C{True} when the event callback is
32 @ivar logThreadID: The id of the thread in which the C{logPrefix} method is
35 @ivar eventThreadID: The id of the thread in which the event callback is
38 @ivar connLostThreadID: The id of the thread in which the C{connectionLost}
41 @ivar _finished: The L{Deferred} which will be fired when the event callback
45 logThreadID = eventThreadID = connLostThreadID = None
47 def __init__(self, finished):
48 self._finished = finished
52 self.logThreadID = get_ident()
58 self.eventThreadID = get_ident()
59 self._finished.callback(None)
62 def brokenOccurred(self):
63 raise RuntimeError("Some problem")
66 def returnValueOccurred(self):
67 return EnvironmentError("Entirely different problem")
70 def connectionLost(self, reason):
71 self.connLostThreadID = get_ident()
72 self._finished.errback(reason)
76 class Win32EventsTestsBuilder(ReactorBuilder):
78 Builder defining tests relating to L{IReactorWin32Events}.
80 requiredInterfaces = [IReactorWin32Events]
82 def test_interface(self):
84 An instance of the reactor has all of the methods defined on
85 L{IReactorWin32Events}.
87 reactor = self.buildReactor()
88 verifyObject(IReactorWin32Events, reactor)
91 def test_addEvent(self):
93 When an event which has been added to the reactor is set, the action
94 associated with the event is invoked in the reactor thread.
96 reactorThreadID = get_ident()
97 reactor = self.buildReactor()
98 event = win32event.CreateEvent(None, False, False, None)
100 finished.addCallback(lambda ignored: reactor.stop())
101 listener = Listener(finished)
102 reactor.addEvent(event, listener, 'occurred')
103 reactor.callWhenRunning(win32event.SetEvent, event)
104 self.runReactor(reactor)
105 self.assertTrue(listener.success)
106 self.assertEqual(reactorThreadID, listener.logThreadID)
107 self.assertEqual(reactorThreadID, listener.eventThreadID)
110 def test_ioThreadDoesNotChange(self):
112 Using L{IReactorWin32Events.addEvent} does not change which thread is
113 reported as the I/O thread.
117 results.append(isInIOThread())
119 reactor = self.buildReactor()
120 event = win32event.CreateEvent(None, False, False, None)
121 finished = Deferred()
122 listener = Listener(finished)
123 finished.addCallback(check)
124 reactor.addEvent(event, listener, 'occurred')
125 reactor.callWhenRunning(win32event.SetEvent, event)
126 self.runReactor(reactor)
127 self.assertTrue(listener.success)
128 self.assertEqual([True], results)
131 def test_disconnectedOnError(self):
133 If the event handler raises an exception, the event is removed from the
134 reactor and the handler's C{connectionLost} method is called in the I/O
135 thread and the exception is logged.
137 reactorThreadID = get_ident()
138 reactor = self.buildReactor()
139 event = win32event.CreateEvent(None, False, False, None)
140 finished = self.assertFailure(Deferred(), RuntimeError)
141 finished.addCallback(lambda ignored: reactor.stop())
142 listener = Listener(finished)
143 reactor.addEvent(event, listener, 'brokenOccurred')
144 reactor.callWhenRunning(win32event.SetEvent, event)
145 self.runReactor(reactor)
146 self.assertEqual(reactorThreadID, listener.connLostThreadID)
147 self.assertEqual(1, len(self.flushLoggedErrors(RuntimeError)))
150 def test_disconnectOnReturnValue(self):
152 If the event handler returns a value, the event is removed from the
153 reactor and the handler's C{connectionLost} method is called in the I/O
156 reactorThreadID = get_ident()
157 reactor = self.buildReactor()
158 event = win32event.CreateEvent(None, False, False, None)
159 finished = self.assertFailure(Deferred(), EnvironmentError)
160 finished.addCallback(lambda ignored: reactor.stop())
161 listener = Listener(finished)
162 reactor.addEvent(event, listener, 'returnValueOccurred')
163 reactor.callWhenRunning(win32event.SetEvent, event)
164 self.runReactor(reactor)
165 self.assertEqual(reactorThreadID, listener.connLostThreadID)
168 def test_notDisconnectedOnShutdown(self):
170 Event handlers added with L{IReactorWin32Events.addEvent} do not have
171 C{connectionLost} called on them if they are still active when the
174 reactor = self.buildReactor()
175 event = win32event.CreateEvent(None, False, False, None)
176 finished = Deferred()
177 listener = Listener(finished)
178 reactor.addEvent(event, listener, 'occurred')
179 reactor.callWhenRunning(reactor.stop)
180 self.runReactor(reactor)
181 self.assertIdentical(None, listener.connLostThreadID)
183 globals().update(Win32EventsTestsBuilder.makeTestCaseClasses())