• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""Tests for futures.py."""
2
3import concurrent.futures
4import gc
5import re
6import sys
7import threading
8import unittest
9from unittest import mock
10
11import asyncio
12from asyncio import futures
13from test.test_asyncio import utils as test_utils
14from test import support
15
16
17def _fakefunc(f):
18    return f
19
20
21def first_cb():
22    pass
23
24
25def last_cb():
26    pass
27
28
29class DuckFuture:
30    # Class that does not inherit from Future but aims to be duck-type
31    # compatible with it.
32
33    _asyncio_future_blocking = False
34    __cancelled = False
35    __result = None
36    __exception = None
37
38    def cancel(self):
39        if self.done():
40            return False
41        self.__cancelled = True
42        return True
43
44    def cancelled(self):
45        return self.__cancelled
46
47    def done(self):
48        return (self.__cancelled
49                or self.__result is not None
50                or self.__exception is not None)
51
52    def result(self):
53        assert not self.cancelled()
54        if self.__exception is not None:
55            raise self.__exception
56        return self.__result
57
58    def exception(self):
59        assert not self.cancelled()
60        return self.__exception
61
62    def set_result(self, result):
63        assert not self.done()
64        assert result is not None
65        self.__result = result
66
67    def set_exception(self, exception):
68        assert not self.done()
69        assert exception is not None
70        self.__exception = exception
71
72    def __iter__(self):
73        if not self.done():
74            self._asyncio_future_blocking = True
75            yield self
76        assert self.done()
77        return self.result()
78
79
80class DuckTests(test_utils.TestCase):
81
82    def setUp(self):
83        super().setUp()
84        self.loop = self.new_test_loop()
85        self.addCleanup(self.loop.close)
86
87    def test_wrap_future(self):
88        f = DuckFuture()
89        g = asyncio.wrap_future(f)
90        assert g is f
91
92    def test_ensure_future(self):
93        f = DuckFuture()
94        g = asyncio.ensure_future(f)
95        assert g is f
96
97
98class BaseFutureTests:
99
100    def _new_future(self,  *args, **kwargs):
101        return self.cls(*args, **kwargs)
102
103    def setUp(self):
104        super().setUp()
105        self.loop = self.new_test_loop()
106        self.addCleanup(self.loop.close)
107
108    def test_isfuture(self):
109        class MyFuture:
110            _asyncio_future_blocking = None
111
112            def __init__(self):
113                self._asyncio_future_blocking = False
114
115        self.assertFalse(asyncio.isfuture(MyFuture))
116        self.assertTrue(asyncio.isfuture(MyFuture()))
117        self.assertFalse(asyncio.isfuture(1))
118
119        # As `isinstance(Mock(), Future)` returns `False`
120        self.assertFalse(asyncio.isfuture(mock.Mock()))
121
122        f = self._new_future(loop=self.loop)
123        self.assertTrue(asyncio.isfuture(f))
124        self.assertFalse(asyncio.isfuture(type(f)))
125
126        # As `isinstance(Mock(Future), Future)` returns `True`
127        self.assertTrue(asyncio.isfuture(mock.Mock(type(f))))
128
129        f.cancel()
130
131    def test_initial_state(self):
132        f = self._new_future(loop=self.loop)
133        self.assertFalse(f.cancelled())
134        self.assertFalse(f.done())
135        f.cancel()
136        self.assertTrue(f.cancelled())
137
138    def test_init_constructor_default_loop(self):
139        asyncio.set_event_loop(self.loop)
140        f = self._new_future()
141        self.assertIs(f._loop, self.loop)
142        self.assertIs(f.get_loop(), self.loop)
143
144    def test_constructor_positional(self):
145        # Make sure Future doesn't accept a positional argument
146        self.assertRaises(TypeError, self._new_future, 42)
147
148    def test_uninitialized(self):
149        # Test that C Future doesn't crash when Future.__init__()
150        # call was skipped.
151
152        fut = self.cls.__new__(self.cls, loop=self.loop)
153        self.assertRaises(asyncio.InvalidStateError, fut.result)
154
155        fut = self.cls.__new__(self.cls, loop=self.loop)
156        self.assertRaises(asyncio.InvalidStateError, fut.exception)
157
158        fut = self.cls.__new__(self.cls, loop=self.loop)
159        with self.assertRaises((RuntimeError, AttributeError)):
160            fut.set_result(None)
161
162        fut = self.cls.__new__(self.cls, loop=self.loop)
163        with self.assertRaises((RuntimeError, AttributeError)):
164            fut.set_exception(Exception)
165
166        fut = self.cls.__new__(self.cls, loop=self.loop)
167        with self.assertRaises((RuntimeError, AttributeError)):
168            fut.cancel()
169
170        fut = self.cls.__new__(self.cls, loop=self.loop)
171        with self.assertRaises((RuntimeError, AttributeError)):
172            fut.add_done_callback(lambda f: None)
173
174        fut = self.cls.__new__(self.cls, loop=self.loop)
175        with self.assertRaises((RuntimeError, AttributeError)):
176            fut.remove_done_callback(lambda f: None)
177
178        fut = self.cls.__new__(self.cls, loop=self.loop)
179        try:
180            repr(fut)
181        except (RuntimeError, AttributeError):
182            pass
183
184        fut = self.cls.__new__(self.cls, loop=self.loop)
185        try:
186            fut.__await__()
187        except RuntimeError:
188            pass
189
190        fut = self.cls.__new__(self.cls, loop=self.loop)
191        try:
192            iter(fut)
193        except RuntimeError:
194            pass
195
196        fut = self.cls.__new__(self.cls, loop=self.loop)
197        self.assertFalse(fut.cancelled())
198        self.assertFalse(fut.done())
199
200    def test_cancel(self):
201        f = self._new_future(loop=self.loop)
202        self.assertTrue(f.cancel())
203        self.assertTrue(f.cancelled())
204        self.assertTrue(f.done())
205        self.assertRaises(asyncio.CancelledError, f.result)
206        self.assertRaises(asyncio.CancelledError, f.exception)
207        self.assertRaises(asyncio.InvalidStateError, f.set_result, None)
208        self.assertRaises(asyncio.InvalidStateError, f.set_exception, None)
209        self.assertFalse(f.cancel())
210
211    def test_result(self):
212        f = self._new_future(loop=self.loop)
213        self.assertRaises(asyncio.InvalidStateError, f.result)
214
215        f.set_result(42)
216        self.assertFalse(f.cancelled())
217        self.assertTrue(f.done())
218        self.assertEqual(f.result(), 42)
219        self.assertEqual(f.exception(), None)
220        self.assertRaises(asyncio.InvalidStateError, f.set_result, None)
221        self.assertRaises(asyncio.InvalidStateError, f.set_exception, None)
222        self.assertFalse(f.cancel())
223
224    def test_exception(self):
225        exc = RuntimeError()
226        f = self._new_future(loop=self.loop)
227        self.assertRaises(asyncio.InvalidStateError, f.exception)
228
229        # StopIteration cannot be raised into a Future - CPython issue26221
230        self.assertRaisesRegex(TypeError, "StopIteration .* cannot be raised",
231                               f.set_exception, StopIteration)
232
233        f.set_exception(exc)
234        self.assertFalse(f.cancelled())
235        self.assertTrue(f.done())
236        self.assertRaises(RuntimeError, f.result)
237        self.assertEqual(f.exception(), exc)
238        self.assertRaises(asyncio.InvalidStateError, f.set_result, None)
239        self.assertRaises(asyncio.InvalidStateError, f.set_exception, None)
240        self.assertFalse(f.cancel())
241
242    def test_exception_class(self):
243        f = self._new_future(loop=self.loop)
244        f.set_exception(RuntimeError)
245        self.assertIsInstance(f.exception(), RuntimeError)
246
247    def test_yield_from_twice(self):
248        f = self._new_future(loop=self.loop)
249
250        def fixture():
251            yield 'A'
252            x = yield from f
253            yield 'B', x
254            y = yield from f
255            yield 'C', y
256
257        g = fixture()
258        self.assertEqual(next(g), 'A')  # yield 'A'.
259        self.assertEqual(next(g), f)  # First yield from f.
260        f.set_result(42)
261        self.assertEqual(next(g), ('B', 42))  # yield 'B', x.
262        # The second "yield from f" does not yield f.
263        self.assertEqual(next(g), ('C', 42))  # yield 'C', y.
264
265    def test_future_repr(self):
266        self.loop.set_debug(True)
267        f_pending_debug = self._new_future(loop=self.loop)
268        frame = f_pending_debug._source_traceback[-1]
269        self.assertEqual(
270            repr(f_pending_debug),
271            f'<{self.cls.__name__} pending created at {frame[0]}:{frame[1]}>')
272        f_pending_debug.cancel()
273
274        self.loop.set_debug(False)
275        f_pending = self._new_future(loop=self.loop)
276        self.assertEqual(repr(f_pending), f'<{self.cls.__name__} pending>')
277        f_pending.cancel()
278
279        f_cancelled = self._new_future(loop=self.loop)
280        f_cancelled.cancel()
281        self.assertEqual(repr(f_cancelled), f'<{self.cls.__name__} cancelled>')
282
283        f_result = self._new_future(loop=self.loop)
284        f_result.set_result(4)
285        self.assertEqual(
286            repr(f_result), f'<{self.cls.__name__} finished result=4>')
287        self.assertEqual(f_result.result(), 4)
288
289        exc = RuntimeError()
290        f_exception = self._new_future(loop=self.loop)
291        f_exception.set_exception(exc)
292        self.assertEqual(
293            repr(f_exception),
294            f'<{self.cls.__name__} finished exception=RuntimeError()>')
295        self.assertIs(f_exception.exception(), exc)
296
297        def func_repr(func):
298            filename, lineno = test_utils.get_function_source(func)
299            text = '%s() at %s:%s' % (func.__qualname__, filename, lineno)
300            return re.escape(text)
301
302        f_one_callbacks = self._new_future(loop=self.loop)
303        f_one_callbacks.add_done_callback(_fakefunc)
304        fake_repr = func_repr(_fakefunc)
305        self.assertRegex(
306            repr(f_one_callbacks),
307            r'<' + self.cls.__name__ + r' pending cb=\[%s\]>' % fake_repr)
308        f_one_callbacks.cancel()
309        self.assertEqual(repr(f_one_callbacks),
310                         f'<{self.cls.__name__} cancelled>')
311
312        f_two_callbacks = self._new_future(loop=self.loop)
313        f_two_callbacks.add_done_callback(first_cb)
314        f_two_callbacks.add_done_callback(last_cb)
315        first_repr = func_repr(first_cb)
316        last_repr = func_repr(last_cb)
317        self.assertRegex(repr(f_two_callbacks),
318                         r'<' + self.cls.__name__ + r' pending cb=\[%s, %s\]>'
319                         % (first_repr, last_repr))
320
321        f_many_callbacks = self._new_future(loop=self.loop)
322        f_many_callbacks.add_done_callback(first_cb)
323        for i in range(8):
324            f_many_callbacks.add_done_callback(_fakefunc)
325        f_many_callbacks.add_done_callback(last_cb)
326        cb_regex = r'%s, <8 more>, %s' % (first_repr, last_repr)
327        self.assertRegex(
328            repr(f_many_callbacks),
329            r'<' + self.cls.__name__ + r' pending cb=\[%s\]>' % cb_regex)
330        f_many_callbacks.cancel()
331        self.assertEqual(repr(f_many_callbacks),
332                         f'<{self.cls.__name__} cancelled>')
333
334    def test_copy_state(self):
335        from asyncio.futures import _copy_future_state
336
337        f = self._new_future(loop=self.loop)
338        f.set_result(10)
339
340        newf = self._new_future(loop=self.loop)
341        _copy_future_state(f, newf)
342        self.assertTrue(newf.done())
343        self.assertEqual(newf.result(), 10)
344
345        f_exception = self._new_future(loop=self.loop)
346        f_exception.set_exception(RuntimeError())
347
348        newf_exception = self._new_future(loop=self.loop)
349        _copy_future_state(f_exception, newf_exception)
350        self.assertTrue(newf_exception.done())
351        self.assertRaises(RuntimeError, newf_exception.result)
352
353        f_cancelled = self._new_future(loop=self.loop)
354        f_cancelled.cancel()
355
356        newf_cancelled = self._new_future(loop=self.loop)
357        _copy_future_state(f_cancelled, newf_cancelled)
358        self.assertTrue(newf_cancelled.cancelled())
359
360    def test_iter(self):
361        fut = self._new_future(loop=self.loop)
362
363        def coro():
364            yield from fut
365
366        def test():
367            arg1, arg2 = coro()
368
369        with self.assertRaisesRegex(RuntimeError, "await wasn't used"):
370            test()
371        fut.cancel()
372
373    def test_log_traceback(self):
374        fut = self._new_future(loop=self.loop)
375        with self.assertRaisesRegex(ValueError, 'can only be set to False'):
376            fut._log_traceback = True
377
378    @mock.patch('asyncio.base_events.logger')
379    def test_tb_logger_abandoned(self, m_log):
380        fut = self._new_future(loop=self.loop)
381        del fut
382        self.assertFalse(m_log.error.called)
383
384    @mock.patch('asyncio.base_events.logger')
385    def test_tb_logger_not_called_after_cancel(self, m_log):
386        fut = self._new_future(loop=self.loop)
387        fut.set_exception(Exception())
388        fut.cancel()
389        del fut
390        self.assertFalse(m_log.error.called)
391
392    @mock.patch('asyncio.base_events.logger')
393    def test_tb_logger_result_unretrieved(self, m_log):
394        fut = self._new_future(loop=self.loop)
395        fut.set_result(42)
396        del fut
397        self.assertFalse(m_log.error.called)
398
399    @mock.patch('asyncio.base_events.logger')
400    def test_tb_logger_result_retrieved(self, m_log):
401        fut = self._new_future(loop=self.loop)
402        fut.set_result(42)
403        fut.result()
404        del fut
405        self.assertFalse(m_log.error.called)
406
407    @mock.patch('asyncio.base_events.logger')
408    def test_tb_logger_exception_unretrieved(self, m_log):
409        fut = self._new_future(loop=self.loop)
410        fut.set_exception(RuntimeError('boom'))
411        del fut
412        test_utils.run_briefly(self.loop)
413        support.gc_collect()
414        self.assertTrue(m_log.error.called)
415
416    @mock.patch('asyncio.base_events.logger')
417    def test_tb_logger_exception_retrieved(self, m_log):
418        fut = self._new_future(loop=self.loop)
419        fut.set_exception(RuntimeError('boom'))
420        fut.exception()
421        del fut
422        self.assertFalse(m_log.error.called)
423
424    @mock.patch('asyncio.base_events.logger')
425    def test_tb_logger_exception_result_retrieved(self, m_log):
426        fut = self._new_future(loop=self.loop)
427        fut.set_exception(RuntimeError('boom'))
428        self.assertRaises(RuntimeError, fut.result)
429        del fut
430        self.assertFalse(m_log.error.called)
431
432    def test_wrap_future(self):
433
434        def run(arg):
435            return (arg, threading.get_ident())
436        ex = concurrent.futures.ThreadPoolExecutor(1)
437        f1 = ex.submit(run, 'oi')
438        f2 = asyncio.wrap_future(f1, loop=self.loop)
439        res, ident = self.loop.run_until_complete(f2)
440        self.assertTrue(asyncio.isfuture(f2))
441        self.assertEqual(res, 'oi')
442        self.assertNotEqual(ident, threading.get_ident())
443        ex.shutdown(wait=True)
444
445    def test_wrap_future_future(self):
446        f1 = self._new_future(loop=self.loop)
447        f2 = asyncio.wrap_future(f1)
448        self.assertIs(f1, f2)
449
450    def test_wrap_future_use_global_loop(self):
451        with mock.patch('asyncio.futures.events') as events:
452            events.get_event_loop = lambda: self.loop
453            def run(arg):
454                return (arg, threading.get_ident())
455            ex = concurrent.futures.ThreadPoolExecutor(1)
456            f1 = ex.submit(run, 'oi')
457            f2 = asyncio.wrap_future(f1)
458            self.assertIs(self.loop, f2._loop)
459            ex.shutdown(wait=True)
460
461    def test_wrap_future_cancel(self):
462        f1 = concurrent.futures.Future()
463        f2 = asyncio.wrap_future(f1, loop=self.loop)
464        f2.cancel()
465        test_utils.run_briefly(self.loop)
466        self.assertTrue(f1.cancelled())
467        self.assertTrue(f2.cancelled())
468
469    def test_wrap_future_cancel2(self):
470        f1 = concurrent.futures.Future()
471        f2 = asyncio.wrap_future(f1, loop=self.loop)
472        f1.set_result(42)
473        f2.cancel()
474        test_utils.run_briefly(self.loop)
475        self.assertFalse(f1.cancelled())
476        self.assertEqual(f1.result(), 42)
477        self.assertTrue(f2.cancelled())
478
479    def test_future_source_traceback(self):
480        self.loop.set_debug(True)
481
482        future = self._new_future(loop=self.loop)
483        lineno = sys._getframe().f_lineno - 1
484        self.assertIsInstance(future._source_traceback, list)
485        self.assertEqual(future._source_traceback[-2][:3],
486                         (__file__,
487                          lineno,
488                          'test_future_source_traceback'))
489
490    @mock.patch('asyncio.base_events.logger')
491    def check_future_exception_never_retrieved(self, debug, m_log):
492        self.loop.set_debug(debug)
493
494        def memory_error():
495            try:
496                raise MemoryError()
497            except BaseException as exc:
498                return exc
499        exc = memory_error()
500
501        future = self._new_future(loop=self.loop)
502        future.set_exception(exc)
503        future = None
504        test_utils.run_briefly(self.loop)
505        support.gc_collect()
506
507        if sys.version_info >= (3, 4):
508            regex = f'^{self.cls.__name__} exception was never retrieved\n'
509            exc_info = (type(exc), exc, exc.__traceback__)
510            m_log.error.assert_called_once_with(mock.ANY, exc_info=exc_info)
511        else:
512            regex = r'^Future/Task exception was never retrieved\n'
513            m_log.error.assert_called_once_with(mock.ANY, exc_info=False)
514        message = m_log.error.call_args[0][0]
515        self.assertRegex(message, re.compile(regex, re.DOTALL))
516
517    def test_future_exception_never_retrieved(self):
518        self.check_future_exception_never_retrieved(False)
519
520    def test_future_exception_never_retrieved_debug(self):
521        self.check_future_exception_never_retrieved(True)
522
523    def test_set_result_unless_cancelled(self):
524        fut = self._new_future(loop=self.loop)
525        fut.cancel()
526        futures._set_result_unless_cancelled(fut, 2)
527        self.assertTrue(fut.cancelled())
528
529    def test_future_stop_iteration_args(self):
530        fut = self._new_future(loop=self.loop)
531        fut.set_result((1, 2))
532        fi = fut.__iter__()
533        result = None
534        try:
535            fi.send(None)
536        except StopIteration as ex:
537            result = ex.args[0]
538        else:
539            self.fail('StopIteration was expected')
540        self.assertEqual(result, (1, 2))
541
542    def test_future_iter_throw(self):
543        fut = self._new_future(loop=self.loop)
544        fi = iter(fut)
545        self.assertRaises(TypeError, fi.throw,
546                          Exception, Exception("elephant"), 32)
547        self.assertRaises(TypeError, fi.throw,
548                          Exception("elephant"), Exception("elephant"))
549        self.assertRaises(TypeError, fi.throw, list)
550
551    def test_future_del_collect(self):
552        class Evil:
553            def __del__(self):
554                gc.collect()
555
556        for i in range(100):
557            fut = self._new_future(loop=self.loop)
558            fut.set_result(Evil())
559
560
561@unittest.skipUnless(hasattr(futures, '_CFuture'),
562                     'requires the C _asyncio module')
563class CFutureTests(BaseFutureTests, test_utils.TestCase):
564    try:
565        cls = futures._CFuture
566    except AttributeError:
567        cls = None
568
569    def test_future_del_segfault(self):
570        fut = self._new_future(loop=self.loop)
571        with self.assertRaises(AttributeError):
572            del fut._asyncio_future_blocking
573        with self.assertRaises(AttributeError):
574            del fut._log_traceback
575
576
577@unittest.skipUnless(hasattr(futures, '_CFuture'),
578                     'requires the C _asyncio module')
579class CSubFutureTests(BaseFutureTests, test_utils.TestCase):
580    try:
581        class CSubFuture(futures._CFuture):
582            pass
583
584        cls = CSubFuture
585    except AttributeError:
586        cls = None
587
588
589class PyFutureTests(BaseFutureTests, test_utils.TestCase):
590    cls = futures._PyFuture
591
592
593class BaseFutureDoneCallbackTests():
594
595    def setUp(self):
596        super().setUp()
597        self.loop = self.new_test_loop()
598
599    def run_briefly(self):
600        test_utils.run_briefly(self.loop)
601
602    def _make_callback(self, bag, thing):
603        # Create a callback function that appends thing to bag.
604        def bag_appender(future):
605            bag.append(thing)
606        return bag_appender
607
608    def _new_future(self):
609        raise NotImplementedError
610
611    def test_callbacks_remove_first_callback(self):
612        bag = []
613        f = self._new_future()
614
615        cb1 = self._make_callback(bag, 42)
616        cb2 = self._make_callback(bag, 17)
617        cb3 = self._make_callback(bag, 100)
618
619        f.add_done_callback(cb1)
620        f.add_done_callback(cb2)
621        f.add_done_callback(cb3)
622
623        f.remove_done_callback(cb1)
624        f.remove_done_callback(cb1)
625
626        self.assertEqual(bag, [])
627        f.set_result('foo')
628
629        self.run_briefly()
630
631        self.assertEqual(bag, [17, 100])
632        self.assertEqual(f.result(), 'foo')
633
634    def test_callbacks_remove_first_and_second_callback(self):
635        bag = []
636        f = self._new_future()
637
638        cb1 = self._make_callback(bag, 42)
639        cb2 = self._make_callback(bag, 17)
640        cb3 = self._make_callback(bag, 100)
641
642        f.add_done_callback(cb1)
643        f.add_done_callback(cb2)
644        f.add_done_callback(cb3)
645
646        f.remove_done_callback(cb1)
647        f.remove_done_callback(cb2)
648        f.remove_done_callback(cb1)
649
650        self.assertEqual(bag, [])
651        f.set_result('foo')
652
653        self.run_briefly()
654
655        self.assertEqual(bag, [100])
656        self.assertEqual(f.result(), 'foo')
657
658    def test_callbacks_remove_third_callback(self):
659        bag = []
660        f = self._new_future()
661
662        cb1 = self._make_callback(bag, 42)
663        cb2 = self._make_callback(bag, 17)
664        cb3 = self._make_callback(bag, 100)
665
666        f.add_done_callback(cb1)
667        f.add_done_callback(cb2)
668        f.add_done_callback(cb3)
669
670        f.remove_done_callback(cb3)
671        f.remove_done_callback(cb3)
672
673        self.assertEqual(bag, [])
674        f.set_result('foo')
675
676        self.run_briefly()
677
678        self.assertEqual(bag, [42, 17])
679        self.assertEqual(f.result(), 'foo')
680
681    def test_callbacks_invoked_on_set_result(self):
682        bag = []
683        f = self._new_future()
684        f.add_done_callback(self._make_callback(bag, 42))
685        f.add_done_callback(self._make_callback(bag, 17))
686
687        self.assertEqual(bag, [])
688        f.set_result('foo')
689
690        self.run_briefly()
691
692        self.assertEqual(bag, [42, 17])
693        self.assertEqual(f.result(), 'foo')
694
695    def test_callbacks_invoked_on_set_exception(self):
696        bag = []
697        f = self._new_future()
698        f.add_done_callback(self._make_callback(bag, 100))
699
700        self.assertEqual(bag, [])
701        exc = RuntimeError()
702        f.set_exception(exc)
703
704        self.run_briefly()
705
706        self.assertEqual(bag, [100])
707        self.assertEqual(f.exception(), exc)
708
709    def test_remove_done_callback(self):
710        bag = []
711        f = self._new_future()
712        cb1 = self._make_callback(bag, 1)
713        cb2 = self._make_callback(bag, 2)
714        cb3 = self._make_callback(bag, 3)
715
716        # Add one cb1 and one cb2.
717        f.add_done_callback(cb1)
718        f.add_done_callback(cb2)
719
720        # One instance of cb2 removed. Now there's only one cb1.
721        self.assertEqual(f.remove_done_callback(cb2), 1)
722
723        # Never had any cb3 in there.
724        self.assertEqual(f.remove_done_callback(cb3), 0)
725
726        # After this there will be 6 instances of cb1 and one of cb2.
727        f.add_done_callback(cb2)
728        for i in range(5):
729            f.add_done_callback(cb1)
730
731        # Remove all instances of cb1. One cb2 remains.
732        self.assertEqual(f.remove_done_callback(cb1), 6)
733
734        self.assertEqual(bag, [])
735        f.set_result('foo')
736
737        self.run_briefly()
738
739        self.assertEqual(bag, [2])
740        self.assertEqual(f.result(), 'foo')
741
742    def test_remove_done_callbacks_list_mutation(self):
743        # see http://bugs.python.org/issue28963 for details
744
745        fut = self._new_future()
746        fut.add_done_callback(str)
747
748        for _ in range(63):
749            fut.add_done_callback(id)
750
751        class evil:
752            def __eq__(self, other):
753                fut.remove_done_callback(id)
754                return False
755
756        fut.remove_done_callback(evil())
757
758    def test_schedule_callbacks_list_mutation_1(self):
759        # see http://bugs.python.org/issue28963 for details
760
761        def mut(f):
762            f.remove_done_callback(str)
763
764        fut = self._new_future()
765        fut.add_done_callback(mut)
766        fut.add_done_callback(str)
767        fut.add_done_callback(str)
768        fut.set_result(1)
769        test_utils.run_briefly(self.loop)
770
771    def test_schedule_callbacks_list_mutation_2(self):
772        # see http://bugs.python.org/issue30828 for details
773
774        fut = self._new_future()
775        fut.add_done_callback(str)
776
777        for _ in range(63):
778            fut.add_done_callback(id)
779
780        max_extra_cbs = 100
781        extra_cbs = 0
782
783        class evil:
784            def __eq__(self, other):
785                nonlocal extra_cbs
786                extra_cbs += 1
787                if extra_cbs < max_extra_cbs:
788                    fut.add_done_callback(id)
789                return False
790
791        fut.remove_done_callback(evil())
792
793
794@unittest.skipUnless(hasattr(futures, '_CFuture'),
795                     'requires the C _asyncio module')
796class CFutureDoneCallbackTests(BaseFutureDoneCallbackTests,
797                               test_utils.TestCase):
798
799    def _new_future(self):
800        return futures._CFuture(loop=self.loop)
801
802
803@unittest.skipUnless(hasattr(futures, '_CFuture'),
804                     'requires the C _asyncio module')
805class CSubFutureDoneCallbackTests(BaseFutureDoneCallbackTests,
806                                  test_utils.TestCase):
807
808    def _new_future(self):
809        class CSubFuture(futures._CFuture):
810            pass
811        return CSubFuture(loop=self.loop)
812
813
814class PyFutureDoneCallbackTests(BaseFutureDoneCallbackTests,
815                                test_utils.TestCase):
816
817    def _new_future(self):
818        return futures._PyFuture(loop=self.loop)
819
820
821if __name__ == '__main__':
822    unittest.main()
823