| OLD | NEW |
| (Empty) |
| 1 # Copyright (c) 2001-2007 Twisted Matrix Laboratories. | |
| 2 # See LICENSE for details. | |
| 3 | |
| 4 from twisted.python.compat import set | |
| 5 | |
| 6 from twisted.trial import unittest | |
| 7 | |
| 8 from twisted.internet import interfaces, task, reactor, defer, error | |
| 9 | |
| 10 # Be compatible with any jerks who used our private stuff | |
| 11 Clock = task.Clock | |
| 12 | |
| 13 from twisted.python import failure | |
| 14 | |
| 15 | |
| 16 class TestableLoopingCall(task.LoopingCall): | |
| 17 def __init__(self, clock, *a, **kw): | |
| 18 super(TestableLoopingCall, self).__init__(*a, **kw) | |
| 19 self.clock = clock | |
| 20 | |
| 21 | |
| 22 | |
| 23 class TestException(Exception): | |
| 24 pass | |
| 25 | |
| 26 | |
| 27 | |
| 28 class ClockTestCase(unittest.TestCase): | |
| 29 """ | |
| 30 Test the non-wallclock based clock implementation. | |
| 31 """ | |
| 32 def testSeconds(self): | |
| 33 """ | |
| 34 Test that the L{seconds} method of the fake clock returns fake time. | |
| 35 """ | |
| 36 c = task.Clock() | |
| 37 self.assertEquals(c.seconds(), 0) | |
| 38 | |
| 39 | |
| 40 def testCallLater(self): | |
| 41 """ | |
| 42 Test that calls can be scheduled for later with the fake clock and | |
| 43 hands back an L{IDelayedCall}. | |
| 44 """ | |
| 45 c = task.Clock() | |
| 46 call = c.callLater(1, lambda a, b: None, 1, b=2) | |
| 47 self.failUnless(interfaces.IDelayedCall.providedBy(call)) | |
| 48 self.assertEquals(call.getTime(), 1) | |
| 49 self.failUnless(call.active()) | |
| 50 | |
| 51 | |
| 52 def testCallLaterCancelled(self): | |
| 53 """ | |
| 54 Test that calls can be cancelled. | |
| 55 """ | |
| 56 c = task.Clock() | |
| 57 call = c.callLater(1, lambda a, b: None, 1, b=2) | |
| 58 call.cancel() | |
| 59 self.failIf(call.active()) | |
| 60 | |
| 61 | |
| 62 def test_callLaterOrdering(self): | |
| 63 """ | |
| 64 Test that the DelayedCall returned is not one previously | |
| 65 created. | |
| 66 """ | |
| 67 c = task.Clock() | |
| 68 call1 = c.callLater(10, lambda a, b: None, 1, b=2) | |
| 69 call2 = c.callLater(1, lambda a, b: None, 3, b=4) | |
| 70 self.failIf(call1 is call2) | |
| 71 | |
| 72 | |
| 73 def testAdvance(self): | |
| 74 """ | |
| 75 Test that advancing the clock will fire some calls. | |
| 76 """ | |
| 77 events = [] | |
| 78 c = task.Clock() | |
| 79 call = c.callLater(2, lambda: events.append(None)) | |
| 80 c.advance(1) | |
| 81 self.assertEquals(events, []) | |
| 82 c.advance(1) | |
| 83 self.assertEquals(events, [None]) | |
| 84 self.failIf(call.active()) | |
| 85 | |
| 86 | |
| 87 def testAdvanceCancel(self): | |
| 88 """ | |
| 89 Test attemping to cancel the call in a callback. | |
| 90 | |
| 91 AlreadyCalled should be raised, not for example a ValueError from | |
| 92 removing the call from Clock.calls. This requires call.called to be | |
| 93 set before the callback is called. | |
| 94 """ | |
| 95 c = task.Clock() | |
| 96 def cb(): | |
| 97 self.assertRaises(error.AlreadyCalled, call.cancel) | |
| 98 call = c.callLater(1, cb) | |
| 99 c.advance(1) | |
| 100 | |
| 101 | |
| 102 def testCallLaterDelayed(self): | |
| 103 """ | |
| 104 Test that calls can be delayed. | |
| 105 """ | |
| 106 events = [] | |
| 107 c = task.Clock() | |
| 108 call = c.callLater(1, lambda a, b: events.append((a, b)), 1, b=2) | |
| 109 call.delay(1) | |
| 110 self.assertEquals(call.getTime(), 2) | |
| 111 c.advance(1.5) | |
| 112 self.assertEquals(events, []) | |
| 113 c.advance(1.0) | |
| 114 self.assertEquals(events, [(1, 2)]) | |
| 115 | |
| 116 | |
| 117 def testCallLaterResetLater(self): | |
| 118 """ | |
| 119 Test that calls can have their time reset to a later time. | |
| 120 """ | |
| 121 events = [] | |
| 122 c = task.Clock() | |
| 123 call = c.callLater(2, lambda a, b: events.append((a, b)), 1, b=2) | |
| 124 c.advance(1) | |
| 125 call.reset(3) | |
| 126 self.assertEquals(call.getTime(), 4) | |
| 127 c.advance(2) | |
| 128 self.assertEquals(events, []) | |
| 129 c.advance(1) | |
| 130 self.assertEquals(events, [(1, 2)]) | |
| 131 | |
| 132 | |
| 133 def testCallLaterResetSooner(self): | |
| 134 """ | |
| 135 Test that calls can have their time reset to an earlier time. | |
| 136 """ | |
| 137 events = [] | |
| 138 c = task.Clock() | |
| 139 call = c.callLater(4, lambda a, b: events.append((a, b)), 1, b=2) | |
| 140 call.reset(3) | |
| 141 self.assertEquals(call.getTime(), 3) | |
| 142 c.advance(3) | |
| 143 self.assertEquals(events, [(1, 2)]) | |
| 144 | |
| 145 | |
| 146 def test_getDelayedCalls(self): | |
| 147 """ | |
| 148 Test that we can get a list of all delayed calls | |
| 149 """ | |
| 150 c = task.Clock() | |
| 151 call = c.callLater(1, lambda x: None) | |
| 152 call2 = c.callLater(2, lambda x: None) | |
| 153 | |
| 154 calls = c.getDelayedCalls() | |
| 155 | |
| 156 self.assertEquals(set([call, call2]), set(calls)) | |
| 157 | |
| 158 | |
| 159 def test_getDelayedCallsEmpty(self): | |
| 160 """ | |
| 161 Test that we get an empty list from getDelayedCalls on a newly | |
| 162 constructed Clock. | |
| 163 """ | |
| 164 c = task.Clock() | |
| 165 self.assertEquals(c.getDelayedCalls(), []) | |
| 166 | |
| 167 | |
| 168 def test_providesIReactorTime(self): | |
| 169 c = task.Clock() | |
| 170 self.failUnless(interfaces.IReactorTime.providedBy(c), | |
| 171 "Clock does not provide IReactorTime") | |
| 172 | |
| 173 | |
| 174 class LoopTestCase(unittest.TestCase): | |
| 175 """ | |
| 176 Tests for L{task.LoopingCall} based on a fake L{IReactorTime} | |
| 177 implementation. | |
| 178 """ | |
| 179 def test_defaultClock(self): | |
| 180 """ | |
| 181 L{LoopingCall}'s default clock should be the reactor. | |
| 182 """ | |
| 183 call = task.LoopingCall(lambda: None) | |
| 184 self.assertEqual(call.clock, reactor) | |
| 185 | |
| 186 | |
| 187 def test_callbackTimeSkips(self): | |
| 188 """ | |
| 189 When more time than the defined interval passes during the execution | |
| 190 of a callback, L{LoopingCall} should schedule the next call for the | |
| 191 next interval which is still in the future. | |
| 192 """ | |
| 193 times = [] | |
| 194 callDuration = None | |
| 195 clock = task.Clock() | |
| 196 def aCallback(): | |
| 197 times.append(clock.seconds()) | |
| 198 clock.advance(callDuration) | |
| 199 call = task.LoopingCall(aCallback) | |
| 200 call.clock = clock | |
| 201 | |
| 202 callDuration = 2 | |
| 203 call.start(0.5) | |
| 204 self.assertEqual(times, [0]) | |
| 205 self.assertEqual(clock.seconds(), 2) | |
| 206 | |
| 207 # An iteration should have occurred at 2, but since 2 is the present | |
| 208 # and not the future, it is skipped. | |
| 209 clock.advance(0) | |
| 210 self.assertEqual(times, [0]) | |
| 211 | |
| 212 # 2.5 is in the future, and is not skipped. | |
| 213 callDuration = 1 | |
| 214 clock.advance(0.5) | |
| 215 self.assertEqual(times, [0, 2.5]) | |
| 216 self.assertEqual(clock.seconds(), 3.5) | |
| 217 | |
| 218 # Another iteration should have occurred, but it is again the | |
| 219 # present and not the future, so it is skipped as well. | |
| 220 clock.advance(0) | |
| 221 self.assertEqual(times, [0, 2.5]) | |
| 222 | |
| 223 # 4 is in the future, and is not skipped. | |
| 224 callDuration = 0 | |
| 225 clock.advance(0.5) | |
| 226 self.assertEqual(times, [0, 2.5, 4]) | |
| 227 self.assertEqual(clock.seconds(), 4) | |
| 228 | |
| 229 | |
| 230 def test_reactorTimeSkips(self): | |
| 231 """ | |
| 232 When more time than the defined interval passes between when | |
| 233 L{LoopingCall} schedules itself to run again and when it actually | |
| 234 runs again, it should schedule the next call for the next interval | |
| 235 which is still in the future. | |
| 236 """ | |
| 237 times = [] | |
| 238 clock = task.Clock() | |
| 239 def aCallback(): | |
| 240 times.append(clock.seconds()) | |
| 241 | |
| 242 call = task.LoopingCall(aCallback) | |
| 243 call.clock = clock | |
| 244 | |
| 245 call.start(0.5) | |
| 246 self.assertEqual(times, [0]) | |
| 247 | |
| 248 clock.advance(2) | |
| 249 self.assertEqual(times, [0, 2]) | |
| 250 | |
| 251 clock.advance(1) | |
| 252 self.assertEqual(times, [0, 2, 3]) | |
| 253 | |
| 254 clock.advance(0) | |
| 255 self.assertEqual(times, [0, 2, 3]) | |
| 256 | |
| 257 | |
| 258 def testBasicFunction(self): | |
| 259 # Arrange to have time advanced enough so that our function is | |
| 260 # called a few times. | |
| 261 # Only need to go to 2.5 to get 3 calls, since the first call | |
| 262 # happens before any time has elapsed. | |
| 263 timings = [0.05, 0.1, 0.1] | |
| 264 | |
| 265 clock = task.Clock() | |
| 266 | |
| 267 L = [] | |
| 268 def foo(a, b, c=None, d=None): | |
| 269 L.append((a, b, c, d)) | |
| 270 | |
| 271 lc = TestableLoopingCall(clock, foo, "a", "b", d="d") | |
| 272 D = lc.start(0.1) | |
| 273 | |
| 274 theResult = [] | |
| 275 def saveResult(result): | |
| 276 theResult.append(result) | |
| 277 D.addCallback(saveResult) | |
| 278 | |
| 279 clock.pump(timings) | |
| 280 | |
| 281 self.assertEquals(len(L), 3, | |
| 282 "got %d iterations, not 3" % (len(L),)) | |
| 283 | |
| 284 for (a, b, c, d) in L: | |
| 285 self.assertEquals(a, "a") | |
| 286 self.assertEquals(b, "b") | |
| 287 self.assertEquals(c, None) | |
| 288 self.assertEquals(d, "d") | |
| 289 | |
| 290 lc.stop() | |
| 291 self.assertIdentical(theResult[0], lc) | |
| 292 | |
| 293 # Make sure it isn't planning to do anything further. | |
| 294 self.failIf(clock.calls) | |
| 295 | |
| 296 | |
| 297 def testDelayedStart(self): | |
| 298 timings = [0.05, 0.1, 0.1] | |
| 299 | |
| 300 clock = task.Clock() | |
| 301 | |
| 302 L = [] | |
| 303 lc = TestableLoopingCall(clock, L.append, None) | |
| 304 d = lc.start(0.1, now=False) | |
| 305 | |
| 306 theResult = [] | |
| 307 def saveResult(result): | |
| 308 theResult.append(result) | |
| 309 d.addCallback(saveResult) | |
| 310 | |
| 311 clock.pump(timings) | |
| 312 | |
| 313 self.assertEquals(len(L), 2, | |
| 314 "got %d iterations, not 2" % (len(L),)) | |
| 315 lc.stop() | |
| 316 self.assertIdentical(theResult[0], lc) | |
| 317 | |
| 318 self.failIf(clock.calls) | |
| 319 | |
| 320 | |
| 321 def testBadDelay(self): | |
| 322 lc = task.LoopingCall(lambda: None) | |
| 323 self.assertRaises(ValueError, lc.start, -1) | |
| 324 | |
| 325 | |
| 326 # Make sure that LoopingCall.stop() prevents any subsequent calls. | |
| 327 def _stoppingTest(self, delay): | |
| 328 ran = [] | |
| 329 def foo(): | |
| 330 ran.append(None) | |
| 331 | |
| 332 clock = task.Clock() | |
| 333 lc = TestableLoopingCall(clock, foo) | |
| 334 d = lc.start(delay, now=False) | |
| 335 lc.stop() | |
| 336 self.failIf(ran) | |
| 337 self.failIf(clock.calls) | |
| 338 | |
| 339 | |
| 340 def testStopAtOnce(self): | |
| 341 return self._stoppingTest(0) | |
| 342 | |
| 343 | |
| 344 def testStoppingBeforeDelayedStart(self): | |
| 345 return self._stoppingTest(10) | |
| 346 | |
| 347 | |
| 348 | |
| 349 class ReactorLoopTestCase(unittest.TestCase): | |
| 350 # Slightly inferior tests which exercise interactions with an actual | |
| 351 # reactor. | |
| 352 def testFailure(self): | |
| 353 def foo(x): | |
| 354 raise TestException(x) | |
| 355 | |
| 356 lc = task.LoopingCall(foo, "bar") | |
| 357 return self.assertFailure(lc.start(0.1), TestException) | |
| 358 | |
| 359 | |
| 360 def testFailAndStop(self): | |
| 361 def foo(x): | |
| 362 lc.stop() | |
| 363 raise TestException(x) | |
| 364 | |
| 365 lc = task.LoopingCall(foo, "bar") | |
| 366 return self.assertFailure(lc.start(0.1), TestException) | |
| 367 | |
| 368 | |
| 369 def testEveryIteration(self): | |
| 370 ran = [] | |
| 371 | |
| 372 def foo(): | |
| 373 ran.append(None) | |
| 374 if len(ran) > 5: | |
| 375 lc.stop() | |
| 376 | |
| 377 lc = task.LoopingCall(foo) | |
| 378 d = lc.start(0) | |
| 379 def stopped(ign): | |
| 380 self.assertEquals(len(ran), 6) | |
| 381 return d.addCallback(stopped) | |
| 382 | |
| 383 | |
| 384 def testStopAtOnceLater(self): | |
| 385 # Ensure that even when LoopingCall.stop() is called from a | |
| 386 # reactor callback, it still prevents any subsequent calls. | |
| 387 d = defer.Deferred() | |
| 388 def foo(): | |
| 389 d.errback(failure.DefaultException( | |
| 390 "This task also should never get called.")) | |
| 391 self._lc = task.LoopingCall(foo) | |
| 392 self._lc.start(1, now=False) | |
| 393 reactor.callLater(0, self._callback_for_testStopAtOnceLater, d) | |
| 394 return d | |
| 395 | |
| 396 | |
| 397 def _callback_for_testStopAtOnceLater(self, d): | |
| 398 self._lc.stop() | |
| 399 reactor.callLater(0, d.callback, "success") | |
| 400 | |
| 401 def testWaitDeferred(self): | |
| 402 # Tests if the callable isn't scheduled again before the returned | |
| 403 # deferred has fired. | |
| 404 timings = [0.2, 0.8] | |
| 405 clock = task.Clock() | |
| 406 | |
| 407 def foo(): | |
| 408 d = defer.Deferred() | |
| 409 d.addCallback(lambda _: lc.stop()) | |
| 410 clock.callLater(1, d.callback, None) | |
| 411 return d | |
| 412 | |
| 413 lc = TestableLoopingCall(clock, foo) | |
| 414 d = lc.start(0.2) | |
| 415 clock.pump(timings) | |
| 416 self.failIf(clock.calls) | |
| 417 | |
| 418 def testFailurePropagation(self): | |
| 419 # Tests if the failure of the errback of the deferred returned by the | |
| 420 # callable is propagated to the lc errback. | |
| 421 # | |
| 422 # To make sure this test does not hang trial when LoopingCall does not | |
| 423 # wait for the callable's deferred, it also checks there are no | |
| 424 # calls in the clock's callLater queue. | |
| 425 timings = [0.3] | |
| 426 clock = task.Clock() | |
| 427 | |
| 428 def foo(): | |
| 429 d = defer.Deferred() | |
| 430 clock.callLater(0.3, d.errback, TestException()) | |
| 431 return d | |
| 432 | |
| 433 lc = TestableLoopingCall(clock, foo) | |
| 434 d = lc.start(1) | |
| 435 self.assertFailure(d, TestException) | |
| 436 | |
| 437 clock.pump(timings) | |
| 438 self.failIf(clock.calls) | |
| 439 return d | |
| 440 | |
| 441 | |
| 442 | |
| 443 class DeferLaterTests(unittest.TestCase): | |
| 444 """ | |
| 445 Tests for L{task.deferLater}. | |
| 446 """ | |
| 447 def test_callback(self): | |
| 448 """ | |
| 449 The L{Deferred} returned by L{task.deferLater} is called back after | |
| 450 the specified delay with the result of the function passed in. | |
| 451 """ | |
| 452 results = [] | |
| 453 flag = object() | |
| 454 def callable(foo, bar): | |
| 455 results.append((foo, bar)) | |
| 456 return flag | |
| 457 | |
| 458 clock = task.Clock() | |
| 459 d = task.deferLater(clock, 3, callable, 'foo', bar='bar') | |
| 460 d.addCallback(self.assertIdentical, flag) | |
| 461 clock.advance(2) | |
| 462 self.assertEqual(results, []) | |
| 463 clock.advance(1) | |
| 464 self.assertEqual(results, [('foo', 'bar')]) | |
| 465 return d | |
| 466 | |
| 467 | |
| 468 def test_errback(self): | |
| 469 """ | |
| 470 The L{Deferred} returned by L{task.deferLater} is errbacked if the | |
| 471 supplied function raises an exception. | |
| 472 """ | |
| 473 def callable(): | |
| 474 raise TestException() | |
| 475 | |
| 476 clock = task.Clock() | |
| 477 d = task.deferLater(clock, 1, callable) | |
| 478 clock.advance(1) | |
| 479 return self.assertFailure(d, TestException) | |
| OLD | NEW |