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