blob: fd41931d35d9f029af149df5d9a09d358750f23d [file] [log] [blame]
Damien George929a6752014-04-02 15:31:39 +01001#! /usr/bin/env python3
Damien39977a52013-12-29 22:34:42 +00002
Markus Siemens19ccc6b2014-01-27 22:53:28 +01003import os
4import subprocess
5import sys
Damien George612045f2014-09-17 22:56:34 +01006import platform
Damien George41f768f2014-05-03 16:43:27 +01007import argparse
Markus Siemens19ccc6b2014-01-27 22:53:28 +01008from glob import glob
Damien39977a52013-12-29 22:34:42 +00009
Paul Sokolovskya7752a42014-04-04 17:28:34 +030010# 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 Siemens19ccc6b2014-01-27 22:53:28 +010013if os.name == 'nt':
Paul Sokolovskya7752a42014-04-04 17:28:34 +030014 CPYTHON3 = os.getenv('MICROPY_CPYTHON3', 'python3.exe')
Andrew Scheller57094532014-04-17 01:22:45 +010015 MICROPYTHON = os.getenv('MICROPY_MICROPYTHON', '../windows/micropython.exe')
Markus Siemens19ccc6b2014-01-27 22:53:28 +010016else:
Paul Sokolovsky34e11992014-04-03 22:06:35 +030017 CPYTHON3 = os.getenv('MICROPY_CPYTHON3', 'python3')
Andrew Scheller57094532014-04-17 01:22:45 +010018 MICROPYTHON = os.getenv('MICROPY_MICROPYTHON', '../unix/micropython')
Damien39977a52013-12-29 22:34:42 +000019
blmorris9d1ca652014-11-19 10:44:31 -050020# Set PYTHONIOENCODING so that CPython will use utf-8 on systems which set another encoding in the locale
21os.environ['PYTHONIOENCODING']='utf-8'
22
Paul Sokolovskyfd232c32014-03-23 01:07:30 +020023def rm_f(fname):
24 if os.path.exists(fname):
25 os.remove(fname)
26
Damien George5a04e2c2014-10-05 22:27:12 +010027def 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 Sokolovsky2cf38102014-07-12 16:34:51 +030045def run_tests(pyb, tests, args):
Damien George41f768f2014-05-03 16:43:27 +010046 test_count = 0
47 testcase_count = 0
48 passed_count = 0
49 failed_tests = []
Paul Sokolovsky43d4a6f2014-05-10 16:52:58 +030050 skipped_tests = []
Damien39977a52013-12-29 22:34:42 +000051
Chris Angelico047db222014-06-06 07:45:55 +100052 skip_tests = set()
Damien39977a52013-12-29 22:34:42 +000053
Damien George5a04e2c2014-10-05 22:27:12 +010054 # 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 Angelico047db222014-06-06 07:45:55 +100060 # Some tests shouldn't be run under Travis CI
61 if os.getenv('TRAVIS') == 'true':
62 skip_tests.add('basics/memoryerror.py')
Damien George23093692014-04-03 22:44:51 +010063
Damien George8594ce22014-09-13 18:43:09 +010064 # 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 George612045f2014-09-17 22:56:34 +010068 # Some tests are known to fail on 64-bit machines
69 if pyb is None and platform.architecture()[0] == '64bit':
Damien George96e20c62014-09-23 12:09:26 +000070 pass
Damien George612045f2014-09-17 22:56:34 +010071
stijndc1ea112014-10-04 08:39:15 +020072 # Some tests use unsupported features on Windows
73 if os.name == 'nt':
stijndc1ea112014-10-04 08:39:15 +020074 skip_tests.add('import\\import_file.py') #works but CPython prints forward slashes
75 skip_tests.add('unix\\ffi_float.py')
76
Damien George15d2fe82014-08-29 19:47:10 +010077 # Some tests are known to fail with native emitter
78 # Remove them from the below when they work
79 if args.emit == 'native':
Damien Georged6230f62014-09-23 14:10:03 +000080 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 George15d2fe82014-08-29 19:47:10 +010081 skip_tests.add('float/string_format.py')
82 skip_tests.add('import/gen_context.py')
Damien George15d2fe82014-08-29 19:47:10 +010083 skip_tests.add('io/file_with.py')
84 skip_tests.add('micropython/heapalloc.py')
Damien George15d2fe82014-08-29 19:47:10 +010085 skip_tests.add('misc/features.py')
86 skip_tests.add('misc/recursion.py')
87 skip_tests.add('misc/rge_sm.py')
88
Damien George41f768f2014-05-03 16:43:27 +010089 for test_file in tests:
Chris Angelico88b11b52014-06-06 07:41:30 +100090 test_basename = os.path.basename(test_file)
91 test_name = os.path.splitext(test_basename)[0]
92
Chris Angelico047db222014-06-06 07:45:55 +100093 if test_file in skip_tests:
Damien George41f768f2014-05-03 16:43:27 +010094 print("skip ", test_file)
Paul Sokolovsky43d4a6f2014-05-10 16:52:58 +030095 skipped_tests.append(test_name)
Damien George41f768f2014-05-03 16:43:27 +010096 continue
Andrew Scheller1b997d52014-04-16 03:28:40 +010097
Damien George41f768f2014-05-03 16:43:27 +010098 # 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()
stijna4dbc732014-05-11 12:45:02 +0200104 if os.name == 'nt':
105 output_expected = output_expected.replace(b'\n', b'\r\n')
Damien George41f768f2014-05-03 16:43:27 +0100106 else:
stijna4dbc732014-05-11 12:45:02 +0200107 # run CPython to work out expected output
Damien George41f768f2014-05-03 16:43:27 +0100108 try:
109 output_expected = subprocess.check_output([CPYTHON3, '-B', test_file])
Paul Sokolovsky2cf38102014-07-12 16:34:51 +0300110 if args.write_exp:
111 with open(test_file_expected, 'wb') as f:
112 f.write(output_expected)
Damien George41f768f2014-05-03 16:43:27 +0100113 except subprocess.CalledProcessError:
114 output_expected = b'CPYTHON3 CRASH'
Damien39977a52013-12-29 22:34:42 +0000115
Paul Sokolovsky2cf38102014-07-12 16:34:51 +0300116 if args.write_exp:
117 continue
118
Damien George41f768f2014-05-03 16:43:27 +0100119 # run Micro Python
Damien George5a04e2c2014-10-05 22:27:12 +0100120 output_mupy = run_micropython(pyb, args, test_file)
Damien George4b34c762014-04-03 23:51:16 +0100121
Paul Sokolovsky43d4a6f2014-05-10 16:52:58 +0300122 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 George41f768f2014-05-03 16:43:27 +0100129 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 George41736f82014-06-28 10:29:12 +0100138 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 George41f768f2014-05-03 16:43:27 +0100142 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 Sokolovsky43d4a6f2014-05-10 16:52:58 +0300150 if len(skipped_tests) > 0:
151 print("{} tests skipped: {}".format(len(skipped_tests), ' '.join(skipped_tests)))
Damien George41f768f2014-05-03 16:43:27 +0100152 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
159def 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')
blmorris3b064372014-10-01 13:53:50 -0400162 cmd_parser.add_argument('--device', default='/dev/ttyACM0', help='the serial device of the pyboard')
Damien Georgea053e372014-05-31 18:11:01 +0100163 cmd_parser.add_argument('-d', '--test-dirs', nargs='*', help='input test directories (if no files given)')
Paul Sokolovsky2cf38102014-07-12 16:34:51 +0300164 cmd_parser.add_argument('--write-exp', action='store_true', help='save .exp files to run tests w/o CPython')
Damien George15d2fe82014-08-29 19:47:10 +0100165 cmd_parser.add_argument('--emit', default='bytecode', help='Micro Python emitter to use (bytecode or native)')
Damien George41f768f2014-05-03 16:43:27 +0100166 cmd_parser.add_argument('files', nargs='*', help='input test files')
167 args = cmd_parser.parse_args()
168
169 if args.pyboard:
170 import pyboard
blmorris3b064372014-10-01 13:53:50 -0400171 pyb = pyboard.Pyboard(args.device)
Damien Georgeb636d022014-04-13 13:48:33 +0100172 pyb.enter_raw_repl()
Damien Georgeb636d022014-04-13 13:48:33 +0100173 else:
Damien George41f768f2014-05-03 16:43:27 +0100174 pyb = None
Damien39977a52013-12-29 22:34:42 +0000175
Damien George41f768f2014-05-03 16:43:27 +0100176 if len(args.files) == 0:
stijn8ac3b572014-05-28 14:27:54 +0200177 if args.test_dirs is None:
178 if pyb is None:
179 # run PC tests
Damien George612045f2014-09-17 22:56:34 +0100180 test_dirs = ('basics', 'micropython', 'float', 'import', 'io', 'misc', 'unicode', 'extmod', 'unix')
stijn8ac3b572014-05-28 14:27:54 +0200181 else:
182 # run pyboard tests
Damien George612045f2014-09-17 22:56:34 +0100183 test_dirs = ('basics', 'micropython', 'float', 'misc', 'extmod', 'pyb', 'pybnative', 'inlineasm')
Damien George41f768f2014-05-03 16:43:27 +0100184 else:
stijn8ac3b572014-05-28 14:27:54 +0200185 # run tests from these directories
186 test_dirs = args.test_dirs
Damien George41f768f2014-05-03 16:43:27 +0100187 tests = sorted(test_file for test_files in (glob('{}/*.py'.format(dir)) for dir in test_dirs) for test_file in test_files)
Markus Siemens19ccc6b2014-01-27 22:53:28 +0100188 else:
Damien George41f768f2014-05-03 16:43:27 +0100189 # tests explicitly given
190 tests = args.files
Markus Siemens19ccc6b2014-01-27 22:53:28 +0100191
Paul Sokolovsky2cf38102014-07-12 16:34:51 +0300192 if not run_tests(pyb, tests, args):
Damien George41f768f2014-05-03 16:43:27 +0100193 sys.exit(1)
Markus Siemens19ccc6b2014-01-27 22:53:28 +0100194
Damien George41f768f2014-05-03 16:43:27 +0100195if __name__ == "__main__":
196 main()