~justin-fathomdb/nova/justinsb-openstack-api-volumes

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/test/test_task.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
        # Start a LoopingCall with a 0.5 second increment, and immediately call
 
203
        # the callable.
 
204
        callDuration = 2
 
205
        call.start(0.5)
 
206
 
 
207
        # Verify that the callable was called, and since it was immediate, with
 
208
        # no skips.
 
209
        self.assertEqual(times, [0])
 
210
 
 
211
        # The callback should have advanced the clock by the callDuration.
 
212
        self.assertEqual(clock.seconds(), callDuration)
 
213
 
 
214
        # An iteration should have occurred at 2, but since 2 is the present
 
215
        # and not the future, it is skipped.
 
216
 
 
217
        clock.advance(0)
 
218
        self.assertEqual(times, [0])
 
219
 
 
220
        # 2.5 is in the future, and is not skipped.
 
221
        callDuration = 1
 
222
        clock.advance(0.5)
 
223
        self.assertEqual(times, [0, 2.5])
 
224
        self.assertEqual(clock.seconds(), 3.5)
 
225
 
 
226
        # Another iteration should have occurred, but it is again the
 
227
        # present and not the future, so it is skipped as well.
 
228
        clock.advance(0)
 
229
        self.assertEqual(times, [0, 2.5])
 
230
 
 
231
        # 4 is in the future, and is not skipped.
 
232
        callDuration = 0
 
233
        clock.advance(0.5)
 
234
        self.assertEqual(times, [0, 2.5, 4])
 
235
        self.assertEqual(clock.seconds(), 4)
 
236
 
 
237
 
 
238
    def test_reactorTimeSkips(self):
 
239
        """
 
240
        When more time than the defined interval passes between when
 
241
        L{LoopingCall} schedules itself to run again and when it actually
 
242
        runs again, it should schedule the next call for the next interval
 
243
        which is still in the future.
 
244
        """
 
245
        times = []
 
246
        clock = task.Clock()
 
247
        def aCallback():
 
248
            times.append(clock.seconds())
 
249
 
 
250
        # Start a LoopingCall that tracks the time passed, with a 0.5 second
 
251
        # increment.
 
252
        call = task.LoopingCall(aCallback)
 
253
        call.clock = clock
 
254
        call.start(0.5)
 
255
 
 
256
        # Initially, no time should have passed!
 
257
        self.assertEqual(times, [0])
 
258
 
 
259
        # Advance the clock by 2 seconds (2 seconds should have passed)
 
260
        clock.advance(2)
 
261
        self.assertEqual(times, [0, 2])
 
262
 
 
263
        # Advance the clock by 1 second (3 total should have passed)
 
264
        clock.advance(1)
 
265
        self.assertEqual(times, [0, 2, 3])
 
266
 
 
267
        # Advance the clock by 0 seconds (this should have no effect!)
 
268
        clock.advance(0)
 
269
        self.assertEqual(times, [0, 2, 3])
 
270
 
 
271
 
 
272
    def test_reactorTimeCountSkips(self):
 
273
        """
 
274
        When L{LoopingCall} schedules itself to run again, if more than the
 
275
        specified interval has passed, it should schedule the next call for the
 
276
        next interval which is still in the future. If it was created
 
277
        using L{LoopingCall.withCount}, a positional argument will be
 
278
        inserted at the beginning of the argument list, indicating the number
 
279
        of calls that should have been made. 
 
280
        """
 
281
        times = []
 
282
        clock = task.Clock()
 
283
        def aCallback(numCalls):
 
284
            times.append((clock.seconds(), numCalls))
 
285
 
 
286
        # Start a LoopingCall that tracks the time passed, and the number of
 
287
        # skips, with a 0.5 second increment.
 
288
        call = task.LoopingCall.withCount(aCallback)
 
289
        call.clock = clock
 
290
        INTERVAL = 0.5
 
291
        REALISTIC_DELAY = 0.01
 
292
        call.start(INTERVAL)
 
293
 
 
294
        # Initially, no seconds should have passed, and one calls should have
 
295
        # been made.
 
296
        self.assertEqual(times, [(0, 1)])
 
297
 
 
298
        # After the interval (plus a small delay, to account for the time that
 
299
        # the reactor takes to wake up and process the LoopingCall), we should
 
300
        # still have only made one call.
 
301
        clock.advance(INTERVAL + REALISTIC_DELAY)
 
302
        self.assertEqual(times, [(0, 1), (INTERVAL + REALISTIC_DELAY, 1)])
 
