blob: 80eff19152f15c498dffaffc6896c3ea0b7cfbb9 [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 Stinner8f4ef3b2019-07-01 18:28:25 +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
Eric Snow7f8bfc92018-01-29 18:23:44 -070022def _captured_script(script):
23 r, w = os.pipe()
24 indented = script.replace('\n', '\n ')
25 wrapped = dedent(f"""
26 import contextlib
Eric Snow6d2cd902018-05-16 15:04:57 -040027 with open({w}, 'w') as spipe:
28 with contextlib.redirect_stdout(spipe):
Eric Snow7f8bfc92018-01-29 18:23:44 -070029 {indented}
30 """)
31 return wrapped, open(r)
32
33
34def _run_output(interp, request, shared=None):
Eric Snow6d2cd902018-05-16 15:04:57 -040035 script, rpipe = _captured_script(request)
36 with rpipe:
Eric Snow7f8bfc92018-01-29 18:23:44 -070037 interpreters.run_string(interp, script, shared)
Eric Snow6d2cd902018-05-16 15:04:57 -040038 return rpipe.read()
Eric Snow7f8bfc92018-01-29 18:23:44 -070039
40
41@contextlib.contextmanager
42def _running(interp):
43 r, w = os.pipe()
44 def run():
45 interpreters.run_string(interp, dedent(f"""
46 # wait for "signal"
Eric Snow6d2cd902018-05-16 15:04:57 -040047 with open({r}) as rpipe:
48 rpipe.read()
Eric Snow7f8bfc92018-01-29 18:23:44 -070049 """))
50
51 t = threading.Thread(target=run)
52 t.start()
53
54 yield
55
Eric Snow6d2cd902018-05-16 15:04:57 -040056 with open(w, 'w') as spipe:
57 spipe.write('done')
Eric Snow7f8bfc92018-01-29 18:23:44 -070058 t.join()
59
60
Eric Snow6d2cd902018-05-16 15:04:57 -040061#@contextmanager
62#def run_threaded(id, source, **shared):
63# def run():
64# run_interp(id, source, **shared)
65# t = threading.Thread(target=run)
66# t.start()
67# yield
68# t.join()
69
70
71def run_interp(id, source, **shared):
72 _run_interp(id, source, shared)
73
74
75def _run_interp(id, source, shared, _mainns={}):
76 source = dedent(source)
77 main = interpreters.get_main()
78 if main == id:
79 if interpreters.get_current() != main:
80 raise RuntimeError
81 # XXX Run a func?
82 exec(source, _mainns)
83 else:
84 interpreters.run_string(id, source, shared)
85
86
Eric Snow6d2cd902018-05-16 15:04:57 -040087class Interpreter(namedtuple('Interpreter', 'name id')):
88
89 @classmethod
90 def from_raw(cls, raw):
91 if isinstance(raw, cls):
92 return raw
93 elif isinstance(raw, str):
94 return cls(raw)
95 else:
96 raise NotImplementedError
97
98 def __new__(cls, name=None, id=None):
99 main = interpreters.get_main()
100 if id == main:
101 if not name:
102 name = 'main'
103 elif name != 'main':
104 raise ValueError(
105 'name mismatch (expected "main", got "{}")'.format(name))
106 id = main
107 elif id is not None:
108 if not name:
109 name = 'interp'
110 elif name == 'main':
111 raise ValueError('name mismatch (unexpected "main")')
112 if not isinstance(id, interpreters.InterpreterID):
113 id = interpreters.InterpreterID(id)
114 elif not name or name == 'main':
115 name = 'main'
116 id = main
117 else:
118 id = interpreters.create()
119 self = super().__new__(cls, name, id)
120 return self
121
122
123# XXX expect_channel_closed() is unnecessary once we improve exc propagation.
124
125@contextlib.contextmanager
126def expect_channel_closed():
127 try:
128 yield
129 except interpreters.ChannelClosedError:
130 pass
131 else:
132 assert False, 'channel not closed'
133
134
135class ChannelAction(namedtuple('ChannelAction', 'action end interp')):
136
137 def __new__(cls, action, end=None, interp=None):
138 if not end:
139 end = 'both'
140 if not interp:
141 interp = 'main'
142 self = super().__new__(cls, action, end, interp)
143 return self
144
145 def __init__(self, *args, **kwargs):
146 if self.action == 'use':
147 if self.end not in ('same', 'opposite', 'send', 'recv'):
148 raise ValueError(self.end)
149 elif self.action in ('close', 'force-close'):
150 if self.end not in ('both', 'same', 'opposite', 'send', 'recv'):
151 raise ValueError(self.end)
152 else:
153 raise ValueError(self.action)
154 if self.interp not in ('main', 'same', 'other', 'extra'):
155 raise ValueError(self.interp)
156
157 def resolve_end(self, end):
158 if self.end == 'same':
159 return end
160 elif self.end == 'opposite':
161 return 'recv' if end == 'send' else 'send'
162 else:
163 return self.end
164
165 def resolve_interp(self, interp, other, extra):
166 if self.interp == 'same':
167 return interp
168 elif self.interp == 'other':
169 if other is None:
170 raise RuntimeError
171 return other
172 elif self.interp == 'extra':
173 if extra is None:
174 raise RuntimeError
175 return extra
176 elif self.interp == 'main':
177 if interp.name == 'main':
178 return interp
179 elif other and other.name == 'main':
180 return other
181 else:
182 raise RuntimeError
183 # Per __init__(), there aren't any others.
184
185
186class ChannelState(namedtuple('ChannelState', 'pending closed')):
187
188 def __new__(cls, pending=0, *, closed=False):
189 self = super().__new__(cls, pending, closed)
190 return self
191
192 def incr(self):
193 return type(self)(self.pending + 1, closed=self.closed)
194
195 def decr(self):
196 return type(self)(self.pending - 1, closed=self.closed)
197
198 def close(self, *, force=True):
199 if self.closed:
200 if not force or self.pending == 0:
201 return self
202 return type(self)(0 if force else self.pending, closed=True)
203
204
205def run_action(cid, action, end, state, *, hideclosed=True):
206 if state.closed:
207 if action == 'use' and end == 'recv' and state.pending:
208 expectfail = False
209 else:
210 expectfail = True
211 else:
212 expectfail = False
213
214 try:
215 result = _run_action(cid, action, end, state)
216 except interpreters.ChannelClosedError:
217 if not hideclosed and not expectfail:
218 raise
219 result = state.close()
220 else:
221 if expectfail:
222 raise ... # XXX
223 return result
224
225
226def _run_action(cid, action, end, state):
227 if action == 'use':
228 if end == 'send':
229 interpreters.channel_send(cid, b'spam')
230 return state.incr()
231 elif end == 'recv':
232 if not state.pending:
233 try:
234 interpreters.channel_recv(cid)
235 except interpreters.ChannelEmptyError:
236 return state
237 else:
238 raise Exception('expected ChannelEmptyError')
239 else:
240 interpreters.channel_recv(cid)
241 return state.decr()
242 else:
243 raise ValueError(end)
244 elif action == 'close':
245 kwargs = {}
246 if end in ('recv', 'send'):
247 kwargs[end] = True
248 interpreters.channel_close(cid, **kwargs)
249 return state.close()
250 elif action == 'force-close':
251 kwargs = {
252 'force': True,
253 }
254 if end in ('recv', 'send'):
255 kwargs[end] = True
256 interpreters.channel_close(cid, **kwargs)
257 return state.close(force=True)
258 else:
259 raise ValueError(action)
260
261
262def clean_up_interpreters():
263 for id in interpreters.list_all():
264 if id == 0: # main
265 continue
266 try:
267 interpreters.destroy(id)
268 except RuntimeError:
269 pass # already destroyed
270
271
272def clean_up_channels():
273 for cid in interpreters.channel_list_all():
274 try:
275 interpreters.channel_destroy(cid)
276 except interpreters.ChannelNotFoundError:
277 pass # already destroyed
278
279
280class TestBase(unittest.TestCase):
281
282 def tearDown(self):
283 clean_up_interpreters()
284 clean_up_channels()
285
286
287##################################
288# misc. tests
289
Eric Snow7f8bfc92018-01-29 18:23:44 -0700290class IsShareableTests(unittest.TestCase):
291
292 def test_default_shareables(self):
293 shareables = [
294 # singletons
295 None,
296 # builtin objects
297 b'spam',
Eric Snow6d2cd902018-05-16 15:04:57 -0400298 'spam',
299 10,
300 -10,
Eric Snow7f8bfc92018-01-29 18:23:44 -0700301 ]
302 for obj in shareables:
303 with self.subTest(obj):
304 self.assertTrue(
305 interpreters.is_shareable(obj))
306
307 def test_not_shareable(self):
308 class Cheese:
309 def __init__(self, name):
310 self.name = name
311 def __str__(self):
312 return self.name
313
314 class SubBytes(bytes):
315 """A subclass of a shareable type."""
316
317 not_shareables = [
318 # singletons
319 True,
320 False,
321 NotImplemented,
322 ...,
323 # builtin types and objects
324 type,
325 object,
326 object(),
327 Exception(),
Eric Snow7f8bfc92018-01-29 18:23:44 -0700328 100.0,
Eric Snow7f8bfc92018-01-29 18:23:44 -0700329 # user-defined types and objects
330 Cheese,
331 Cheese('Wensleydale'),
332 SubBytes(b'spam'),
333 ]
334 for obj in not_shareables:
Eric Snow6d2cd902018-05-16 15:04:57 -0400335 with self.subTest(repr(obj)):
Eric Snow7f8bfc92018-01-29 18:23:44 -0700336 self.assertFalse(
337 interpreters.is_shareable(obj))
338
339
Eric Snow6d2cd902018-05-16 15:04:57 -0400340class ShareableTypeTests(unittest.TestCase):
341
342 def setUp(self):
343 super().setUp()
344 self.cid = interpreters.channel_create()
Eric Snow7f8bfc92018-01-29 18:23:44 -0700345
346 def tearDown(self):
Eric Snow6d2cd902018-05-16 15:04:57 -0400347 interpreters.channel_destroy(self.cid)
348 super().tearDown()
Eric Snow7f8bfc92018-01-29 18:23:44 -0700349
Eric Snow6d2cd902018-05-16 15:04:57 -0400350 def _assert_values(self, values):
351 for obj in values:
352 with self.subTest(obj):
353 interpreters.channel_send(self.cid, obj)
354 got = interpreters.channel_recv(self.cid)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700355
Eric Snow6d2cd902018-05-16 15:04:57 -0400356 self.assertEqual(got, obj)
357 self.assertIs(type(got), type(obj))
358 # XXX Check the following in the channel tests?
359 #self.assertIsNot(got, obj)
360
361 def test_singletons(self):
362 for obj in [None]:
363 with self.subTest(obj):
364 interpreters.channel_send(self.cid, obj)
365 got = interpreters.channel_recv(self.cid)
366
367 # XXX What about between interpreters?
368 self.assertIs(got, obj)
369
370 def test_types(self):
371 self._assert_values([
372 b'spam',
373 9999,
374 self.cid,
375 ])
376
377 def test_bytes(self):
378 self._assert_values(i.to_bytes(2, 'little', signed=True)
379 for i in range(-1, 258))
380
381 def test_int(self):
Alexey Izbyshev16f842d2019-02-12 19:06:43 +0300382 self._assert_values(itertools.chain(range(-1, 258),
383 [sys.maxsize, -sys.maxsize - 1]))
384
385 def test_non_shareable_int(self):
386 ints = [
387 sys.maxsize + 1,
388 -sys.maxsize - 2,
389 2**1000,
390 ]
391 for i in ints:
392 with self.subTest(i):
393 with self.assertRaises(OverflowError):
394 interpreters.channel_send(self.cid, i)
Eric Snow6d2cd902018-05-16 15:04:57 -0400395
396
397##################################
398# interpreter tests
Eric Snow7f8bfc92018-01-29 18:23:44 -0700399
400class ListAllTests(TestBase):
401
402 def test_initial(self):
403 main = interpreters.get_main()
404 ids = interpreters.list_all()
405 self.assertEqual(ids, [main])
406
407 def test_after_creating(self):
408 main = interpreters.get_main()
409 first = interpreters.create()
410 second = interpreters.create()
411 ids = interpreters.list_all()
412 self.assertEqual(ids, [main, first, second])
413
414 def test_after_destroying(self):
415 main = interpreters.get_main()
416 first = interpreters.create()
417 second = interpreters.create()
418 interpreters.destroy(first)
419 ids = interpreters.list_all()
420 self.assertEqual(ids, [main, second])
421
422
423class GetCurrentTests(TestBase):
424
425 def test_main(self):
426 main = interpreters.get_main()
427 cur = interpreters.get_current()
428 self.assertEqual(cur, main)
Eric Snow6d2cd902018-05-16 15:04:57 -0400429 self.assertIsInstance(cur, interpreters.InterpreterID)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700430
431 def test_subinterpreter(self):
432 main = interpreters.get_main()
433 interp = interpreters.create()
434 out = _run_output(interp, dedent("""
435 import _xxsubinterpreters as _interpreters
Eric Snow6d2cd902018-05-16 15:04:57 -0400436 cur = _interpreters.get_current()
437 print(cur)
438 assert isinstance(cur, _interpreters.InterpreterID)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700439 """))
440 cur = int(out.strip())
441 _, expected = interpreters.list_all()
442 self.assertEqual(cur, expected)
443 self.assertNotEqual(cur, main)
444
445
446class GetMainTests(TestBase):
447
448 def test_from_main(self):
449 [expected] = interpreters.list_all()
450 main = interpreters.get_main()
451 self.assertEqual(main, expected)
Eric Snow6d2cd902018-05-16 15:04:57 -0400452 self.assertIsInstance(main, interpreters.InterpreterID)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700453
454 def test_from_subinterpreter(self):
455 [expected] = interpreters.list_all()
456 interp = interpreters.create()
457 out = _run_output(interp, dedent("""
458 import _xxsubinterpreters as _interpreters
Eric Snow6d2cd902018-05-16 15:04:57 -0400459 main = _interpreters.get_main()
460 print(main)
461 assert isinstance(main, _interpreters.InterpreterID)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700462 """))
463 main = int(out.strip())
464 self.assertEqual(main, expected)
465
466
467class IsRunningTests(TestBase):
468
469 def test_main(self):
470 main = interpreters.get_main()
471 self.assertTrue(interpreters.is_running(main))
472
473 def test_subinterpreter(self):
474 interp = interpreters.create()
475 self.assertFalse(interpreters.is_running(interp))
476
477 with _running(interp):
478 self.assertTrue(interpreters.is_running(interp))
479 self.assertFalse(interpreters.is_running(interp))
480
481 def test_from_subinterpreter(self):
482 interp = interpreters.create()
483 out = _run_output(interp, dedent(f"""
484 import _xxsubinterpreters as _interpreters
Eric Snow6d2cd902018-05-16 15:04:57 -0400485 if _interpreters.is_running({interp}):
Eric Snow7f8bfc92018-01-29 18:23:44 -0700486 print(True)
487 else:
488 print(False)
489 """))
490 self.assertEqual(out.strip(), 'True')
491
492 def test_already_destroyed(self):
493 interp = interpreters.create()
494 interpreters.destroy(interp)
495 with self.assertRaises(RuntimeError):
496 interpreters.is_running(interp)
497
498 def test_does_not_exist(self):
499 with self.assertRaises(RuntimeError):
500 interpreters.is_running(1_000_000)
501
502 def test_bad_id(self):
Serhiy Storchaka543a3952019-09-25 18:35:57 +0300503 with self.assertRaises(ValueError):
Eric Snow7f8bfc92018-01-29 18:23:44 -0700504 interpreters.is_running(-1)
505
506
Eric Snow4c6955e2018-02-16 18:53:40 -0700507class InterpreterIDTests(TestBase):
508
509 def test_with_int(self):
510 id = interpreters.InterpreterID(10, force=True)
511
512 self.assertEqual(int(id), 10)
513
514 def test_coerce_id(self):
Eric Snow4c6955e2018-02-16 18:53:40 -0700515 class Int(str):
Serhiy Storchakabf169912019-09-13 22:50:27 +0300516 def __index__(self):
517 return 10
Eric Snow4c6955e2018-02-16 18:53:40 -0700518
Serhiy Storchaka543a3952019-09-25 18:35:57 +0300519 id = interpreters.InterpreterID(Int(), force=True)
520 self.assertEqual(int(id), 10)
Eric Snow4c6955e2018-02-16 18:53:40 -0700521
522 def test_bad_id(self):
Serhiy Storchakabf169912019-09-13 22:50:27 +0300523 self.assertRaises(TypeError, interpreters.InterpreterID, object())
524 self.assertRaises(TypeError, interpreters.InterpreterID, 10.0)
Serhiy Storchaka543a3952019-09-25 18:35:57 +0300525 self.assertRaises(TypeError, interpreters.InterpreterID, '10')
Serhiy Storchakabf169912019-09-13 22:50:27 +0300526 self.assertRaises(TypeError, interpreters.InterpreterID, b'10')
527 self.assertRaises(ValueError, interpreters.InterpreterID, -1)
Serhiy Storchakabf169912019-09-13 22:50:27 +0300528 self.assertRaises(OverflowError, interpreters.InterpreterID, 2**64)
Eric Snow4c6955e2018-02-16 18:53:40 -0700529
530 def test_does_not_exist(self):
531 id = interpreters.channel_create()
532 with self.assertRaises(RuntimeError):
533 interpreters.InterpreterID(int(id) + 1) # unforced
534
Eric Snow6d2cd902018-05-16 15:04:57 -0400535 def test_str(self):
536 id = interpreters.InterpreterID(10, force=True)
537 self.assertEqual(str(id), '10')
538
Eric Snow4c6955e2018-02-16 18:53:40 -0700539 def test_repr(self):
540 id = interpreters.InterpreterID(10, force=True)
541 self.assertEqual(repr(id), 'InterpreterID(10)')
542
543 def test_equality(self):
544 id1 = interpreters.create()
545 id2 = interpreters.InterpreterID(int(id1))
546 id3 = interpreters.create()
547
548 self.assertTrue(id1 == id1)
549 self.assertTrue(id1 == id2)
550 self.assertTrue(id1 == int(id1))
Serhiy Storchakabf169912019-09-13 22:50:27 +0300551 self.assertTrue(int(id1) == id1)
552 self.assertTrue(id1 == float(int(id1)))
553 self.assertTrue(float(int(id1)) == id1)
554 self.assertFalse(id1 == float(int(id1)) + 0.1)
555 self.assertFalse(id1 == str(int(id1)))
556 self.assertFalse(id1 == 2**1000)
557 self.assertFalse(id1 == float('inf'))
558 self.assertFalse(id1 == 'spam')
Eric Snow4c6955e2018-02-16 18:53:40 -0700559 self.assertFalse(id1 == id3)
560
561 self.assertFalse(id1 != id1)
562 self.assertFalse(id1 != id2)
563 self.assertTrue(id1 != id3)
564
565
Eric Snow7f8bfc92018-01-29 18:23:44 -0700566class CreateTests(TestBase):
567
568 def test_in_main(self):
569 id = interpreters.create()
Eric Snow6d2cd902018-05-16 15:04:57 -0400570 self.assertIsInstance(id, interpreters.InterpreterID)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700571
572 self.assertIn(id, interpreters.list_all())
573
574 @unittest.skip('enable this test when working on pystate.c')
575 def test_unique_id(self):
576 seen = set()
577 for _ in range(100):
578 id = interpreters.create()
579 interpreters.destroy(id)
580 seen.add(id)
581
582 self.assertEqual(len(seen), 100)
583
584 def test_in_thread(self):
585 lock = threading.Lock()
586 id = None
587 def f():
588 nonlocal id
589 id = interpreters.create()
590 lock.acquire()
591 lock.release()
592
593 t = threading.Thread(target=f)
594 with lock:
595 t.start()
596 t.join()
597 self.assertIn(id, interpreters.list_all())
598
599 def test_in_subinterpreter(self):
600 main, = interpreters.list_all()
601 id1 = interpreters.create()
602 out = _run_output(id1, dedent("""
603 import _xxsubinterpreters as _interpreters
604 id = _interpreters.create()
Eric Snow6d2cd902018-05-16 15:04:57 -0400605 print(id)
606 assert isinstance(id, _interpreters.InterpreterID)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700607 """))
608 id2 = int(out.strip())
609
610 self.assertEqual(set(interpreters.list_all()), {main, id1, id2})
611
612 def test_in_threaded_subinterpreter(self):
613 main, = interpreters.list_all()
614 id1 = interpreters.create()
615 id2 = None
616 def f():
617 nonlocal id2
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)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700622 """))
623 id2 = int(out.strip())
624
625 t = threading.Thread(target=f)
626 t.start()
627 t.join()
628
629 self.assertEqual(set(interpreters.list_all()), {main, id1, id2})
630
631 def test_after_destroy_all(self):
632 before = set(interpreters.list_all())
633 # Create 3 subinterpreters.
634 ids = []
635 for _ in range(3):
636 id = interpreters.create()
637 ids.append(id)
638 # Now destroy them.
639 for id in ids:
640 interpreters.destroy(id)
641 # Finally, create another.
642 id = interpreters.create()
643 self.assertEqual(set(interpreters.list_all()), before | {id})
644
645 def test_after_destroy_some(self):
646 before = set(interpreters.list_all())
647 # Create 3 subinterpreters.
648 id1 = interpreters.create()
649 id2 = interpreters.create()
650 id3 = interpreters.create()
651 # Now destroy 2 of them.
652 interpreters.destroy(id1)
653 interpreters.destroy(id3)
654 # Finally, create another.
655 id = interpreters.create()
656 self.assertEqual(set(interpreters.list_all()), before | {id, id2})
657
658
659class DestroyTests(TestBase):
660
661 def test_one(self):
662 id1 = interpreters.create()
663 id2 = interpreters.create()
664 id3 = interpreters.create()
665 self.assertIn(id2, interpreters.list_all())
666 interpreters.destroy(id2)
667 self.assertNotIn(id2, interpreters.list_all())
668 self.assertIn(id1, interpreters.list_all())
669 self.assertIn(id3, interpreters.list_all())
670
671 def test_all(self):
672 before = set(interpreters.list_all())
673 ids = set()
674 for _ in range(3):
675 id = interpreters.create()
676 ids.add(id)
677 self.assertEqual(set(interpreters.list_all()), before | ids)
678 for id in ids:
679 interpreters.destroy(id)
680 self.assertEqual(set(interpreters.list_all()), before)
681
682 def test_main(self):
683 main, = interpreters.list_all()
684 with self.assertRaises(RuntimeError):
685 interpreters.destroy(main)
686
687 def f():
688 with self.assertRaises(RuntimeError):
689 interpreters.destroy(main)
690
691 t = threading.Thread(target=f)
692 t.start()
693 t.join()
694
695 def test_already_destroyed(self):
696 id = interpreters.create()
697 interpreters.destroy(id)
698 with self.assertRaises(RuntimeError):
699 interpreters.destroy(id)
700
701 def test_does_not_exist(self):
702 with self.assertRaises(RuntimeError):
703 interpreters.destroy(1_000_000)
704
705 def test_bad_id(self):
Serhiy Storchaka543a3952019-09-25 18:35:57 +0300706 with self.assertRaises(ValueError):
Eric Snow7f8bfc92018-01-29 18:23:44 -0700707 interpreters.destroy(-1)
708
709 def test_from_current(self):
710 main, = interpreters.list_all()
711 id = interpreters.create()
Eric Snow4e9da0d2018-02-02 21:49:49 -0700712 script = dedent(f"""
Eric Snow7f8bfc92018-01-29 18:23:44 -0700713 import _xxsubinterpreters as _interpreters
Eric Snow4e9da0d2018-02-02 21:49:49 -0700714 try:
Eric Snow6d2cd902018-05-16 15:04:57 -0400715 _interpreters.destroy({id})
Eric Snow4e9da0d2018-02-02 21:49:49 -0700716 except RuntimeError:
717 pass
718 """)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700719
Eric Snow4e9da0d2018-02-02 21:49:49 -0700720 interpreters.run_string(id, script)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700721 self.assertEqual(set(interpreters.list_all()), {main, id})
722
723 def test_from_sibling(self):
724 main, = interpreters.list_all()
725 id1 = interpreters.create()
726 id2 = interpreters.create()
Eric Snow4c6955e2018-02-16 18:53:40 -0700727 script = dedent(f"""
Eric Snow7f8bfc92018-01-29 18:23:44 -0700728 import _xxsubinterpreters as _interpreters
Eric Snow6d2cd902018-05-16 15:04:57 -0400729 _interpreters.destroy({id2})
Eric Snow4c6955e2018-02-16 18:53:40 -0700730 """)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700731 interpreters.run_string(id1, script)
732
733 self.assertEqual(set(interpreters.list_all()), {main, id1})
734
735 def test_from_other_thread(self):
736 id = interpreters.create()
737 def f():
738 interpreters.destroy(id)
739
740 t = threading.Thread(target=f)
741 t.start()
742 t.join()
743
744 def test_still_running(self):
745 main, = interpreters.list_all()
746 interp = interpreters.create()
747 with _running(interp):
Kyle Stanleyf03a8f82020-01-31 15:07:09 -0500748 self.assertTrue(interpreters.is_running(interp),
749 msg=f"Interp {interp} should be running before destruction.")
750
751 with self.assertRaises(RuntimeError,
752 msg=f"Should not be able to destroy interp {interp} while it's still running."):
Eric Snow7f8bfc92018-01-29 18:23:44 -0700753 interpreters.destroy(interp)
754 self.assertTrue(interpreters.is_running(interp))
755
756
757class RunStringTests(TestBase):
758
759 SCRIPT = dedent("""
760 with open('{}', 'w') as out:
761 out.write('{}')
762 """)
763 FILENAME = 'spam'
764
765 def setUp(self):
766 super().setUp()
767 self.id = interpreters.create()
768 self._fs = None
769
770 def tearDown(self):
771 if self._fs is not None:
772 self._fs.close()
773 super().tearDown()
774
Eric Snow7f8bfc92018-01-29 18:23:44 -0700775 def test_success(self):
776 script, file = _captured_script('print("it worked!", end="")')
777 with file:
778 interpreters.run_string(self.id, script)
779 out = file.read()
780
781 self.assertEqual(out, 'it worked!')
782
783 def test_in_thread(self):
784 script, file = _captured_script('print("it worked!", end="")')
785 with file:
786 def f():
787 interpreters.run_string(self.id, script)
788
789 t = threading.Thread(target=f)
790 t.start()
791 t.join()
792 out = file.read()
793
794 self.assertEqual(out, 'it worked!')
795
796 def test_create_thread(self):
797 script, file = _captured_script("""
798 import threading
799 def f():
800 print('it worked!', end='')
801
802 t = threading.Thread(target=f)
803 t.start()
804 t.join()
805 """)
806 with file:
807 interpreters.run_string(self.id, script)
808 out = file.read()
809
810 self.assertEqual(out, 'it worked!')
811
812 @unittest.skipUnless(hasattr(os, 'fork'), "test needs os.fork()")
813 def test_fork(self):
814 import tempfile
815 with tempfile.NamedTemporaryFile('w+') as file:
816 file.write('')
817 file.flush()
818
819 expected = 'spam spam spam spam spam'
820 script = dedent(f"""
Eric Snow7f8bfc92018-01-29 18:23:44 -0700821 import os
Eric Snow59032962018-09-14 14:17:20 -0700822 try:
823 os.fork()
824 except RuntimeError:
Eric Snow7f8bfc92018-01-29 18:23:44 -0700825 with open('{file.name}', 'w') as out:
826 out.write('{expected}')
Eric Snow7f8bfc92018-01-29 18:23:44 -0700827 """)
828 interpreters.run_string(self.id, script)
829
830 file.seek(0)
831 content = file.read()
832 self.assertEqual(content, expected)
833
834 def test_already_running(self):
835 with _running(self.id):
836 with self.assertRaises(RuntimeError):
837 interpreters.run_string(self.id, 'print("spam")')
838
839 def test_does_not_exist(self):
840 id = 0
841 while id in interpreters.list_all():
842 id += 1
843 with self.assertRaises(RuntimeError):
844 interpreters.run_string(id, 'print("spam")')
845
846 def test_error_id(self):
Serhiy Storchaka543a3952019-09-25 18:35:57 +0300847 with self.assertRaises(ValueError):
Eric Snow7f8bfc92018-01-29 18:23:44 -0700848 interpreters.run_string(-1, 'print("spam")')
849
850 def test_bad_id(self):
851 with self.assertRaises(TypeError):
852 interpreters.run_string('spam', 'print("spam")')
853
854 def test_bad_script(self):
855 with self.assertRaises(TypeError):
856 interpreters.run_string(self.id, 10)
857
858 def test_bytes_for_script(self):
859 with self.assertRaises(TypeError):
860 interpreters.run_string(self.id, b'print("spam")')
861
862 @contextlib.contextmanager
863 def assert_run_failed(self, exctype, msg=None):
864 with self.assertRaises(interpreters.RunFailedError) as caught:
865 yield
866 if msg is None:
867 self.assertEqual(str(caught.exception).split(':')[0],
868 str(exctype))
869 else:
870 self.assertEqual(str(caught.exception),
871 "{}: {}".format(exctype, msg))
872
873 def test_invalid_syntax(self):
874 with self.assert_run_failed(SyntaxError):
875 # missing close paren
876 interpreters.run_string(self.id, 'print("spam"')
877
878 def test_failure(self):
879 with self.assert_run_failed(Exception, 'spam'):
880 interpreters.run_string(self.id, 'raise Exception("spam")')
881
882 def test_SystemExit(self):
883 with self.assert_run_failed(SystemExit, '42'):
884 interpreters.run_string(self.id, 'raise SystemExit(42)')
885
886 def test_sys_exit(self):
887 with self.assert_run_failed(SystemExit):
888 interpreters.run_string(self.id, dedent("""
889 import sys
890 sys.exit()
891 """))
892
893 with self.assert_run_failed(SystemExit, '42'):
894 interpreters.run_string(self.id, dedent("""
895 import sys
896 sys.exit(42)
897 """))
898
899 def test_with_shared(self):
900 r, w = os.pipe()
901
902 shared = {
903 'spam': b'ham',
904 'eggs': b'-1',
905 'cheddar': None,
906 }
907 script = dedent(f"""
908 eggs = int(eggs)
909 spam = 42
910 result = spam + eggs
911
912 ns = dict(vars())
913 del ns['__builtins__']
914 import pickle
915 with open({w}, 'wb') as chan:
916 pickle.dump(ns, chan)
917 """)
918 interpreters.run_string(self.id, script, shared)
919 with open(r, 'rb') as chan:
920 ns = pickle.load(chan)
921
922 self.assertEqual(ns['spam'], 42)
923 self.assertEqual(ns['eggs'], -1)
924 self.assertEqual(ns['result'], 41)
925 self.assertIsNone(ns['cheddar'])
926
927 def test_shared_overwrites(self):
928 interpreters.run_string(self.id, dedent("""
929 spam = 'eggs'
930 ns1 = dict(vars())
931 del ns1['__builtins__']
932 """))
933
934 shared = {'spam': b'ham'}
935 script = dedent(f"""
936 ns2 = dict(vars())
937 del ns2['__builtins__']
938 """)
939 interpreters.run_string(self.id, script, shared)
940
941 r, w = os.pipe()
942 script = dedent(f"""
943 ns = dict(vars())
944 del ns['__builtins__']
945 import pickle
946 with open({w}, 'wb') as chan:
947 pickle.dump(ns, chan)
948 """)
949 interpreters.run_string(self.id, script)
950 with open(r, 'rb') as chan:
951 ns = pickle.load(chan)
952
953 self.assertEqual(ns['ns1']['spam'], 'eggs')
954 self.assertEqual(ns['ns2']['spam'], b'ham')
955 self.assertEqual(ns['spam'], b'ham')
956
957 def test_shared_overwrites_default_vars(self):
958 r, w = os.pipe()
959
960 shared = {'__name__': b'not __main__'}
961 script = dedent(f"""
962 spam = 42
963
964 ns = dict(vars())
965 del ns['__builtins__']
966 import pickle
967 with open({w}, 'wb') as chan:
968 pickle.dump(ns, chan)
969 """)
970 interpreters.run_string(self.id, script, shared)
971 with open(r, 'rb') as chan:
972 ns = pickle.load(chan)
973
974 self.assertEqual(ns['__name__'], b'not __main__')
975
976 def test_main_reused(self):
977 r, w = os.pipe()
978 interpreters.run_string(self.id, dedent(f"""
979 spam = True
980
981 ns = dict(vars())
982 del ns['__builtins__']
983 import pickle
984 with open({w}, 'wb') as chan:
985 pickle.dump(ns, chan)
986 del ns, pickle, chan
987 """))
988 with open(r, 'rb') as chan:
989 ns1 = pickle.load(chan)
990
991 r, w = os.pipe()
992 interpreters.run_string(self.id, dedent(f"""
993 eggs = False
994
995 ns = dict(vars())
996 del ns['__builtins__']
997 import pickle
998 with open({w}, 'wb') as chan:
999 pickle.dump(ns, chan)
1000 """))
1001 with open(r, 'rb') as chan:
1002 ns2 = pickle.load(chan)
1003
1004 self.assertIn('spam', ns1)
1005 self.assertNotIn('eggs', ns1)
1006 self.assertIn('eggs', ns2)
1007 self.assertIn('spam', ns2)
1008
1009 def test_execution_namespace_is_main(self):
1010 r, w = os.pipe()
1011
1012 script = dedent(f"""
1013 spam = 42
1014
1015 ns = dict(vars())
1016 ns['__builtins__'] = str(ns['__builtins__'])
1017 import pickle
1018 with open({w}, 'wb') as chan:
1019 pickle.dump(ns, chan)
1020 """)
1021 interpreters.run_string(self.id, script)
1022 with open(r, 'rb') as chan:
1023 ns = pickle.load(chan)
1024
1025 ns.pop('__builtins__')
1026 ns.pop('__loader__')
1027 self.assertEqual(ns, {
1028 '__name__': '__main__',
1029 '__annotations__': {},
1030 '__doc__': None,
1031 '__package__': None,
1032 '__spec__': None,
1033 'spam': 42,
1034 })
1035
Eric Snow4c6955e2018-02-16 18:53:40 -07001036 # XXX Fix this test!
1037 @unittest.skip('blocking forever')
Eric Snow7f8bfc92018-01-29 18:23:44 -07001038 def test_still_running_at_exit(self):
1039 script = dedent(f"""
1040 from textwrap import dedent
1041 import threading
1042 import _xxsubinterpreters as _interpreters
Eric Snow4c6955e2018-02-16 18:53:40 -07001043 id = _interpreters.create()
Eric Snow7f8bfc92018-01-29 18:23:44 -07001044 def f():
1045 _interpreters.run_string(id, dedent('''
1046 import time
1047 # Give plenty of time for the main interpreter to finish.
1048 time.sleep(1_000_000)
1049 '''))
1050
1051 t = threading.Thread(target=f)
1052 t.start()
1053 """)
1054 with support.temp_dir() as dirname:
1055 filename = script_helper.make_script(dirname, 'interp', script)
1056 with script_helper.spawn_python(filename) as proc:
1057 retcode = proc.wait()
1058
1059 self.assertEqual(retcode, 0)
1060
1061
Eric Snow6d2cd902018-05-16 15:04:57 -04001062##################################
1063# channel tests
1064
Eric Snow7f8bfc92018-01-29 18:23:44 -07001065class ChannelIDTests(TestBase):
1066
1067 def test_default_kwargs(self):
1068 cid = interpreters._channel_id(10, force=True)
1069
1070 self.assertEqual(int(cid), 10)
1071 self.assertEqual(cid.end, 'both')
1072
1073 def test_with_kwargs(self):
1074 cid = interpreters._channel_id(10, send=True, force=True)
1075 self.assertEqual(cid.end, 'send')
1076
1077 cid = interpreters._channel_id(10, send=True, recv=False, force=True)
1078 self.assertEqual(cid.end, 'send')
1079
1080 cid = interpreters._channel_id(10, recv=True, force=True)
1081 self.assertEqual(cid.end, 'recv')
1082
1083 cid = interpreters._channel_id(10, recv=True, send=False, force=True)
1084 self.assertEqual(cid.end, 'recv')
1085
1086 cid = interpreters._channel_id(10, send=True, recv=True, force=True)
1087 self.assertEqual(cid.end, 'both')
1088
1089 def test_coerce_id(self):
Eric Snow7f8bfc92018-01-29 18:23:44 -07001090 class Int(str):
Serhiy Storchakabf169912019-09-13 22:50:27 +03001091 def __index__(self):
1092 return 10
Eric Snow7f8bfc92018-01-29 18:23:44 -07001093
Serhiy Storchakabf169912019-09-13 22:50:27 +03001094 cid = interpreters._channel_id(Int(), force=True)
Eric Snow7f8bfc92018-01-29 18:23:44 -07001095 self.assertEqual(int(cid), 10)
1096
1097 def test_bad_id(self):
Serhiy Storchakabf169912019-09-13 22:50:27 +03001098 self.assertRaises(TypeError, interpreters._channel_id, object())
1099 self.assertRaises(TypeError, interpreters._channel_id, 10.0)
1100 self.assertRaises(TypeError, interpreters._channel_id, '10')
1101 self.assertRaises(TypeError, interpreters._channel_id, b'10')
1102 self.assertRaises(ValueError, interpreters._channel_id, -1)
1103 self.assertRaises(OverflowError, interpreters._channel_id, 2**64)
Eric Snow7f8bfc92018-01-29 18:23:44 -07001104
1105 def test_bad_kwargs(self):
1106 with self.assertRaises(ValueError):
1107 interpreters._channel_id(10, send=False, recv=False)
1108
1109 def test_does_not_exist(self):
1110 cid = interpreters.channel_create()
1111 with self.assertRaises(interpreters.ChannelNotFoundError):
1112 interpreters._channel_id(int(cid) + 1) # unforced
1113
Eric Snow6d2cd902018-05-16 15:04:57 -04001114 def test_str(self):
1115 cid = interpreters._channel_id(10, force=True)
1116 self.assertEqual(str(cid), '10')
1117
Eric Snow7f8bfc92018-01-29 18:23:44 -07001118 def test_repr(self):
1119 cid = interpreters._channel_id(10, force=True)
1120 self.assertEqual(repr(cid), 'ChannelID(10)')
1121
1122 cid = interpreters._channel_id(10, send=True, force=True)
1123 self.assertEqual(repr(cid), 'ChannelID(10, send=True)')
1124
1125 cid = interpreters._channel_id(10, recv=True, force=True)
1126 self.assertEqual(repr(cid), 'ChannelID(10, recv=True)')
1127
1128 cid = interpreters._channel_id(10, send=True, recv=True, force=True)
1129 self.assertEqual(repr(cid), 'ChannelID(10)')
1130
1131 def test_equality(self):
1132 cid1 = interpreters.channel_create()
1133 cid2 = interpreters._channel_id(int(cid1))
1134 cid3 = interpreters.channel_create()
1135
1136 self.assertTrue(cid1 == cid1)
1137 self.assertTrue(cid1 == cid2)
1138 self.assertTrue(cid1 == int(cid1))
Serhiy Storchakabf169912019-09-13 22:50:27 +03001139 self.assertTrue(int(cid1) == cid1)
1140 self.assertTrue(cid1 == float(int(cid1)))
1141 self.assertTrue(float(int(cid1)) == cid1)
1142 self.assertFalse(cid1 == float(int(cid1)) + 0.1)
1143 self.assertFalse(cid1 == str(int(cid1)))
1144 self.assertFalse(cid1 == 2**1000)
1145 self.assertFalse(cid1 == float('inf'))
1146 self.assertFalse(cid1 == 'spam')
Eric Snow7f8bfc92018-01-29 18:23:44 -07001147 self.assertFalse(cid1 == cid3)
1148
1149 self.assertFalse(cid1 != cid1)
1150 self.assertFalse(cid1 != cid2)
1151 self.assertTrue(cid1 != cid3)
1152
1153
1154class ChannelTests(TestBase):
1155
Eric Snow6d2cd902018-05-16 15:04:57 -04001156 def test_create_cid(self):
1157 cid = interpreters.channel_create()
1158 self.assertIsInstance(cid, interpreters.ChannelID)
1159
Eric Snow7f8bfc92018-01-29 18:23:44 -07001160 def test_sequential_ids(self):
1161 before = interpreters.channel_list_all()
1162 id1 = interpreters.channel_create()
1163 id2 = interpreters.channel_create()
1164 id3 = interpreters.channel_create()
1165 after = interpreters.channel_list_all()
1166
1167 self.assertEqual(id2, int(id1) + 1)
1168 self.assertEqual(id3, int(id2) + 1)
1169 self.assertEqual(set(after) - set(before), {id1, id2, id3})
1170
1171 def test_ids_global(self):
1172 id1 = interpreters.create()
1173 out = _run_output(id1, dedent("""
1174 import _xxsubinterpreters as _interpreters
1175 cid = _interpreters.channel_create()
Eric Snow6d2cd902018-05-16 15:04:57 -04001176 print(cid)
Eric Snow7f8bfc92018-01-29 18:23:44 -07001177 """))
1178 cid1 = int(out.strip())
1179
1180 id2 = interpreters.create()
1181 out = _run_output(id2, dedent("""
1182 import _xxsubinterpreters as _interpreters
1183 cid = _interpreters.channel_create()
Eric Snow6d2cd902018-05-16 15:04:57 -04001184 print(cid)
Eric Snow7f8bfc92018-01-29 18:23:44 -07001185 """))
1186 cid2 = int(out.strip())
1187
1188 self.assertEqual(cid2, int(cid1) + 1)
1189
Lewis Gaulf7bbf582020-04-29 01:18:42 +01001190 def test_channel_list_interpreters_none(self):
1191 """Test listing interpreters for a channel with no associations."""
1192 # Test for channel with no associated interpreters.
1193 cid = interpreters.channel_create()
1194 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1195 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1196 self.assertEqual(send_interps, [])
1197 self.assertEqual(recv_interps, [])
1198
1199 def test_channel_list_interpreters_basic(self):
1200 """Test basic listing channel interpreters."""
1201 interp0 = interpreters.get_main()
1202 cid = interpreters.channel_create()
1203 interpreters.channel_send(cid, "send")
1204 # Test for a channel that has one end associated to an interpreter.
1205 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1206 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1207 self.assertEqual(send_interps, [interp0])
1208 self.assertEqual(recv_interps, [])
1209
1210 interp1 = interpreters.create()
1211 _run_output(interp1, dedent(f"""
1212 import _xxsubinterpreters as _interpreters
1213 obj = _interpreters.channel_recv({cid})
1214 """))
1215 # Test for channel that has boths ends associated to an interpreter.
1216 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1217 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1218 self.assertEqual(send_interps, [interp0])
1219 self.assertEqual(recv_interps, [interp1])
1220
1221 def test_channel_list_interpreters_multiple(self):
1222 """Test listing interpreters for a channel with many associations."""
1223 interp0 = interpreters.get_main()
1224 interp1 = interpreters.create()
1225 interp2 = interpreters.create()
1226 interp3 = interpreters.create()
1227 cid = interpreters.channel_create()
1228
1229 interpreters.channel_send(cid, "send")
1230 _run_output(interp1, dedent(f"""
1231 import _xxsubinterpreters as _interpreters
1232 _interpreters.channel_send({cid}, "send")
1233 """))
1234 _run_output(interp2, dedent(f"""
1235 import _xxsubinterpreters as _interpreters
1236 obj = _interpreters.channel_recv({cid})
1237 """))
1238 _run_output(interp3, dedent(f"""
1239 import _xxsubinterpreters as _interpreters
1240 obj = _interpreters.channel_recv({cid})
1241 """))
1242 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1243 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1244 self.assertEqual(set(send_interps), {interp0, interp1})
1245 self.assertEqual(set(recv_interps), {interp2, interp3})
1246
1247 def test_channel_list_interpreters_destroyed(self):
1248 """Test listing channel interpreters with a destroyed interpreter."""
1249 interp0 = interpreters.get_main()
1250 interp1 = interpreters.create()
1251 cid = interpreters.channel_create()
1252 interpreters.channel_send(cid, "send")
1253 _run_output(interp1, dedent(f"""
1254 import _xxsubinterpreters as _interpreters
1255 obj = _interpreters.channel_recv({cid})
1256 """))
1257 # Should be one interpreter associated with each end.
1258 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1259 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1260 self.assertEqual(send_interps, [interp0])
1261 self.assertEqual(recv_interps, [interp1])
1262
1263 interpreters.destroy(interp1)
1264 # Destroyed interpreter should not be listed.
1265 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1266 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1267 self.assertEqual(send_interps, [interp0])
1268 self.assertEqual(recv_interps, [])
1269
1270 def test_channel_list_interpreters_released(self):
1271 """Test listing channel interpreters with a released channel."""
1272 # Set up one channel with main interpreter on the send end and two
1273 # subinterpreters on the receive end.
1274 interp0 = interpreters.get_main()
1275 interp1 = interpreters.create()
1276 interp2 = interpreters.create()
1277 cid = interpreters.channel_create()
1278 interpreters.channel_send(cid, "data")
1279 _run_output(interp1, dedent(f"""
1280 import _xxsubinterpreters as _interpreters
1281 obj = _interpreters.channel_recv({cid})
1282 """))
1283 interpreters.channel_send(cid, "data")
1284 _run_output(interp2, dedent(f"""
1285 import _xxsubinterpreters as _interpreters
1286 obj = _interpreters.channel_recv({cid})
1287 """))
1288 # Check the setup.
1289 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1290 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1291 self.assertEqual(len(send_interps), 1)
1292 self.assertEqual(len(recv_interps), 2)
1293
1294 # Release the main interpreter from the send end.
1295 interpreters.channel_release(cid, send=True)
1296 # Send end should have no associated interpreters.
1297 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1298 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1299 self.assertEqual(len(send_interps), 0)
1300 self.assertEqual(len(recv_interps), 2)
1301
1302 # Release one of the subinterpreters from the receive end.
1303 _run_output(interp2, dedent(f"""
1304 import _xxsubinterpreters as _interpreters
1305 _interpreters.channel_release({cid})
1306 """))
1307 # Receive end should have the released interpreter removed.
1308 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1309 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1310 self.assertEqual(len(send_interps), 0)
1311 self.assertEqual(recv_interps, [interp1])
1312
1313 def test_channel_list_interpreters_closed(self):
1314 """Test listing channel interpreters with a closed channel."""
1315 interp0 = interpreters.get_main()
1316 interp1 = interpreters.create()
1317 cid = interpreters.channel_create()
1318 # Put something in the channel so that it's not empty.
1319 interpreters.channel_send(cid, "send")
1320
1321 # Check initial state.
1322 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1323 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1324 self.assertEqual(len(send_interps), 1)
1325 self.assertEqual(len(recv_interps), 0)
1326
1327 # Force close the channel.
1328 interpreters.channel_close(cid, force=True)
1329 # Both ends should raise an error.
1330 with self.assertRaises(interpreters.ChannelClosedError):
1331 interpreters.channel_list_interpreters(cid, send=True)
1332 with self.assertRaises(interpreters.ChannelClosedError):
1333 interpreters.channel_list_interpreters(cid, send=False)
1334
1335 def test_channel_list_interpreters_closed_send_end(self):
1336 """Test listing channel interpreters with a channel's send end closed."""
1337 interp0 = interpreters.get_main()
1338 interp1 = interpreters.create()
1339 cid = interpreters.channel_create()
1340 # Put something in the channel so that it's not empty.
1341 interpreters.channel_send(cid, "send")
1342
1343 # Check initial state.
1344 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1345 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1346 self.assertEqual(len(send_interps), 1)
1347 self.assertEqual(len(recv_interps), 0)
1348
1349 # Close the send end of the channel.
1350 interpreters.channel_close(cid, send=True)
1351 # Send end should raise an error.
1352 with self.assertRaises(interpreters.ChannelClosedError):
1353 interpreters.channel_list_interpreters(cid, send=True)
1354 # Receive end should not be closed (since channel is not empty).
1355 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1356 self.assertEqual(len(recv_interps), 0)
1357
1358 # Close the receive end of the channel from a subinterpreter.
1359 _run_output(interp1, dedent(f"""
1360 import _xxsubinterpreters as _interpreters
1361 _interpreters.channel_close({cid}, force=True)
1362 """))
1363 # Both ends should raise an error.
1364 with self.assertRaises(interpreters.ChannelClosedError):
1365 interpreters.channel_list_interpreters(cid, send=True)
1366 with self.assertRaises(interpreters.ChannelClosedError):
1367 interpreters.channel_list_interpreters(cid, send=False)
1368
Eric Snow7f8bfc92018-01-29 18:23:44 -07001369 ####################
1370
Eric Snow7f8bfc92018-01-29 18:23:44 -07001371 def test_send_recv_main(self):
1372 cid = interpreters.channel_create()
1373 orig = b'spam'
1374 interpreters.channel_send(cid, orig)
1375 obj = interpreters.channel_recv(cid)
1376
1377 self.assertEqual(obj, orig)
1378 self.assertIsNot(obj, orig)
1379
1380 def test_send_recv_same_interpreter(self):
1381 id1 = interpreters.create()
1382 out = _run_output(id1, dedent("""
1383 import _xxsubinterpreters as _interpreters
1384 cid = _interpreters.channel_create()
1385 orig = b'spam'
1386 _interpreters.channel_send(cid, orig)
1387 obj = _interpreters.channel_recv(cid)
1388 assert obj is not orig
1389 assert obj == orig
1390 """))
1391
1392 def test_send_recv_different_interpreters(self):
1393 cid = interpreters.channel_create()
1394 id1 = interpreters.create()
1395 out = _run_output(id1, dedent(f"""
1396 import _xxsubinterpreters as _interpreters
Eric Snow6d2cd902018-05-16 15:04:57 -04001397 _interpreters.channel_send({cid}, b'spam')
Eric Snow7f8bfc92018-01-29 18:23:44 -07001398 """))
1399 obj = interpreters.channel_recv(cid)
1400
1401 self.assertEqual(obj, b'spam')
1402
Eric Snowf53d9f22018-02-20 16:30:17 -07001403 def test_send_recv_different_threads(self):
1404 cid = interpreters.channel_create()
1405
1406 def f():
1407 while True:
1408 try:
1409 obj = interpreters.channel_recv(cid)
1410 break
1411 except interpreters.ChannelEmptyError:
1412 time.sleep(0.1)
1413 interpreters.channel_send(cid, obj)
1414 t = threading.Thread(target=f)
1415 t.start()
1416
1417 interpreters.channel_send(cid, b'spam')
1418 t.join()
1419 obj = interpreters.channel_recv(cid)
1420
1421 self.assertEqual(obj, b'spam')
1422
1423 def test_send_recv_different_interpreters_and_threads(self):
1424 cid = interpreters.channel_create()
1425 id1 = interpreters.create()
1426 out = None
1427
1428 def f():
1429 nonlocal out
1430 out = _run_output(id1, dedent(f"""
1431 import time
1432 import _xxsubinterpreters as _interpreters
1433 while True:
1434 try:
Eric Snow6d2cd902018-05-16 15:04:57 -04001435 obj = _interpreters.channel_recv({cid})
Eric Snowf53d9f22018-02-20 16:30:17 -07001436 break
1437 except _interpreters.ChannelEmptyError:
1438 time.sleep(0.1)
1439 assert(obj == b'spam')
Eric Snow6d2cd902018-05-16 15:04:57 -04001440 _interpreters.channel_send({cid}, b'eggs')
Eric Snowf53d9f22018-02-20 16:30:17 -07001441 """))
1442 t = threading.Thread(target=f)
1443 t.start()
1444
1445 interpreters.channel_send(cid, b'spam')
1446 t.join()
1447 obj = interpreters.channel_recv(cid)
1448
1449 self.assertEqual(obj, b'eggs')
1450
Eric Snow7f8bfc92018-01-29 18:23:44 -07001451 def test_send_not_found(self):
1452 with self.assertRaises(interpreters.ChannelNotFoundError):
1453 interpreters.channel_send(10, b'spam')
1454
1455 def test_recv_not_found(self):
1456 with self.assertRaises(interpreters.ChannelNotFoundError):
1457 interpreters.channel_recv(10)
1458
1459 def test_recv_empty(self):
1460 cid = interpreters.channel_create()
1461 with self.assertRaises(interpreters.ChannelEmptyError):
1462 interpreters.channel_recv(cid)
1463
Eric Snow5e8c6912020-04-28 17:11:32 -06001464 def test_recv_default(self):
1465 default = object()
1466 cid = interpreters.channel_create()
1467 obj1 = interpreters.channel_recv(cid, default)
1468 interpreters.channel_send(cid, None)
1469 interpreters.channel_send(cid, 1)
1470 interpreters.channel_send(cid, b'spam')
1471 interpreters.channel_send(cid, b'eggs')
1472 obj2 = interpreters.channel_recv(cid, default)
1473 obj3 = interpreters.channel_recv(cid, default)
1474 obj4 = interpreters.channel_recv(cid)
1475 obj5 = interpreters.channel_recv(cid, default)
1476 obj6 = interpreters.channel_recv(cid, default)
1477
1478 self.assertIs(obj1, default)
1479 self.assertIs(obj2, None)
1480 self.assertEqual(obj3, 1)
1481 self.assertEqual(obj4, b'spam')
1482 self.assertEqual(obj5, b'eggs')
1483 self.assertIs(obj6, default)
1484
Eric Snow6d2cd902018-05-16 15:04:57 -04001485 def test_run_string_arg_unresolved(self):
Eric Snow7f8bfc92018-01-29 18:23:44 -07001486 cid = interpreters.channel_create()
1487 interp = interpreters.create()
1488
1489 out = _run_output(interp, dedent("""
1490 import _xxsubinterpreters as _interpreters
1491 print(cid.end)
1492 _interpreters.channel_send(cid, b'spam')
1493 """),
1494 dict(cid=cid.send))
1495 obj = interpreters.channel_recv(cid)
1496
1497 self.assertEqual(obj, b'spam')
1498 self.assertEqual(out.strip(), 'send')
1499
Eric Snowab4a1982018-06-13 08:02:39 -06001500 # XXX For now there is no high-level channel into which the
1501 # sent channel ID can be converted...
1502 # Note: this test caused crashes on some buildbots (bpo-33615).
1503 @unittest.skip('disabled until high-level channels exist')
Eric Snow6d2cd902018-05-16 15:04:57 -04001504 def test_run_string_arg_resolved(self):
1505 cid = interpreters.channel_create()
1506 cid = interpreters._channel_id(cid, _resolve=True)
1507 interp = interpreters.create()
1508
1509 out = _run_output(interp, dedent("""
1510 import _xxsubinterpreters as _interpreters
Eric Snowab4a1982018-06-13 08:02:39 -06001511 print(chan.id.end)
1512 _interpreters.channel_send(chan.id, b'spam')
Eric Snow6d2cd902018-05-16 15:04:57 -04001513 """),
1514 dict(chan=cid.send))
1515 obj = interpreters.channel_recv(cid)
1516
1517 self.assertEqual(obj, b'spam')
1518 self.assertEqual(out.strip(), 'send')
1519
1520 # close
1521
1522 def test_close_single_user(self):
1523 cid = interpreters.channel_create()
1524 interpreters.channel_send(cid, b'spam')
1525 interpreters.channel_recv(cid)
1526 interpreters.channel_close(cid)
1527
1528 with self.assertRaises(interpreters.ChannelClosedError):
1529 interpreters.channel_send(cid, b'eggs')
1530 with self.assertRaises(interpreters.ChannelClosedError):
1531 interpreters.channel_recv(cid)
1532
1533 def test_close_multiple_users(self):
1534 cid = interpreters.channel_create()
1535 id1 = interpreters.create()
1536 id2 = interpreters.create()
1537 interpreters.run_string(id1, dedent(f"""
1538 import _xxsubinterpreters as _interpreters
1539 _interpreters.channel_send({cid}, b'spam')
1540 """))
1541 interpreters.run_string(id2, dedent(f"""
1542 import _xxsubinterpreters as _interpreters
1543 _interpreters.channel_recv({cid})
1544 """))
1545 interpreters.channel_close(cid)
1546 with self.assertRaises(interpreters.RunFailedError) as cm:
1547 interpreters.run_string(id1, dedent(f"""
1548 _interpreters.channel_send({cid}, b'spam')
1549 """))
1550 self.assertIn('ChannelClosedError', str(cm.exception))
1551 with self.assertRaises(interpreters.RunFailedError) as cm:
1552 interpreters.run_string(id2, dedent(f"""
1553 _interpreters.channel_send({cid}, b'spam')
1554 """))
1555 self.assertIn('ChannelClosedError', str(cm.exception))
1556
1557 def test_close_multiple_times(self):
1558 cid = interpreters.channel_create()
1559 interpreters.channel_send(cid, b'spam')
1560 interpreters.channel_recv(cid)
1561 interpreters.channel_close(cid)
1562
1563 with self.assertRaises(interpreters.ChannelClosedError):
1564 interpreters.channel_close(cid)
1565
Eric Snow3ab01362018-05-17 10:27:09 -04001566 def test_close_empty(self):
1567 tests = [
1568 (False, False),
1569 (True, False),
1570 (False, True),
1571 (True, True),
1572 ]
1573 for send, recv in tests:
1574 with self.subTest((send, recv)):
1575 cid = interpreters.channel_create()
1576 interpreters.channel_send(cid, b'spam')
1577 interpreters.channel_recv(cid)
1578 interpreters.channel_close(cid, send=send, recv=recv)
1579
1580 with self.assertRaises(interpreters.ChannelClosedError):
1581 interpreters.channel_send(cid, b'eggs')
1582 with self.assertRaises(interpreters.ChannelClosedError):
1583 interpreters.channel_recv(cid)
1584
1585 def test_close_defaults_with_unused_items(self):
Eric Snow6d2cd902018-05-16 15:04:57 -04001586 cid = interpreters.channel_create()
1587 interpreters.channel_send(cid, b'spam')
1588 interpreters.channel_send(cid, b'ham')
Eric Snow6d2cd902018-05-16 15:04:57 -04001589
Eric Snow3ab01362018-05-17 10:27:09 -04001590 with self.assertRaises(interpreters.ChannelNotEmptyError):
1591 interpreters.channel_close(cid)
1592 interpreters.channel_recv(cid)
1593 interpreters.channel_send(cid, b'eggs')
1594
1595 def test_close_recv_with_unused_items_unforced(self):
1596 cid = interpreters.channel_create()
1597 interpreters.channel_send(cid, b'spam')
1598 interpreters.channel_send(cid, b'ham')
1599
1600 with self.assertRaises(interpreters.ChannelNotEmptyError):
1601 interpreters.channel_close(cid, recv=True)
1602 interpreters.channel_recv(cid)
1603 interpreters.channel_send(cid, b'eggs')
1604 interpreters.channel_recv(cid)
1605 interpreters.channel_recv(cid)
1606 interpreters.channel_close(cid, recv=True)
1607
1608 def test_close_send_with_unused_items_unforced(self):
1609 cid = interpreters.channel_create()
1610 interpreters.channel_send(cid, b'spam')
1611 interpreters.channel_send(cid, b'ham')
1612 interpreters.channel_close(cid, send=True)
1613
1614 with self.assertRaises(interpreters.ChannelClosedError):
1615 interpreters.channel_send(cid, b'eggs')
1616 interpreters.channel_recv(cid)
1617 interpreters.channel_recv(cid)
1618 with self.assertRaises(interpreters.ChannelClosedError):
1619 interpreters.channel_recv(cid)
1620
1621 def test_close_both_with_unused_items_unforced(self):
1622 cid = interpreters.channel_create()
1623 interpreters.channel_send(cid, b'spam')
1624 interpreters.channel_send(cid, b'ham')
1625
1626 with self.assertRaises(interpreters.ChannelNotEmptyError):
1627 interpreters.channel_close(cid, recv=True, send=True)
1628 interpreters.channel_recv(cid)
1629 interpreters.channel_send(cid, b'eggs')
1630 interpreters.channel_recv(cid)
1631 interpreters.channel_recv(cid)
1632 interpreters.channel_close(cid, recv=True)
1633
1634 def test_close_recv_with_unused_items_forced(self):
1635 cid = interpreters.channel_create()
1636 interpreters.channel_send(cid, b'spam')
1637 interpreters.channel_send(cid, b'ham')
1638 interpreters.channel_close(cid, recv=True, force=True)
1639
1640 with self.assertRaises(interpreters.ChannelClosedError):
1641 interpreters.channel_send(cid, b'eggs')
1642 with self.assertRaises(interpreters.ChannelClosedError):
1643 interpreters.channel_recv(cid)
1644
1645 def test_close_send_with_unused_items_forced(self):
1646 cid = interpreters.channel_create()
1647 interpreters.channel_send(cid, b'spam')
1648 interpreters.channel_send(cid, b'ham')
1649 interpreters.channel_close(cid, send=True, force=True)
1650
1651 with self.assertRaises(interpreters.ChannelClosedError):
1652 interpreters.channel_send(cid, b'eggs')
1653 with self.assertRaises(interpreters.ChannelClosedError):
1654 interpreters.channel_recv(cid)
1655
1656 def test_close_both_with_unused_items_forced(self):
1657 cid = interpreters.channel_create()
1658 interpreters.channel_send(cid, b'spam')
1659 interpreters.channel_send(cid, b'ham')
1660 interpreters.channel_close(cid, send=True, recv=True, force=True)
1661
1662 with self.assertRaises(interpreters.ChannelClosedError):
1663 interpreters.channel_send(cid, b'eggs')
Eric Snow6d2cd902018-05-16 15:04:57 -04001664 with self.assertRaises(interpreters.ChannelClosedError):
1665 interpreters.channel_recv(cid)
1666
1667 def test_close_never_used(self):
1668 cid = interpreters.channel_create()
1669 interpreters.channel_close(cid)
1670
1671 with self.assertRaises(interpreters.ChannelClosedError):
1672 interpreters.channel_send(cid, b'spam')
1673 with self.assertRaises(interpreters.ChannelClosedError):
1674 interpreters.channel_recv(cid)
1675
1676 def test_close_by_unassociated_interp(self):
1677 cid = interpreters.channel_create()
1678 interpreters.channel_send(cid, b'spam')
1679 interp = interpreters.create()
1680 interpreters.run_string(interp, dedent(f"""
1681 import _xxsubinterpreters as _interpreters
Eric Snow3ab01362018-05-17 10:27:09 -04001682 _interpreters.channel_close({cid}, force=True)
Eric Snow6d2cd902018-05-16 15:04:57 -04001683 """))
1684 with self.assertRaises(interpreters.ChannelClosedError):
1685 interpreters.channel_recv(cid)
1686 with self.assertRaises(interpreters.ChannelClosedError):
1687 interpreters.channel_close(cid)
1688
1689 def test_close_used_multiple_times_by_single_user(self):
1690 cid = interpreters.channel_create()
1691 interpreters.channel_send(cid, b'spam')
1692 interpreters.channel_send(cid, b'spam')
1693 interpreters.channel_send(cid, b'spam')
1694 interpreters.channel_recv(cid)
Eric Snow3ab01362018-05-17 10:27:09 -04001695 interpreters.channel_close(cid, force=True)
Eric Snow6d2cd902018-05-16 15:04:57 -04001696
1697 with self.assertRaises(interpreters.ChannelClosedError):
1698 interpreters.channel_send(cid, b'eggs')
1699 with self.assertRaises(interpreters.ChannelClosedError):
1700 interpreters.channel_recv(cid)
1701
Lewis Gaulf7bbf582020-04-29 01:18:42 +01001702 def test_channel_list_interpreters_invalid_channel(self):
1703 cid = interpreters.channel_create()
1704 # Test for invalid channel ID.
1705 with self.assertRaises(interpreters.ChannelNotFoundError):
1706 interpreters.channel_list_interpreters(1000, send=True)
1707
1708 interpreters.channel_close(cid)
1709 # Test for a channel that has been closed.
1710 with self.assertRaises(interpreters.ChannelClosedError):
1711 interpreters.channel_list_interpreters(cid, send=True)
1712
1713 def test_channel_list_interpreters_invalid_args(self):
1714 # Tests for invalid arguments passed to the API.
1715 cid = interpreters.channel_create()
1716 with self.assertRaises(TypeError):
1717 interpreters.channel_list_interpreters(cid)
1718
Eric Snow6d2cd902018-05-16 15:04:57 -04001719
1720class ChannelReleaseTests(TestBase):
1721
1722 # XXX Add more test coverage a la the tests for close().
1723
1724 """
1725 - main / interp / other
1726 - run in: current thread / new thread / other thread / different threads
1727 - end / opposite
1728 - force / no force
1729 - used / not used (associated / not associated)
1730 - empty / emptied / never emptied / partly emptied
1731 - closed / not closed
1732 - released / not released
1733 - creator (interp) / other
1734 - associated interpreter not running
1735 - associated interpreter destroyed
1736 """
1737
1738 """
1739 use
1740 pre-release
1741 release
1742 after
1743 check
1744 """
1745
1746 """
1747 release in: main, interp1
1748 creator: same, other (incl. interp2)
1749
1750 use: None,send,recv,send/recv in None,same,other(incl. interp2),same+other(incl. interp2),all
1751 pre-release: None,send,recv,both in None,same,other(incl. interp2),same+other(incl. interp2),all
1752 pre-release forced: None,send,recv,both in None,same,other(incl. interp2),same+other(incl. interp2),all
1753
1754 release: same
1755 release forced: same
1756
1757 use after: None,send,recv,send/recv in None,same,other(incl. interp2),same+other(incl. interp2),all
1758 release after: None,send,recv,send/recv in None,same,other(incl. interp2),same+other(incl. interp2),all
1759 check released: send/recv for same/other(incl. interp2)
1760 check closed: send/recv for same/other(incl. interp2)
1761 """
1762
1763 def test_single_user(self):
1764 cid = interpreters.channel_create()
1765 interpreters.channel_send(cid, b'spam')
1766 interpreters.channel_recv(cid)
1767 interpreters.channel_release(cid, send=True, recv=True)
1768
1769 with self.assertRaises(interpreters.ChannelClosedError):
1770 interpreters.channel_send(cid, b'eggs')
1771 with self.assertRaises(interpreters.ChannelClosedError):
1772 interpreters.channel_recv(cid)
1773
1774 def test_multiple_users(self):
1775 cid = interpreters.channel_create()
1776 id1 = interpreters.create()
1777 id2 = interpreters.create()
1778 interpreters.run_string(id1, dedent(f"""
1779 import _xxsubinterpreters as _interpreters
1780 _interpreters.channel_send({cid}, b'spam')
1781 """))
1782 out = _run_output(id2, dedent(f"""
1783 import _xxsubinterpreters as _interpreters
1784 obj = _interpreters.channel_recv({cid})
1785 _interpreters.channel_release({cid})
1786 print(repr(obj))
1787 """))
1788 interpreters.run_string(id1, dedent(f"""
1789 _interpreters.channel_release({cid})
1790 """))
1791
1792 self.assertEqual(out.strip(), "b'spam'")
1793
1794 def test_no_kwargs(self):
1795 cid = interpreters.channel_create()
1796 interpreters.channel_send(cid, b'spam')
1797 interpreters.channel_recv(cid)
1798 interpreters.channel_release(cid)
1799
1800 with self.assertRaises(interpreters.ChannelClosedError):
1801 interpreters.channel_send(cid, b'eggs')
1802 with self.assertRaises(interpreters.ChannelClosedError):
1803 interpreters.channel_recv(cid)
1804
1805 def test_multiple_times(self):
1806 cid = interpreters.channel_create()
1807 interpreters.channel_send(cid, b'spam')
1808 interpreters.channel_recv(cid)
1809 interpreters.channel_release(cid, send=True, recv=True)
1810
1811 with self.assertRaises(interpreters.ChannelClosedError):
1812 interpreters.channel_release(cid, send=True, recv=True)
1813
1814 def test_with_unused_items(self):
1815 cid = interpreters.channel_create()
1816 interpreters.channel_send(cid, b'spam')
1817 interpreters.channel_send(cid, b'ham')
1818 interpreters.channel_release(cid, send=True, recv=True)
1819
1820 with self.assertRaises(interpreters.ChannelClosedError):
1821 interpreters.channel_recv(cid)
1822
1823 def test_never_used(self):
1824 cid = interpreters.channel_create()
1825 interpreters.channel_release(cid)
1826
1827 with self.assertRaises(interpreters.ChannelClosedError):
1828 interpreters.channel_send(cid, b'spam')
1829 with self.assertRaises(interpreters.ChannelClosedError):
1830 interpreters.channel_recv(cid)
1831
1832 def test_by_unassociated_interp(self):
1833 cid = interpreters.channel_create()
1834 interpreters.channel_send(cid, b'spam')
1835 interp = interpreters.create()
1836 interpreters.run_string(interp, dedent(f"""
1837 import _xxsubinterpreters as _interpreters
1838 _interpreters.channel_release({cid})
1839 """))
1840 obj = interpreters.channel_recv(cid)
1841 interpreters.channel_release(cid)
1842
1843 with self.assertRaises(interpreters.ChannelClosedError):
1844 interpreters.channel_send(cid, b'eggs')
1845 self.assertEqual(obj, b'spam')
1846
1847 def test_close_if_unassociated(self):
1848 # XXX Something's not right with this test...
1849 cid = interpreters.channel_create()
1850 interp = interpreters.create()
1851 interpreters.run_string(interp, dedent(f"""
1852 import _xxsubinterpreters as _interpreters
1853 obj = _interpreters.channel_send({cid}, b'spam')
1854 _interpreters.channel_release({cid})
1855 """))
1856
1857 with self.assertRaises(interpreters.ChannelClosedError):
1858 interpreters.channel_recv(cid)
1859
1860 def test_partially(self):
1861 # XXX Is partial close too weird/confusing?
1862 cid = interpreters.channel_create()
1863 interpreters.channel_send(cid, None)
1864 interpreters.channel_recv(cid)
1865 interpreters.channel_send(cid, b'spam')
1866 interpreters.channel_release(cid, send=True)
1867 obj = interpreters.channel_recv(cid)
1868
1869 self.assertEqual(obj, b'spam')
1870
1871 def test_used_multiple_times_by_single_user(self):
1872 cid = interpreters.channel_create()
1873 interpreters.channel_send(cid, b'spam')
1874 interpreters.channel_send(cid, b'spam')
1875 interpreters.channel_send(cid, b'spam')
1876 interpreters.channel_recv(cid)
1877 interpreters.channel_release(cid, send=True, recv=True)
1878
1879 with self.assertRaises(interpreters.ChannelClosedError):
1880 interpreters.channel_send(cid, b'eggs')
1881 with self.assertRaises(interpreters.ChannelClosedError):
1882 interpreters.channel_recv(cid)
1883
1884
1885class ChannelCloseFixture(namedtuple('ChannelCloseFixture',
1886 'end interp other extra creator')):
1887
1888 # Set this to True to avoid creating interpreters, e.g. when
1889 # scanning through test permutations without running them.
1890 QUICK = False
1891
1892 def __new__(cls, end, interp, other, extra, creator):
1893 assert end in ('send', 'recv')
1894 if cls.QUICK:
1895 known = {}
1896 else:
1897 interp = Interpreter.from_raw(interp)
1898 other = Interpreter.from_raw(other)
1899 extra = Interpreter.from_raw(extra)
1900 known = {
1901 interp.name: interp,
1902 other.name: other,
1903 extra.name: extra,
1904 }
1905 if not creator:
1906 creator = 'same'
1907 self = super().__new__(cls, end, interp, other, extra, creator)
1908 self._prepped = set()
1909 self._state = ChannelState()
1910 self._known = known
1911 return self
1912
1913 @property
1914 def state(self):
1915 return self._state
1916
1917 @property
1918 def cid(self):
1919 try:
1920 return self._cid
1921 except AttributeError:
1922 creator = self._get_interpreter(self.creator)
1923 self._cid = self._new_channel(creator)
1924 return self._cid
1925
1926 def get_interpreter(self, interp):
1927 interp = self._get_interpreter(interp)
1928 self._prep_interpreter(interp)
1929 return interp
1930
1931 def expect_closed_error(self, end=None):
1932 if end is None:
1933 end = self.end
1934 if end == 'recv' and self.state.closed == 'send':
1935 return False
1936 return bool(self.state.closed)
1937
1938 def prep_interpreter(self, interp):
1939 self._prep_interpreter(interp)
1940
1941 def record_action(self, action, result):
1942 self._state = result
1943
1944 def clean_up(self):
1945 clean_up_interpreters()
1946 clean_up_channels()
1947
1948 # internal methods
1949
1950 def _new_channel(self, creator):
1951 if creator.name == 'main':
1952 return interpreters.channel_create()
1953 else:
1954 ch = interpreters.channel_create()
1955 run_interp(creator.id, f"""
1956 import _xxsubinterpreters
1957 cid = _xxsubinterpreters.channel_create()
1958 # We purposefully send back an int to avoid tying the
1959 # channel to the other interpreter.
1960 _xxsubinterpreters.channel_send({ch}, int(cid))
1961 del _xxsubinterpreters
1962 """)
1963 self._cid = interpreters.channel_recv(ch)
1964 return self._cid
1965
1966 def _get_interpreter(self, interp):
1967 if interp in ('same', 'interp'):
1968 return self.interp
1969 elif interp == 'other':
1970 return self.other
1971 elif interp == 'extra':
1972 return self.extra
1973 else:
1974 name = interp
1975 try:
1976 interp = self._known[name]
1977 except KeyError:
1978 interp = self._known[name] = Interpreter(name)
1979 return interp
1980
1981 def _prep_interpreter(self, interp):
1982 if interp.id in self._prepped:
1983 return
1984 self._prepped.add(interp.id)
1985 if interp.name == 'main':
1986 return
1987 run_interp(interp.id, f"""
1988 import _xxsubinterpreters as interpreters
1989 import test.test__xxsubinterpreters as helpers
1990 ChannelState = helpers.ChannelState
1991 try:
1992 cid
1993 except NameError:
1994 cid = interpreters._channel_id({self.cid})
1995 """)
1996
1997
1998@unittest.skip('these tests take several hours to run')
1999class ExhaustiveChannelTests(TestBase):
2000
2001 """
2002 - main / interp / other
2003 - run in: current thread / new thread / other thread / different threads
2004 - end / opposite
2005 - force / no force
2006 - used / not used (associated / not associated)
2007 - empty / emptied / never emptied / partly emptied
2008 - closed / not closed
2009 - released / not released
2010 - creator (interp) / other
2011 - associated interpreter not running
2012 - associated interpreter destroyed
2013
2014 - close after unbound
2015 """
2016
2017 """
2018 use
2019 pre-close
2020 close
2021 after
2022 check
2023 """
2024
2025 """
2026 close in: main, interp1
2027 creator: same, other, extra
2028
2029 use: None,send,recv,send/recv in None,same,other,same+other,all
2030 pre-close: None,send,recv in None,same,other,same+other,all
2031 pre-close forced: None,send,recv in None,same,other,same+other,all
2032
2033 close: same
2034 close forced: same
2035
2036 use after: None,send,recv,send/recv in None,same,other,extra,same+other,all
2037 close after: None,send,recv,send/recv in None,same,other,extra,same+other,all
2038 check closed: send/recv for same/other(incl. interp2)
2039 """
2040
2041 def iter_action_sets(self):
2042 # - used / not used (associated / not associated)
2043 # - empty / emptied / never emptied / partly emptied
2044 # - closed / not closed
2045 # - released / not released
2046
2047 # never used
2048 yield []
2049
2050 # only pre-closed (and possible used after)
2051 for closeactions in self._iter_close_action_sets('same', 'other'):
2052 yield closeactions
2053 for postactions in self._iter_post_close_action_sets():
2054 yield closeactions + postactions
2055 for closeactions in self._iter_close_action_sets('other', 'extra'):
2056 yield closeactions
2057 for postactions in self._iter_post_close_action_sets():
2058 yield closeactions + postactions
2059
2060 # used
2061 for useactions in self._iter_use_action_sets('same', 'other'):
2062 yield useactions
2063 for closeactions in self._iter_close_action_sets('same', 'other'):
2064 actions = useactions + closeactions
2065 yield actions
2066 for postactions in self._iter_post_close_action_sets():
2067 yield actions + postactions
2068 for closeactions in self._iter_close_action_sets('other', 'extra'):
2069 actions = useactions + closeactions
2070 yield actions
2071 for postactions in self._iter_post_close_action_sets():
2072 yield actions + postactions
2073 for useactions in self._iter_use_action_sets('other', 'extra'):
2074 yield useactions
2075 for closeactions in self._iter_close_action_sets('same', 'other'):
2076 actions = useactions + closeactions
2077 yield actions
2078 for postactions in self._iter_post_close_action_sets():
2079 yield actions + postactions
2080 for closeactions in self._iter_close_action_sets('other', 'extra'):
2081 actions = useactions + closeactions
2082 yield actions
2083 for postactions in self._iter_post_close_action_sets():
2084 yield actions + postactions
2085
2086 def _iter_use_action_sets(self, interp1, interp2):
2087 interps = (interp1, interp2)
2088
2089 # only recv end used
2090 yield [
2091 ChannelAction('use', 'recv', interp1),
2092 ]
2093 yield [
2094 ChannelAction('use', 'recv', interp2),
2095 ]
2096 yield [
2097 ChannelAction('use', 'recv', interp1),
2098 ChannelAction('use', 'recv', interp2),
2099 ]
2100
2101 # never emptied
2102 yield [
2103 ChannelAction('use', 'send', interp1),
2104 ]
2105 yield [
2106 ChannelAction('use', 'send', interp2),
2107 ]
2108 yield [
2109 ChannelAction('use', 'send', interp1),
2110 ChannelAction('use', 'send', interp2),
2111 ]
2112
2113 # partially emptied
2114 for interp1 in interps:
2115 for interp2 in interps:
2116 for interp3 in interps:
2117 yield [
2118 ChannelAction('use', 'send', interp1),
2119 ChannelAction('use', 'send', interp2),
2120 ChannelAction('use', 'recv', interp3),
2121 ]
2122
2123 # fully emptied
2124 for interp1 in interps:
2125 for interp2 in interps:
2126 for interp3 in interps:
2127 for interp4 in interps:
2128 yield [
2129 ChannelAction('use', 'send', interp1),
2130 ChannelAction('use', 'send', interp2),
2131 ChannelAction('use', 'recv', interp3),
2132 ChannelAction('use', 'recv', interp4),
2133 ]
2134
2135 def _iter_close_action_sets(self, interp1, interp2):
2136 ends = ('recv', 'send')
2137 interps = (interp1, interp2)
2138 for force in (True, False):
2139 op = 'force-close' if force else 'close'
2140 for interp in interps:
2141 for end in ends:
2142 yield [
2143 ChannelAction(op, end, interp),
2144 ]
2145 for recvop in ('close', 'force-close'):
2146 for sendop in ('close', 'force-close'):
2147 for recv in interps:
2148 for send in interps:
2149 yield [
2150 ChannelAction(recvop, 'recv', recv),
2151 ChannelAction(sendop, 'send', send),
2152 ]
2153
2154 def _iter_post_close_action_sets(self):
2155 for interp in ('same', 'extra', 'other'):
2156 yield [
2157 ChannelAction('use', 'recv', interp),
2158 ]
2159 yield [
2160 ChannelAction('use', 'send', interp),
2161 ]
2162
2163 def run_actions(self, fix, actions):
2164 for action in actions:
2165 self.run_action(fix, action)
2166
2167 def run_action(self, fix, action, *, hideclosed=True):
2168 end = action.resolve_end(fix.end)
2169 interp = action.resolve_interp(fix.interp, fix.other, fix.extra)
2170 fix.prep_interpreter(interp)
2171 if interp.name == 'main':
2172 result = run_action(
2173 fix.cid,
2174 action.action,
2175 end,
2176 fix.state,
2177 hideclosed=hideclosed,
2178 )
2179 fix.record_action(action, result)
2180 else:
2181 _cid = interpreters.channel_create()
2182 run_interp(interp.id, f"""
2183 result = helpers.run_action(
2184 {fix.cid},
2185 {repr(action.action)},
2186 {repr(end)},
2187 {repr(fix.state)},
2188 hideclosed={hideclosed},
2189 )
2190 interpreters.channel_send({_cid}, result.pending.to_bytes(1, 'little'))
2191 interpreters.channel_send({_cid}, b'X' if result.closed else b'')
2192 """)
2193 result = ChannelState(
2194 pending=int.from_bytes(interpreters.channel_recv(_cid), 'little'),
2195 closed=bool(interpreters.channel_recv(_cid)),
2196 )
2197 fix.record_action(action, result)
2198
2199 def iter_fixtures(self):
2200 # XXX threads?
2201 interpreters = [
2202 ('main', 'interp', 'extra'),
2203 ('interp', 'main', 'extra'),
2204 ('interp1', 'interp2', 'extra'),
2205 ('interp1', 'interp2', 'main'),
2206 ]
2207 for interp, other, extra in interpreters:
2208 for creator in ('same', 'other', 'creator'):
2209 for end in ('send', 'recv'):
2210 yield ChannelCloseFixture(end, interp, other, extra, creator)
2211
2212 def _close(self, fix, *, force):
2213 op = 'force-close' if force else 'close'
2214 close = ChannelAction(op, fix.end, 'same')
2215 if not fix.expect_closed_error():
2216 self.run_action(fix, close, hideclosed=False)
2217 else:
2218 with self.assertRaises(interpreters.ChannelClosedError):
2219 self.run_action(fix, close, hideclosed=False)
2220
2221 def _assert_closed_in_interp(self, fix, interp=None):
2222 if interp is None or interp.name == 'main':
2223 with self.assertRaises(interpreters.ChannelClosedError):
2224 interpreters.channel_recv(fix.cid)
2225 with self.assertRaises(interpreters.ChannelClosedError):
2226 interpreters.channel_send(fix.cid, b'spam')
2227 with self.assertRaises(interpreters.ChannelClosedError):
2228 interpreters.channel_close(fix.cid)
2229 with self.assertRaises(interpreters.ChannelClosedError):
2230 interpreters.channel_close(fix.cid, force=True)
2231 else:
2232 run_interp(interp.id, f"""
2233 with helpers.expect_channel_closed():
2234 interpreters.channel_recv(cid)
2235 """)
2236 run_interp(interp.id, f"""
2237 with helpers.expect_channel_closed():
2238 interpreters.channel_send(cid, b'spam')
2239 """)
2240 run_interp(interp.id, f"""
2241 with helpers.expect_channel_closed():
2242 interpreters.channel_close(cid)
2243 """)
2244 run_interp(interp.id, f"""
2245 with helpers.expect_channel_closed():
2246 interpreters.channel_close(cid, force=True)
2247 """)
2248
2249 def _assert_closed(self, fix):
2250 self.assertTrue(fix.state.closed)
2251
2252 for _ in range(fix.state.pending):
2253 interpreters.channel_recv(fix.cid)
2254 self._assert_closed_in_interp(fix)
2255
2256 for interp in ('same', 'other'):
2257 interp = fix.get_interpreter(interp)
2258 if interp.name == 'main':
2259 continue
2260 self._assert_closed_in_interp(fix, interp)
2261
2262 interp = fix.get_interpreter('fresh')
2263 self._assert_closed_in_interp(fix, interp)
2264
2265 def _iter_close_tests(self, verbose=False):
2266 i = 0
2267 for actions in self.iter_action_sets():
2268 print()
2269 for fix in self.iter_fixtures():
2270 i += 1
2271 if i > 1000:
2272 return
2273 if verbose:
2274 if (i - 1) % 6 == 0:
2275 print()
2276 print(i, fix, '({} actions)'.format(len(actions)))
2277 else:
2278 if (i - 1) % 6 == 0:
2279 print(' ', end='')
2280 print('.', end=''); sys.stdout.flush()
2281 yield i, fix, actions
2282 if verbose:
2283 print('---')
2284 print()
2285
2286 # This is useful for scanning through the possible tests.
2287 def _skim_close_tests(self):
2288 ChannelCloseFixture.QUICK = True
2289 for i, fix, actions in self._iter_close_tests():
2290 pass
2291
2292 def test_close(self):
2293 for i, fix, actions in self._iter_close_tests():
2294 with self.subTest('{} {} {}'.format(i, fix, actions)):
2295 fix.prep_interpreter(fix.interp)
2296 self.run_actions(fix, actions)
2297
2298 self._close(fix, force=False)
2299
2300 self._assert_closed(fix)
2301 # XXX Things slow down if we have too many interpreters.
2302 fix.clean_up()
2303
2304 def test_force_close(self):
2305 for i, fix, actions in self._iter_close_tests():
2306 with self.subTest('{} {} {}'.format(i, fix, actions)):
2307 fix.prep_interpreter(fix.interp)
2308 self.run_actions(fix, actions)
2309
2310 self._close(fix, force=True)
2311
2312 self._assert_closed(fix)
2313 # XXX Things slow down if we have too many interpreters.
2314 fix.clean_up()
2315
Eric Snow7f8bfc92018-01-29 18:23:44 -07002316
2317if __name__ == '__main__':
2318 unittest.main()