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