5
from landscape.reactor import Reactor, FakeReactor, TwistedReactor
7
from landscape.tests.helpers import LandscapeTest
10
class ReactorTest(LandscapeTest):
12
def get_reactor(self):
15
def test_call_later(self):
16
reactor = self.get_reactor()
19
called.append("Hello!")
21
reactor.call_later(0, dummy)
23
self.assertEquals(called, ["Hello!"])
25
def test_call_later_with_args(self):
26
reactor = self.get_reactor()
31
reactor.call_later(0, dummy, "a", b="b")
33
self.assertEquals(called, [("a", "b")])
35
def test_call_later_only_calls_once(self):
36
reactor = self.get_reactor()
41
reactor.call_later(0, append)
42
reactor.call_later(0.3, reactor.stop)
44
self.assertEquals(len(called), 1)
46
def test_cancel_call(self):
47
reactor = self.get_reactor()
49
id = reactor.call_later(0, called.append, "hi")
50
reactor.cancel_call(id)
51
reactor.call_later(0.3, reactor.stop)
53
self.assertEquals(len(called), 0)
55
def test_call_every(self):
56
reactor = self.get_reactor()
58
reactor.call_every(0.01, called.append, "hi")
59
reactor.call_later(0.5, reactor.stop)
61
self.failUnless(5 < len(called) < 100, len(called))
63
def test_cancel_call_every(self):
64
reactor = self.get_reactor()
66
id = reactor.call_every(0, called.append, "hi")
67
reactor.cancel_call(id)
68
reactor.call_later(0.3, reactor.stop)
70
self.assertEquals(len(called), 0)
72
def test_cancel_call_every_after_first_call(self):
73
reactor = self.get_reactor()
76
reactor.cancel_call(id)
78
id = reactor.call_every(0, cancel_call)
79
reactor.call_later(0.1, reactor.stop)
81
self.assertEquals(len(called), 1)
83
def test_cancel_later_called(self):
84
reactor = self.get_reactor()
85
id = reactor.call_later(0, lambda: None)
86
reactor.call_later(0.3, reactor.stop)
88
reactor.cancel_call(id)
90
def test_cancel_call_twice(self):
92
Multiple cancellations of a call will not raise any exceptions.
94
reactor = self.get_reactor()
95
id = reactor.call_later(3, lambda: None)
96
reactor.cancel_call(id)
97
reactor.cancel_call(id)
99
def test_reactor_doesnt_leak(self):
100
reactor = self.get_reactor()
102
reactor.call_later(0, called.append, "hi")
103
reactor = self.get_reactor()
104
reactor.call_later(0.01, reactor.stop)
106
self.assertEquals(called, [])
108
def test_event(self):
109
reactor = self.get_reactor()
113
reactor.call_on("foobar", handle_foobar)
114
reactor.fire("foobar")
115
self.assertEquals(called, [True])
117
def test_event_with_args(self):
118
reactor = self.get_reactor()
120
def handle_foobar(a, b=3):
121
called.append((a, b))
123
reactor.call_on("foobar", handle_foobar)
124
reactor.fire("foobar", "a", b=6)
125
self.assertEquals(called, [("a", 6)])
127
def test_events(self):
128
reactor = self.get_reactor()
131
reactor.call_on("foobar", called.append)
132
reactor.call_on("foobar", called.append)
134
reactor.fire("foobar", "a")
135
self.assertEquals(called, ["a", "a"])
137
def test_events_result(self):
138
reactor = self.get_reactor()
140
generator = iter([1, 2, 3]).next
142
reactor.call_on("foobar", generator)
143
reactor.call_on("foobar", generator)
144
reactor.call_on("foobar", generator)
146
self.assertEquals(reactor.fire("foobar"), [1, 2, 3])
148
def test_event_priority(self):
150
Event callbacks should be able to be scheduled with a priority
151
which specifies the order they are run in.
153
reactor = self.get_reactor()
155
reactor.call_on("foobar", lambda: called.append(5), priority=5)
156
reactor.call_on("foobar", lambda: called.append(3), priority=3)
157
reactor.call_on("foobar", lambda: called.append(4), priority=4)
158
reactor.fire("foobar")
159
self.assertEquals(called, [3, 4, 5])
161
def test_default_priority(self):
163
The default priority of an event callback should be 0.
165
reactor = self.get_reactor()
167
reactor.call_on("foobar", lambda: called.append(1), 1)
168
reactor.call_on("foobar", lambda: called.append(0))
169
reactor.call_on("foobar", lambda: called.append(-1), -1)
170
reactor.fire("foobar")
171
self.assertEquals(called, [-1, 0, 1])
173
def test_exploding_event_handler(self):
174
self.log_helper.ignore_errors(ZeroDivisionError)
175
reactor = self.get_reactor()
177
def handle_one(): called.append(1)
180
def handle_three(): called.append(3)
182
reactor.call_on("foobar", handle_one)
183
reactor.call_on("foobar", handle_two)
184
reactor.call_on("foobar", handle_three)
186
reactor.fire("foobar")
187
self.assertTrue(1 in called)
188
self.assertTrue(3 in called)
189
self.assertTrue("handle_two" in self.logfile.getvalue())
190
self.assertTrue("ZeroDivisionError" in self.logfile.getvalue(),
191
self.logfile.getvalue())
193
def test_weird_event_type(self):
194
#This can be useful for "namespaced" event types
195
reactor = self.get_reactor()
197
reactor.call_on(("message", "foobar"), called.append)
198
reactor.fire(("message", "foobar"), "namespaced!")
199
self.assertEquals(called, ["namespaced!"])
201
def test_nonexistent_event_type(self):
202
reactor = self.get_reactor()
203
reactor.fire("Blat!")
205
def test_cancel_event(self):
206
reactor = self.get_reactor()
208
id = reactor.call_on("foobar", called.append)
209
reactor.cancel_call(id)
210
reactor.fire("foobar")
211
self.assertEquals(called, [])
213
def test_run_stop_events(self):
214
reactor = self.get_reactor()
219
reactor.call_on("run", lambda: called.append("run"))
220
reactor.call_on("stop", lambda: called.append("stop"))
221
reactor.call_later(0.0, lambda: called_copy.extend(called))
222
reactor.call_later(0.5, reactor.stop)
226
self.assertEquals(called, ["run", "stop"])
227
self.assertEquals(called_copy, ["run"])
229
def test_call_in_thread(self):
230
reactor = self.get_reactor()
235
called.append((a, b, c))
236
called.append(thread.get_ident())
238
reactor.call_in_thread(None, None, f, 1, 2, c=3)
243
reactor.call_later(0.7, reactor.stop)
246
self.assertEquals(len(called), 2)
247
self.assertEquals(called[0], (1, 2, 3))
249
if not isinstance(reactor, FakeReactor):
250
self.assertNotEquals(called[1], thread.get_ident())
252
def test_call_in_thread_with_callback(self):
253
reactor = self.get_reactor()
261
def callback(result):
262
called.append("callback")
263
called.append(result)
265
def errback(type, value, traceback):
266
called.append("errback")
267
called.append((type, value, traceback))
269
reactor.call_in_thread(callback, errback, f)
274
reactor.call_later(0.7, reactor.stop)
277
self.assertEquals(called, ["f", "callback", 32])
279
def test_call_in_thread_with_errback(self):
280
reactor = self.get_reactor()
288
def callback(result):
289
called.append("callback")
290
called.append(result)
293
called.append("errback")
296
reactor.call_in_thread(callback, errback, f)
301
reactor.call_later(0.7, reactor.stop)
304
self.assertEquals(called[:2], ["f", "errback"])
305
self.assertEquals(len(called), 3)
306
self.assertEquals(called[2][0], ZeroDivisionError)
307
self.assertTrue(isinstance(called[2][1], ZeroDivisionError))
308
self.assertTrue(isinstance(called[2][2], types.TracebackType))
310
def test_call_in_thread_with_error_but_no_errback(self):
311
self.log_helper.ignore_errors(ZeroDivisionError)
312
reactor = self.get_reactor()
320
def callback(result):
321
called.append("callback")
322
called.append(result)
324
reactor.call_in_thread(callback, None, f)
329
reactor.call_later(0.7, reactor.stop)
332
self.assertEquals(called, ["f"])
333
self.assertTrue("ZeroDivisionError" in self.logfile.getvalue(),
334
self.logfile.getvalue())
336
def test_call_in_main(self):
337
reactor = self.get_reactor()
343
called.append(thread.get_ident())
344
reactor.call_in_main(g, 1, 2, c=3)
348
called.append(thread.get_ident())
350
reactor.call_in_thread(None, None, f)
355
reactor.call_later(0.7, reactor.stop)
358
self.assertEquals(len(called), 4)
359
self.assertEquals(called[0], "f")
360
if not isinstance(reactor, FakeReactor):
361
self.assertNotEquals(called[1], thread.get_ident())
362
self.assertEquals(called[2], "g")
363
self.assertEquals(called[3], thread.get_ident())
366
class FakeReactorTest(ReactorTest):
368
def get_reactor(self):
371
def test_incremental_advance(self):
372
reactor = self.get_reactor()
378
reactor.call_later(2, callback)
380
self.assertFalse(called)
382
self.assertFalse(called)
384
self.assertTrue(called)
388
The time method of FakeReactor should return the current
391
reactor = self.get_reactor()
392
self.assertEquals(reactor.time(), 0)
393
reactor.advance(10.5)
394
self.assertEquals(reactor.time(), 10.5)
396
self.assertEquals(reactor.time(), 13.5)
399
class TwistedReactorTest(ReactorTest):
401
def get_reactor(self):
402
return TwistedReactor()
404
def test_real_time(self):
405
reactor = self.get_reactor()
406
self.assertTrue(reactor.time() - time.time() < 3)
409
# FIXME This is here because the GObject reactor frequently causes the
410
# test suite to segfault. When it becomes stable this can be removed.