303
 
 
304
        # After advancing the clock by three intervals (plus a small delay to
 
305
        # account for the reactor), we should have skipped two calls; one less
 
306
        # than the number of intervals which have completely elapsed. Along
 
307
        # with the call we did actually make, the final number of calls is 3.
 
308
        clock.advance((3 * INTERVAL) + REALISTIC_DELAY)
 
309
        self.assertEqual(times,
 
310
                         [(0, 1), (INTERVAL + REALISTIC_DELAY, 1),
 
311
                          ((4 * INTERVAL) + (2 * REALISTIC_DELAY), 3)])
 
312
 
 
313
        # Advancing the clock by 0 seconds should not cause any changes!
 
314
        clock.advance(0)
 
315
        self.assertEqual(times,
 
316
                         [(0, 1), (INTERVAL + REALISTIC_DELAY, 1),
 
317
                          ((4 * INTERVAL) + (2 * REALISTIC_DELAY), 3)])
 
318
 
 
319
 
 
320
    def test_countLengthyIntervalCounts(self):
 
321
        """
 
322
        L{LoopingCall.withCount} counts only calls that were expected to be
 
323
        made.  So, if more than one, but less than two intervals pass between
 
324
        invocations, it won't increase the count above 1.  For example, a
 
325
        L{LoopingCall} with interval T expects to be invoked at T, 2T, 3T, etc.
 
326
        However, the reactor takes some time to get around to calling it, so in
 
327
        practice it will be called at T+something, 2T+something, 3T+something;
 
328
        and due to other things going on in the reactor, "something" is
 
329
        variable.  It won't increase the count unless "something" is greater
 
330
        than T.  So if the L{LoopingCall} is invoked at T, 2.75T, and 3T,
 
331
        the count has not increased, even though the distance between
 
332
        invocation 1 and invocation 2 is 1.75T.
 
333
        """
 
334
        times = []
 
335
        clock = task.Clock()
 
336
        def aCallback(count):
 
337
            times.append((clock.seconds(), count))
 
338
 
 
339
        # Start a LoopingCall that tracks the time passed, and the number of
 
340
        # calls, with a 0.5 second increment.
 
341
        call = task.LoopingCall.withCount(aCallback)
 
342
        call.clock = clock
 
343
        INTERVAL = 0.5
 
344
        REALISTIC_DELAY = 0.01
 
345
        call.start(INTERVAL)
 
346
        self.assertEqual(times.pop(), (0, 1))
 
347
 
 
348
        # About one interval... So far, so good
 
349
        clock.advance(INTERVAL + REALISTIC_DELAY)
 
350
        self.assertEqual(times.pop(), (INTERVAL + REALISTIC_DELAY, 1))
 
351
 
 
352
        # Oh no, something delayed us for a while.
 
353
        clock.advance(INTERVAL * 1.75)
 
354
        self.assertEqual(times.pop(), ((2.75 * INTERVAL) + REALISTIC_DELAY, 1))
 
355
 
 
356
        # Back on track!  We got invoked when we expected this time.
 
357
        clock.advance(INTERVAL * 0.25)
 
358
        self.assertEqual(times.pop(), ((3.0 * INTERVAL) + REALISTIC_DELAY, 1))
 
359
 
 
360
 
 
361
    def testBasicFunction(self):
 
362
        # Arrange to have time advanced enough so that our function is
 
363
        # called a few times.
 
364
        # Only need to go to 2.5 to get 3 calls, since the first call
 
365
        # happens before any time has elapsed.
 
366
        timings = [0.05, 0.1, 0.1]
 
367
 
 
368
        clock = task.Clock()
 
369
 
 
370
        L = []
 
371
        def foo(a, b, c=None, d=None):
 
372
            L.append((a, b, c, d))
 
373
 
 
374
        lc = TestableLoopingCall(clock, foo, "a", "b", d="d")
 
375
        D = lc.start(0.1)
 
376
 
 
377
        theResult = []
 
378
        def saveResult(result):
 
379
            theResult.append(result)
 
380
        D.addCallback(saveResult)
 
381
 
 
382
        clock.pump(timings)
 
383
 
 
384
        self.assertEquals(len(L), 3,
 
385
                          "got %d iterations, not 3" % (len(L),))
 
386
 
 
387
        for (a, b, c, d) in L:
 
388
            self.assertEquals(a, "a")
 
389
            self.assertEquals(b, "b")
 
390
            self.assertEquals(c, None)
 
391
            self.assertEquals(d, "d")
 
392
 
 
393
        lc.stop()
 
