blob: 1eece965924914a67e48415bba0393661d354965 [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):
Alexey Izbyshev16f842d2019-02-12 19:06:43 +0300396 self._assert_values(itertools.chain(range(-1, 258),
397 [sys.maxsize, -sys.maxsize - 1]))
398
399 def test_non_shareable_int(self):
400 ints = [
401 sys.maxsize + 1,
402 -sys.maxsize - 2,
403 2**1000,
404 ]
405 for i in ints:
406 with self.subTest(i):
407 with self.assertRaises(OverflowError):
408 interpreters.channel_send(self.cid, i)
Eric Snow6d2cd902018-05-16 15:04:57 -0400409
410
411##################################
412# interpreter tests
Eric Snow7f8bfc92018-01-29 18:23:44 -0700413
414class ListAllTests(TestBase):
415
416 def test_initial(self):
417 main = interpreters.get_main()
418 ids = interpreters.list_all()
419 self.assertEqual(ids, [main])
420
421 def test_after_creating(self):
422 main = interpreters.get_main()
423 first = interpreters.create()
424 second = interpreters.create()
425 ids = interpreters.list_all()
426 self.assertEqual(ids, [main, first, second])
427
428 def test_after_destroying(self):
429 main = interpreters.get_main()
430 first = interpreters.create()
431 second = interpreters.create()
432 interpreters.destroy(first)
433 ids = interpreters.list_all()
434 self.assertEqual(ids, [main, second])
435
436
437class GetCurrentTests(TestBase):
438
439 def test_main(self):
440 main = interpreters.get_main()
441 cur = interpreters.get_current()
442 self.assertEqual(cur, main)
Eric Snow6d2cd902018-05-16 15:04:57 -0400443 self.assertIsInstance(cur, interpreters.InterpreterID)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700444
445 def test_subinterpreter(self):
446 main = interpreters.get_main()
447 interp = interpreters.create()
448 out = _run_output(interp, dedent("""
449 import _xxsubinterpreters as _interpreters
Eric Snow6d2cd902018-05-16 15:04:57 -0400450 cur = _interpreters.get_current()
451 print(cur)
452 assert isinstance(cur, _interpreters.InterpreterID)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700453 """))
454 cur = int(out.strip())
455 _, expected = interpreters.list_all()
456 self.assertEqual(cur, expected)
457 self.assertNotEqual(cur, main)
458
459
460class GetMainTests(TestBase):
461
462 def test_from_main(self):
463 [expected] = interpreters.list_all()
464 main = interpreters.get_main()
465 self.assertEqual(main, expected)
Eric Snow6d2cd902018-05-16 15:04:57 -0400466 self.assertIsInstance(main, interpreters.InterpreterID)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700467
468 def test_from_subinterpreter(self):
469 [expected] = interpreters.list_all()
470 interp = interpreters.create()
471 out = _run_output(interp, dedent("""
472 import _xxsubinterpreters as _interpreters
Eric Snow6d2cd902018-05-16 15:04:57 -0400473 main = _interpreters.get_main()
474 print(main)
475 assert isinstance(main, _interpreters.InterpreterID)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700476 """))
477 main = int(out.strip())
478 self.assertEqual(main, expected)
479
480
481class IsRunningTests(TestBase):
482
483 def test_main(self):
484 main = interpreters.get_main()
485 self.assertTrue(interpreters.is_running(main))
486
487 def test_subinterpreter(self):
488 interp = interpreters.create()
489 self.assertFalse(interpreters.is_running(interp))
490
491 with _running(interp):
492 self.assertTrue(interpreters.is_running(interp))
493 self.assertFalse(interpreters.is_running(interp))
494
495 def test_from_subinterpreter(self):
496 interp = interpreters.create()
497 out = _run_output(interp, dedent(f"""
498 import _xxsubinterpreters as _interpreters
Eric Snow6d2cd902018-05-16 15:04:57 -0400499 if _interpreters.is_running({interp}):
Eric Snow7f8bfc92018-01-29 18:23:44 -0700500 print(True)
501 else:
502 print(False)
503 """))
504 self.assertEqual(out.strip(), 'True')
505
506 def test_already_destroyed(self):
507 interp = interpreters.create()
508 interpreters.destroy(interp)
509 with self.assertRaises(RuntimeError):
510 interpreters.is_running(interp)
511
512 def test_does_not_exist(self):
513 with self.assertRaises(RuntimeError):
514 interpreters.is_running(1_000_000)
515
516 def test_bad_id(self):
517 with self.assertRaises(RuntimeError):
518 interpreters.is_running(-1)
519
520
Eric Snow4c6955e2018-02-16 18:53:40 -0700521class InterpreterIDTests(TestBase):
522
523 def test_with_int(self):
524 id = interpreters.InterpreterID(10, force=True)
525
526 self.assertEqual(int(id), 10)
527
528 def test_coerce_id(self):
529 id = interpreters.InterpreterID('10', force=True)
530 self.assertEqual(int(id), 10)
531
532 id = interpreters.InterpreterID(10.0, force=True)
533 self.assertEqual(int(id), 10)
534
535 class Int(str):
536 def __init__(self, value):
537 self._value = value
538 def __int__(self):
539 return self._value
540
541 id = interpreters.InterpreterID(Int(10), force=True)
542 self.assertEqual(int(id), 10)
543
544 def test_bad_id(self):
545 for id in [-1, 'spam']:
546 with self.subTest(id):
547 with self.assertRaises(ValueError):
548 interpreters.InterpreterID(id)
549 with self.assertRaises(OverflowError):
550 interpreters.InterpreterID(2**64)
551 with self.assertRaises(TypeError):
552 interpreters.InterpreterID(object())
553
554 def test_does_not_exist(self):
555 id = interpreters.channel_create()
556 with self.assertRaises(RuntimeError):
557 interpreters.InterpreterID(int(id) + 1) # unforced
558
Eric Snow6d2cd902018-05-16 15:04:57 -0400559 def test_str(self):
560 id = interpreters.InterpreterID(10, force=True)
561 self.assertEqual(str(id), '10')
562
Eric Snow4c6955e2018-02-16 18:53:40 -0700563 def test_repr(self):
564 id = interpreters.InterpreterID(10, force=True)
565 self.assertEqual(repr(id), 'InterpreterID(10)')
566
567 def test_equality(self):
568 id1 = interpreters.create()
569 id2 = interpreters.InterpreterID(int(id1))
570 id3 = interpreters.create()
571
572 self.assertTrue(id1 == id1)
573 self.assertTrue(id1 == id2)
574 self.assertTrue(id1 == int(id1))
575 self.assertFalse(id1 == id3)
576
577 self.assertFalse(id1 != id1)
578 self.assertFalse(id1 != id2)
579 self.assertTrue(id1 != id3)
580
581
Eric Snow7f8bfc92018-01-29 18:23:44 -0700582class CreateTests(TestBase):
583
584 def test_in_main(self):
585 id = interpreters.create()
Eric Snow6d2cd902018-05-16 15:04:57 -0400586 self.assertIsInstance(id, interpreters.InterpreterID)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700587
588 self.assertIn(id, interpreters.list_all())
589
590 @unittest.skip('enable this test when working on pystate.c')
591 def test_unique_id(self):
592 seen = set()
593 for _ in range(100):
594 id = interpreters.create()
595 interpreters.destroy(id)
596 seen.add(id)
597
598 self.assertEqual(len(seen), 100)
599
600 def test_in_thread(self):
601 lock = threading.Lock()
602 id = None
603 def f():
604 nonlocal id
605 id = interpreters.create()
606 lock.acquire()
607 lock.release()
608
609 t = threading.Thread(target=f)
610 with lock:
611 t.start()
612 t.join()
613 self.assertIn(id, interpreters.list_all())
614
615 def test_in_subinterpreter(self):
616 main, = interpreters.list_all()
617 id1 = interpreters.create()
618 out = _run_output(id1, dedent("""
619 import _xxsubinterpreters as _interpreters
620 id = _interpreters.create()
Eric Snow6d2cd902018-05-16 15:04:57 -0400621 print(id)
622 assert isinstance(id, _interpreters.InterpreterID)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700623 """))
624 id2 = int(out.strip())
625
626 self.assertEqual(set(interpreters.list_all()), {main, id1, id2})
627
628 def test_in_threaded_subinterpreter(self):
629 main, = interpreters.list_all()
630 id1 = interpreters.create()
631 id2 = None
632 def f():
633 nonlocal id2
634 out = _run_output(id1, dedent("""
635 import _xxsubinterpreters as _interpreters
636 id = _interpreters.create()
Eric Snow6d2cd902018-05-16 15:04:57 -0400637 print(id)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700638 """))
639 id2 = int(out.strip())
640
641 t = threading.Thread(target=f)
642 t.start()
643 t.join()
644
645 self.assertEqual(set(interpreters.list_all()), {main, id1, id2})
646
647 def test_after_destroy_all(self):
648 before = set(interpreters.list_all())
649 # Create 3 subinterpreters.
650 ids = []
651 for _ in range(3):
652 id = interpreters.create()
653 ids.append(id)
654 # Now destroy them.
655 for id in ids:
656 interpreters.destroy(id)
657 # Finally, create another.
658 id = interpreters.create()
659 self.assertEqual(set(interpreters.list_all()), before | {id})
660
661 def test_after_destroy_some(self):
662 before = set(interpreters.list_all())
663 # Create 3 subinterpreters.
664 id1 = interpreters.create()
665 id2 = interpreters.create()
666 id3 = interpreters.create()
667 # Now destroy 2 of them.
668 interpreters.destroy(id1)
669 interpreters.destroy(id3)
670 # Finally, create another.
671 id = interpreters.create()
672 self.assertEqual(set(interpreters.list_all()), before | {id, id2})
673
674
675class DestroyTests(TestBase):
676
677 def test_one(self):
678 id1 = interpreters.create()
679 id2 = interpreters.create()
680 id3 = interpreters.create()
681 self.assertIn(id2, interpreters.list_all())
682 interpreters.destroy(id2)
683 self.assertNotIn(id2, interpreters.list_all())
684 self.assertIn(id1, interpreters.list_all())
685 self.assertIn(id3, interpreters.list_all())
686
687 def test_all(self):
688 before = set(interpreters.list_all())
689 ids = set()
690 for _ in range(3):
691 id = interpreters.create()
692 ids.add(id)
693 self.assertEqual(set(interpreters.list_all()), before | ids)
694 for id in ids:
695 interpreters.destroy(id)
696 self.assertEqual(set(interpreters.list_all()), before)
697
698 def test_main(self):
699 main, = interpreters.list_all()
700 with self.assertRaises(RuntimeError):
701 interpreters.destroy(main)
702
703 def f():
704 with self.assertRaises(RuntimeError):
705 interpreters.destroy(main)
706
707 t = threading.Thread(target=f)
708 t.start()
709 t.join()
710
711 def test_already_destroyed(self):
712 id = interpreters.create()
713 interpreters.destroy(id)
714 with self.assertRaises(RuntimeError):
715 interpreters.destroy(id)
716
717 def test_does_not_exist(self):
718 with self.assertRaises(RuntimeError):
719 interpreters.destroy(1_000_000)
720
721 def test_bad_id(self):
722 with self.assertRaises(RuntimeError):
723 interpreters.destroy(-1)
724
725 def test_from_current(self):
726 main, = interpreters.list_all()
727 id = interpreters.create()
Eric Snow4e9da0d2018-02-02 21:49:49 -0700728 script = dedent(f"""
Eric Snow7f8bfc92018-01-29 18:23:44 -0700729 import _xxsubinterpreters as _interpreters
Eric Snow4e9da0d2018-02-02 21:49:49 -0700730 try:
Eric Snow6d2cd902018-05-16 15:04:57 -0400731 _interpreters.destroy({id})
Eric Snow4e9da0d2018-02-02 21:49:49 -0700732 except RuntimeError:
733 pass
734 """)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700735
Eric Snow4e9da0d2018-02-02 21:49:49 -0700736 interpreters.run_string(id, script)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700737 self.assertEqual(set(interpreters.list_all()), {main, id})
738
739 def test_from_sibling(self):
740 main, = interpreters.list_all()
741 id1 = interpreters.create()
742 id2 = interpreters.create()
Eric Snow4c6955e2018-02-16 18:53:40 -0700743 script = dedent(f"""
Eric Snow7f8bfc92018-01-29 18:23:44 -0700744 import _xxsubinterpreters as _interpreters
Eric Snow6d2cd902018-05-16 15:04:57 -0400745 _interpreters.destroy({id2})
Eric Snow4c6955e2018-02-16 18:53:40 -0700746 """)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700747 interpreters.run_string(id1, script)
748
749 self.assertEqual(set(interpreters.list_all()), {main, id1})
750
751 def test_from_other_thread(self):
752 id = interpreters.create()
753 def f():
754 interpreters.destroy(id)
755
756 t = threading.Thread(target=f)
757 t.start()
758 t.join()
759
760 def test_still_running(self):
761 main, = interpreters.list_all()
762 interp = interpreters.create()
763 with _running(interp):
764 with self.assertRaises(RuntimeError):
765 interpreters.destroy(interp)
766 self.assertTrue(interpreters.is_running(interp))
767
768
769class RunStringTests(TestBase):
770
771 SCRIPT = dedent("""
772 with open('{}', 'w') as out:
773 out.write('{}')
774 """)
775 FILENAME = 'spam'
776
777 def setUp(self):
778 super().setUp()
779 self.id = interpreters.create()
780 self._fs = None
781
782 def tearDown(self):
783 if self._fs is not None:
784 self._fs.close()
785 super().tearDown()
786
787 @property
788 def fs(self):
789 if self._fs is None:
790 self._fs = FSFixture(self)
791 return self._fs
792
793 def test_success(self):
794 script, file = _captured_script('print("it worked!", end="")')
795 with file:
796 interpreters.run_string(self.id, script)
797 out = file.read()
798
799 self.assertEqual(out, 'it worked!')
800
801 def test_in_thread(self):
802 script, file = _captured_script('print("it worked!", end="")')
803 with file:
804 def f():
805 interpreters.run_string(self.id, script)
806
807 t = threading.Thread(target=f)
808 t.start()
809 t.join()
810 out = file.read()
811
812 self.assertEqual(out, 'it worked!')
813
814 def test_create_thread(self):
815 script, file = _captured_script("""
816 import threading
817 def f():
818 print('it worked!', end='')
819
820 t = threading.Thread(target=f)
821 t.start()
822 t.join()
823 """)
824 with file:
825 interpreters.run_string(self.id, script)
826 out = file.read()
827
828 self.assertEqual(out, 'it worked!')
829
830 @unittest.skipUnless(hasattr(os, 'fork'), "test needs os.fork()")
831 def test_fork(self):
832 import tempfile
833 with tempfile.NamedTemporaryFile('w+') as file:
834 file.write('')
835 file.flush()
836
837 expected = 'spam spam spam spam spam'
838 script = dedent(f"""
Eric Snow7f8bfc92018-01-29 18:23:44 -0700839 import os
Eric Snow59032962018-09-14 14:17:20 -0700840 try:
841 os.fork()
842 except RuntimeError:
Eric Snow7f8bfc92018-01-29 18:23:44 -0700843 with open('{file.name}', 'w') as out:
844 out.write('{expected}')
Eric Snow7f8bfc92018-01-29 18:23:44 -0700845 """)
846 interpreters.run_string(self.id, script)
847
848 file.seek(0)
849 content = file.read()
850 self.assertEqual(content, expected)
851
852 def test_already_running(self):
853 with _running(self.id):
854 with self.assertRaises(RuntimeError):
855 interpreters.run_string(self.id, 'print("spam")')
856
857 def test_does_not_exist(self):
858 id = 0
859 while id in interpreters.list_all():
860 id += 1
861 with self.assertRaises(RuntimeError):
862 interpreters.run_string(id, 'print("spam")')
863
864 def test_error_id(self):
865 with self.assertRaises(RuntimeError):
866 interpreters.run_string(-1, 'print("spam")')
867
868 def test_bad_id(self):
869 with self.assertRaises(TypeError):
870 interpreters.run_string('spam', 'print("spam")')
871
872 def test_bad_script(self):
873 with self.assertRaises(TypeError):
874 interpreters.run_string(self.id, 10)
875
876 def test_bytes_for_script(self):
877 with self.assertRaises(TypeError):
878 interpreters.run_string(self.id, b'print("spam")')
879
880 @contextlib.contextmanager
881 def assert_run_failed(self, exctype, msg=None):
882 with self.assertRaises(interpreters.RunFailedError) as caught:
883 yield
884 if msg is None:
885 self.assertEqual(str(caught.exception).split(':')[0],
886 str(exctype))
887 else:
888 self.assertEqual(str(caught.exception),
889 "{}: {}".format(exctype, msg))
890
891 def test_invalid_syntax(self):
892 with self.assert_run_failed(SyntaxError):
893 # missing close paren
894 interpreters.run_string(self.id, 'print("spam"')
895
896 def test_failure(self):
897 with self.assert_run_failed(Exception, 'spam'):
898 interpreters.run_string(self.id, 'raise Exception("spam")')
899
900 def test_SystemExit(self):
901 with self.assert_run_failed(SystemExit, '42'):
902 interpreters.run_string(self.id, 'raise SystemExit(42)')
903
904 def test_sys_exit(self):
905 with self.assert_run_failed(SystemExit):
906 interpreters.run_string(self.id, dedent("""
907 import sys
908 sys.exit()
909 """))
910
911 with self.assert_run_failed(SystemExit, '42'):
912 interpreters.run_string(self.id, dedent("""
913 import sys
914 sys.exit(42)
915 """))
916
917 def test_with_shared(self):
918 r, w = os.pipe()
919
920 shared = {
921 'spam': b'ham',
922 'eggs': b'-1',
923 'cheddar': None,
924 }
925 script = dedent(f"""
926 eggs = int(eggs)
927 spam = 42
928 result = spam + eggs
929
930 ns = dict(vars())
931 del ns['__builtins__']
932 import pickle
933 with open({w}, 'wb') as chan:
934 pickle.dump(ns, chan)
935 """)
936 interpreters.run_string(self.id, script, shared)
937 with open(r, 'rb') as chan:
938 ns = pickle.load(chan)
939
940 self.assertEqual(ns['spam'], 42)
941 self.assertEqual(ns['eggs'], -1)
942 self.assertEqual(ns['result'], 41)
943 self.assertIsNone(ns['cheddar'])
944
945 def test_shared_overwrites(self):
946 interpreters.run_string(self.id, dedent("""
947 spam = 'eggs'
948 ns1 = dict(vars())
949 del ns1['__builtins__']
950 """))
951
952 shared = {'spam': b'ham'}
953 script = dedent(f"""
954 ns2 = dict(vars())
955 del ns2['__builtins__']
956 """)
957 interpreters.run_string(self.id, script, shared)
958
959 r, w = os.pipe()
960 script = dedent(f"""
961 ns = dict(vars())
962 del ns['__builtins__']
963 import pickle
964 with open({w}, 'wb') as chan:
965 pickle.dump(ns, chan)
966 """)
967 interpreters.run_string(self.id, script)
968 with open(r, 'rb') as chan:
969 ns = pickle.load(chan)
970
971 self.assertEqual(ns['ns1']['spam'], 'eggs')
972 self.assertEqual(ns['ns2']['spam'], b'ham')
973 self.assertEqual(ns['spam'], b'ham')
974
975 def test_shared_overwrites_default_vars(self):
976 r, w = os.pipe()
977
978 shared = {'__name__': b'not __main__'}
979 script = dedent(f"""
980 spam = 42
981
982 ns = dict(vars())
983 del ns['__builtins__']
984 import pickle
985 with open({w}, 'wb') as chan:
986 pickle.dump(ns, chan)
987 """)
988 interpreters.run_string(self.id, script, shared)
989 with open(r, 'rb') as chan:
990 ns = pickle.load(chan)
991
992 self.assertEqual(ns['__name__'], b'not __main__')
993
994 def test_main_reused(self):
995 r, w = os.pipe()
996 interpreters.run_string(self.id, dedent(f"""
997 spam = True
998
999 ns = dict(vars())
1000 del ns['__builtins__']
1001 import pickle
1002 with open({w}, 'wb') as chan:
1003 pickle.dump(ns, chan)
1004 del ns, pickle, chan
1005 """))
1006 with open(r, 'rb') as chan:
1007 ns1 = pickle.load(chan)
1008
1009 r, w = os.pipe()
1010 interpreters.run_string(self.id, dedent(f"""
1011 eggs = False
1012
1013 ns = dict(vars())
1014 del ns['__builtins__']
1015 import pickle
1016 with open({w}, 'wb') as chan:
1017 pickle.dump(ns, chan)
1018 """))
1019 with open(r, 'rb') as chan:
1020 ns2 = pickle.load(chan)
1021
1022 self.assertIn('spam', ns1)
1023 self.assertNotIn('eggs', ns1)
1024 self.assertIn('eggs', ns2)
1025 self.assertIn('spam', ns2)
1026
1027 def test_execution_namespace_is_main(self):
1028 r, w = os.pipe()
1029
1030 script = dedent(f"""
1031 spam = 42
1032
1033 ns = dict(vars())
1034 ns['__builtins__'] = str(ns['__builtins__'])
1035 import pickle
1036 with open({w}, 'wb') as chan:
1037 pickle.dump(ns, chan)
1038 """)
1039 interpreters.run_string(self.id, script)
1040 with open(r, 'rb') as chan:
1041 ns = pickle.load(chan)
1042
1043 ns.pop('__builtins__')
1044 ns.pop('__loader__')
1045 self.assertEqual(ns, {
1046 '__name__': '__main__',
1047 '__annotations__': {},
1048 '__doc__': None,
1049 '__package__': None,
1050 '__spec__': None,
1051 'spam': 42,
1052 })
1053
Eric Snow4c6955e2018-02-16 18:53:40 -07001054 # XXX Fix this test!
1055 @unittest.skip('blocking forever')
Eric Snow7f8bfc92018-01-29 18:23:44 -07001056 def test_still_running_at_exit(self):
1057 script = dedent(f"""
1058 from textwrap import dedent
1059 import threading
1060 import _xxsubinterpreters as _interpreters
Eric Snow4c6955e2018-02-16 18:53:40 -07001061 id = _interpreters.create()
Eric Snow7f8bfc92018-01-29 18:23:44 -07001062 def f():
1063 _interpreters.run_string(id, dedent('''
1064 import time
1065 # Give plenty of time for the main interpreter to finish.
1066 time.sleep(1_000_000)
1067 '''))
1068
1069 t = threading.Thread(target=f)
1070 t.start()
1071 """)
1072 with support.temp_dir() as dirname:
1073 filename = script_helper.make_script(dirname, 'interp', script)
1074 with script_helper.spawn_python(filename) as proc:
1075 retcode = proc.wait()
1076
1077 self.assertEqual(retcode, 0)
1078
1079
Eric Snow6d2cd902018-05-16 15:04:57 -04001080##################################
1081# channel tests
1082
Eric Snow7f8bfc92018-01-29 18:23:44 -07001083class ChannelIDTests(TestBase):
1084
1085 def test_default_kwargs(self):
1086 cid = interpreters._channel_id(10, force=True)
1087
1088 self.assertEqual(int(cid), 10)
1089 self.assertEqual(cid.end, 'both')
1090
1091 def test_with_kwargs(self):
1092 cid = interpreters._channel_id(10, send=True, force=True)
1093 self.assertEqual(cid.end, 'send')
1094
1095 cid = interpreters._channel_id(10, send=True, recv=False, force=True)
1096 self.assertEqual(cid.end, 'send')
1097
1098 cid = interpreters._channel_id(10, recv=True, force=True)
1099 self.assertEqual(cid.end, 'recv')
1100
1101 cid = interpreters._channel_id(10, recv=True, send=False, force=True)
1102 self.assertEqual(cid.end, 'recv')
1103
1104 cid = interpreters._channel_id(10, send=True, recv=True, force=True)
1105 self.assertEqual(cid.end, 'both')
1106
1107 def test_coerce_id(self):
1108 cid = interpreters._channel_id('10', force=True)
1109 self.assertEqual(int(cid), 10)
1110
1111 cid = interpreters._channel_id(10.0, force=True)
1112 self.assertEqual(int(cid), 10)
1113
1114 class Int(str):
1115 def __init__(self, value):
1116 self._value = value
1117 def __int__(self):
1118 return self._value
1119
1120 cid = interpreters._channel_id(Int(10), force=True)
1121 self.assertEqual(int(cid), 10)
1122
1123 def test_bad_id(self):
Eric Snow4e9da0d2018-02-02 21:49:49 -07001124 for cid in [-1, 'spam']:
Eric Snow7f8bfc92018-01-29 18:23:44 -07001125 with self.subTest(cid):
1126 with self.assertRaises(ValueError):
1127 interpreters._channel_id(cid)
Eric Snow4e9da0d2018-02-02 21:49:49 -07001128 with self.assertRaises(OverflowError):
1129 interpreters._channel_id(2**64)
Eric Snow7f8bfc92018-01-29 18:23:44 -07001130 with self.assertRaises(TypeError):
1131 interpreters._channel_id(object())
1132
1133 def test_bad_kwargs(self):
1134 with self.assertRaises(ValueError):
1135 interpreters._channel_id(10, send=False, recv=False)
1136
1137 def test_does_not_exist(self):
1138 cid = interpreters.channel_create()
1139 with self.assertRaises(interpreters.ChannelNotFoundError):
1140 interpreters._channel_id(int(cid) + 1) # unforced
1141
Eric Snow6d2cd902018-05-16 15:04:57 -04001142 def test_str(self):
1143 cid = interpreters._channel_id(10, force=True)
1144 self.assertEqual(str(cid), '10')
1145
Eric Snow7f8bfc92018-01-29 18:23:44 -07001146 def test_repr(self):
1147 cid = interpreters._channel_id(10, force=True)
1148 self.assertEqual(repr(cid), 'ChannelID(10)')
1149
1150 cid = interpreters._channel_id(10, send=True, force=True)
1151 self.assertEqual(repr(cid), 'ChannelID(10, send=True)')
1152
1153 cid = interpreters._channel_id(10, recv=True, force=True)
1154 self.assertEqual(repr(cid), 'ChannelID(10, recv=True)')
1155
1156 cid = interpreters._channel_id(10, send=True, recv=True, force=True)
1157 self.assertEqual(repr(cid), 'ChannelID(10)')
1158
1159 def test_equality(self):
1160 cid1 = interpreters.channel_create()
1161 cid2 = interpreters._channel_id(int(cid1))
1162 cid3 = interpreters.channel_create()
1163
1164 self.assertTrue(cid1 == cid1)
1165 self.assertTrue(cid1 == cid2)
1166 self.assertTrue(cid1 == int(cid1))
1167 self.assertFalse(cid1 == cid3)
1168
1169 self.assertFalse(cid1 != cid1)
1170 self.assertFalse(cid1 != cid2)
1171 self.assertTrue(cid1 != cid3)
1172
1173
1174class ChannelTests(TestBase):
1175
Eric Snow6d2cd902018-05-16 15:04:57 -04001176 def test_create_cid(self):
1177 cid = interpreters.channel_create()
1178 self.assertIsInstance(cid, interpreters.ChannelID)
1179
Eric Snow7f8bfc92018-01-29 18:23:44 -07001180 def test_sequential_ids(self):
1181 before = interpreters.channel_list_all()
1182 id1 = interpreters.channel_create()
1183 id2 = interpreters.channel_create()
1184 id3 = interpreters.channel_create()
1185 after = interpreters.channel_list_all()
1186
1187 self.assertEqual(id2, int(id1) + 1)
1188 self.assertEqual(id3, int(id2) + 1)
1189 self.assertEqual(set(after) - set(before), {id1, id2, id3})
1190
1191 def test_ids_global(self):
1192 id1 = interpreters.create()
1193 out = _run_output(id1, dedent("""
1194 import _xxsubinterpreters as _interpreters
1195 cid = _interpreters.channel_create()
Eric Snow6d2cd902018-05-16 15:04:57 -04001196 print(cid)
Eric Snow7f8bfc92018-01-29 18:23:44 -07001197 """))
1198 cid1 = int(out.strip())
1199
1200 id2 = interpreters.create()
1201 out = _run_output(id2, dedent("""
1202 import _xxsubinterpreters as _interpreters
1203 cid = _interpreters.channel_create()
Eric Snow6d2cd902018-05-16 15:04:57 -04001204 print(cid)
Eric Snow7f8bfc92018-01-29 18:23:44 -07001205 """))
1206 cid2 = int(out.strip())
1207
1208 self.assertEqual(cid2, int(cid1) + 1)
1209
1210 ####################
1211
Eric Snow7f8bfc92018-01-29 18:23:44 -07001212 def test_send_recv_main(self):
1213 cid = interpreters.channel_create()
1214 orig = b'spam'
1215 interpreters.channel_send(cid, orig)
1216 obj = interpreters.channel_recv(cid)
1217
1218 self.assertEqual(obj, orig)
1219 self.assertIsNot(obj, orig)
1220
1221 def test_send_recv_same_interpreter(self):
1222 id1 = interpreters.create()
1223 out = _run_output(id1, dedent("""
1224 import _xxsubinterpreters as _interpreters
1225 cid = _interpreters.channel_create()
1226 orig = b'spam'
1227 _interpreters.channel_send(cid, orig)
1228 obj = _interpreters.channel_recv(cid)
1229 assert obj is not orig
1230 assert obj == orig
1231 """))
1232
1233 def test_send_recv_different_interpreters(self):
1234 cid = interpreters.channel_create()
1235 id1 = interpreters.create()
1236 out = _run_output(id1, dedent(f"""
1237 import _xxsubinterpreters as _interpreters
Eric Snow6d2cd902018-05-16 15:04:57 -04001238 _interpreters.channel_send({cid}, b'spam')
Eric Snow7f8bfc92018-01-29 18:23:44 -07001239 """))
1240 obj = interpreters.channel_recv(cid)
1241
1242 self.assertEqual(obj, b'spam')
1243
Eric Snowf53d9f22018-02-20 16:30:17 -07001244 def test_send_recv_different_threads(self):
1245 cid = interpreters.channel_create()
1246
1247 def f():
1248 while True:
1249 try:
1250 obj = interpreters.channel_recv(cid)
1251 break
1252 except interpreters.ChannelEmptyError:
1253 time.sleep(0.1)
1254 interpreters.channel_send(cid, obj)
1255 t = threading.Thread(target=f)
1256 t.start()
1257
1258 interpreters.channel_send(cid, b'spam')
1259 t.join()
1260 obj = interpreters.channel_recv(cid)
1261
1262 self.assertEqual(obj, b'spam')
1263
1264 def test_send_recv_different_interpreters_and_threads(self):
1265 cid = interpreters.channel_create()
1266 id1 = interpreters.create()
1267 out = None
1268
1269 def f():
1270 nonlocal out
1271 out = _run_output(id1, dedent(f"""
1272 import time
1273 import _xxsubinterpreters as _interpreters
1274 while True:
1275 try:
Eric Snow6d2cd902018-05-16 15:04:57 -04001276 obj = _interpreters.channel_recv({cid})
Eric Snowf53d9f22018-02-20 16:30:17 -07001277 break
1278 except _interpreters.ChannelEmptyError:
1279 time.sleep(0.1)
1280 assert(obj == b'spam')
Eric Snow6d2cd902018-05-16 15:04:57 -04001281 _interpreters.channel_send({cid}, b'eggs')
Eric Snowf53d9f22018-02-20 16:30:17 -07001282 """))
1283 t = threading.Thread(target=f)
1284 t.start()
1285
1286 interpreters.channel_send(cid, b'spam')
1287 t.join()
1288 obj = interpreters.channel_recv(cid)
1289
1290 self.assertEqual(obj, b'eggs')
1291
Eric Snow7f8bfc92018-01-29 18:23:44 -07001292 def test_send_not_found(self):
1293 with self.assertRaises(interpreters.ChannelNotFoundError):
1294 interpreters.channel_send(10, b'spam')
1295
1296 def test_recv_not_found(self):
1297 with self.assertRaises(interpreters.ChannelNotFoundError):
1298 interpreters.channel_recv(10)
1299
1300 def test_recv_empty(self):
1301 cid = interpreters.channel_create()
1302 with self.assertRaises(interpreters.ChannelEmptyError):
1303 interpreters.channel_recv(cid)
1304
Eric Snow6d2cd902018-05-16 15:04:57 -04001305 def test_run_string_arg_unresolved(self):
Eric Snow7f8bfc92018-01-29 18:23:44 -07001306 cid = interpreters.channel_create()
1307 interp = interpreters.create()
1308
1309 out = _run_output(interp, dedent("""
1310 import _xxsubinterpreters as _interpreters
1311 print(cid.end)
1312 _interpreters.channel_send(cid, b'spam')
1313 """),
1314 dict(cid=cid.send))
1315 obj = interpreters.channel_recv(cid)
1316
1317 self.assertEqual(obj, b'spam')
1318 self.assertEqual(out.strip(), 'send')
1319
Eric Snowab4a1982018-06-13 08:02:39 -06001320 # XXX For now there is no high-level channel into which the
1321 # sent channel ID can be converted...
1322 # Note: this test caused crashes on some buildbots (bpo-33615).
1323 @unittest.skip('disabled until high-level channels exist')
Eric Snow6d2cd902018-05-16 15:04:57 -04001324 def test_run_string_arg_resolved(self):
1325 cid = interpreters.channel_create()
1326 cid = interpreters._channel_id(cid, _resolve=True)
1327 interp = interpreters.create()
1328
1329 out = _run_output(interp, dedent("""
1330 import _xxsubinterpreters as _interpreters
Eric Snowab4a1982018-06-13 08:02:39 -06001331 print(chan.id.end)
1332 _interpreters.channel_send(chan.id, b'spam')
Eric Snow6d2cd902018-05-16 15:04:57 -04001333 """),
1334 dict(chan=cid.send))
1335 obj = interpreters.channel_recv(cid)
1336
1337 self.assertEqual(obj, b'spam')
1338 self.assertEqual(out.strip(), 'send')
1339
1340 # close
1341
1342 def test_close_single_user(self):
1343 cid = interpreters.channel_create()
1344 interpreters.channel_send(cid, b'spam')
1345 interpreters.channel_recv(cid)
1346 interpreters.channel_close(cid)
1347
1348 with self.assertRaises(interpreters.ChannelClosedError):
1349 interpreters.channel_send(cid, b'eggs')
1350 with self.assertRaises(interpreters.ChannelClosedError):
1351 interpreters.channel_recv(cid)
1352
1353 def test_close_multiple_users(self):
1354 cid = interpreters.channel_create()
1355 id1 = interpreters.create()
1356 id2 = interpreters.create()
1357 interpreters.run_string(id1, dedent(f"""
1358 import _xxsubinterpreters as _interpreters
1359 _interpreters.channel_send({cid}, b'spam')
1360 """))
1361 interpreters.run_string(id2, dedent(f"""
1362 import _xxsubinterpreters as _interpreters
1363 _interpreters.channel_recv({cid})
1364 """))
1365 interpreters.channel_close(cid)
1366 with self.assertRaises(interpreters.RunFailedError) as cm:
1367 interpreters.run_string(id1, dedent(f"""
1368 _interpreters.channel_send({cid}, b'spam')
1369 """))
1370 self.assertIn('ChannelClosedError', str(cm.exception))
1371 with self.assertRaises(interpreters.RunFailedError) as cm:
1372 interpreters.run_string(id2, dedent(f"""
1373 _interpreters.channel_send({cid}, b'spam')
1374 """))
1375 self.assertIn('ChannelClosedError', str(cm.exception))
1376
1377 def test_close_multiple_times(self):
1378 cid = interpreters.channel_create()
1379 interpreters.channel_send(cid, b'spam')
1380 interpreters.channel_recv(cid)
1381 interpreters.channel_close(cid)
1382
1383 with self.assertRaises(interpreters.ChannelClosedError):
1384 interpreters.channel_close(cid)
1385
Eric Snow3ab01362018-05-17 10:27:09 -04001386 def test_close_empty(self):
1387 tests = [
1388 (False, False),
1389 (True, False),
1390 (False, True),
1391 (True, True),
1392 ]
1393 for send, recv in tests:
1394 with self.subTest((send, recv)):
1395 cid = interpreters.channel_create()
1396 interpreters.channel_send(cid, b'spam')
1397 interpreters.channel_recv(cid)
1398 interpreters.channel_close(cid, send=send, recv=recv)
1399
1400 with self.assertRaises(interpreters.ChannelClosedError):
1401 interpreters.channel_send(cid, b'eggs')
1402 with self.assertRaises(interpreters.ChannelClosedError):
1403 interpreters.channel_recv(cid)
1404
1405 def test_close_defaults_with_unused_items(self):
Eric Snow6d2cd902018-05-16 15:04:57 -04001406 cid = interpreters.channel_create()
1407 interpreters.channel_send(cid, b'spam')
1408 interpreters.channel_send(cid, b'ham')
Eric Snow6d2cd902018-05-16 15:04:57 -04001409
Eric Snow3ab01362018-05-17 10:27:09 -04001410 with self.assertRaises(interpreters.ChannelNotEmptyError):
1411 interpreters.channel_close(cid)
1412 interpreters.channel_recv(cid)
1413 interpreters.channel_send(cid, b'eggs')
1414
1415 def test_close_recv_with_unused_items_unforced(self):
1416 cid = interpreters.channel_create()
1417 interpreters.channel_send(cid, b'spam')
1418 interpreters.channel_send(cid, b'ham')
1419
1420 with self.assertRaises(interpreters.ChannelNotEmptyError):
1421 interpreters.channel_close(cid, recv=True)
1422 interpreters.channel_recv(cid)
1423 interpreters.channel_send(cid, b'eggs')
1424 interpreters.channel_recv(cid)
1425 interpreters.channel_recv(cid)
1426 interpreters.channel_close(cid, recv=True)
1427
1428 def test_close_send_with_unused_items_unforced(self):
1429 cid = interpreters.channel_create()
1430 interpreters.channel_send(cid, b'spam')
1431 interpreters.channel_send(cid, b'ham')
1432 interpreters.channel_close(cid, send=True)
1433
1434 with self.assertRaises(interpreters.ChannelClosedError):
1435 interpreters.channel_send(cid, b'eggs')
1436 interpreters.channel_recv(cid)
1437 interpreters.channel_recv(cid)
1438 with self.assertRaises(interpreters.ChannelClosedError):
1439 interpreters.channel_recv(cid)
1440
1441 def test_close_both_with_unused_items_unforced(self):
1442 cid = interpreters.channel_create()
1443 interpreters.channel_send(cid, b'spam')
1444 interpreters.channel_send(cid, b'ham')
1445
1446 with self.assertRaises(interpreters.ChannelNotEmptyError):
1447 interpreters.channel_close(cid, recv=True, send=True)
1448 interpreters.channel_recv(cid)
1449 interpreters.channel_send(cid, b'eggs')
1450 interpreters.channel_recv(cid)
1451 interpreters.channel_recv(cid)
1452 interpreters.channel_close(cid, recv=True)
1453
1454 def test_close_recv_with_unused_items_forced(self):
1455 cid = interpreters.channel_create()
1456 interpreters.channel_send(cid, b'spam')
1457 interpreters.channel_send(cid, b'ham')
1458 interpreters.channel_close(cid, recv=True, force=True)
1459
1460 with self.assertRaises(interpreters.ChannelClosedError):
1461 interpreters.channel_send(cid, b'eggs')
1462 with self.assertRaises(interpreters.ChannelClosedError):
1463 interpreters.channel_recv(cid)
1464
1465 def test_close_send_with_unused_items_forced(self):
1466 cid = interpreters.channel_create()
1467 interpreters.channel_send(cid, b'spam')
1468 interpreters.channel_send(cid, b'ham')
1469 interpreters.channel_close(cid, send=True, force=True)
1470
1471 with self.assertRaises(interpreters.ChannelClosedError):
1472 interpreters.channel_send(cid, b'eggs')
1473 with self.assertRaises(interpreters.ChannelClosedError):
1474 interpreters.channel_recv(cid)
1475
1476 def test_close_both_with_unused_items_forced(self):
1477 cid = interpreters.channel_create()
1478 interpreters.channel_send(cid, b'spam')
1479 interpreters.channel_send(cid, b'ham')
1480 interpreters.channel_close(cid, send=True, recv=True, force=True)
1481
1482 with self.assertRaises(interpreters.ChannelClosedError):
1483 interpreters.channel_send(cid, b'eggs')
Eric Snow6d2cd902018-05-16 15:04:57 -04001484 with self.assertRaises(interpreters.ChannelClosedError):
1485 interpreters.channel_recv(cid)
1486
1487 def test_close_never_used(self):
1488 cid = interpreters.channel_create()
1489 interpreters.channel_close(cid)
1490
1491 with self.assertRaises(interpreters.ChannelClosedError):
1492 interpreters.channel_send(cid, b'spam')
1493 with self.assertRaises(interpreters.ChannelClosedError):
1494 interpreters.channel_recv(cid)
1495
1496 def test_close_by_unassociated_interp(self):
1497 cid = interpreters.channel_create()
1498 interpreters.channel_send(cid, b'spam')
1499 interp = interpreters.create()
1500 interpreters.run_string(interp, dedent(f"""
1501 import _xxsubinterpreters as _interpreters
Eric Snow3ab01362018-05-17 10:27:09 -04001502 _interpreters.channel_close({cid}, force=True)
Eric Snow6d2cd902018-05-16 15:04:57 -04001503 """))
1504 with self.assertRaises(interpreters.ChannelClosedError):
1505 interpreters.channel_recv(cid)
1506 with self.assertRaises(interpreters.ChannelClosedError):
1507 interpreters.channel_close(cid)
1508
1509 def test_close_used_multiple_times_by_single_user(self):
1510 cid = interpreters.channel_create()
1511 interpreters.channel_send(cid, b'spam')
1512 interpreters.channel_send(cid, b'spam')
1513 interpreters.channel_send(cid, b'spam')
1514 interpreters.channel_recv(cid)
Eric Snow3ab01362018-05-17 10:27:09 -04001515 interpreters.channel_close(cid, force=True)
Eric Snow6d2cd902018-05-16 15:04:57 -04001516
1517 with self.assertRaises(interpreters.ChannelClosedError):
1518 interpreters.channel_send(cid, b'eggs')
1519 with self.assertRaises(interpreters.ChannelClosedError):
1520 interpreters.channel_recv(cid)
1521
1522
1523class ChannelReleaseTests(TestBase):
1524
1525 # XXX Add more test coverage a la the tests for close().
1526
1527 """
1528 - main / interp / other
1529 - run in: current thread / new thread / other thread / different threads
1530 - end / opposite
1531 - force / no force
1532 - used / not used (associated / not associated)
1533 - empty / emptied / never emptied / partly emptied
1534 - closed / not closed
1535 - released / not released
1536 - creator (interp) / other
1537 - associated interpreter not running
1538 - associated interpreter destroyed
1539 """
1540
1541 """
1542 use
1543 pre-release
1544 release
1545 after
1546 check
1547 """
1548
1549 """
1550 release in: main, interp1
1551 creator: same, other (incl. interp2)
1552
1553 use: None,send,recv,send/recv in None,same,other(incl. interp2),same+other(incl. interp2),all
1554 pre-release: None,send,recv,both in None,same,other(incl. interp2),same+other(incl. interp2),all
1555 pre-release forced: None,send,recv,both in None,same,other(incl. interp2),same+other(incl. interp2),all
1556
1557 release: same
1558 release forced: same
1559
1560 use after: None,send,recv,send/recv in None,same,other(incl. interp2),same+other(incl. interp2),all
1561 release after: None,send,recv,send/recv in None,same,other(incl. interp2),same+other(incl. interp2),all
1562 check released: send/recv for same/other(incl. interp2)
1563 check closed: send/recv for same/other(incl. interp2)
1564 """
1565
1566 def test_single_user(self):
1567 cid = interpreters.channel_create()
1568 interpreters.channel_send(cid, b'spam')
1569 interpreters.channel_recv(cid)
1570 interpreters.channel_release(cid, send=True, recv=True)
1571
1572 with self.assertRaises(interpreters.ChannelClosedError):
1573 interpreters.channel_send(cid, b'eggs')
1574 with self.assertRaises(interpreters.ChannelClosedError):
1575 interpreters.channel_recv(cid)
1576
1577 def test_multiple_users(self):
1578 cid = interpreters.channel_create()
1579 id1 = interpreters.create()
1580 id2 = interpreters.create()
1581 interpreters.run_string(id1, dedent(f"""
1582 import _xxsubinterpreters as _interpreters
1583 _interpreters.channel_send({cid}, b'spam')
1584 """))
1585 out = _run_output(id2, dedent(f"""
1586 import _xxsubinterpreters as _interpreters
1587 obj = _interpreters.channel_recv({cid})
1588 _interpreters.channel_release({cid})
1589 print(repr(obj))
1590 """))
1591 interpreters.run_string(id1, dedent(f"""
1592 _interpreters.channel_release({cid})
1593 """))
1594
1595 self.assertEqual(out.strip(), "b'spam'")
1596
1597 def test_no_kwargs(self):
1598 cid = interpreters.channel_create()
1599 interpreters.channel_send(cid, b'spam')
1600 interpreters.channel_recv(cid)
1601 interpreters.channel_release(cid)
1602
1603 with self.assertRaises(interpreters.ChannelClosedError):
1604 interpreters.channel_send(cid, b'eggs')
1605 with self.assertRaises(interpreters.ChannelClosedError):
1606 interpreters.channel_recv(cid)
1607
1608 def test_multiple_times(self):
1609 cid = interpreters.channel_create()
1610 interpreters.channel_send(cid, b'spam')
1611 interpreters.channel_recv(cid)
1612 interpreters.channel_release(cid, send=True, recv=True)
1613
1614 with self.assertRaises(interpreters.ChannelClosedError):
1615 interpreters.channel_release(cid, send=True, recv=True)
1616
1617 def test_with_unused_items(self):
1618 cid = interpreters.channel_create()
1619 interpreters.channel_send(cid, b'spam')
1620 interpreters.channel_send(cid, b'ham')
1621 interpreters.channel_release(cid, send=True, recv=True)
1622
1623 with self.assertRaises(interpreters.ChannelClosedError):
1624 interpreters.channel_recv(cid)
1625
1626 def test_never_used(self):
1627 cid = interpreters.channel_create()
1628 interpreters.channel_release(cid)
1629
1630 with self.assertRaises(interpreters.ChannelClosedError):
1631 interpreters.channel_send(cid, b'spam')
1632 with self.assertRaises(interpreters.ChannelClosedError):
1633 interpreters.channel_recv(cid)
1634
1635 def test_by_unassociated_interp(self):
1636 cid = interpreters.channel_create()
1637 interpreters.channel_send(cid, b'spam')
1638 interp = interpreters.create()
1639 interpreters.run_string(interp, dedent(f"""
1640 import _xxsubinterpreters as _interpreters
1641 _interpreters.channel_release({cid})
1642 """))
1643 obj = interpreters.channel_recv(cid)
1644 interpreters.channel_release(cid)
1645
1646 with self.assertRaises(interpreters.ChannelClosedError):
1647 interpreters.channel_send(cid, b'eggs')
1648 self.assertEqual(obj, b'spam')
1649
1650 def test_close_if_unassociated(self):
1651 # XXX Something's not right with this test...
1652 cid = interpreters.channel_create()
1653 interp = interpreters.create()
1654 interpreters.run_string(interp, dedent(f"""
1655 import _xxsubinterpreters as _interpreters
1656 obj = _interpreters.channel_send({cid}, b'spam')
1657 _interpreters.channel_release({cid})
1658 """))
1659
1660 with self.assertRaises(interpreters.ChannelClosedError):
1661 interpreters.channel_recv(cid)
1662
1663 def test_partially(self):
1664 # XXX Is partial close too weird/confusing?
1665 cid = interpreters.channel_create()
1666 interpreters.channel_send(cid, None)
1667 interpreters.channel_recv(cid)
1668 interpreters.channel_send(cid, b'spam')
1669 interpreters.channel_release(cid, send=True)
1670 obj = interpreters.channel_recv(cid)
1671
1672 self.assertEqual(obj, b'spam')
1673
1674 def test_used_multiple_times_by_single_user(self):
1675 cid = interpreters.channel_create()
1676 interpreters.channel_send(cid, b'spam')
1677 interpreters.channel_send(cid, b'spam')
1678 interpreters.channel_send(cid, b'spam')
1679 interpreters.channel_recv(cid)
1680 interpreters.channel_release(cid, send=True, recv=True)
1681
1682 with self.assertRaises(interpreters.ChannelClosedError):
1683 interpreters.channel_send(cid, b'eggs')
1684 with self.assertRaises(interpreters.ChannelClosedError):
1685 interpreters.channel_recv(cid)
1686
1687
1688class ChannelCloseFixture(namedtuple('ChannelCloseFixture',
1689 'end interp other extra creator')):
1690
1691 # Set this to True to avoid creating interpreters, e.g. when
1692 # scanning through test permutations without running them.
1693 QUICK = False
1694
1695 def __new__(cls, end, interp, other, extra, creator):
1696 assert end in ('send', 'recv')
1697 if cls.QUICK:
1698 known = {}
1699 else:
1700 interp = Interpreter.from_raw(interp)
1701 other = Interpreter.from_raw(other)
1702 extra = Interpreter.from_raw(extra)
1703 known = {
1704 interp.name: interp,
1705 other.name: other,
1706 extra.name: extra,
1707 }
1708 if not creator:
1709 creator = 'same'
1710 self = super().__new__(cls, end, interp, other, extra, creator)
1711 self._prepped = set()
1712 self._state = ChannelState()
1713 self._known = known
1714 return self
1715
1716 @property
1717 def state(self):
1718 return self._state
1719
1720 @property
1721 def cid(self):
1722 try:
1723 return self._cid
1724 except AttributeError:
1725 creator = self._get_interpreter(self.creator)
1726 self._cid = self._new_channel(creator)
1727 return self._cid
1728
1729 def get_interpreter(self, interp):
1730 interp = self._get_interpreter(interp)
1731 self._prep_interpreter(interp)
1732 return interp
1733
1734 def expect_closed_error(self, end=None):
1735 if end is None:
1736 end = self.end
1737 if end == 'recv' and self.state.closed == 'send':
1738 return False
1739 return bool(self.state.closed)
1740
1741 def prep_interpreter(self, interp):
1742 self._prep_interpreter(interp)
1743
1744 def record_action(self, action, result):
1745 self._state = result
1746
1747 def clean_up(self):
1748 clean_up_interpreters()
1749 clean_up_channels()
1750
1751 # internal methods
1752
1753 def _new_channel(self, creator):
1754 if creator.name == 'main':
1755 return interpreters.channel_create()
1756 else:
1757 ch = interpreters.channel_create()
1758 run_interp(creator.id, f"""
1759 import _xxsubinterpreters
1760 cid = _xxsubinterpreters.channel_create()
1761 # We purposefully send back an int to avoid tying the
1762 # channel to the other interpreter.
1763 _xxsubinterpreters.channel_send({ch}, int(cid))
1764 del _xxsubinterpreters
1765 """)
1766 self._cid = interpreters.channel_recv(ch)
1767 return self._cid
1768
1769 def _get_interpreter(self, interp):
1770 if interp in ('same', 'interp'):
1771 return self.interp
1772 elif interp == 'other':
1773 return self.other
1774 elif interp == 'extra':
1775 return self.extra
1776 else:
1777 name = interp
1778 try:
1779 interp = self._known[name]
1780 except KeyError:
1781 interp = self._known[name] = Interpreter(name)
1782 return interp
1783
1784 def _prep_interpreter(self, interp):
1785 if interp.id in self._prepped:
1786 return
1787 self._prepped.add(interp.id)
1788 if interp.name == 'main':
1789 return
1790 run_interp(interp.id, f"""
1791 import _xxsubinterpreters as interpreters
1792 import test.test__xxsubinterpreters as helpers
1793 ChannelState = helpers.ChannelState
1794 try:
1795 cid
1796 except NameError:
1797 cid = interpreters._channel_id({self.cid})
1798 """)
1799
1800
1801@unittest.skip('these tests take several hours to run')
1802class ExhaustiveChannelTests(TestBase):
1803
1804 """
1805 - main / interp / other
1806 - run in: current thread / new thread / other thread / different threads
1807 - end / opposite
1808 - force / no force
1809 - used / not used (associated / not associated)
1810 - empty / emptied / never emptied / partly emptied
1811 - closed / not closed
1812 - released / not released
1813 - creator (interp) / other
1814 - associated interpreter not running
1815 - associated interpreter destroyed
1816
1817 - close after unbound
1818 """
1819
1820 """
1821 use
1822 pre-close
1823 close
1824 after
1825 check
1826 """
1827
1828 """
1829 close in: main, interp1
1830 creator: same, other, extra
1831
1832 use: None,send,recv,send/recv in None,same,other,same+other,all
1833 pre-close: None,send,recv in None,same,other,same+other,all
1834 pre-close forced: None,send,recv in None,same,other,same+other,all
1835
1836 close: same
1837 close forced: same
1838
1839 use after: None,send,recv,send/recv in None,same,other,extra,same+other,all
1840 close after: None,send,recv,send/recv in None,same,other,extra,same+other,all
1841 check closed: send/recv for same/other(incl. interp2)
1842 """
1843
1844 def iter_action_sets(self):
1845 # - used / not used (associated / not associated)
1846 # - empty / emptied / never emptied / partly emptied
1847 # - closed / not closed
1848 # - released / not released
1849
1850 # never used
1851 yield []
1852
1853 # only pre-closed (and possible used after)
1854 for closeactions in self._iter_close_action_sets('same', 'other'):
1855 yield closeactions
1856 for postactions in self._iter_post_close_action_sets():
1857 yield closeactions + postactions
1858 for closeactions in self._iter_close_action_sets('other', 'extra'):
1859 yield closeactions
1860 for postactions in self._iter_post_close_action_sets():
1861 yield closeactions + postactions
1862
1863 # used
1864 for useactions in self._iter_use_action_sets('same', 'other'):
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 for useactions in self._iter_use_action_sets('other', 'extra'):
1877 yield useactions
1878 for closeactions in self._iter_close_action_sets('same', 'other'):
1879 actions = useactions + closeactions
1880 yield actions
1881 for postactions in self._iter_post_close_action_sets():
1882 yield actions + postactions
1883 for closeactions in self._iter_close_action_sets('other', 'extra'):
1884 actions = useactions + closeactions
1885 yield actions
1886 for postactions in self._iter_post_close_action_sets():
1887 yield actions + postactions
1888
1889 def _iter_use_action_sets(self, interp1, interp2):
1890 interps = (interp1, interp2)
1891
1892 # only recv end used
1893 yield [
1894 ChannelAction('use', 'recv', interp1),
1895 ]
1896 yield [
1897 ChannelAction('use', 'recv', interp2),
1898 ]
1899 yield [
1900 ChannelAction('use', 'recv', interp1),
1901 ChannelAction('use', 'recv', interp2),
1902 ]
1903
1904 # never emptied
1905 yield [
1906 ChannelAction('use', 'send', interp1),
1907 ]
1908 yield [
1909 ChannelAction('use', 'send', interp2),
1910 ]
1911 yield [
1912 ChannelAction('use', 'send', interp1),
1913 ChannelAction('use', 'send', interp2),
1914 ]
1915
1916 # partially emptied
1917 for interp1 in interps:
1918 for interp2 in interps:
1919 for interp3 in interps:
1920 yield [
1921 ChannelAction('use', 'send', interp1),
1922 ChannelAction('use', 'send', interp2),
1923 ChannelAction('use', 'recv', interp3),
1924 ]
1925
1926 # fully emptied
1927 for interp1 in interps:
1928 for interp2 in interps:
1929 for interp3 in interps:
1930 for interp4 in interps:
1931 yield [
1932 ChannelAction('use', 'send', interp1),
1933 ChannelAction('use', 'send', interp2),
1934 ChannelAction('use', 'recv', interp3),
1935 ChannelAction('use', 'recv', interp4),
1936 ]
1937
1938 def _iter_close_action_sets(self, interp1, interp2):
1939 ends = ('recv', 'send')
1940 interps = (interp1, interp2)
1941 for force in (True, False):
1942 op = 'force-close' if force else 'close'
1943 for interp in interps:
1944 for end in ends:
1945 yield [
1946 ChannelAction(op, end, interp),
1947 ]
1948 for recvop in ('close', 'force-close'):
1949 for sendop in ('close', 'force-close'):
1950 for recv in interps:
1951 for send in interps:
1952 yield [
1953 ChannelAction(recvop, 'recv', recv),
1954 ChannelAction(sendop, 'send', send),
1955 ]
1956
1957 def _iter_post_close_action_sets(self):
1958 for interp in ('same', 'extra', 'other'):
1959 yield [
1960 ChannelAction('use', 'recv', interp),
1961 ]
1962 yield [
1963 ChannelAction('use', 'send', interp),
1964 ]
1965
1966 def run_actions(self, fix, actions):
1967 for action in actions:
1968 self.run_action(fix, action)
1969
1970 def run_action(self, fix, action, *, hideclosed=True):
1971 end = action.resolve_end(fix.end)
1972 interp = action.resolve_interp(fix.interp, fix.other, fix.extra)
1973 fix.prep_interpreter(interp)
1974 if interp.name == 'main':
1975 result = run_action(
1976 fix.cid,
1977 action.action,
1978 end,
1979 fix.state,
1980 hideclosed=hideclosed,
1981 )
1982 fix.record_action(action, result)
1983 else:
1984 _cid = interpreters.channel_create()
1985 run_interp(interp.id, f"""
1986 result = helpers.run_action(
1987 {fix.cid},
1988 {repr(action.action)},
1989 {repr(end)},
1990 {repr(fix.state)},
1991 hideclosed={hideclosed},
1992 )
1993 interpreters.channel_send({_cid}, result.pending.to_bytes(1, 'little'))
1994 interpreters.channel_send({_cid}, b'X' if result.closed else b'')
1995 """)
1996 result = ChannelState(
1997 pending=int.from_bytes(interpreters.channel_recv(_cid), 'little'),
1998 closed=bool(interpreters.channel_recv(_cid)),
1999 )
2000 fix.record_action(action, result)
2001
2002 def iter_fixtures(self):
2003 # XXX threads?
2004 interpreters = [
2005 ('main', 'interp', 'extra'),
2006 ('interp', 'main', 'extra'),
2007 ('interp1', 'interp2', 'extra'),
2008 ('interp1', 'interp2', 'main'),
2009 ]
2010 for interp, other, extra in interpreters:
2011 for creator in ('same', 'other', 'creator'):
2012 for end in ('send', 'recv'):
2013 yield ChannelCloseFixture(end, interp, other, extra, creator)
2014
2015 def _close(self, fix, *, force):
2016 op = 'force-close' if force else 'close'
2017 close = ChannelAction(op, fix.end, 'same')
2018 if not fix.expect_closed_error():
2019 self.run_action(fix, close, hideclosed=False)
2020 else:
2021 with self.assertRaises(interpreters.ChannelClosedError):
2022 self.run_action(fix, close, hideclosed=False)
2023
2024 def _assert_closed_in_interp(self, fix, interp=None):
2025 if interp is None or interp.name == 'main':
2026 with self.assertRaises(interpreters.ChannelClosedError):
2027 interpreters.channel_recv(fix.cid)
2028 with self.assertRaises(interpreters.ChannelClosedError):
2029 interpreters.channel_send(fix.cid, b'spam')
2030 with self.assertRaises(interpreters.ChannelClosedError):
2031 interpreters.channel_close(fix.cid)
2032 with self.assertRaises(interpreters.ChannelClosedError):
2033 interpreters.channel_close(fix.cid, force=True)
2034 else:
2035 run_interp(interp.id, f"""
2036 with helpers.expect_channel_closed():
2037 interpreters.channel_recv(cid)
2038 """)
2039 run_interp(interp.id, f"""
2040 with helpers.expect_channel_closed():
2041 interpreters.channel_send(cid, b'spam')
2042 """)
2043 run_interp(interp.id, f"""
2044 with helpers.expect_channel_closed():
2045 interpreters.channel_close(cid)
2046 """)
2047 run_interp(interp.id, f"""
2048 with helpers.expect_channel_closed():
2049 interpreters.channel_close(cid, force=True)
2050 """)
2051
2052 def _assert_closed(self, fix):
2053 self.assertTrue(fix.state.closed)
2054
2055 for _ in range(fix.state.pending):
2056 interpreters.channel_recv(fix.cid)
2057 self._assert_closed_in_interp(fix)
2058
2059 for interp in ('same', 'other'):
2060 interp = fix.get_interpreter(interp)
2061 if interp.name == 'main':
2062 continue
2063 self._assert_closed_in_interp(fix, interp)
2064
2065 interp = fix.get_interpreter('fresh')
2066 self._assert_closed_in_interp(fix, interp)
2067
2068 def _iter_close_tests(self, verbose=False):
2069 i = 0
2070 for actions in self.iter_action_sets():
2071 print()
2072 for fix in self.iter_fixtures():
2073 i += 1
2074 if i > 1000:
2075 return
2076 if verbose:
2077 if (i - 1) % 6 == 0:
2078 print()
2079 print(i, fix, '({} actions)'.format(len(actions)))
2080 else:
2081 if (i - 1) % 6 == 0:
2082 print(' ', end='')
2083 print('.', end=''); sys.stdout.flush()
2084 yield i, fix, actions
2085 if verbose:
2086 print('---')
2087 print()
2088
2089 # This is useful for scanning through the possible tests.
2090 def _skim_close_tests(self):
2091 ChannelCloseFixture.QUICK = True
2092 for i, fix, actions in self._iter_close_tests():
2093 pass
2094
2095 def test_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=False)
2102
2103 self._assert_closed(fix)
2104 # XXX Things slow down if we have too many interpreters.
2105 fix.clean_up()
2106
2107 def test_force_close(self):
2108 for i, fix, actions in self._iter_close_tests():
2109 with self.subTest('{} {} {}'.format(i, fix, actions)):
2110 fix.prep_interpreter(fix.interp)
2111 self.run_actions(fix, actions)
2112
2113 self._close(fix, force=True)
2114
2115 self._assert_closed(fix)
2116 # XXX Things slow down if we have too many interpreters.
2117 fix.clean_up()
2118
Eric Snow7f8bfc92018-01-29 18:23:44 -07002119
2120if __name__ == '__main__':
2121 unittest.main()