1 | """Testing tools for byterun."""
|
2 |
|
3 | from __future__ import print_function
|
4 |
|
5 | import dis
|
6 | import sys
|
7 | import textwrap
|
8 | import types
|
9 | import unittest
|
10 |
|
11 | import six
|
12 |
|
13 | from pyvm2 import VirtualMachine, VirtualMachineError
|
14 |
|
15 | # Make this false if you need to run the debugger inside a test.
|
16 | CAPTURE_STDOUT = ('-s' not in sys.argv)
|
17 | # Make this false to see the traceback from a failure inside pyvm2.
|
18 | CAPTURE_EXCEPTION = 1
|
19 |
|
20 |
|
21 | def dis_code(code):
|
22 | """Disassemble `code` and all the code it refers to."""
|
23 | for const in code.co_consts:
|
24 | if isinstance(const, types.CodeType):
|
25 | dis_code(const)
|
26 |
|
27 | print("")
|
28 | print(code)
|
29 | dis.dis(code)
|
30 |
|
31 |
|
32 | class VmTestCase(unittest.TestCase):
|
33 |
|
34 | def assert_ok(self, code, raises=None):
|
35 | """Run `code` in our VM and in real Python: they behave the same."""
|
36 |
|
37 | code = textwrap.dedent(code)
|
38 | code = compile(code, "<%s>" % self.id(), "exec", 0, 1)
|
39 |
|
40 | # Print the disassembly so we'll see it if the test fails.
|
41 | dis_code(code)
|
42 |
|
43 | real_stdout = sys.stdout
|
44 |
|
45 | # Run the code through our VM.
|
46 |
|
47 | vm_stdout = six.StringIO()
|
48 | if CAPTURE_STDOUT: # pragma: no branch
|
49 | sys.stdout = vm_stdout
|
50 | vm = VirtualMachine()
|
51 |
|
52 | vm_value = vm_exc = None
|
53 | try:
|
54 | vm_value = vm.run_code(code)
|
55 | except VirtualMachineError: # pragma: no cover
|
56 | # If the VM code raises an error, show it.
|
57 | raise
|
58 | except AssertionError: # pragma: no cover
|
59 | # If test code fails an assert, show it.
|
60 | raise
|
61 | except Exception as e:
|
62 | # Otherwise, keep the exception for comparison later.
|
63 | if not CAPTURE_EXCEPTION: # pragma: no cover
|
64 | raise
|
65 | vm_exc = e
|
66 | finally:
|
67 | real_stdout.write("-- stdout ----------\n")
|
68 | real_stdout.write(vm_stdout.getvalue())
|
69 |
|
70 | # Run the code through the real Python interpreter, for comparison.
|
71 |
|
72 | py_stdout = six.StringIO()
|
73 | sys.stdout = py_stdout
|
74 |
|
75 | py_value = py_exc = None
|
76 | globs = {}
|
77 | try:
|
78 | py_value = eval(code, globs, globs)
|
79 | except AssertionError: # pragma: no cover
|
80 | raise
|
81 | except Exception as e:
|
82 | py_exc = e
|
83 |
|
84 | sys.stdout = real_stdout
|
85 |
|
86 | self.assert_same_exception(vm_exc, py_exc)
|
87 | self.assertEqual(vm_stdout.getvalue(), py_stdout.getvalue())
|
88 | self.assertEqual(vm_value, py_value)
|
89 | if raises:
|
90 | self.assertIsInstance(vm_exc, raises)
|
91 | else:
|
92 | self.assertIsNone(vm_exc)
|
93 |
|
94 | def assert_same_exception(self, e1, e2):
|
95 | """Exceptions don't implement __eq__, check it ourselves."""
|
96 | self.assertEqual(str(e1), str(e2))
|
97 | self.assertIs(type(e1), type(e2))
|