394
        self.assertIdentical(theResult[0], lc)
 
395
 
 
396
        # Make sure it isn't planning to do anything further.
 
397
        self.failIf(clock.calls)
 
398
 
 
399
 
 
400
    def testDelayedStart(self):
 
401
        timings = [0.05, 0.1, 0.1]
 
402
 
 
403
        clock = task.Clock()
 
404
 
 
405
        L = []
 
406
        lc = TestableLoopingCall(clock, L.append, None)
 
407
        d = lc.start(0.1, now=False)
 
408
 
 
409
        theResult = []
 
410
        def saveResult(result):
 
411
            theResult.append(result)
 
412
        d.addCallback(saveResult)
 
413
 
 
414
        clock.pump(timings)
 
415
 
 
416
        self.assertEquals(len(L), 2,
 
417
                          "got %d iterations, not 2" % (len(L),))
 
418
        lc.stop()
 
419
        self.assertIdentical(theResult[0], lc)
 
420
 
 
421
        self.failIf(clock.calls)
 
422
 
 
423
 
 
424
    def testBadDelay(self):
 
425
        lc = task.LoopingCall(lambda: None)
 
426
        self.assertRaises(ValueError, lc.start, -1)
 
427
 
 
428
 
 
429
    # Make sure that LoopingCall.stop() prevents any subsequent calls.
 
430
    def _stoppingTest(self, delay):
 
431
        ran = []
 
432
        def foo():
 
433
            ran.append(None)
 
434
 
 
435
        clock = task.Clock()
 
436
        lc = TestableLoopingCall(clock, foo)
 
437
        d = lc.start(delay, now=False)
 
438
        lc.stop()
 
439
        self.failIf(ran)
 
440
        self.failIf(clock.calls)
 
441
 
 
442
 
 
443
    def testStopAtOnce(self):
 
444
        return self._stoppingTest(0)
 
445
 
 
446
 
 
447
    def testStoppingBeforeDelayedStart(self):
 
448
        return self._stoppingTest(10)
 
449
 
 
450
 
 
451
 
 
452
class ReactorLoopTestCase(unittest.TestCase):
 
453
    # Slightly inferior tests which exercise interactions with an actual
 
454
    # reactor.
 
455
    def testFailure(self):
 
456
        def foo(x):
 
457
            raise TestException(x)
 
458
 
 
459
        lc = task.LoopingCall(foo, "bar")
 
460
        return self.assertFailure(lc.start(0.1), TestException)
 
461
 
 
462
 
 
463
    def testFailAndStop(self):
 
464
        def foo(x):
 
465
            lc.stop()
 
466
            raise TestException(x)
 
467
 
 
468
        lc = task.LoopingCall(foo, "bar")
 
469
        return self.assertFailure(lc.start(0.1), TestException)
 
470
 
 
471
 
 
472
    def testEveryIteration(self):
 
473
        ran = []
 
474
 
 
475
        def foo():
 
476
            ran.append(None)
 
477
            if len(ran) > 5:
 
478
                lc.stop()
 
479
 
 
480
        lc = task.LoopingCall(foo)
 
481
        d = lc.start(0)
 
482
        def stopped(ign):
 
483
            self.assertEquals(len(ran), 6)
 
484
        return d.addCallback(stopped)
 
485
 
 
486
 
 
487
    def testStopAtOnceLater(self):
 
488
        # Ensure that even when LoopingCall.stop() is called from a
 
489
        # reactor callback, it still prevents any subsequent calls.
 
490
        d = defer.Deferred()
 
491
        def foo():
 
492
            d.errback(failure.DefaultException(
 
493
                "This task also should never get called."))
 
494
        self._lc = task.LoopingCall(foo)
 
495
        self._lc.start(1, now=False)
 
496
        reactor.callLater(0, self._callback_for_testStopAtOnceLater, d)
 
497
        return d
 
498
 
 
499
 
 
500
    def _callback_for_testStopAtOnceLater(self, d):
 
501
        self._lc.stop()
 
502
        reactor.callLater(0, d.callback, "success")
 
503
 
 
504
    def testWaitDeferred(self):
 
505
        # Tests if the callable isn't scheduled again before the returned
 
506
        # deferred has fired.
 
507
        timings = [0.2, 0.8]
 
508
        clock = task.Clock()
 
509
 
 
510
        def foo():
 
511
            d = defer.Deferred()
 
512
            d.addCallback(lambda _: lc.stop())
 
513
            clock.callLater(1, d.callback, None)
 
