blob: 26032d6c85f2a51e16986bee0087dd1d2917bfcc [file] [log] [blame]
Eric Snow6d2cd902018-05-16 15:04:57 -04001from collections import namedtuple
Eric Snow7f8bfc92018-01-29 18:23:44 -07002import contextlib
Eric Snow6d2cd902018-05-16 15:04:57 -04003import itertools
Eric Snow7f8bfc92018-01-29 18:23:44 -07004import os
5import pickle
Eric Snow6d2cd902018-05-16 15:04:57 -04006import sys
Eric Snow7f8bfc92018-01-29 18:23:44 -07007from textwrap import dedent, indent
8import threading
Eric Snowf53d9f22018-02-20 16:30:17 -07009import time
Eric Snow7f8bfc92018-01-29 18:23:44 -070010import unittest
11
12from test import support
13from test.support import script_helper
14
Victor Stinnerc4f3cb72018-06-08 02:28:28 +020015
Eric Snow7f8bfc92018-01-29 18:23:44 -070016interpreters = support.import_module('_xxsubinterpreters')
17
18
Eric Snow6d2cd902018-05-16 15:04:57 -040019##################################
20# helpers
21
22def powerset(*sets):
23 return itertools.chain.from_iterable(
24 combinations(sets, r)
25 for r in range(len(sets)+1))
26
27
Eric Snow7f8bfc92018-01-29 18:23:44 -070028def _captured_script(script):
29 r, w = os.pipe()
30 indented = script.replace('\n', '\n ')
31 wrapped = dedent(f"""
32 import contextlib
Eric Snow6d2cd902018-05-16 15:04:57 -040033 with open({w}, 'w') as spipe:
34 with contextlib.redirect_stdout(spipe):
Eric Snow7f8bfc92018-01-29 18:23:44 -070035 {indented}
36 """)
37 return wrapped, open(r)
38
39
40def _run_output(interp, request, shared=None):
Eric Snow6d2cd902018-05-16 15:04:57 -040041 script, rpipe = _captured_script(request)
42 with rpipe:
Eric Snow7f8bfc92018-01-29 18:23:44 -070043 interpreters.run_string(interp, script, shared)
Eric Snow6d2cd902018-05-16 15:04:57 -040044 return rpipe.read()
Eric Snow7f8bfc92018-01-29 18:23:44 -070045
46
47@contextlib.contextmanager
48def _running(interp):
49 r, w = os.pipe()
50 def run():
51 interpreters.run_string(interp, dedent(f"""
52 # wait for "signal"
Eric Snow6d2cd902018-05-16 15:04:57 -040053 with open({r}) as rpipe:
54 rpipe.read()
Eric Snow7f8bfc92018-01-29 18:23:44 -070055 """))
56
57 t = threading.Thread(target=run)
58 t.start()
59
60 yield
61
Eric Snow6d2cd902018-05-16 15:04:57 -040062 with open(w, 'w') as spipe:
63 spipe.write('done')
Eric Snow7f8bfc92018-01-29 18:23:44 -070064 t.join()
65
66
Eric Snow6d2cd902018-05-16 15:04:57 -040067#@contextmanager
68#def run_threaded(id, source, **shared):
69# def run():
70# run_interp(id, source, **shared)
71# t = threading.Thread(target=run)
72# t.start()
73# yield
74# t.join()
75
76
77def run_interp(id, source, **shared):
78 _run_interp(id, source, shared)
79
80
81def _run_interp(id, source, shared, _mainns={}):
82 source = dedent(source)
83 main = interpreters.get_main()
84 if main == id:
85 if interpreters.get_current() != main:
86 raise RuntimeError
87 # XXX Run a func?
88 exec(source, _mainns)
89 else:
90 interpreters.run_string(id, source, shared)
91
92
93def run_interp_threaded(id, source, **shared):
94 def run():
95 _run(id, source, shared)
96 t = threading.Thread(target=run)
97 t.start()
98 t.join()
99
100
101class Interpreter(namedtuple('Interpreter', 'name id')):
102
103 @classmethod
104 def from_raw(cls, raw):
105 if isinstance(raw, cls):
106 return raw
107 elif isinstance(raw, str):
108 return cls(raw)
109 else:
110 raise NotImplementedError
111
112 def __new__(cls, name=None, id=None):
113 main = interpreters.get_main()
114 if id == main:
115 if not name:
116 name = 'main'
117 elif name != 'main':
118 raise ValueError(
119 'name mismatch (expected "main", got "{}")'.format(name))
120 id = main
121 elif id is not None:
122 if not name:
123 name = 'interp'
124 elif name == 'main':
125 raise ValueError('name mismatch (unexpected "main")')
126 if not isinstance(id, interpreters.InterpreterID):
127 id = interpreters.InterpreterID(id)
128 elif not name or name == 'main':
129 name = 'main'
130 id = main
131 else:
132 id = interpreters.create()
133 self = super().__new__(cls, name, id)
134 return self
135
136
137# XXX expect_channel_closed() is unnecessary once we improve exc propagation.
138
139@contextlib.contextmanager
140def expect_channel_closed():
141 try:
142 yield
143 except interpreters.ChannelClosedError:
144 pass
145 else:
146 assert False, 'channel not closed'
147
148
149class ChannelAction(namedtuple('ChannelAction', 'action end interp')):
150
151 def __new__(cls, action, end=None, interp=None):
152 if not end:
153 end = 'both'
154 if not interp:
155 interp = 'main'
156 self = super().__new__(cls, action, end, interp)
157 return self
158
159 def __init__(self, *args, **kwargs):
160 if self.action == 'use':
161 if self.end not in ('same', 'opposite', 'send', 'recv'):
162 raise ValueError(self.end)
163 elif self.action in ('close', 'force-close'):
164 if self.end not in ('both', 'same', 'opposite', 'send', 'recv'):
165 raise ValueError(self.end)
166 else:
167 raise ValueError(self.action)
168 if self.interp not in ('main', 'same', 'other', 'extra'):
169 raise ValueError(self.interp)
170
171 def resolve_end(self, end):
172 if self.end == 'same':
173 return end
174 elif self.end == 'opposite':
175 return 'recv' if end == 'send' else 'send'
176 else:
177 return self.end
178
179 def resolve_interp(self, interp, other, extra):
180 if self.interp == 'same':
181 return interp
182 elif self.interp == 'other':
183 if other is None:
184 raise RuntimeError
185 return other
186 elif self.interp == 'extra':
187 if extra is None:
188 raise RuntimeError
189 return extra
190 elif self.interp == 'main':
191 if interp.name == 'main':
192 return interp
193 elif other and other.name == 'main':
194 return other
195 else:
196 raise RuntimeError
197 # Per __init__(), there aren't any others.
198
199
200class ChannelState(namedtuple('ChannelState', 'pending closed')):
201
202 def __new__(cls, pending=0, *, closed=False):
203 self = super().__new__(cls, pending, closed)
204 return self
205
206 def incr(self):
207 return type(self)(self.pending + 1, closed=self.closed)
208
209 def decr(self):
210 return type(self)(self.pending - 1, closed=self.closed)
211
212 def close(self, *, force=True):
213 if self.closed:
214 if not force or self.pending == 0:
215 return self
216 return type(self)(0 if force else self.pending, closed=True)
217
218
219def run_action(cid, action, end, state, *, hideclosed=True):
220 if state.closed:
221 if action == 'use' and end == 'recv' and state.pending:
222 expectfail = False
223 else:
224 expectfail = True
225 else:
226 expectfail = False
227
228 try:
229 result = _run_action(cid, action, end, state)
230 except interpreters.ChannelClosedError:
231 if not hideclosed and not expectfail:
232 raise
233 result = state.close()
234 else:
235 if expectfail:
236 raise ... # XXX
237 return result
238
239
240def _run_action(cid, action, end, state):
241 if action == 'use':
242 if end == 'send':
243 interpreters.channel_send(cid, b'spam')
244 return state.incr()
245 elif end == 'recv':
246 if not state.pending:
247 try:
248 interpreters.channel_recv(cid)
249 except interpreters.ChannelEmptyError:
250 return state
251 else:
252 raise Exception('expected ChannelEmptyError')
253 else:
254 interpreters.channel_recv(cid)
255 return state.decr()
256 else:
257 raise ValueError(end)
258 elif action == 'close':
259 kwargs = {}
260 if end in ('recv', 'send'):
261 kwargs[end] = True
262 interpreters.channel_close(cid, **kwargs)
263 return state.close()
264 elif action == 'force-close':
265 kwargs = {
266 'force': True,
267 }
268 if end in ('recv', 'send'):
269 kwargs[end] = True
270 interpreters.channel_close(cid, **kwargs)
271 return state.close(force=True)
272 else:
273 raise ValueError(action)
274
275
276def clean_up_interpreters():
277 for id in interpreters.list_all():
278 if id == 0: # main
279 continue
280 try:
281 interpreters.destroy(id)
282 except RuntimeError:
283 pass # already destroyed
284
285
286def clean_up_channels():
287 for cid in interpreters.channel_list_all():
288 try:
289 interpreters.channel_destroy(cid)
290 except interpreters.ChannelNotFoundError:
291 pass # already destroyed
292
293
294class TestBase(unittest.TestCase):
295
296 def tearDown(self):
297 clean_up_interpreters()
298 clean_up_channels()
299
300
301##################################
302# misc. tests
303
Eric Snow7f8bfc92018-01-29 18:23:44 -0700304class IsShareableTests(unittest.TestCase):
305
306 def test_default_shareables(self):
307 shareables = [
308 # singletons
309 None,
310 # builtin objects
311 b'spam',
Eric Snow6d2cd902018-05-16 15:04:57 -0400312 'spam',
313 10,
314 -10,
Eric Snow7f8bfc92018-01-29 18:23:44 -0700315 ]
316 for obj in shareables:
317 with self.subTest(obj):
318 self.assertTrue(
319 interpreters.is_shareable(obj))
320
321 def test_not_shareable(self):
322 class Cheese:
323 def __init__(self, name):
324 self.name = name
325 def __str__(self):
326 return self.name
327
328 class SubBytes(bytes):
329 """A subclass of a shareable type."""
330
331 not_shareables = [
332 # singletons
333 True,
334 False,
335 NotImplemented,
336 ...,
337 # builtin types and objects
338 type,
339 object,
340 object(),
341 Exception(),
Eric Snow7f8bfc92018-01-29 18:23:44 -0700342 100.0,
Eric Snow7f8bfc92018-01-29 18:23:44 -0700343 # user-defined types and objects
344 Cheese,
345 Cheese('Wensleydale'),
346 SubBytes(b'spam'),
347 ]
348 for obj in not_shareables:
Eric Snow6d2cd902018-05-16 15:04:57 -0400349 with self.subTest(repr(obj)):
Eric Snow7f8bfc92018-01-29 18:23:44 -0700350 self.assertFalse(
351 interpreters.is_shareable(obj))
352
353
Eric Snow6d2cd902018-05-16 15:04:57 -0400354class ShareableTypeTests(unittest.TestCase):
355
356 def setUp(self):
357 super().setUp()
358 self.cid = interpreters.channel_create()
Eric Snow7f8bfc92018-01-29 18:23:44 -0700359
360 def tearDown(self):
Eric Snow6d2cd902018-05-16 15:04:57 -0400361 interpreters.channel_destroy(self.cid)
362 super().tearDown()
Eric Snow7f8bfc92018-01-29 18:23:44 -0700363
Eric Snow6d2cd902018-05-16 15:04:57 -0400364 def _assert_values(self, values):
365 for obj in values:
366 with self.subTest(obj):
367 interpreters.channel_send(self.cid, obj)
368 got = interpreters.channel_recv(self.cid)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700369
Eric Snow6d2cd902018-05-16 15:04:57 -0400370 self.assertEqual(got, obj)
371 self.assertIs(type(got), type(obj))
372 # XXX Check the following in the channel tests?
373 #self.assertIsNot(got, obj)
374
375 def test_singletons(self):
376 for obj in [None]:
377 with self.subTest(obj):
378 interpreters.channel_send(self.cid, obj)
379 got = interpreters.channel_recv(self.cid)
380
381 # XXX What about between interpreters?
382 self.assertIs(got, obj)
383
384 def test_types(self):
385 self._assert_values([
386 b'spam',
387 9999,
388 self.cid,
389 ])
390
391 def test_bytes(self):
392 self._assert_values(i.to_bytes(2, 'little', signed=True)
393 for i in range(-1, 258))
394
395 def test_int(self):
396 self._assert_values(range(-1, 258))
397
398
399##################################
400# interpreter tests
Eric Snow7f8bfc92018-01-29 18:23:44 -0700401
402class ListAllTests(TestBase):
403
404 def test_initial(self):
405 main = interpreters.get_main()
406 ids = interpreters.list_all()
407 self.assertEqual(ids, [main])
408
409 def test_after_creating(self):
410 main = interpreters.get_main()
411 first = interpreters.create()
412 second = interpreters.create()
413 ids = interpreters.list_all()
414 self.assertEqual(ids, [main, first, second])
415
416 def test_after_destroying(self):
417 main = interpreters.get_main()
418 first = interpreters.create()
419 second = interpreters.create()
420 interpreters.destroy(first)
421 ids = interpreters.list_all()
422 self.assertEqual(ids, [main, second])
423
424
425class GetCurrentTests(TestBase):
426
427 def test_main(self):
428 main = interpreters.get_main()
429 cur = interpreters.get_current()
430 self.assertEqual(cur, main)
Eric Snow6d2cd902018-05-16 15:04:57 -0400431 self.assertIsInstance(cur, interpreters.InterpreterID)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700432
433 def test_subinterpreter(self):
434 main = interpreters.get_main()
435 interp = interpreters.create()
436 out = _run_output(interp, dedent("""
437 import _xxsubinterpreters as _interpreters
Eric Snow6d2cd902018-05-16 15:04:57 -0400438 cur = _interpreters.get_current()
439 print(cur)
440 assert isinstance(cur, _interpreters.InterpreterID)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700441 """))
442 cur = int(out.strip())
443 _, expected = interpreters.list_all()
444 self.assertEqual(cur, expected)
445 self.assertNotEqual(cur, main)
446
447
448class GetMainTests(TestBase):
449
450 def test_from_main(self):
451 [expected] = interpreters.list_all()
452 main = interpreters.get_main()
453 self.assertEqual(main, expected)
Eric Snow6d2cd902018-05-16 15:04:57 -0400454 self.assertIsInstance(main, interpreters.InterpreterID)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700455
456 def test_from_subinterpreter(self):
457 [expected] = interpreters.list_all()
458 interp = interpreters.create()
459 out = _run_output(interp, dedent("""
460 import _xxsubinterpreters as _interpreters
Eric Snow6d2cd902018-05-16 15:04:57 -0400461 main = _interpreters.get_main()
462 print(main)
463 assert isinstance(main, _interpreters.InterpreterID)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700464 """))
465 main = int(out.strip())
466 self.assertEqual(main, expected)
467
468
469class IsRunningTests(TestBase):
470
471 def test_main(self):
472 main = interpreters.get_main()
473 self.assertTrue(interpreters.is_running(main))
474
475 def test_subinterpreter(self):
476 interp = interpreters.create()
477 self.assertFalse(interpreters.is_running(interp))
478
479 with _running(interp):
480 self.assertTrue(interpreters.is_running(interp))
481 self.assertFalse(interpreters.is_running(interp))
482
483 def test_from_subinterpreter(self):
484 interp = interpreters.create()
485 out = _run_output(interp, dedent(f"""
486 import _xxsubinterpreters as _interpreters
Eric Snow6d2cd902018-05-16 15:04:57 -0400487 if _interpreters.is_running({interp}):
Eric Snow7f8bfc92018-01-29 18:23:44 -0700488 print(True)
489 else:
490 print(False)
491 """))
492 self.assertEqual(out.strip(), 'True')
493
494 def test_already_destroyed(self):
495 interp = interpreters.create()
496 interpreters.destroy(interp)
497 with self.assertRaises(RuntimeError):
498 interpreters.is_running(interp)
499
500 def test_does_not_exist(self):
501 with self.assertRaises(RuntimeError):
502 interpreters.is_running(1_000_000)
503
504 def test_bad_id(self):
505 with self.assertRaises(RuntimeError):
506 interpreters.is_running(-1)
507
508
Eric Snow4c6955e2018-02-16 18:53:40 -0700509class InterpreterIDTests(TestBase):
510
511 def test_with_int(self):
512 id = interpreters.InterpreterID(10, force=True)
513
514 self.assertEqual(int(id), 10)
515
516 def test_coerce_id(self):
517 id = interpreters.InterpreterID('10', force=True)
518 self.assertEqual(int(id), 10)
519
520 id = interpreters.InterpreterID(10.0, force=True)
521 self.assertEqual(int(id), 10)
522
523 class Int(str):
524 def __init__(self, value):
525 self._value = value
526 def __int__(self):
527 return self._value
528
529 id = interpreters.InterpreterID(Int(10), force=True)
530 self.assertEqual(int(id), 10)
531
532 def test_bad_id(self):
533 for id in [-1, 'spam']:
534 with self.subTest(id):
535 with self.assertRaises(ValueError):
536 interpreters.InterpreterID(id)
537 with self.assertRaises(OverflowError):
538 interpreters.InterpreterID(2**64)
539 with self.assertRaises(TypeError):
540 interpreters.InterpreterID(object())
541
542 def test_does_not_exist(self):
543 id = interpreters.channel_create()
544 with self.assertRaises(RuntimeError):
545 interpreters.InterpreterID(int(id) + 1) # unforced
546
Eric Snow6d2cd902018-05-16 15:04:57 -0400547 def test_str(self):
548 id = interpreters.InterpreterID(10, force=True)
549 self.assertEqual(str(id), '10')
550
Eric Snow4c6955e2018-02-16 18:53:40 -0700551 def test_repr(self):
552 id = interpreters.InterpreterID(10, force=True)
553 self.assertEqual(repr(id), 'InterpreterID(10)')
554
555 def test_equality(self):
556 id1 = interpreters.create()
557 id2 = interpreters.InterpreterID(int(id1))
558 id3 = interpreters.create()
559
560 self.assertTrue(id1 == id1)
561 self.assertTrue(id1 == id2)
562 self.assertTrue(id1 == int(id1))
563 self.assertFalse(id1 == id3)
564
565 self.assertFalse(id1 != id1)
566 self.assertFalse(id1 != id2)
567 self.assertTrue(id1 != id3)
568
569
Eric Snow7f8bfc92018-01-29 18:23:44 -0700570class CreateTests(TestBase):
571
572 def test_in_main(self):
573 id = interpreters.create()
Eric Snow6d2cd902018-05-16 15:04:57 -0400574 self.assertIsInstance(id, interpreters.InterpreterID)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700575
576 self.assertIn(id, interpreters.list_all())
577
578 @unittest.skip('enable this test when working on pystate.c')
579 def test_unique_id(self):
580 seen = set()
581 for _ in range(100):
582 id = interpreters.create()
583 interpreters.destroy(id)
584 seen.add(id)
585
586 self.assertEqual(len(seen), 100)
587
588 def test_in_thread(self):
589 lock = threading.Lock()
590 id = None
591 def f():
592 nonlocal id
593 id = interpreters.create()
594 lock.acquire()
595 lock.release()
596
597 t = threading.Thread(target=f)
598 with lock:
599 t.start()
600 t.join()
601 self.assertIn(id, interpreters.list_all())
602
603 def test_in_subinterpreter(self):
604 main, = interpreters.list_all()
605 id1 = interpreters.create()
606 out = _run_output(id1, dedent("""
607 import _xxsubinterpreters as _interpreters
608 id = _interpreters.create()
Eric Snow6d2cd902018-05-16 15:04:57 -0400609 print(id)
610 assert isinstance(id, _interpreters.InterpreterID)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700611 """))
612 id2 = int(out.strip())
613
614 self.assertEqual(set(interpreters.list_all()), {main, id1, id2})
615
616 def test_in_threaded_subinterpreter(self):
617 main, = interpreters.list_all()
618 id1 = interpreters.create()
619 id2 = None
620 def f():
621 nonlocal id2
622 out = _run_output(id1, dedent("""
623 import _xxsubinterpreters as _interpreters
624 id = _interpreters.create()
Eric Snow6d2cd902018-05-16 15:04:57 -0400625 print(id)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700626 """))
627 id2 = int(out.strip())
628
629 t = threading.Thread(target=f)
630 t.start()
631 t.join()
632
633 self.assertEqual(set(interpreters.list_all()), {main, id1, id2})
634
635 def test_after_destroy_all(self):
636 before = set(interpreters.list_all())
637 # Create 3 subinterpreters.
638 ids = []
639 for _ in range(3):
640 id = interpreters.create()
641 ids.append(id)
642 # Now destroy them.
643 for id in ids:
644 interpreters.destroy(id)
645 # Finally, create another.
646 id = interpreters.create()
647 self.assertEqual(set(interpreters.list_all()), before | {id})
648
649 def test_after_destroy_some(self):
650 before = set(interpreters.list_all())
651 # Create 3 subinterpreters.
652 id1 = interpreters.create()
653 id2 = interpreters.create()
654 id3 = interpreters.create()
655 # Now destroy 2 of them.
656 interpreters.destroy(id1)
657 interpreters.destroy(id3)
658 # Finally, create another.
659 id = interpreters.create()
660 self.assertEqual(set(interpreters.list_all()), before | {id, id2})
661
662
663class DestroyTests(TestBase):
664
665 def test_one(self):
666 id1 = interpreters.create()
667 id2 = interpreters.create()
668 id3 = interpreters.create()
669 self.assertIn(id2, interpreters.list_all())
670 interpreters.destroy(id2)
671 self.assertNotIn(id2, interpreters.list_all())
672 self.assertIn(id1, interpreters.list_all())
673 self.assertIn(id3, interpreters.list_all())
674
675 def test_all(self):
676 before = set(interpreters.list_all())
677 ids = set()
678 for _ in range(3):
679 id = interpreters.create()
680 ids.add(id)
681 self.assertEqual(set(interpreters.list_all()), before | ids)
682 for id in ids:
683 interpreters.destroy(id)
684 self.assertEqual(set(interpreters.list_all()), before)
685
686 def test_main(self):
687 main, = interpreters.list_all()
688 with self.assertRaises(RuntimeError):
689 interpreters.destroy(main)
690
691 def f():
692 with self.assertRaises(RuntimeError):
693 interpreters.destroy(main)
694
695 t = threading.Thread(target=f)
696 t.start()
697 t.join()
698
699 def test_already_destroyed(self):
700 id = interpreters.create()
701 interpreters.destroy(id)
702 with self.assertRaises(RuntimeError):
703 interpreters.destroy(id)
704
705 def test_does_not_exist(self):
706 with self.assertRaises(RuntimeError):
707 interpreters.destroy(1_000_000)
708
709 def test_bad_id(self):
710 with self.assertRaises(RuntimeError):
711 interpreters.destroy(-1)
712
713 def test_from_current(self):
714 main, = interpreters.list_all()
715 id = interpreters.create()
Eric Snow4e9da0d2018-02-02 21:49:49 -0700716 script = dedent(f"""
Eric Snow7f8bfc92018-01-29 18:23:44 -0700717 import _xxsubinterpreters as _interpreters
Eric Snow4e9da0d2018-02-02 21:49:49 -0700718 try:
Eric Snow6d2cd902018-05-16 15:04:57 -0400719 _interpreters.destroy({id})
Eric Snow4e9da0d2018-02-02 21:49:49 -0700720 except RuntimeError:
721 pass
722 """)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700723
Eric Snow4e9da0d2018-02-02 21:49:49 -0700724 interpreters.run_string(id, script)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700725 self.assertEqual(set(interpreters.list_all()), {main, id})
726
727 def test_from_sibling(self):
728 main, = interpreters.list_all()
729 id1 = interpreters.create()
730 id2 = interpreters.create()
Eric Snow4c6955e2018-02-16 18:53:40 -0700731 script = dedent(f"""
Eric Snow7f8bfc92018-01-29 18:23:44 -0700732 import _xxsubinterpreters as _interpreters
Eric Snow6d2cd902018-05-16 15:04:57 -0400733 _interpreters.destroy({id2})
Eric Snow4c6955e2018-02-16 18:53:40 -0700734 """)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700735 interpreters.run_string(id1, script)
736
737 self.assertEqual(set(interpreters.list_all()), {main, id1})
738
739 def test_from_other_thread(self):
740 id = interpreters.create()
741 def f():
742 interpreters.destroy(id)
743
744 t = threading.Thread(target=f)
745 t.start()
746 t.join()
747
748 def test_still_running(self):
749 main, = interpreters.list_all()
750 interp = interpreters.create()
751 with _running(interp):
752 with self.assertRaises(RuntimeError):
753 interpreters.destroy(interp)
754 self.assertTrue(interpreters.is_running(interp))
755
756
757class RunStringTests(TestBase):
758
759 SCRIPT = dedent("""
760 with open('{}', 'w') as out:
761 out.write('{}')
762 """)
763 FILENAME = 'spam'
764
765 def setUp(self):
766 super().setUp()
767 self.id = interpreters.create()
768 self._fs = None
769
770 def tearDown(self):
771 if self._fs is not None:
772 self._fs.close()
773 super().tearDown()
774
775 @property
776 def fs(self):
777 if self._fs is None:
778 self._fs = FSFixture(self)
779 return self._fs
780
781 def test_success(self):
782 script, file = _captured_script('print("it worked!", end="")')
783 with file:
784 interpreters.run_string(self.id, script)
785 out = file.read()
786
787 self.assertEqual(out, 'it worked!')
788
789 def test_in_thread(self):
790 script, file = _captured_script('print("it worked!", end="")')
791 with file:
792 def f():
793 interpreters.run_string(self.id, script)
794
795 t = threading.Thread(target=f)
796 t.start()
797 t.join()
798 out = file.read()
799
800 self.assertEqual(out, 'it worked!')
801
802 def test_create_thread(self):
803 script, file = _captured_script("""
804 import threading
805 def f():
806 print('it worked!', end='')
807
808 t = threading.Thread(target=f)
809 t.start()
810 t.join()
811 """)
812 with file:
813 interpreters.run_string(self.id, script)
814 out = file.read()
815
816 self.assertEqual(out, 'it worked!')
817
818 @unittest.skipUnless(hasattr(os, 'fork'), "test needs os.fork()")
819 def test_fork(self):
820 import tempfile
821 with tempfile.NamedTemporaryFile('w+') as file:
822 file.write('')
823 file.flush()
824
825 expected = 'spam spam spam spam spam'
826 script = dedent(f"""
Eric Snow7f8bfc92018-01-29 18:23:44 -0700827 import os
Eric Snow59032962018-09-14 14:17:20 -0700828 try:
829 os.fork()
830 except RuntimeError:
Eric Snow7f8bfc92018-01-29 18:23:44 -0700831 with open('{file.name}', 'w') as out:
832 out.write('{expected}')
Eric Snow7f8bfc92018-01-29 18:23:44 -0700833 """)
834 interpreters.run_string(self.id, script)
835
836 file.seek(0)
837 content = file.read()
838 self.assertEqual(content, expected)
839
840 def test_already_running(self):
841 with _running(self.id):
842 with self.assertRaises(RuntimeError):
843 interpreters.run_string(self.id, 'print("spam")')
844
845 def test_does_not_exist(self):
846 id = 0
847 while id in interpreters.list_all():
848 id += 1
849 with self.assertRaises(RuntimeError):
850 interpreters.run_string(id, 'print("spam")')
851
852 def test_error_id(self):
853 with self.assertRaises(RuntimeError):
854 interpreters.run_string(-1, 'print("spam")')
855
856 def test_bad_id(self):
857 with self.assertRaises(TypeError):
858 interpreters.run_string('spam', 'print("spam")')
859
860 def test_bad_script(self):
861 with self.assertRaises(TypeError):
862 interpreters.run_string(self.id, 10)
863
864 def test_bytes_for_script(self):
865 with self.assertRaises(TypeError):
866 interpreters.run_string(self.id, b'print("spam")')
867
868 @contextlib.contextmanager
869 def assert_run_failed(self, exctype, msg=None):
870 with self.assertRaises(interpreters.RunFailedError) as caught:
871 yield
872 if msg is None:
873 self.assertEqual(str(caught.exception).split(':')[0],
874 str(exctype))
875 else:
876 self.assertEqual(str(caught.exception),
877 "{}: {}".format(exctype, msg))
878
879 def test_invalid_syntax(self):
880 with self.assert_run_failed(SyntaxError):
881 # missing close paren
882 interpreters.run_string(self.id, 'print("spam"')
883
884 def test_failure(self):
885 with self.assert_run_failed(Exception, 'spam'):
886 interpreters.run_string(self.id, 'raise Exception("spam")')
887
888 def test_SystemExit(self):
889 with self.assert_run_failed(SystemExit, '42'):
890 interpreters.run_string(self.id, 'raise SystemExit(42)')
891
892 def test_sys_exit(self):
893 with self.assert_run_failed(SystemExit):
894 interpreters.run_string(self.id, dedent("""
895 import sys
896 sys.exit()
897 """))
898
899 with self.assert_run_failed(SystemExit, '42'):
900 interpreters.run_string(self.id, dedent("""
901 import sys
902 sys.exit(42)
903 """))
904
905 def test_with_shared(self):
906 r, w = os.pipe()
907
908 shared = {
909 'spam': b'ham',
910 'eggs': b'-1',
911 'cheddar': None,
912 }
913 script = dedent(f"""
914 eggs = int(eggs)
915 spam = 42
916 result = spam + eggs
917
918 ns = dict(vars())
919 del ns['__builtins__']
920 import pickle
921 with open({w}, 'wb') as chan:
922 pickle.dump(ns, chan)
923 """)
924 interpreters.run_string(self.id, script, shared)
925 with open(r, 'rb') as chan:
926 ns = pickle.load(chan)
927
928 self.assertEqual(ns['spam'], 42)
929 self.assertEqual(ns['eggs'], -1)
930 self.assertEqual(ns['result'], 41)
931 self.assertIsNone(ns['cheddar'])
932
933 def test_shared_overwrites(self):
934 interpreters.run_string(self.id, dedent("""
935 spam = 'eggs'
936 ns1 = dict(vars())
937 del ns1['__builtins__']
938 """))
939
940 shared = {'spam': b'ham'}
941 script = dedent(f"""
942 ns2 = dict(vars())
943 del ns2['__builtins__']
944 """)
945 interpreters.run_string(self.id, script, shared)
946
947 r, w = os.pipe()
948 script = dedent(f"""
949 ns = dict(vars())
950 del ns['__builtins__']
951 import pickle
952 with open({w}, 'wb') as chan:
953 pickle.dump(ns, chan)
954 """)
955 interpreters.run_string(self.id, script)
956 with open(r, 'rb') as chan:
957 ns = pickle.load(chan)
958
959 self.assertEqual(ns['ns1']['spam'], 'eggs')
960 self.assertEqual(ns['ns2']['spam'], b'ham')
961 self.assertEqual(ns['spam'], b'ham')
962
963 def test_shared_overwrites_default_vars(self):
964 r, w = os.pipe()
965
966 shared = {'__name__': b'not __main__'}
967 script = dedent(f"""
968 spam = 42
969
970 ns = dict(vars())
971 del ns['__builtins__']
972 import pickle
973 with open({w}, 'wb') as chan:
974 pickle.dump(ns, chan)
975 """)
976 interpreters.run_string(self.id, script, shared)
977 with open(r, 'rb') as chan:
978 ns = pickle.load(chan)
979
980 self.assertEqual(ns['__name__'], b'not __main__')
981
982 def test_main_reused(self):
983 r, w = os.pipe()
984 interpreters.run_string(self.id, dedent(f"""
985 spam = True
986
987 ns = dict(vars())
988 del ns['__builtins__']
989 import pickle
990 with open({w}, 'wb') as chan:
991 pickle.dump(ns, chan)
992 del ns, pickle, chan
993 """))
994 with open(r, 'rb') as chan:
995 ns1 = pickle.load(chan)
996
997 r, w = os.pipe()
998 interpreters.run_string(self.id, dedent(f"""
999 eggs = False
1000
1001 ns = dict(vars())
1002 del ns['__builtins__']
1003 import pickle
1004 with open({w}, 'wb') as chan:
1005 pickle.dump(ns, chan)
1006 """))
1007 with open(r, 'rb') as chan:
1008 ns2 = pickle.load(chan)
1009
1010 self.assertIn('spam', ns1)
1011 self.assertNotIn('eggs', ns1)
1012 self.assertIn('eggs', ns2)
1013 self.assertIn('spam', ns2)
1014
1015 def test_execution_namespace_is_main(self):
1016 r, w = os.pipe()
1017
1018 script = dedent(f"""
1019 spam = 42
1020
1021 ns = dict(vars())
1022 ns['__builtins__'] = str(ns['__builtins__'])
1023 import pickle
1024 with open({w}, 'wb') as chan:
1025 pickle.dump(ns, chan)
1026 """)
1027 interpreters.run_string(self.id, script)
1028 with open(r, 'rb') as chan:
1029 ns = pickle.load(chan)
1030
1031 ns.pop('__builtins__')
1032 ns.pop('__loader__')
1033 self.assertEqual(ns, {
1034 '__name__': '__main__',
1035 '__annotations__': {},
1036 '__doc__': None,
1037 '__package__': None,
1038 '__spec__': None,
1039 'spam': 42,
1040 })
1041
Eric Snow4c6955e2018-02-16 18:53:40 -07001042 # XXX Fix this test!
1043 @unittest.skip('blocking forever')
Eric Snow7f8bfc92018-01-29 18:23:44 -07001044 def test_still_running_at_exit(self):
1045 script = dedent(f"""
1046 from textwrap import dedent
1047 import threading
1048 import _xxsubinterpreters as _interpreters
Eric Snow4c6955e2018-02-16 18:53:40 -07001049 id = _interpreters.create()
Eric Snow7f8bfc92018-01-29 18:23:44 -07001050 def f():
1051 _interpreters.run_string(id, dedent('''
1052 import time
1053 # Give plenty of time for the main interpreter to finish.
1054 time.sleep(1_000_000)
1055 '''))
1056
1057 t = threading.Thread(target=f)
1058 t.start()
1059 """)
1060 with support.temp_dir() as dirname:
1061 filename = script_helper.make_script(dirname, 'interp', script)
1062 with script_helper.spawn_python(filename) as proc:
1063 retcode = proc.wait()
1064
1065 self.assertEqual(retcode, 0)
1066
1067
Eric Snow6d2cd902018-05-16 15:04:57 -04001068##################################
1069# channel tests
1070
Eric Snow7f8bfc92018-01-29 18:23:44 -07001071class ChannelIDTests(TestBase):
1072
1073 def test_default_kwargs(self):
1074 cid = interpreters._channel_id(10, force=True)
1075
1076 self.assertEqual(int(cid), 10)
1077 self.assertEqual(cid.end, 'both')
1078
1079 def test_with_kwargs(self):
1080 cid = interpreters._channel_id(10, send=True, force=True)
1081 self.assertEqual(cid.end, 'send')
1082
1083 cid = interpreters._channel_id(10, send=True, recv=False, force=True)
1084 self.assertEqual(cid.end, 'send')
1085
1086 cid = interpreters._channel_id(10, recv=True, force=True)
1087 self.assertEqual(cid.end, 'recv')
1088
1089 cid = interpreters._channel_id(10, recv=True, send=False, force=True)
1090 self.assertEqual(cid.end, 'recv')
1091
1092 cid = interpreters._channel_id(10, send=True, recv=True, force=True)
1093 self.assertEqual(cid.end, 'both')
1094
1095 def test_coerce_id(self):
1096 cid = interpreters._channel_id('10', force=True)
1097 self.assertEqual(int(cid), 10)
1098
1099 cid = interpreters._channel_id(10.0, force=True)
1100 self.assertEqual(int(cid), 10)
1101
1102 class Int(str):
1103 def __init__(self, value):
1104 self._value = value
1105 def __int__(self):
1106 return self._value
1107
1108 cid = interpreters._channel_id(Int(10), force=True)
1109 self.assertEqual(int(cid), 10)
1110
1111 def test_bad_id(self):
Eric Snow4e9da0d2018-02-02 21:49:49 -07001112 for cid in [-1, 'spam']:
Eric Snow7f8bfc92018-01-29 18:23:44 -07001113 with self.subTest(cid):
1114 with self.assertRaises(ValueError):
1115 interpreters._channel_id(cid)
Eric Snow4e9da0d2018-02-02 21:49:49 -07001116 with self.assertRaises(OverflowError):
1117 interpreters._channel_id(2**64)
Eric Snow7f8bfc92018-01-29 18:23:44 -07001118 with self.assertRaises(TypeError):
1119 interpreters._channel_id(object())
1120
1121 def test_bad_kwargs(self):
1122 with self.assertRaises(ValueError):
1123 interpreters._channel_id(10, send=False, recv=False)
1124
1125 def test_does_not_exist(self):
1126 cid = interpreters.channel_create()
1127 with self.assertRaises(interpreters.ChannelNotFoundError):
1128 interpreters._channel_id(int(cid) + 1) # unforced
1129
Eric Snow6d2cd902018-05-16 15:04:57 -04001130 def test_str(self):
1131 cid = interpreters._channel_id(10, force=True)
1132 self.assertEqual(str(cid), '10')
1133
Eric Snow7f8bfc92018-01-29 18:23:44 -07001134 def test_repr(self):
1135 cid = interpreters._channel_id(10, force=True)
1136 self.assertEqual(repr(cid), 'ChannelID(10)')
1137
1138 cid = interpreters._channel_id(10, send=True, force=True)
1139 self.assertEqual(repr(cid), 'ChannelID(10, send=True)')
1140
1141 cid = interpreters._channel_id(10, recv=True, force=True)
1142 self.assertEqual(repr(cid), 'ChannelID(10, recv=True)')
1143
1144 cid = interpreters._channel_id(10, send=True, recv=True, force=True)
1145 self.assertEqual(repr(cid), 'ChannelID(10)')
1146
1147 def test_equality(self):
1148 cid1 = interpreters.channel_create()
1149 cid2 = interpreters._channel_id(int(cid1))
1150 cid3 = interpreters.channel_create()
1151
1152 self.assertTrue(cid1 == cid1)
1153 self.assertTrue(cid1 == cid2)
1154 self.assertTrue(cid1 == int(cid1))
1155 self.assertFalse(cid1 == cid3)
1156
1157 self.assertFalse(cid1 != cid1)
1158 self.assertFalse(cid1 != cid2)
1159 self.assertTrue(cid1 != cid3)
1160
1161
1162class ChannelTests(TestBase):
1163
Eric Snow6d2cd902018-05-16 15:04:57 -04001164 def test_create_cid(self):
1165 cid = interpreters.channel_create()
1166 self.assertIsInstance(cid, interpreters.ChannelID)
1167
Eric Snow7f8bfc92018-01-29 18:23:44 -07001168 def test_sequential_ids(self):
1169 before = interpreters.channel_list_all()
1170 id1 = interpreters.channel_create()
1171 id2 = interpreters.channel_create()
1172 id3 = interpreters.channel_create()
1173 after = interpreters.channel_list_all()
1174
1175 self.assertEqual(id2, int(id1) + 1)
1176 self.assertEqual(id3, int(id2) + 1)
1177 self.assertEqual(set(after) - set(before), {id1, id2, id3})
1178
1179 def test_ids_global(self):
1180 id1 = interpreters.create()
1181 out = _run_output(id1, dedent("""
1182 import _xxsubinterpreters as _interpreters
1183 cid = _interpreters.channel_create()
Eric Snow6d2cd902018-05-16 15:04:57 -04001184 print(cid)
Eric Snow7f8bfc92018-01-29 18:23:44 -07001185 """))
1186 cid1 = int(out.strip())
1187
1188 id2 = interpreters.create()
1189 out = _run_output(id2, dedent("""
1190 import _xxsubinterpreters as _interpreters
1191 cid = _interpreters.channel_create()
Eric Snow6d2cd902018-05-16 15:04:57 -04001192 print(cid)
Eric Snow7f8bfc92018-01-29 18:23:44 -07001193 """))
1194 cid2 = int(out.strip())
1195
1196 self.assertEqual(cid2, int(cid1) + 1)
1197
1198 ####################
1199
Eric Snow7f8bfc92018-01-29 18:23:44 -07001200 def test_send_recv_main(self):
1201 cid = interpreters.channel_create()
1202 orig = b'spam'
1203 interpreters.channel_send(cid, orig)
1204 obj = interpreters.channel_recv(cid)
1205
1206 self.assertEqual(obj, orig)
1207 self.assertIsNot(obj, orig)
1208
1209 def test_send_recv_same_interpreter(self):
1210 id1 = interpreters.create()
1211 out = _run_output(id1, dedent("""
1212 import _xxsubinterpreters as _interpreters
1213 cid = _interpreters.channel_create()
1214 orig = b'spam'
1215 _interpreters.channel_send(cid, orig)
1216 obj = _interpreters.channel_recv(cid)
1217 assert obj is not orig
1218 assert obj == orig
1219 """))
1220
1221 def test_send_recv_different_interpreters(self):
1222 cid = interpreters.channel_create()
1223 id1 = interpreters.create()
1224 out = _run_output(id1, dedent(f"""
1225 import _xxsubinterpreters as _interpreters
Eric Snow6d2cd902018-05-16 15:04:57 -04001226 _interpreters.channel_send({cid}, b'spam')
Eric Snow7f8bfc92018-01-29 18:23:44 -07001227 """))
1228 obj = interpreters.channel_recv(cid)
1229
1230 self.assertEqual(obj, b'spam')
1231
Eric Snowf53d9f22018-02-20 16:30:17 -07001232 def test_send_recv_different_threads(self):
1233 cid = interpreters.channel_create()
1234
1235 def f():
1236 while True:
1237 try:
1238 obj = interpreters.channel_recv(cid)
1239 break
1240 except interpreters.ChannelEmptyError:
1241 time.sleep(0.1)
1242 interpreters.channel_send(cid, obj)
1243 t = threading.Thread(target=f)
1244 t.start()
1245
1246 interpreters.channel_send(cid, b'spam')
1247 t.join()
1248 obj = interpreters.channel_recv(cid)
1249
1250 self.assertEqual(obj, b'spam')
1251
1252 def test_send_recv_different_interpreters_and_threads(self):
1253 cid = interpreters.channel_create()
1254 id1 = interpreters.create()
1255 out = None
1256
1257 def f():
1258 nonlocal out
1259 out = _run_output(id1, dedent(f"""
1260 import time
1261 import _xxsubinterpreters as _interpreters
1262 while True:
1263 try:
Eric Snow6d2cd902018-05-16 15:04:57 -04001264 obj = _interpreters.channel_recv({cid})
Eric Snowf53d9f22018-02-20 16:30:17 -07001265 break
1266 except _interpreters.ChannelEmptyError:
1267 time.sleep(0.1)
1268 assert(obj == b'spam')
Eric Snow6d2cd902018-05-16 15:04:57 -04001269 _interpreters.channel_send({cid}, b'eggs')
Eric Snowf53d9f22018-02-20 16:30:17 -07001270 """))
1271 t = threading.Thread(target=f)
1272 t.start()
1273
1274 interpreters.channel_send(cid, b'spam')
1275 t.join()
1276 obj = interpreters.channel_recv(cid)
1277
1278 self.assertEqual(obj, b'eggs')
1279
Eric Snow7f8bfc92018-01-29 18:23:44 -07001280 def test_send_not_found(self):
1281 with self.assertRaises(interpreters.ChannelNotFoundError):
1282 interpreters.channel_send(10, b'spam')
1283
1284 def test_recv_not_found(self):
1285 with self.assertRaises(interpreters.ChannelNotFoundError):
1286 interpreters.channel_recv(10)
1287
1288 def test_recv_empty(self):
1289 cid = interpreters.channel_create()
1290 with self.assertRaises(interpreters.ChannelEmptyError):
1291 interpreters.channel_recv(cid)
1292
Eric Snow6d2cd902018-05-16 15:04:57 -04001293 def test_run_string_arg_unresolved(self):
Eric Snow7f8bfc92018-01-29 18:23:44 -07001294 cid = interpreters.channel_create()
1295 interp = interpreters.create()
1296
1297 out = _run_output(interp, dedent("""
1298 import _xxsubinterpreters as _interpreters
1299 print(cid.end)
1300 _interpreters.channel_send(cid, b'spam')
1301 """),
1302 dict(cid=cid.send))
1303 obj = interpreters.channel_recv(cid)
1304
1305 self.assertEqual(obj, b'spam')
1306 self.assertEqual(out.strip(), 'send')
1307
Eric Snowab4a1982018-06-13 08:02:39 -06001308 # XXX For now there is no high-level channel into which the
1309 # sent channel ID can be converted...
1310 # Note: this test caused crashes on some buildbots (bpo-33615).
1311 @unittest.skip('disabled until high-level channels exist')
Eric Snow6d2cd902018-05-16 15:04:57 -04001312 def test_run_string_arg_resolved(self):
1313 cid = interpreters.channel_create()
1314 cid = interpreters._channel_id(cid, _resolve=True)
1315 interp = interpreters.create()
1316
1317 out = _run_output(interp, dedent("""
1318 import _xxsubinterpreters as _interpreters
Eric Snowab4a1982018-06-13 08:02:39 -06001319 print(chan.id.end)
1320 _interpreters.channel_send(chan.id, b'spam')
Eric Snow6d2cd902018-05-16 15:04:57 -04001321 """),
1322 dict(chan=cid.send))
1323 obj = interpreters.channel_recv(cid)
1324
1325 self.assertEqual(obj, b'spam')
1326 self.assertEqual(out.strip(), 'send')
1327
1328 # close
1329
1330 def test_close_single_user(self):
1331 cid = interpreters.channel_create()
1332 interpreters.channel_send(cid, b'spam')
1333 interpreters.channel_recv(cid)
1334 interpreters.channel_close(cid)
1335
1336 with self.assertRaises(interpreters.ChannelClosedError):
1337 interpreters.channel_send(cid, b'eggs')
1338 with self.assertRaises(interpreters.ChannelClosedError):
1339 interpreters.channel_recv(cid)
1340
1341 def test_close_multiple_users(self):
1342 cid = interpreters.channel_create()
1343 id1 = interpreters.create()
1344 id2 = interpreters.create()
1345 interpreters.run_string(id1, dedent(f"""
1346 import _xxsubinterpreters as _interpreters
1347 _interpreters.channel_send({cid}, b'spam')
1348 """))
1349 interpreters.run_string(id2, dedent(f"""
1350 import _xxsubinterpreters as _interpreters
1351 _interpreters.channel_recv({cid})
1352 """))
1353 interpreters.channel_close(cid)
1354 with self.assertRaises(interpreters.RunFailedError) as cm:
1355 interpreters.run_string(id1, dedent(f"""
1356 _interpreters.channel_send({cid}, b'spam')
1357 """))
1358 self.assertIn('ChannelClosedError', str(cm.exception))
1359 with self.assertRaises(interpreters.RunFailedError) as cm:
1360 interpreters.run_string(id2, dedent(f"""
1361 _interpreters.channel_send({cid}, b'spam')
1362 """))
1363 self.assertIn('ChannelClosedError', str(cm.exception))
1364
1365 def test_close_multiple_times(self):
1366 cid = interpreters.channel_create()
1367 interpreters.channel_send(cid, b'spam')
1368 interpreters.channel_recv(cid)
1369 interpreters.channel_close(cid)
1370
1371 with self.assertRaises(interpreters.ChannelClosedError):
1372 interpreters.channel_close(cid)
1373
Eric Snow3ab01362018-05-17 10:27:09 -04001374 def test_close_empty(self):
1375 tests = [
1376 (False, False),
1377 (True, False),
1378 (False, True),
1379 (True, True),
1380 ]
1381 for send, recv in tests:
1382 with self.subTest((send, recv)):
1383 cid = interpreters.channel_create()
1384 interpreters.channel_send(cid, b'spam')
1385 interpreters.channel_recv(cid)
1386 interpreters.channel_close(cid, send=send, recv=recv)
1387
1388 with self.assertRaises(interpreters.ChannelClosedError):
1389 interpreters.channel_send(cid, b'eggs')
1390 with self.assertRaises(interpreters.ChannelClosedError):
1391 interpreters.channel_recv(cid)
1392
1393 def test_close_defaults_with_unused_items(self):
Eric Snow6d2cd902018-05-16 15:04:57 -04001394 cid = interpreters.channel_create()
1395 interpreters.channel_send(cid, b'spam')
1396 interpreters.channel_send(cid, b'ham')
Eric Snow6d2cd902018-05-16 15:04:57 -04001397
Eric Snow3ab01362018-05-17 10:27:09 -04001398 with self.assertRaises(interpreters.ChannelNotEmptyError):
1399 interpreters.channel_close(cid)
1400 interpreters.channel_recv(cid)
1401 interpreters.channel_send(cid, b'eggs')
1402
1403 def test_close_recv_with_unused_items_unforced(self):
1404 cid = interpreters.channel_create()
1405 interpreters.channel_send(cid, b'spam')
1406 interpreters.channel_send(cid, b'ham')
1407
1408 with self.assertRaises(interpreters.ChannelNotEmptyError):
1409 interpreters.channel_close(cid, recv=True)
1410 interpreters.channel_recv(cid)
1411 interpreters.channel_send(cid, b'eggs')
1412 interpreters.channel_recv(cid)
1413 interpreters.channel_recv(cid)
1414 interpreters.channel_close(cid, recv=True)
1415
1416 def test_close_send_with_unused_items_unforced(self):
1417 cid = interpreters.channel_create()
1418 interpreters.channel_send(cid, b'spam')
1419 interpreters.channel_send(cid, b'ham')
1420 interpreters.channel_close(cid, send=True)
1421
1422 with self.assertRaises(interpreters.ChannelClosedError):
1423 interpreters.channel_send(cid, b'eggs')
1424 interpreters.channel_recv(cid)
1425 interpreters.channel_recv(cid)
1426 with self.assertRaises(interpreters.ChannelClosedError):
1427 interpreters.channel_recv(cid)
1428
1429 def test_close_both_with_unused_items_unforced(self):
1430 cid = interpreters.channel_create()
1431 interpreters.channel_send(cid, b'spam')
1432 interpreters.channel_send(cid, b'ham')
1433
1434 with self.assertRaises(interpreters.ChannelNotEmptyError):
1435 interpreters.channel_close(cid, recv=True, send=True)
1436 interpreters.channel_recv(cid)
1437 interpreters.channel_send(cid, b'eggs')
1438 interpreters.channel_recv(cid)
1439 interpreters.channel_recv(cid)
1440 interpreters.channel_close(cid, recv=True)
1441
1442 def test_close_recv_with_unused_items_forced(self):
1443 cid = interpreters.channel_create()
1444 interpreters.channel_send(cid, b'spam')
1445 interpreters.channel_send(cid, b'ham')
1446 interpreters.channel_close(cid, recv=True, force=True)
1447
1448 with self.assertRaises(interpreters.ChannelClosedError):
1449 interpreters.channel_send(cid, b'eggs')
1450 with self.assertRaises(interpreters.ChannelClosedError):
1451 interpreters.channel_recv(cid)
1452
1453 def test_close_send_with_unused_items_forced(self):
1454 cid = interpreters.channel_create()
1455 interpreters.channel_send(cid, b'spam')
1456 interpreters.channel_send(cid, b'ham')
1457 interpreters.channel_close(cid, send=True, force=True)
1458
1459 with self.assertRaises(interpreters.ChannelClosedError):
1460 interpreters.channel_send(cid, b'eggs')
1461 with self.assertRaises(interpreters.ChannelClosedError):
1462 interpreters.channel_recv(cid)
1463
1464 def test_close_both_with_unused_items_forced(self):
1465 cid = interpreters.channel_create()
1466 interpreters.channel_send(cid, b'spam')
1467 interpreters.channel_send(cid, b'ham')
1468 interpreters.channel_close(cid, send=True, recv=True, force=True)
1469
1470 with self.assertRaises(interpreters.ChannelClosedError):
1471 interpreters.channel_send(cid, b'eggs')
Eric Snow6d2cd902018-05-16 15:04:57 -04001472 with self.assertRaises(interpreters.ChannelClosedError):
1473 interpreters.channel_recv(cid)
1474
1475 def test_close_never_used(self):
1476 cid = interpreters.channel_create()
1477 interpreters.channel_close(cid)
1478
1479 with self.assertRaises(interpreters.ChannelClosedError):
1480 interpreters.channel_send(cid, b'spam')
1481 with self.assertRaises(interpreters.ChannelClosedError):
1482 interpreters.channel_recv(cid)
1483
1484 def test_close_by_unassociated_interp(self):
1485 cid = interpreters.channel_create()
1486 interpreters.channel_send(cid, b'spam')
1487 interp = interpreters.create()
1488 interpreters.run_string(interp, dedent(f"""
1489 import _xxsubinterpreters as _interpreters
Eric Snow3ab01362018-05-17 10:27:09 -04001490 _interpreters.channel_close({cid}, force=True)
Eric Snow6d2cd902018-05-16 15:04:57 -04001491 """))
1492 with self.assertRaises(interpreters.ChannelClosedError):
1493 interpreters.channel_recv(cid)
1494 with self.assertRaises(interpreters.ChannelClosedError):
1495 interpreters.channel_close(cid)
1496
1497 def test_close_used_multiple_times_by_single_user(self):
1498 cid = interpreters.channel_create()
1499 interpreters.channel_send(cid, b'spam')
1500 interpreters.channel_send(cid, b'spam')
1501 interpreters.channel_send(cid, b'spam')
1502 interpreters.channel_recv(cid)
Eric Snow3ab01362018-05-17 10:27:09 -04001503 interpreters.channel_close(cid, force=True)
Eric Snow6d2cd902018-05-16 15:04:57 -04001504
1505 with self.assertRaises(interpreters.ChannelClosedError):
1506 interpreters.channel_send(cid, b'eggs')
1507 with self.assertRaises(interpreters.ChannelClosedError):
1508 interpreters.channel_recv(cid)
1509
1510
1511class ChannelReleaseTests(TestBase):
1512
1513 # XXX Add more test coverage a la the tests for close().
1514
1515 """
1516 - main / interp / other
1517 - run in: current thread / new thread / other thread / different threads
1518 - end / opposite
1519 - force / no force
1520 - used / not used (associated / not associated)
1521 - empty / emptied / never emptied / partly emptied
1522 - closed / not closed
1523 - released / not released
1524 - creator (interp) / other
1525 - associated interpreter not running
1526 - associated interpreter destroyed
1527 """
1528
1529 """
1530 use
1531 pre-release
1532 release
1533 after
1534 check
1535 """
1536
1537 """
1538 release in: main, interp1
1539 creator: same, other (incl. interp2)
1540
1541 use: None,send,recv,send/recv in None,same,other(incl. interp2),same+other(incl. interp2),all
1542 pre-release: None,send,recv,both in None,same,other(incl. interp2),same+other(incl. interp2),all
1543 pre-release forced: None,send,recv,both in None,same,other(incl. interp2),same+other(incl. interp2),all
1544
1545 release: same
1546 release forced: same
1547
1548 use after: None,send,recv,send/recv in None,same,other(incl. interp2),same+other(incl. interp2),all
1549 release after: None,send,recv,send/recv in None,same,other(incl. interp2),same+other(incl. interp2),all
1550 check released: send/recv for same/other(incl. interp2)
1551 check closed: send/recv for same/other(incl. interp2)
1552 """
1553
1554 def test_single_user(self):
1555 cid = interpreters.channel_create()
1556 interpreters.channel_send(cid, b'spam')
1557 interpreters.channel_recv(cid)
1558 interpreters.channel_release(cid, send=True, recv=True)
1559
1560 with self.assertRaises(interpreters.ChannelClosedError):
1561 interpreters.channel_send(cid, b'eggs')
1562 with self.assertRaises(interpreters.ChannelClosedError):
1563 interpreters.channel_recv(cid)
1564
1565 def test_multiple_users(self):
1566 cid = interpreters.channel_create()
1567 id1 = interpreters.create()
1568 id2 = interpreters.create()
1569 interpreters.run_string(id1, dedent(f"""
1570 import _xxsubinterpreters as _interpreters
1571 _interpreters.channel_send({cid}, b'spam')
1572 """))
1573 out = _run_output(id2, dedent(f"""
1574 import _xxsubinterpreters as _interpreters
1575 obj = _interpreters.channel_recv({cid})
1576 _interpreters.channel_release({cid})
1577 print(repr(obj))
1578 """))
1579 interpreters.run_string(id1, dedent(f"""
1580 _interpreters.channel_release({cid})
1581 """))
1582
1583 self.assertEqual(out.strip(), "b'spam'")
1584
1585 def test_no_kwargs(self):
1586 cid = interpreters.channel_create()
1587 interpreters.channel_send(cid, b'spam')
1588 interpreters.channel_recv(cid)
1589 interpreters.channel_release(cid)
1590
1591 with self.assertRaises(interpreters.ChannelClosedError):
1592 interpreters.channel_send(cid, b'eggs')
1593 with self.assertRaises(interpreters.ChannelClosedError):
1594 interpreters.channel_recv(cid)
1595
1596 def test_multiple_times(self):
1597 cid = interpreters.channel_create()
1598 interpreters.channel_send(cid, b'spam')
1599 interpreters.channel_recv(cid)
1600 interpreters.channel_release(cid, send=True, recv=True)
1601
1602 with self.assertRaises(interpreters.ChannelClosedError):
1603 interpreters.channel_release(cid, send=True, recv=True)
1604
1605 def test_with_unused_items(self):
1606 cid = interpreters.channel_create()
1607 interpreters.channel_send(cid, b'spam')
1608 interpreters.channel_send(cid, b'ham')
1609 interpreters.channel_release(cid, send=True, recv=True)
1610
1611 with self.assertRaises(interpreters.ChannelClosedError):
1612 interpreters.channel_recv(cid)
1613
1614 def test_never_used(self):
1615 cid = interpreters.channel_create()
1616 interpreters.channel_release(cid)
1617
1618 with self.assertRaises(interpreters.ChannelClosedError):
1619 interpreters.channel_send(cid, b'spam')
1620 with self.assertRaises(interpreters.ChannelClosedError):
1621 interpreters.channel_recv(cid)
1622
1623 def test_by_unassociated_interp(self):
1624 cid = interpreters.channel_create()
1625 interpreters.channel_send(cid, b'spam')
1626 interp = interpreters.create()
1627 interpreters.run_string(interp, dedent(f"""
1628 import _xxsubinterpreters as _interpreters
1629 _interpreters.channel_release({cid})
1630 """))
1631 obj = interpreters.channel_recv(cid)
1632 interpreters.channel_release(cid)
1633
1634 with self.assertRaises(interpreters.ChannelClosedError):
1635 interpreters.channel_send(cid, b'eggs')
1636 self.assertEqual(obj, b'spam')
1637
1638 def test_close_if_unassociated(self):
1639 # XXX Something's not right with this test...
1640 cid = interpreters.channel_create()
1641 interp = interpreters.create()
1642 interpreters.run_string(interp, dedent(f"""
1643 import _xxsubinterpreters as _interpreters
1644 obj = _interpreters.channel_send({cid}, b'spam')
1645 _interpreters.channel_release({cid})
1646 """))
1647
1648 with self.assertRaises(interpreters.ChannelClosedError):
1649 interpreters.channel_recv(cid)
1650
1651 def test_partially(self):
1652 # XXX Is partial close too weird/confusing?
1653 cid = interpreters.channel_create()
1654 interpreters.channel_send(cid, None)
1655 interpreters.channel_recv(cid)
1656 interpreters.channel_send(cid, b'spam')
1657 interpreters.channel_release(cid, send=True)
1658 obj = interpreters.channel_recv(cid)
1659
1660 self.assertEqual(obj, b'spam')
1661
1662 def test_used_multiple_times_by_single_user(self):
1663 cid = interpreters.channel_create()
1664 interpreters.channel_send(cid, b'spam')
1665 interpreters.channel_send(cid, b'spam')
1666 interpreters.channel_send(cid, b'spam')
1667 interpreters.channel_recv(cid)
1668 interpreters.channel_release(cid, send=True, recv=True)
1669
1670 with self.assertRaises(interpreters.ChannelClosedError):
1671 interpreters.channel_send(cid, b'eggs')
1672 with self.assertRaises(interpreters.ChannelClosedError):
1673 interpreters.channel_recv(cid)
1674
1675
1676class ChannelCloseFixture(namedtuple('ChannelCloseFixture',
1677 'end interp other extra creator')):
1678
1679 # Set this to True to avoid creating interpreters, e.g. when
1680 # scanning through test permutations without running them.
1681 QUICK = False
1682
1683 def __new__(cls, end, interp, other, extra, creator):
1684 assert end in ('send', 'recv')
1685 if cls.QUICK:
1686 known = {}
1687 else:
1688 interp = Interpreter.from_raw(interp)
1689 other = Interpreter.from_raw(other)
1690 extra = Interpreter.from_raw(extra)
1691 known = {
1692 interp.name: interp,
1693 other.name: other,
1694 extra.name: extra,
1695 }
1696 if not creator:
1697 creator = 'same'
1698 self = super().__new__(cls, end, interp, other, extra, creator)
1699 self._prepped = set()
1700 self._state = ChannelState()
1701 self._known = known
1702 return self
1703
1704 @property
1705 def state(self):
1706 return self._state
1707
1708 @property
1709 def cid(self):
1710 try:
1711 return self._cid
1712 except AttributeError:
1713 creator = self._get_interpreter(self.creator)
1714 self._cid = self._new_channel(creator)
1715 return self._cid
1716
1717 def get_interpreter(self, interp):
1718 interp = self._get_interpreter(interp)
1719 self._prep_interpreter(interp)
1720 return interp
1721
1722 def expect_closed_error(self, end=None):
1723 if end is None:
1724 end = self.end
1725 if end == 'recv' and self.state.closed == 'send':
1726 return False
1727 return bool(self.state.closed)
1728
1729 def prep_interpreter(self, interp):
1730 self._prep_interpreter(interp)
1731
1732 def record_action(self, action, result):
1733 self._state = result
1734
1735 def clean_up(self):
1736 clean_up_interpreters()
1737 clean_up_channels()
1738
1739 # internal methods
1740
1741 def _new_channel(self, creator):
1742 if creator.name == 'main':
1743 return interpreters.channel_create()
1744 else:
1745 ch = interpreters.channel_create()
1746 run_interp(creator.id, f"""
1747 import _xxsubinterpreters
1748 cid = _xxsubinterpreters.channel_create()
1749 # We purposefully send back an int to avoid tying the
1750 # channel to the other interpreter.
1751 _xxsubinterpreters.channel_send({ch}, int(cid))
1752 del _xxsubinterpreters
1753 """)
1754 self._cid = interpreters.channel_recv(ch)
1755 return self._cid
1756
1757 def _get_interpreter(self, interp):
1758 if interp in ('same', 'interp'):
1759 return self.interp
1760 elif interp == 'other':
1761 return self.other
1762 elif interp == 'extra':
1763 return self.extra
1764 else:
1765 name = interp
1766 try:
1767 interp = self._known[name]
1768 except KeyError:
1769 interp = self._known[name] = Interpreter(name)
1770 return interp
1771
1772 def _prep_interpreter(self, interp):
1773 if interp.id in self._prepped:
1774 return
1775 self._prepped.add(interp.id)
1776 if interp.name == 'main':
1777 return
1778 run_interp(interp.id, f"""
1779 import _xxsubinterpreters as interpreters
1780 import test.test__xxsubinterpreters as helpers
1781 ChannelState = helpers.ChannelState
1782 try:
1783 cid
1784 except NameError:
1785 cid = interpreters._channel_id({self.cid})
1786 """)
1787
1788
1789@unittest.skip('these tests take several hours to run')
1790class ExhaustiveChannelTests(TestBase):
1791
1792 """
1793 - main / interp / other
1794 - run in: current thread / new thread / other thread / different threads
1795 - end / opposite
1796 - force / no force
1797 - used / not used (associated / not associated)
1798 - empty / emptied / never emptied / partly emptied
1799 - closed / not closed
1800 - released / not released
1801 - creator (interp) / other
1802 - associated interpreter not running
1803 - associated interpreter destroyed
1804
1805 - close after unbound
1806 """
1807
1808 """
1809 use
1810 pre-close
1811 close
1812 after
1813 check
1814 """
1815
1816 """
1817 close in: main, interp1
1818 creator: same, other, extra
1819
1820 use: None,send,recv,send/recv in None,same,other,same+other,all
1821 pre-close: None,send,recv in None,same,other,same+other,all
1822 pre-close forced: None,send,recv in None,same,other,same+other,all
1823
1824 close: same
1825 close forced: same
1826
1827 use after: None,send,recv,send/recv in None,same,other,extra,same+other,all
1828 close after: None,send,recv,send/recv in None,same,other,extra,same+other,all
1829 check closed: send/recv for same/other(incl. interp2)
1830 """
1831
1832 def iter_action_sets(self):
1833 # - used / not used (associated / not associated)
1834 # - empty / emptied / never emptied / partly emptied
1835 # - closed / not closed
1836 # - released / not released
1837
1838 # never used
1839 yield []
1840
1841 # only pre-closed (and possible used after)
1842 for closeactions in self._iter_close_action_sets('same', 'other'):
1843 yield closeactions
1844 for postactions in self._iter_post_close_action_sets():
1845 yield closeactions + postactions
1846 for closeactions in self._iter_close_action_sets('other', 'extra'):
1847 yield closeactions
1848 for postactions in self._iter_post_close_action_sets():
1849 yield closeactions + postactions
1850
1851 # used
1852 for useactions in self._iter_use_action_sets('same', 'other'):
1853 yield useactions
1854 for closeactions in self._iter_close_action_sets('same', 'other'):
1855 actions = useactions + closeactions
1856 yield actions
1857 for postactions in self._iter_post_close_action_sets():
1858 yield actions + postactions
1859 for closeactions in self._iter_close_action_sets('other', 'extra'):
1860 actions = useactions + closeactions
1861 yield actions
1862 for postactions in self._iter_post_close_action_sets():
1863 yield actions + postactions
1864 for useactions in self._iter_use_action_sets('other', 'extra'):
1865 yield useactions
1866 for closeactions in self._iter_close_action_sets('same', 'other'):
1867 actions = useactions + closeactions
1868 yield actions
1869 for postactions in self._iter_post_close_action_sets():
1870 yield actions + postactions
1871 for closeactions in self._iter_close_action_sets('other', 'extra'):
1872 actions = useactions + closeactions
1873 yield actions
1874 for postactions in self._iter_post_close_action_sets():
1875 yield actions + postactions
1876
1877 def _iter_use_action_sets(self, interp1, interp2):
1878 interps = (interp1, interp2)
1879
1880 # only recv end used
1881 yield [
1882 ChannelAction('use', 'recv', interp1),
1883 ]
1884 yield [
1885 ChannelAction('use', 'recv', interp2),
1886 ]
1887 yield [
1888 ChannelAction('use', 'recv', interp1),
1889 ChannelAction('use', 'recv', interp2),
1890 ]
1891
1892 # never emptied
1893 yield [
1894 ChannelAction('use', 'send', interp1),
1895 ]
1896 yield [
1897 ChannelAction('use', 'send', interp2),
1898 ]
1899 yield [
1900 ChannelAction('use', 'send', interp1),
1901 ChannelAction('use', 'send', interp2),
1902 ]
1903
1904 # partially emptied
1905 for interp1 in interps:
1906 for interp2 in interps:
1907 for interp3 in interps:
1908 yield [
1909 ChannelAction('use', 'send', interp1),
1910 ChannelAction('use', 'send', interp2),
1911 ChannelAction('use', 'recv', interp3),
1912 ]
1913
1914 # fully emptied
1915 for interp1 in interps:
1916 for interp2 in interps:
1917 for interp3 in interps:
1918 for interp4 in interps:
1919 yield [
1920 ChannelAction('use', 'send', interp1),
1921 ChannelAction('use', 'send', interp2),
1922 ChannelAction('use', 'recv', interp3),
1923 ChannelAction('use', 'recv', interp4),
1924 ]
1925
1926 def _iter_close_action_sets(self, interp1, interp2):
1927 ends = ('recv', 'send')
1928 interps = (interp1, interp2)
1929 for force in (True, False):
1930 op = 'force-close' if force else 'close'
1931 for interp in interps:
1932 for end in ends:
1933 yield [
1934 ChannelAction(op, end, interp),
1935 ]
1936 for recvop in ('close', 'force-close'):
1937 for sendop in ('close', 'force-close'):
1938 for recv in interps:
1939 for send in interps:
1940 yield [
1941 ChannelAction(recvop, 'recv', recv),
1942 ChannelAction(sendop, 'send', send),
1943 ]
1944
1945 def _iter_post_close_action_sets(self):
1946 for interp in ('same', 'extra', 'other'):
1947 yield [
1948 ChannelAction('use', 'recv', interp),
1949 ]
1950 yield [
1951 ChannelAction('use', 'send', interp),
1952 ]
1953
1954 def run_actions(self, fix, actions):
1955 for action in actions:
1956 self.run_action(fix, action)
1957
1958 def run_action(self, fix, action, *, hideclosed=True):
1959 end = action.resolve_end(fix.end)
1960 interp = action.resolve_interp(fix.interp, fix.other, fix.extra)
1961 fix.prep_interpreter(interp)
1962 if interp.name == 'main':
1963 result = run_action(
1964 fix.cid,
1965 action.action,
1966 end,
1967 fix.state,
1968 hideclosed=hideclosed,
1969 )
1970 fix.record_action(action, result)
1971 else:
1972 _cid = interpreters.channel_create()
1973 run_interp(interp.id, f"""
1974 result = helpers.run_action(
1975 {fix.cid},
1976 {repr(action.action)},
1977 {repr(end)},
1978 {repr(fix.state)},
1979 hideclosed={hideclosed},
1980 )
1981 interpreters.channel_send({_cid}, result.pending.to_bytes(1, 'little'))
1982 interpreters.channel_send({_cid}, b'X' if result.closed else b'')
1983 """)
1984 result = ChannelState(
1985 pending=int.from_bytes(interpreters.channel_recv(_cid), 'little'),
1986 closed=bool(interpreters.channel_recv(_cid)),
1987 )
1988 fix.record_action(action, result)
1989
1990 def iter_fixtures(self):
1991 # XXX threads?
1992 interpreters = [
1993 ('main', 'interp', 'extra'),
1994 ('interp', 'main', 'extra'),
1995 ('interp1', 'interp2', 'extra'),
1996 ('interp1', 'interp2', 'main'),
1997 ]
1998 for interp, other, extra in interpreters:
1999 for creator in ('same', 'other', 'creator'):
2000 for end in ('send', 'recv'):
2001 yield ChannelCloseFixture(end, interp, other, extra, creator)
2002
2003 def _close(self, fix, *, force):
2004 op = 'force-close' if force else 'close'
2005 close = ChannelAction(op, fix.end, 'same')
2006 if not fix.expect_closed_error():
2007 self.run_action(fix, close, hideclosed=False)
2008 else:
2009 with self.assertRaises(interpreters.ChannelClosedError):
2010 self.run_action(fix, close, hideclosed=False)
2011
2012 def _assert_closed_in_interp(self, fix, interp=None):
2013 if interp is None or interp.name == 'main':
2014 with self.assertRaises(interpreters.ChannelClosedError):
2015 interpreters.channel_recv(fix.cid)
2016 with self.assertRaises(interpreters.ChannelClosedError):
2017 interpreters.channel_send(fix.cid, b'spam')
2018 with self.assertRaises(interpreters.ChannelClosedError):
2019 interpreters.channel_close(fix.cid)
2020 with self.assertRaises(interpreters.ChannelClosedError):
2021 interpreters.channel_close(fix.cid, force=True)
2022 else:
2023 run_interp(interp.id, f"""
2024 with helpers.expect_channel_closed():
2025 interpreters.channel_recv(cid)
2026 """)
2027 run_interp(interp.id, f"""
2028 with helpers.expect_channel_closed():
2029 interpreters.channel_send(cid, b'spam')
2030 """)
2031 run_interp(interp.id, f"""
2032 with helpers.expect_channel_closed():
2033 interpreters.channel_close(cid)
2034 """)
2035 run_interp(interp.id, f"""
2036 with helpers.expect_channel_closed():
2037 interpreters.channel_close(cid, force=True)
2038 """)
2039
2040 def _assert_closed(self, fix):
2041 self.assertTrue(fix.state.closed)
2042
2043 for _ in range(fix.state.pending):
2044 interpreters.channel_recv(fix.cid)
2045 self._assert_closed_in_interp(fix)
2046
2047 for interp in ('same', 'other'):
2048 interp = fix.get_interpreter(interp)
2049 if interp.name == 'main':
2050 continue
2051 self._assert_closed_in_interp(fix, interp)
2052
2053 interp = fix.get_interpreter('fresh')
2054 self._assert_closed_in_interp(fix, interp)
2055
2056 def _iter_close_tests(self, verbose=False):
2057 i = 0
2058 for actions in self.iter_action_sets():
2059 print()
2060 for fix in self.iter_fixtures():
2061 i += 1
2062 if i > 1000:
2063 return
2064 if verbose:
2065 if (i - 1) % 6 == 0:
2066 print()
2067 print(i, fix, '({} actions)'.format(len(actions)))
2068 else:
2069 if (i - 1) % 6 == 0:
2070 print(' ', end='')
2071 print('.', end=''); sys.stdout.flush()
2072 yield i, fix, actions
2073 if verbose:
2074 print('---')
2075 print()
2076
2077 # This is useful for scanning through the possible tests.
2078 def _skim_close_tests(self):
2079 ChannelCloseFixture.QUICK = True
2080 for i, fix, actions in self._iter_close_tests():
2081 pass
2082
2083 def test_close(self):
2084 for i, fix, actions in self._iter_close_tests():
2085 with self.subTest('{} {} {}'.format(i, fix, actions)):
2086 fix.prep_interpreter(fix.interp)
2087 self.run_actions(fix, actions)
2088
2089 self._close(fix, force=False)
2090
2091 self._assert_closed(fix)
2092 # XXX Things slow down if we have too many interpreters.
2093 fix.clean_up()
2094
2095 def test_force_close(self):
2096 for i, fix, actions in self._iter_close_tests():
2097 with self.subTest('{} {} {}'.format(i, fix, actions)):
2098 fix.prep_interpreter(fix.interp)
2099 self.run_actions(fix, actions)
2100
2101 self._close(fix, force=True)
2102
2103 self._assert_closed(fix)
2104 # XXX Things slow down if we have too many interpreters.
2105 fix.clean_up()
2106
Eric Snow7f8bfc92018-01-29 18:23:44 -07002107
2108if __name__ == '__main__':
2109 unittest.main()