Damien George | 929a675 | 2014-04-02 15:31:39 +0100 | [diff] [blame] | 1 | #! /usr/bin/env python3 |
Damien | 39977a5 | 2013-12-29 22:34:42 +0000 | [diff] [blame] | 2 | |
Markus Siemens | 19ccc6b | 2014-01-27 22:53:28 +0100 | [diff] [blame] | 3 | import os |
| 4 | import subprocess |
| 5 | import sys |
Damien George | 612045f | 2014-09-17 22:56:34 +0100 | [diff] [blame] | 6 | import platform |
Damien George | 41f768f | 2014-05-03 16:43:27 +0100 | [diff] [blame] | 7 | import argparse |
Markus Siemens | 19ccc6b | 2014-01-27 22:53:28 +0100 | [diff] [blame] | 8 | from glob import glob |
Damien | 39977a5 | 2013-12-29 22:34:42 +0000 | [diff] [blame] | 9 | |
Paul Sokolovsky | a7752a4 | 2014-04-04 17:28:34 +0300 | [diff] [blame] | 10 | # Tests require at least CPython 3.3. If your default python3 executable |
| 11 | # is of lower version, you can point MICROPY_CPYTHON3 environment var |
| 12 | # to the correct executable. |
Markus Siemens | 19ccc6b | 2014-01-27 22:53:28 +0100 | [diff] [blame] | 13 | if os.name == 'nt': |
Paul Sokolovsky | a7752a4 | 2014-04-04 17:28:34 +0300 | [diff] [blame] | 14 | CPYTHON3 = os.getenv('MICROPY_CPYTHON3', 'python3.exe') |
Andrew Scheller | 5709453 | 2014-04-17 01:22:45 +0100 | [diff] [blame] | 15 | MICROPYTHON = os.getenv('MICROPY_MICROPYTHON', '../windows/micropython.exe') |
Markus Siemens | 19ccc6b | 2014-01-27 22:53:28 +0100 | [diff] [blame] | 16 | else: |
Paul Sokolovsky | 34e1199 | 2014-04-03 22:06:35 +0300 | [diff] [blame] | 17 | CPYTHON3 = os.getenv('MICROPY_CPYTHON3', 'python3') |
Andrew Scheller | 5709453 | 2014-04-17 01:22:45 +0100 | [diff] [blame] | 18 | MICROPYTHON = os.getenv('MICROPY_MICROPYTHON', '../unix/micropython') |
Damien | 39977a5 | 2013-12-29 22:34:42 +0000 | [diff] [blame] | 19 | |
blmorris | 9d1ca65 | 2014-11-19 10:44:31 -0500 | [diff] [blame^] | 20 | # Set PYTHONIOENCODING so that CPython will use utf-8 on systems which set another encoding in the locale |
| 21 | os.environ['PYTHONIOENCODING']='utf-8' |
| 22 | |
Paul Sokolovsky | fd232c3 | 2014-03-23 01:07:30 +0200 | [diff] [blame] | 23 | def rm_f(fname): |
| 24 | if os.path.exists(fname): |
| 25 | os.remove(fname) |
| 26 | |
Damien George | 5a04e2c | 2014-10-05 22:27:12 +0100 | [diff] [blame] | 27 | def run_micropython(pyb, args, test_file): |
| 28 | if pyb is None: |
| 29 | # run on PC |
| 30 | try: |
| 31 | output_mupy = subprocess.check_output([MICROPYTHON, '-X', 'emit=' + args.emit, test_file]) |
| 32 | except subprocess.CalledProcessError: |
| 33 | output_mupy = b'CRASH' |
| 34 | else: |
| 35 | # run on pyboard |
| 36 | import pyboard |
| 37 | pyb.enter_raw_repl() |
| 38 | try: |
| 39 | output_mupy = pyb.execfile(test_file).replace(b'\r\n', b'\n') |
| 40 | except pyboard.PyboardError: |
| 41 | output_mupy = b'CRASH' |
| 42 | |
| 43 | return output_mupy |
| 44 | |
Paul Sokolovsky | 2cf3810 | 2014-07-12 16:34:51 +0300 | [diff] [blame] | 45 | def run_tests(pyb, tests, args): |
Damien George | 41f768f | 2014-05-03 16:43:27 +0100 | [diff] [blame] | 46 | test_count = 0 |
| 47 | testcase_count = 0 |
| 48 | passed_count = 0 |
| 49 | failed_tests = [] |
Paul Sokolovsky | 43d4a6f | 2014-05-10 16:52:58 +0300 | [diff] [blame] | 50 | skipped_tests = [] |
Damien | 39977a5 | 2013-12-29 22:34:42 +0000 | [diff] [blame] | 51 | |
Chris Angelico | 047db22 | 2014-06-06 07:45:55 +1000 | [diff] [blame] | 52 | skip_tests = set() |
Damien | 39977a5 | 2013-12-29 22:34:42 +0000 | [diff] [blame] | 53 | |
Damien George | 5a04e2c | 2014-10-05 22:27:12 +0100 | [diff] [blame] | 54 | # Check if micropython.native is supported, and skip such tests if it's not |
| 55 | native = run_micropython(pyb, args, 'micropython/native_check.py') |
| 56 | if native == b'CRASH': |
| 57 | skip_tests.update({'micropython/native_%s.py' % t for t in 'check misc'.split()}) |
| 58 | skip_tests.update({'micropython/viper_%s.py' % t for t in 'binop_arith binop_comp cond ptr16_store ptr8_store misc'.split()}) |
| 59 | |
Chris Angelico | 047db22 | 2014-06-06 07:45:55 +1000 | [diff] [blame] | 60 | # Some tests shouldn't be run under Travis CI |
| 61 | if os.getenv('TRAVIS') == 'true': |
| 62 | skip_tests.add('basics/memoryerror.py') |
Damien George | 2309369 | 2014-04-03 22:44:51 +0100 | [diff] [blame] | 63 | |
Damien George | 8594ce2 | 2014-09-13 18:43:09 +0100 | [diff] [blame] | 64 | # Some tests shouldn't be run on pyboard |
| 65 | if pyb is not None: |
| 66 | skip_tests.add('float/float_divmod.py') # tested by float/float_divmod_relaxed.py instead |
| 67 | |
Damien George | 612045f | 2014-09-17 22:56:34 +0100 | [diff] [blame] | 68 | # Some tests are known to fail on 64-bit machines |
| 69 | if pyb is None and platform.architecture()[0] == '64bit': |
Damien George | 96e20c6 | 2014-09-23 12:09:26 +0000 | [diff] [blame] | 70 | pass |
Damien George | 612045f | 2014-09-17 22:56:34 +0100 | [diff] [blame] | 71 | |
stijn | dc1ea11 | 2014-10-04 08:39:15 +0200 | [diff] [blame] | 72 | # Some tests use unsupported features on Windows |
| 73 | if os.name == 'nt': |
stijn | dc1ea11 | 2014-10-04 08:39:15 +0200 | [diff] [blame] | 74 | skip_tests.add('import\\import_file.py') #works but CPython prints forward slashes |
| 75 | skip_tests.add('unix\\ffi_float.py') |
| 76 | |
Damien George | 15d2fe8 | 2014-08-29 19:47:10 +0100 | [diff] [blame] | 77 | # Some tests are known to fail with native emitter |
| 78 | # Remove them from the below when they work |
| 79 | if args.emit == 'native': |
Damien George | d6230f6 | 2014-09-23 14:10:03 +0000 | [diff] [blame] | 80 | skip_tests.update({'basics/%s.py' % t for t in 'bytes_gen class_store_class class_super class_super_object closure1 closure2 closure_defargs del_deref del_local fun3 fun_calldblstar fun_callstar fun_callstardblstar fun_defargs fun_defargs2 fun_kwargs fun_kwonly fun_kwonlydef fun_kwvarargs fun_varargs gen_yield_from gen_yield_from_close gen_yield_from_ducktype gen_yield_from_exc gen_yield_from_iter gen_yield_from_send gen_yield_from_throw generator1 generator2 generator_args generator_close generator_closure generator_exc generator_return generator_send globals_del string_format string_join subclass_native2_list subclass_native2_tuple try2 try_finally1 try_finally_loops try_finally_return try_reraise try_reraise2 unboundlocal with1 with_break with_continue with_return'.split()}) |
Damien George | 15d2fe8 | 2014-08-29 19:47:10 +0100 | [diff] [blame] | 81 | skip_tests.add('float/string_format.py') |
| 82 | skip_tests.add('import/gen_context.py') |
Damien George | 15d2fe8 | 2014-08-29 19:47:10 +0100 | [diff] [blame] | 83 | skip_tests.add('io/file_with.py') |
| 84 | skip_tests.add('micropython/heapalloc.py') |
Damien George | 15d2fe8 | 2014-08-29 19:47:10 +0100 | [diff] [blame] | 85 | skip_tests.add('misc/features.py') |
| 86 | skip_tests.add('misc/recursion.py') |
| 87 | skip_tests.add('misc/rge_sm.py') |
| 88 | |
Damien George | 41f768f | 2014-05-03 16:43:27 +0100 | [diff] [blame] | 89 | for test_file in tests: |
Chris Angelico | 88b11b5 | 2014-06-06 07:41:30 +1000 | [diff] [blame] | 90 | test_basename = os.path.basename(test_file) |
| 91 | test_name = os.path.splitext(test_basename)[0] |
| 92 | |
Chris Angelico | 047db22 | 2014-06-06 07:45:55 +1000 | [diff] [blame] | 93 | if test_file in skip_tests: |
Damien George | 41f768f | 2014-05-03 16:43:27 +0100 | [diff] [blame] | 94 | print("skip ", test_file) |
Paul Sokolovsky | 43d4a6f | 2014-05-10 16:52:58 +0300 | [diff] [blame] | 95 | skipped_tests.append(test_name) |
Damien George | 41f768f | 2014-05-03 16:43:27 +0100 | [diff] [blame] | 96 | continue |
Andrew Scheller | 1b997d5 | 2014-04-16 03:28:40 +0100 | [diff] [blame] | 97 | |
Damien George | 41f768f | 2014-05-03 16:43:27 +0100 | [diff] [blame] | 98 | # get expected output |
| 99 | test_file_expected = test_file + '.exp' |
| 100 | if os.path.isfile(test_file_expected): |
| 101 | # expected output given by a file, so read that in |
| 102 | with open(test_file_expected, 'rb') as f: |
| 103 | output_expected = f.read() |
stijn | a4dbc73 | 2014-05-11 12:45:02 +0200 | [diff] [blame] | 104 | if os.name == 'nt': |
| 105 | output_expected = output_expected.replace(b'\n', b'\r\n') |
Damien George | 41f768f | 2014-05-03 16:43:27 +0100 | [diff] [blame] | 106 | else: |
stijn | a4dbc73 | 2014-05-11 12:45:02 +0200 | [diff] [blame] | 107 | # run CPython to work out expected output |
Damien George | 41f768f | 2014-05-03 16:43:27 +0100 | [diff] [blame] | 108 | try: |
| 109 | output_expected = subprocess.check_output([CPYTHON3, '-B', test_file]) |
Paul Sokolovsky | 2cf3810 | 2014-07-12 16:34:51 +0300 | [diff] [blame] | 110 | if args.write_exp: |
| 111 | with open(test_file_expected, 'wb') as f: |
| 112 | f.write(output_expected) |
Damien George | 41f768f | 2014-05-03 16:43:27 +0100 | [diff] [blame] | 113 | except subprocess.CalledProcessError: |
| 114 | output_expected = b'CPYTHON3 CRASH' |
Damien | 39977a5 | 2013-12-29 22:34:42 +0000 | [diff] [blame] | 115 | |
Paul Sokolovsky | 2cf3810 | 2014-07-12 16:34:51 +0300 | [diff] [blame] | 116 | if args.write_exp: |
| 117 | continue |
| 118 | |
Damien George | 41f768f | 2014-05-03 16:43:27 +0100 | [diff] [blame] | 119 | # run Micro Python |
Damien George | 5a04e2c | 2014-10-05 22:27:12 +0100 | [diff] [blame] | 120 | output_mupy = run_micropython(pyb, args, test_file) |
Damien George | 4b34c76 | 2014-04-03 23:51:16 +0100 | [diff] [blame] | 121 | |
Paul Sokolovsky | 43d4a6f | 2014-05-10 16:52:58 +0300 | [diff] [blame] | 122 | if output_mupy == b'SKIP\n': |
| 123 | print("skip ", test_file) |
| 124 | skipped_tests.append(test_name) |
| 125 | continue |
| 126 | |
| 127 | testcase_count += len(output_expected.splitlines()) |
| 128 | |
Damien George | 41f768f | 2014-05-03 16:43:27 +0100 | [diff] [blame] | 129 | filename_expected = test_basename + ".exp" |
| 130 | filename_mupy = test_basename + ".out" |
| 131 | |
| 132 | if output_expected == output_mupy: |
| 133 | print("pass ", test_file) |
| 134 | passed_count += 1 |
| 135 | rm_f(filename_expected) |
| 136 | rm_f(filename_mupy) |
| 137 | else: |
Damien George | 41736f8 | 2014-06-28 10:29:12 +0100 | [diff] [blame] | 138 | with open(filename_expected, "wb") as f: |
| 139 | f.write(output_expected) |
| 140 | with open(filename_mupy, "wb") as f: |
| 141 | f.write(output_mupy) |
Damien George | 41f768f | 2014-05-03 16:43:27 +0100 | [diff] [blame] | 142 | print("FAIL ", test_file) |
| 143 | failed_tests.append(test_name) |
| 144 | |
| 145 | test_count += 1 |
| 146 | |
| 147 | print("{} tests performed ({} individual testcases)".format(test_count, testcase_count)) |
| 148 | print("{} tests passed".format(passed_count)) |
| 149 | |
Paul Sokolovsky | 43d4a6f | 2014-05-10 16:52:58 +0300 | [diff] [blame] | 150 | if len(skipped_tests) > 0: |
| 151 | print("{} tests skipped: {}".format(len(skipped_tests), ' '.join(skipped_tests))) |
Damien George | 41f768f | 2014-05-03 16:43:27 +0100 | [diff] [blame] | 152 | if len(failed_tests) > 0: |
| 153 | print("{} tests failed: {}".format(len(failed_tests), ' '.join(failed_tests))) |
| 154 | return False |
| 155 | |
| 156 | # all tests succeeded |
| 157 | return True |
| 158 | |
| 159 | def main(): |
| 160 | cmd_parser = argparse.ArgumentParser(description='Run tests for Micro Python.') |
| 161 | cmd_parser.add_argument('--pyboard', action='store_true', help='run the tests on the pyboard') |
blmorris | 3b06437 | 2014-10-01 13:53:50 -0400 | [diff] [blame] | 162 | cmd_parser.add_argument('--device', default='/dev/ttyACM0', help='the serial device of the pyboard') |
Damien George | a053e37 | 2014-05-31 18:11:01 +0100 | [diff] [blame] | 163 | cmd_parser.add_argument('-d', '--test-dirs', nargs='*', help='input test directories (if no files given)') |
Paul Sokolovsky | 2cf3810 | 2014-07-12 16:34:51 +0300 | [diff] [blame] | 164 | cmd_parser.add_argument('--write-exp', action='store_true', help='save .exp files to run tests w/o CPython') |
Damien George | 15d2fe8 | 2014-08-29 19:47:10 +0100 | [diff] [blame] | 165 | cmd_parser.add_argument('--emit', default='bytecode', help='Micro Python emitter to use (bytecode or native)') |
Damien George | 41f768f | 2014-05-03 16:43:27 +0100 | [diff] [blame] | 166 | cmd_parser.add_argument('files', nargs='*', help='input test files') |
| 167 | args = cmd_parser.parse_args() |
| 168 | |
| 169 | if args.pyboard: |
| 170 | import pyboard |
blmorris | 3b06437 | 2014-10-01 13:53:50 -0400 | [diff] [blame] | 171 | pyb = pyboard.Pyboard(args.device) |
Damien George | b636d02 | 2014-04-13 13:48:33 +0100 | [diff] [blame] | 172 | pyb.enter_raw_repl() |
Damien George | b636d02 | 2014-04-13 13:48:33 +0100 | [diff] [blame] | 173 | else: |
Damien George | 41f768f | 2014-05-03 16:43:27 +0100 | [diff] [blame] | 174 | pyb = None |
Damien | 39977a5 | 2013-12-29 22:34:42 +0000 | [diff] [blame] | 175 | |
Damien George | 41f768f | 2014-05-03 16:43:27 +0100 | [diff] [blame] | 176 | if len(args.files) == 0: |
stijn | 8ac3b57 | 2014-05-28 14:27:54 +0200 | [diff] [blame] | 177 | if args.test_dirs is None: |
| 178 | if pyb is None: |
| 179 | # run PC tests |
Damien George | 612045f | 2014-09-17 22:56:34 +0100 | [diff] [blame] | 180 | test_dirs = ('basics', 'micropython', 'float', 'import', 'io', 'misc', 'unicode', 'extmod', 'unix') |
stijn | 8ac3b57 | 2014-05-28 14:27:54 +0200 | [diff] [blame] | 181 | else: |
| 182 | # run pyboard tests |
Damien George | 612045f | 2014-09-17 22:56:34 +0100 | [diff] [blame] | 183 | test_dirs = ('basics', 'micropython', 'float', 'misc', 'extmod', 'pyb', 'pybnative', 'inlineasm') |
Damien George | 41f768f | 2014-05-03 16:43:27 +0100 | [diff] [blame] | 184 | else: |
stijn | 8ac3b57 | 2014-05-28 14:27:54 +0200 | [diff] [blame] | 185 | # run tests from these directories |
| 186 | test_dirs = args.test_dirs |
Damien George | 41f768f | 2014-05-03 16:43:27 +0100 | [diff] [blame] | 187 | tests = sorted(test_file for test_files in (glob('{}/*.py'.format(dir)) for dir in test_dirs) for test_file in test_files) |
Markus Siemens | 19ccc6b | 2014-01-27 22:53:28 +0100 | [diff] [blame] | 188 | else: |
Damien George | 41f768f | 2014-05-03 16:43:27 +0100 | [diff] [blame] | 189 | # tests explicitly given |
| 190 | tests = args.files |
Markus Siemens | 19ccc6b | 2014-01-27 22:53:28 +0100 | [diff] [blame] | 191 | |
Paul Sokolovsky | 2cf3810 | 2014-07-12 16:34:51 +0300 | [diff] [blame] | 192 | if not run_tests(pyb, tests, args): |
Damien George | 41f768f | 2014-05-03 16:43:27 +0100 | [diff] [blame] | 193 | sys.exit(1) |
Markus Siemens | 19ccc6b | 2014-01-27 22:53:28 +0100 | [diff] [blame] | 194 | |
Damien George | 41f768f | 2014-05-03 16:43:27 +0100 | [diff] [blame] | 195 | if __name__ == "__main__": |
| 196 | main() |