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