514
            return d
 
515
 
 
516
        lc = TestableLoopingCall(clock, foo)
 
517
        d = lc.start(0.2)
 
518
        clock.pump(timings)
 
519
        self.failIf(clock.calls)
 
520
 
 
521
    def testFailurePropagation(self):
 
522
        # Tests if the failure of the errback of the deferred returned by the
 
523
        # callable is propagated to the lc errback.
 
524
        #
 
525
        # To make sure this test does not hang trial when LoopingCall does not
 
526
        # wait for the callable's deferred, it also checks there are no
 
527
        # calls in the clock's callLater queue.
 
528
        timings = [0.3]
 
529
        clock = task.Clock()
 
530
 
 
531
        def foo():
 
532
            d = defer.Deferred()
 
533
            clock.callLater(0.3, d.errback, TestException())
 
534
            return d
 
535
 
 
536
        lc = TestableLoopingCall(clock, foo)
 
537
        d = lc.start(1)
 
538
        self.assertFailure(d, TestException)
 
539
 
 
540
        clock.pump(timings)
 
541
        self.failIf(clock.calls)
 
542
        return d
 
543
 
 
544
 
 
545
    def test_deferredWithCount(self):
 
546
        """
 
547
        In the case that the function passed to L{LoopingCall.withCount}
 
548
        returns a deferred, which does not fire before the next interval
 
549
        elapses, the function should not be run again. And if a function call
 
550
        is skipped in this fashion, the appropriate count should be
 
551
        provided.
 
552
        """
 
553
        testClock = task.Clock()
 
554
        d = defer.Deferred()
 
555
        deferredCounts = []
 
556
 
 
557
        def countTracker(possibleCount):
 
558
            # Keep a list of call counts
 
559
            deferredCounts.append(possibleCount)
 
560
            # Return a deferred, but only on the first request
 
561
            if len(deferredCounts) == 1:
 
562
                return d
 
563
            else:
 
564
                return None
 
565
 
 
566
        # Start a looping call for our countTracker function
 
567
        # Set the increment to 0.2, and do not call the function on startup.
 
568
        lc = task.LoopingCall.withCount(countTracker)
 
569
        lc.clock = testClock
 
570
        d = lc.start(0.2, now=False)
 
571
 
 
572
        # Confirm that nothing has happened yet.
 
573
        self.assertEquals(deferredCounts, [])
 
574
 
 
575
        # Advance the clock by 0.2 and then 0.4;
 
576
        testClock.pump([0.2, 0.4])
 
577
        # We should now have exactly one count (of 1 call)
 
578
        self.assertEquals(len(deferredCounts), 1)
 
579
 
 
580
        # Fire the deferred, and advance the clock by another 0.2
 
581
        d.callback(None)
 
582
        testClock.pump([0.2])
 
583
        # We should now have exactly 2 counts...
 
584
        self.assertEquals(len(deferredCounts), 2)
 
585
        # The first count should be 1 (one call)
 
586
        # The second count should be 3 (calls were missed at about 0.6 and 0.8)
 
587
        self.assertEquals(deferredCounts, [1, 3])
 
588
 
 
589
 
 
590
 
 
591
class DeferLaterTests(unittest.TestCase):
 
592
    """
 
593
    Tests for L{task.deferLater}.
 
594
    """
 
595
    def test_callback(self):
 
596
        """
 
597
        The L{Deferred} returned by L{task.deferLater} is called back after
 
598
        the specified delay with the result of the function passed in.
 
599
        """
 
600
        results = []
 
601
        flag = object()
 
602
        def callable(foo, bar):
 
603
            results.append((foo, bar))
 
604
            return flag
 
605
 
 
606
        clock = task.Clock()
 
607
        d = task.deferLater(clock, 3, callable, 'foo', bar='bar')
 
608
        d.addCallback(self.assertIdentical, flag)
 
609
        clock.advance(2)
 
610
        self.assertEqual(results, [])
 
611
        clock.advance(1)
 
612
        self.assertEqual(results, [('foo', 'bar')])
 
613
        return d
 
614
 
 
615
 
 
616
    def test_errback(self):
 
617
        """
 
618
        The L{Deferred} returned by L{task.deferLater} is errbacked if the
 
619
        supplied function raises an exception.
 
620
        """
 
621
        def callable():
 
622
            raise TestException()
 
623
 
 
624
        clock = task.Clock()
 
625
        d = task.deferLater(clock, 1, callable)
 
626
        clock.advance(1)
 
627
        return self.assertFailure(d, TestException)