diff --git a/tests/cmdline/cmd_parsetree.py.exp b/tests/cmdline/cmd_parsetree.py.exp index e64f4f782951a..f56a28a1f2faa 100644 --- a/tests/cmdline/cmd_parsetree.py.exp +++ b/tests/cmdline/cmd_parsetree.py.exp @@ -29,7 +29,7 @@ id(g) int(123) ---------------- -File cmdline/cmd_parsetree.py, code block '' (descriptor: \.\+, bytecode @\.\+ bytes) +File \.\*cmdline/cmd_parsetree.py, code block '' (descriptor: \.\+, bytecode @\.\+ bytes) Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): ######## \.\+63 diff --git a/tests/cmdline/cmd_showbc.py.exp b/tests/cmdline/cmd_showbc.py.exp index 325efc7dbaaad..1be0a414584b0 100644 --- a/tests/cmdline/cmd_showbc.py.exp +++ b/tests/cmdline/cmd_showbc.py.exp @@ -1,4 +1,4 @@ -File cmdline/cmd_showbc.py, code block '' (descriptor: \.\+, bytecode @\.\+ bytes) +File \.\*cmd_showbc.py, code block '' (descriptor: \.\+, bytecode @\.\+ bytes) Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): ######## \.\+63 @@ -34,7 +34,7 @@ arg names: \\d\+ IMPORT_STAR \\d\+ LOAD_CONST_NONE \\d\+ RETURN_VALUE -File cmdline/cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ bytes) +File \.\*cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ bytes) Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): ######## \.\+rg names: @@ -312,7 +312,7 @@ Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): \\d\+ RETURN_VALUE \\d\+ LOAD_CONST_SMALL_INT 1 \\d\+ RETURN_VALUE -File cmdline/cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ bytes) +File \.\*cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ bytes) Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): ######## \.\+rg names: @@ -367,7 +367,7 @@ Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): 48 POP_TOP 49 LOAD_CONST_NONE 50 RETURN_VALUE -File cmdline/cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ bytes) +File \.\*cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ bytes) Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): ######## \.\+63 @@ -385,7 +385,7 @@ arg names: a \\d\+ STORE_FAST 1 \\d\+ LOAD_CONST_NONE \\d\+ RETURN_VALUE -File cmdline/cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ bytes) +File \.\*cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ bytes) Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): ######## \.\+63 @@ -409,7 +409,7 @@ arg names: 10 POP_TOP 11 LOAD_CONST_NONE 12 RETURN_VALUE -File cmdline/cmd_showbc.py, code block 'Class' (descriptor: \.\+, bytecode @\.\+ bytes) +File \.\*cmd_showbc.py, code block 'Class' (descriptor: \.\+, bytecode @\.\+ bytes) Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): ######## \.\+63 @@ -425,7 +425,7 @@ arg names: 10 STORE_NAME __qualname__ 13 LOAD_CONST_NONE 14 RETURN_VALUE -File cmdline/cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ bytes) +File \.\*cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ bytes) Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): ######## \.\+63 @@ -442,7 +442,7 @@ arg names: self \\d\+ POP_TOP \\d\+ LOAD_CONST_NONE \\d\+ RETURN_VALUE -File cmdline/cmd_showbc.py, code block '' (descriptor: \.\+, bytecode @\.\+ bytes) +File \.\*cmd_showbc.py, code block '' (descriptor: \.\+, bytecode @\.\+ bytes) Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): ######## \.\+63 @@ -466,7 +466,7 @@ arg names: * * * 17 JUMP 4 20 LOAD_CONST_NONE 21 RETURN_VALUE -File cmdline/cmd_showbc.py, code block '' (descriptor: \.\+, bytecode @\.\+ bytes) +File \.\*cmd_showbc.py, code block '' (descriptor: \.\+, bytecode @\.\+ bytes) Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): ######## \.\+63 @@ -487,7 +487,7 @@ arg names: * * * 15 STORE_COMP 20 17 JUMP 4 20 RETURN_VALUE -File cmdline/cmd_showbc.py, code block '' (descriptor: \.\+, bytecode @\.\+ bytes) +File \.\*cmd_showbc.py, code block '' (descriptor: \.\+, bytecode @\.\+ bytes) Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): ######## \.\+63 @@ -508,7 +508,7 @@ arg names: * * * 17 STORE_COMP 25 19 JUMP 4 22 RETURN_VALUE -File cmdline/cmd_showbc.py, code block 'closure' (descriptor: \.\+, bytecode @\.\+ bytes) +File \.\*cmd_showbc.py, code block 'closure' (descriptor: \.\+, bytecode @\.\+ bytes) Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): ######## \.\+63 @@ -527,7 +527,7 @@ arg names: * 08 DELETE_DEREF 0 10 LOAD_CONST_NONE 11 RETURN_VALUE -File cmdline/cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ bytes) +File \.\*cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ bytes) Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): ######## \.\+63 diff --git a/tests/cmdline/cmd_verbose.py.exp b/tests/cmdline/cmd_verbose.py.exp index a2fdf1f00deb3..f158a560eafc8 100644 --- a/tests/cmdline/cmd_verbose.py.exp +++ b/tests/cmdline/cmd_verbose.py.exp @@ -1,4 +1,4 @@ -File cmdline/cmd_verbose.py, code block '' (descriptor: \.\+, bytecode \.\+ bytes) +File \.\*cmd_verbose.py, code block '' (descriptor: \.\+, bytecode \.\+ bytes) Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): 08 \.\+ ######## diff --git a/tests/cmdline/repl_micropyinspect.py b/tests/cmdline/repl_micropyinspect.py index 220dd43d80d87..01a79cc5c8990 100644 --- a/tests/cmdline/repl_micropyinspect.py +++ b/tests/cmdline/repl_micropyinspect.py @@ -1,2 +1,2 @@ -# cmdline: cmdline/repl_micropyinspect +# cmdline: ./cmdline/repl_micropyinspect # setting MICROPYINSPECT environment variable before program exit triggers REPL diff --git a/tests/cmdline/repl_micropyinspect.py.exp b/tests/cmdline/repl_micropyinspect.py.exp index 93ff43546eace..c394b14e70076 100644 --- a/tests/cmdline/repl_micropyinspect.py.exp +++ b/tests/cmdline/repl_micropyinspect.py.exp @@ -1,5 +1,5 @@ MicroPython \.\+ version Use \.\+ ->>> # cmdline: cmdline/repl_micropyinspect +>>> # cmdline: ./cmdline/repl_micropyinspect >>> # setting MICROPYINSPECT environment variable before program exit triggers REPL >>> diff --git a/tests/io/file1.py b/tests/io/file1.py index de30045d316bb..1274a653d1f91 100644 --- a/tests/io/file1.py +++ b/tests/io/file1.py @@ -1,20 +1,20 @@ -f = open("io/data/file1") +f = open("data/file1") print(f.read(5)) print(f.readline()) print(f.read()) -f = open("io/data/file1") +f = open("data/file1") print(f.readlines()) -f = open("io/data/file1", "r") +f = open("data/file1", "r") print(f.readlines()) -f = open("io/data/file1", "rb") +f = open("data/file1", "rb") print(f.readlines()) -f = open("io/data/file1", mode="r") +f = open("data/file1", mode="r") print(f.readlines()) -f = open("io/data/file1", mode="rb") +f = open("data/file1", mode="rb") print(f.readlines()) # write() error -f = open("io/data/file1", "r") +f = open("data/file1", "r") try: f.write("x") except OSError: @@ -22,7 +22,7 @@ f.close() # read(n) error on binary file -f = open("io/data/file1", "ab") +f = open("data/file1", "ab") try: f.read(1) except OSError: @@ -30,7 +30,7 @@ f.close() # read(n) error on text file -f = open("io/data/file1", "at") +f = open("data/file1", "at") try: f.read(1) except OSError: @@ -38,7 +38,7 @@ f.close() # read() w/o args error -f = open("io/data/file1", "ab") +f = open("data/file1", "ab") try: f.read() except OSError: diff --git a/tests/io/file_iter.py b/tests/io/file_iter.py index 48e8739966179..26e82b9b1ae09 100644 --- a/tests/io/file_iter.py +++ b/tests/io/file_iter.py @@ -1,3 +1,3 @@ -f = open("io/data/file1") +f = open("data/file1") for l in f: print(l) diff --git a/tests/io/file_long_read.py b/tests/io/file_long_read.py index 8bdd484504444..3f57d5594d1c0 100644 --- a/tests/io/file_long_read.py +++ b/tests/io/file_long_read.py @@ -1,3 +1,3 @@ -f = open("io/data/file1") +f = open("data/file1") b = f.read(100) print(len(b)) diff --git a/tests/io/file_long_read2.py b/tests/io/file_long_read2.py index 337a5fba96987..ea87f91f10fdc 100644 --- a/tests/io/file_long_read2.py +++ b/tests/io/file_long_read2.py @@ -1,4 +1,4 @@ -f = open("io/data/bigfile1") +f = open("data/bigfile1") b = f.read() print(len(b)) print(b) diff --git a/tests/io/file_long_read3.py b/tests/io/file_long_read3.py index d8b0cce550326..1ea47e1853bd3 100644 --- a/tests/io/file_long_read3.py +++ b/tests/io/file_long_read3.py @@ -1,4 +1,4 @@ -f = open("io/data/bigfile1", "rb") +f = open("data/bigfile1", "rb") b = f.read(512) print(len(b)) print(b) diff --git a/tests/io/file_readinto.py b/tests/io/file_readinto.py index 1f3702a217e60..f9004013d9d12 100644 --- a/tests/io/file_readinto.py +++ b/tests/io/file_readinto.py @@ -1,13 +1,13 @@ b = bytearray(30) -f = open("io/data/file1", "rb") +f = open("data/file1", "rb") print(f.readinto(b)) print(b) -f = open("io/data/file2", "rb") +f = open("data/file2", "rb") print(f.readinto(b)) print(b) # readinto() on writable file -f = open("io/data/file1", "ab") +f = open("data/file1", "ab") try: f.readinto(bytearray(4)) except OSError: diff --git a/tests/io/file_readinto_len.py b/tests/io/file_readinto_len.py index 84cc8cf5e17b8..d6eb1dfc4188e 100644 --- a/tests/io/file_readinto_len.py +++ b/tests/io/file_readinto_len.py @@ -1,10 +1,10 @@ b = bytearray(30) -f = open("io/data/file1", "rb") +f = open("data/file1", "rb") # 2nd arg (length to read) is extension to CPython print(f.readinto(b, 8)) print(b) b = bytearray(4) -f = open("io/data/file1", "rb") +f = open("data/file1", "rb") print(f.readinto(b, 8)) print(b) diff --git a/tests/io/file_readline.py b/tests/io/file_readline.py index 86d010eaf657a..3d270db5149bc 100644 --- a/tests/io/file_readline.py +++ b/tests/io/file_readline.py @@ -1,4 +1,4 @@ -f = open("io/data/file1") +f = open("data/file1") print(f.readline()) print(f.readline(3)) print(f.readline(4)) @@ -6,7 +6,7 @@ print(f.readline()) # readline() on writable file -f = open("io/data/file1", "ab") +f = open("data/file1", "ab") try: f.readline() except OSError: diff --git a/tests/io/file_seek.py b/tests/io/file_seek.py index 2fe57692c63bc..3990df8409051 100644 --- a/tests/io/file_seek.py +++ b/tests/io/file_seek.py @@ -1,4 +1,4 @@ -f = open("io/data/file1", "rb") +f = open("data/file1", "rb") print(f.seek(6)) print(f.read(5)) print(f.tell()) @@ -18,14 +18,14 @@ f.close() # test text mode -f = open("io/data/file1", "rt") +f = open("data/file1", "rt") print(f.seek(6)) print(f.read(5)) print(f.tell()) f.close() # seek closed file -f = open("io/data/file1", "r") +f = open("data/file1", "r") f.close() try: f.seek(1) diff --git a/tests/io/file_with.py b/tests/io/file_with.py index 899c0f9287be4..d5217dfe963c0 100644 --- a/tests/io/file_with.py +++ b/tests/io/file_with.py @@ -1,4 +1,4 @@ -f = open("io/data/file1") +f = open("data/file1") with f as f2: print(f2.read()) diff --git a/tests/run-tests b/tests/run-tests index b733d8a9fc582..fe10548057456 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -7,37 +7,58 @@ import platform import argparse import inspect import re +import shutil +import tempfile from glob import glob +# This is the path containing run-tests (i.e. the absolute path of micropython/tests). # See stackoverflow.com/questions/2632199: __file__ nor sys.argv[0] # are guaranteed to always work, this one should though. BASEPATH = os.path.dirname(os.path.abspath(inspect.getsourcefile(lambda: None))) + +# Convert a path relative to micropython/tests to an absolute path. def base_path(*p): - return os.path.abspath(os.path.join(BASEPATH, *p)).replace('\\', '/') + return os.path.abspath(os.path.join(BASEPATH, *p)).replace("\\", "/") + # Tests require at least CPython 3.3. If your default python3 executable # is of lower version, you can point MICROPY_CPYTHON3 environment var # to the correct executable. -if os.name == 'nt': - CPYTHON3 = os.getenv('MICROPY_CPYTHON3', 'python') - MICROPYTHON = os.getenv('MICROPY_MICROPYTHON', base_path('../ports/windows/micropython.exe')) +if os.name == "nt": + DEFAULT_CPYTHON3 = "python" + DEFAULT_MICROPYTHON = base_path("../ports/windows/micropython.exe") + DEFAULT_MPYCROSS = base_path("../mpy-cross/mpy-cross.exe") else: - CPYTHON3 = os.getenv('MICROPY_CPYTHON3', 'python3') - MICROPYTHON = os.getenv('MICROPY_MICROPYTHON', base_path('../ports/unix/micropython')) + DEFAULT_CPYTHON3 = "python3" + DEFAULT_MICROPYTHON = base_path("../ports/unix/micropython") + DEFAULT_MPYCROSS = base_path("../mpy-cross/mpy-cross") + +# Tests are run chdir'ed into a temporary directory. As the executable paths +# may be in $PATH, relative, or absolute. Ensure they're absolute so they work +# inside the chdir. +CPYTHON3 = os.path.abspath( + shutil.which(os.getenv("MICROPY_CPYTHON3", DEFAULT_CPYTHON3)) +) +MICROPYTHON = os.path.abspath( + shutil.which(os.getenv("MICROPY_MICROPYTHON", DEFAULT_MICROPYTHON)) or "" +) # Use CPython options to not save .pyc files, to only access the core standard library # (not site packages which may clash with u-module names), and improve start up time. CPYTHON3_CMD = [CPYTHON3, "-BS"] # mpy-cross is only needed if --via-mpy command-line arg is passed -MPYCROSS = os.getenv('MICROPY_MPYCROSS', base_path('../mpy-cross/mpy-cross')) +MPYCROSS = os.path.abspath( + shutil.which(os.getenv("MICROPY_MPYCROSS", DEFAULT_MPYCROSS)) or "" +) # For diff'ing test output -DIFF = os.getenv('MICROPY_DIFF', 'diff -u') +DIFF = os.getenv("MICROPY_DIFF", "diff -u") # Set PYTHONIOENCODING so that CPython will use utf-8 on systems which set another encoding in the locale -os.environ['PYTHONIOENCODING'] = 'utf-8' +os.environ["PYTHONIOENCODING"] = "utf-8" + def rm_f(fname): if os.path.exists(fname): @@ -48,57 +69,77 @@ def rm_f(fname): def convert_regex_escapes(line): cs = [] escape = False - for c in str(line, 'utf8'): + for c in str(line, "utf8"): if escape: escape = False cs.append(c) - elif c == '\\': + elif c == "\\": escape = True - elif c in ('(', ')', '[', ']', '{', '}', '.', '*', '+', '^', '$'): - cs.append('\\' + c) + elif c in ("(", ")", "[", "]", "{", "}", ".", "*", "+", "^", "$"): + cs.append("\\" + c) else: cs.append(c) # accept carriage-return(s) before final newline - if cs[-1] == '\n': - cs[-1] = '\r*\n' - return bytes(''.join(cs), 'utf8') + if cs[-1] == "\n": + cs[-1] = "\r*\n" + return bytes("".join(cs), "utf8") + + +# Creates a temp directory (to be used with a context manager) that +# contains any data files necessary for this test. +def create_working_dir(test_file): + working_dir = tempfile.TemporaryDirectory() + # Find "data" directory in same dir as test, and copy it to the working dir. + data_path = os.path.join(os.path.dirname(test_file), "data") + if os.path.isdir(data_path): + shutil.copytree(data_path, os.path.join(working_dir.name, "data")) + return working_dir def run_micropython(pyb, args, test_file, is_special=False): special_tests = ( - 'micropython/meminfo.py', 'basics/bytes_compare3.py', - 'basics/builtin_help.py', 'thread/thread_exc2.py', - 'esp32/partition_ota.py', + "micropython/meminfo.py", + "basics/bytes_compare3.py", + "basics/builtin_help.py", + "thread/thread_exc2.py", + "esp32/partition_ota.py", ) had_crash = False if pyb is None: # run on PC - if test_file.startswith(('cmdline/', base_path('feature_check/'))) or test_file in special_tests: + if ( + test_file.startswith(("cmdline/", base_path("feature_check/"))) + or test_file in special_tests + ): # special handling for tests of the unix cmdline program is_special = True if is_special: # check for any cmdline options needed for this test args = [MICROPYTHON] - with open(test_file, 'rb') as f: + with open(test_file, "rb") as f: line = f.readline() - if line.startswith(b'# cmdline:'): - # subprocess.check_output on Windows only accepts strings, not bytes - args += [str(c, 'utf-8') for c in line[10:].strip().split()] + if line.startswith(b"# cmdline:"): + for c in line[10:].strip().split(): + # subprocess.check_output on Windows only accepts strings, not bytes + c = str(c, "utf-8") + if c.startswith("./"): + c = os.path.abspath(c) + args.append(c) # run the test, possibly with redirected input try: - if 'repl_' in test_file: + if "repl_" in test_file: # Need to use a PTY to test command line editing try: import pty except ImportError: # in case pty module is not available, like on Windows - return b'SKIP\n' + return b"SKIP\n" import select def get(required=False): - rv = b'' + rv = b"" while True: ready = select.select([master], [], [], 0.02) if ready[0] == [master]: @@ -111,56 +152,81 @@ def run_micropython(pyb, args, test_file, is_special=False): os.write(master, what) return get() - with open(test_file, 'rb') as f: - # instead of: output_mupy = subprocess.check_output(args, stdin=f) - master, slave = pty.openpty() - p = subprocess.Popen(args, stdin=slave, stdout=slave, - stderr=subprocess.STDOUT, bufsize=0) - banner = get(True) - output_mupy = banner + b''.join(send_get(line) for line in f) - send_get(b'\x04') # exit the REPL, so coverage info is saved - # At this point the process might have exited already, but trying to - # kill it 'again' normally doesn't result in exceptions as Python and/or - # the OS seem to try to handle this nicely. When running Linux on WSL - # though, the situation differs and calling Popen.kill after the process - # terminated results in a ProcessLookupError. Just catch that one here - # since we just want the process to be gone and that's the case. - try: - p.kill() - except ProcessLookupError: - pass - os.close(master) - os.close(slave) + with create_working_dir(test_file) as working_dir: + with open(test_file, "rb") as f: + # instead of: output_mupy = subprocess.check_output(args, stdin=f) + master, slave = pty.openpty() + p = subprocess.Popen( + args, + stdin=slave, + stdout=slave, + stderr=subprocess.STDOUT, + bufsize=0, + cwd=working_dir, + ) + banner = get(True) + output_mupy = banner + b"".join( + send_get(line) for line in f + ) + send_get( + b"\x04" + ) # exit the REPL, so coverage info is saved + # At this point the process might have exited already, but trying to + # kill it 'again' normally doesn't result in exceptions as Python and/or + # the OS seem to try to handle this nicely. When running Linux on WSL + # though, the situation differs and calling Popen.kill after the process + # terminated results in a ProcessLookupError. Just catch that one here + # since we just want the process to be gone and that's the case. + try: + p.kill() + except ProcessLookupError: + pass + os.close(master) + os.close(slave) else: - output_mupy = subprocess.check_output(args + [test_file], stderr=subprocess.STDOUT) + with create_working_dir(test_file) as working_dir: + output_mupy = subprocess.check_output( + args + [os.path.abspath(test_file)], + stderr=subprocess.STDOUT, + cwd=working_dir, + ) except subprocess.CalledProcessError: - return b'CRASH' + return b"CRASH" else: # a standard test run on PC # create system command - cmdlist = [MICROPYTHON, '-X', 'emit=' + args.emit] + cmdlist = [MICROPYTHON, "-X", "emit=" + args.emit] if args.heapsize is not None: - cmdlist.extend(['-X', 'heapsize=' + args.heapsize]) - - # if running via .mpy, first compile the .py file - if args.via_mpy: - subprocess.check_output([MPYCROSS] + args.mpy_cross_flags.split() + ['-o', 'mpytest.mpy', '-X', 'emit=' + args.emit, test_file]) - cmdlist.extend(['-m', 'mpytest']) - else: - cmdlist.append(test_file) + cmdlist.extend(["-X", "heapsize=" + args.heapsize]) + + with create_working_dir(test_file) as working_dir: + if args.via_mpy: + # if running via .mpy, first compile the .py file and + # place it in the working dir + mpy_testfile = os.path.join(working_dir, "mpytest.mpy") + subprocess.check_output( + [MPYCROSS] + + args.mpy_cross_flags.split() + + ["-o", mpy_testfile, "-X", "emit=" + args.emit, test_file] + ) + cmdlist.extend(["-m", "mpytest"]) + else: + cmdlist.append(os.path.abspath(test_file)) - # run the actual test - try: - output_mupy = subprocess.check_output(cmdlist, stderr=subprocess.STDOUT) - except subprocess.CalledProcessError as er: - had_crash = True - output_mupy = er.output + b'CRASH' + # run the actual test + try: + output_mupy = subprocess.check_output( + cmdlist, stderr=subprocess.STDOUT, cwd=working_dir + ) + except subprocess.CalledProcessError as er: + had_crash = True + output_mupy = er.output + b"CRASH" - # clean up if we had an intermediate .mpy file - if args.via_mpy: - rm_f('mpytest.mpy') + # clean up if we had an intermediate .mpy file + if args.via_mpy: + rm_f(mpy_testfile) else: # run on pyboard @@ -169,65 +235,69 @@ def run_micropython(pyb, args, test_file, is_special=False): output_mupy = pyb.execfile(test_file) except pyboard.PyboardError as e: had_crash = True - if not is_special and e.args[0] == 'exception': - output_mupy = e.args[1] + e.args[2] + b'CRASH' + if not is_special and e.args[0] == "exception": + output_mupy = e.args[1] + e.args[2] + b"CRASH" else: - output_mupy = b'CRASH' + output_mupy = b"CRASH" # canonical form for all ports/platforms is to use \n for end-of-line - output_mupy = output_mupy.replace(b'\r\n', b'\n') + output_mupy = output_mupy.replace(b"\r\n", b"\n") # don't try to convert the output if we should skip this test - if had_crash or output_mupy in (b'SKIP\n', b'CRASH'): + if had_crash or output_mupy in (b"SKIP\n", b"CRASH"): return output_mupy if is_special or test_file in special_tests: # convert parts of the output that are not stable across runs - with open(test_file + '.exp', 'rb') as f: + with open(test_file + ".exp", "rb") as f: lines_exp = [] for line in f.readlines(): - if line == b'########\n': + if line == b"########\n": line = (line,) else: line = (line, re.compile(convert_regex_escapes(line))) lines_exp.append(line) - lines_mupy = [line + b'\n' for line in output_mupy.split(b'\n')] - if output_mupy.endswith(b'\n'): - lines_mupy = lines_mupy[:-1] # remove erroneous last empty line + lines_mupy = [line + b"\n" for line in output_mupy.split(b"\n")] + if output_mupy.endswith(b"\n"): + lines_mupy = lines_mupy[:-1] # remove erroneous last empty line i_mupy = 0 for i in range(len(lines_exp)): - if lines_exp[i][0] == b'########\n': + if lines_exp[i][0] == b"########\n": # 8x #'s means match 0 or more whole lines line_exp = lines_exp[i + 1] skip = 0 - while i_mupy + skip < len(lines_mupy) and not line_exp[1].match(lines_mupy[i_mupy + skip]): + while i_mupy + skip < len(lines_mupy) and not line_exp[1].match( + lines_mupy[i_mupy + skip] + ): skip += 1 if i_mupy + skip >= len(lines_mupy): - lines_mupy[i_mupy] = b'######## FAIL\n' + lines_mupy[i_mupy] = b"######## FAIL\n" break - del lines_mupy[i_mupy:i_mupy + skip] - lines_mupy.insert(i_mupy, b'########\n') + del lines_mupy[i_mupy : i_mupy + skip] + lines_mupy.insert(i_mupy, b"########\n") i_mupy += 1 else: # a regex if lines_exp[i][1].match(lines_mupy[i_mupy]): lines_mupy[i_mupy] = lines_exp[i][0] else: - #print("don't match: %r %s" % (lines_exp[i][1], lines_mupy[i_mupy])) # DEBUG + # print("don't match: %r %s" % (lines_exp[i][1], lines_mupy[i_mupy])) # DEBUG pass i_mupy += 1 if i_mupy >= len(lines_mupy): break - output_mupy = b''.join(lines_mupy) + output_mupy = b"".join(lines_mupy) return output_mupy -def run_feature_check(pyb, args, base_path, test_file): +def run_feature_check(pyb, args, test_file): if pyb is not None and test_file.startswith("repl_"): # REPL feature tests will not run via pyboard because they require prompt interactivity return b"" - return run_micropython(pyb, args, base_path("feature_check", test_file), is_special=True) + return run_micropython( + pyb, args, base_path("feature_check", test_file), is_special=True + ) def run_tests(pyb, tests, args, result_dir): @@ -261,193 +331,239 @@ def run_tests(pyb, tests, args, result_dir): # run-tests script itself so use base_path. # Check if micropython.native is supported, and skip such tests if it's not - output = run_feature_check(pyb, args, base_path, 'native_check.py') - if output == b'CRASH': + output = run_feature_check(pyb, args, "native_check.py") + if output == b"CRASH": skip_native = True # Check if arbitrary-precision integers are supported, and skip such tests if it's not - output = run_feature_check(pyb, args, base_path, 'int_big.py') - if output != b'1000000000000000000000000000000000000000000000\n': + output = run_feature_check(pyb, args, "int_big.py") + if output != b"1000000000000000000000000000000000000000000000\n": skip_int_big = True # Check if bytearray is supported, and skip such tests if it's not - output = run_feature_check(pyb, args, base_path, 'bytearray.py') - if output != b'bytearray\n': + output = run_feature_check(pyb, args, "bytearray.py") + if output != b"bytearray\n": skip_bytearray = True # Check if set type (and set literals) is supported, and skip such tests if it's not - output = run_feature_check(pyb, args, base_path, 'set_check.py') - if output == b'CRASH': + output = run_feature_check(pyb, args, "set_check.py") + if output == b"CRASH": skip_set_type = True # Check if slice is supported, and skip such tests if it's not - output = run_feature_check(pyb, args, base_path, 'slice.py') - if output != b'slice\n': + output = run_feature_check(pyb, args, "slice.py") + if output != b"slice\n": skip_slice = True # Check if async/await keywords are supported, and skip such tests if it's not - output = run_feature_check(pyb, args, base_path, 'async_check.py') - if output == b'CRASH': + output = run_feature_check(pyb, args, "async_check.py") + if output == b"CRASH": skip_async = True # Check if const keyword (MicroPython extension) is supported, and skip such tests if it's not - output = run_feature_check(pyb, args, base_path, 'const.py') - if output == b'CRASH': + output = run_feature_check(pyb, args, "const.py") + if output == b"CRASH": skip_const = True # Check if __rOP__ special methods are supported, and skip such tests if it's not - output = run_feature_check(pyb, args, base_path, 'reverse_ops.py') - if output == b'TypeError\n': + output = run_feature_check(pyb, args, "reverse_ops.py") + if output == b"TypeError\n": skip_revops = True # Check if uio module exists, and skip such tests if it doesn't - output = run_feature_check(pyb, args, base_path, 'uio_module.py') - if output != b'uio\n': + output = run_feature_check(pyb, args, "uio_module.py") + if output != b"uio\n": skip_io_module = True # Check if emacs repl is supported, and skip such tests if it's not - t = run_feature_check(pyb, args, base_path, 'repl_emacs_check.py') - if 'True' not in str(t, 'ascii'): - skip_tests.add('cmdline/repl_emacs_keys.py') + t = run_feature_check(pyb, args, "repl_emacs_check.py") + if "True" not in str(t, "ascii"): + skip_tests.add("cmdline/repl_emacs_keys.py") # Check if words movement in repl is supported, and skip such tests if it's not - t = run_feature_check(pyb, args, base_path, 'repl_words_move_check.py') - if 'True' not in str(t, 'ascii'): - skip_tests.add('cmdline/repl_words_move.py') + t = run_feature_check(pyb, args, "repl_words_move_check.py") + if "True" not in str(t, "ascii"): + skip_tests.add("cmdline/repl_words_move.py") - upy_byteorder = run_feature_check(pyb, args, base_path, 'byteorder.py') - upy_float_precision = run_feature_check(pyb, args, base_path, 'float.py') - if upy_float_precision == b'CRASH': + upy_byteorder = run_feature_check(pyb, args, "byteorder.py") + upy_float_precision = run_feature_check(pyb, args, "float.py") + if upy_float_precision == b"CRASH": upy_float_precision = 0 else: upy_float_precision = int(upy_float_precision) - has_complex = run_feature_check(pyb, args, base_path, 'complex.py') == b'complex\n' - has_coverage = run_feature_check(pyb, args, base_path, 'coverage.py') == b'coverage\n' - cpy_byteorder = subprocess.check_output(CPYTHON3_CMD + [base_path('feature_check/byteorder.py')]) - skip_endian = (upy_byteorder != cpy_byteorder) + has_complex = run_feature_check(pyb, args, "complex.py") == b"complex\n" + has_coverage = run_feature_check(pyb, args, "coverage.py") == b"coverage\n" + cpy_byteorder = subprocess.check_output( + CPYTHON3_CMD + [base_path("feature_check/byteorder.py")] + ) + skip_endian = upy_byteorder != cpy_byteorder # These tests don't test slice explicitly but rather use it to perform the test misc_slice_tests = ( - 'builtin_range', - 'class_super', - 'containment', - 'errno1', - 'fun_str', - 'generator1', - 'globals_del', - 'memoryview1', - 'memoryview_gc', - 'object1', - 'python34', - 'struct_endian', + "builtin_range", + "class_super", + "containment", + "errno1", + "fun_str", + "generator1", + "globals_del", + "memoryview1", + "memoryview_gc", + "object1", + "python34", + "struct_endian", ) # Some tests shouldn't be run on GitHub Actions - if os.getenv('GITHUB_ACTIONS') == 'true': - skip_tests.add('thread/stress_schedule.py') # has reliability issues + if os.getenv("GITHUB_ACTIONS") == "true": + skip_tests.add("thread/stress_schedule.py") # has reliability issues if upy_float_precision == 0: - skip_tests.add('extmod/uctypes_le_float.py') - skip_tests.add('extmod/uctypes_native_float.py') - skip_tests.add('extmod/uctypes_sizeof_float.py') - skip_tests.add('extmod/ujson_dumps_float.py') - skip_tests.add('extmod/ujson_loads_float.py') - skip_tests.add('extmod/urandom_extra_float.py') - skip_tests.add('misc/rge_sm.py') + skip_tests.add("extmod/uctypes_le_float.py") + skip_tests.add("extmod/uctypes_native_float.py") + skip_tests.add("extmod/uctypes_sizeof_float.py") + skip_tests.add("extmod/ujson_dumps_float.py") + skip_tests.add("extmod/ujson_loads_float.py") + skip_tests.add("extmod/urandom_extra_float.py") + skip_tests.add("misc/rge_sm.py") if upy_float_precision < 32: - skip_tests.add('float/float2int_intbig.py') # requires fp32, there's float2int_fp30_intbig.py instead - skip_tests.add('float/string_format.py') # requires fp32, there's string_format_fp30.py instead - skip_tests.add('float/bytes_construct.py') # requires fp32 - skip_tests.add('float/bytearray_construct.py') # requires fp32 + skip_tests.add( + "float/float2int_intbig.py" + ) # requires fp32, there's float2int_fp30_intbig.py instead + skip_tests.add( + "float/string_format.py" + ) # requires fp32, there's string_format_fp30.py instead + skip_tests.add("float/bytes_construct.py") # requires fp32 + skip_tests.add("float/bytearray_construct.py") # requires fp32 if upy_float_precision < 64: - skip_tests.add('float/float_divmod.py') # tested by float/float_divmod_relaxed.py instead - skip_tests.add('float/float2int_doubleprec_intbig.py') - skip_tests.add('float/float_parse_doubleprec.py') + skip_tests.add( + "float/float_divmod.py" + ) # tested by float/float_divmod_relaxed.py instead + skip_tests.add("float/float2int_doubleprec_intbig.py") + skip_tests.add("float/float_parse_doubleprec.py") if not has_complex: - skip_tests.add('float/complex1.py') - skip_tests.add('float/complex1_intbig.py') - skip_tests.add('float/complex_special_methods.py') - skip_tests.add('float/int_big_float.py') - skip_tests.add('float/true_value.py') - skip_tests.add('float/types.py') + skip_tests.add("float/complex1.py") + skip_tests.add("float/complex1_intbig.py") + skip_tests.add("float/complex_special_methods.py") + skip_tests.add("float/int_big_float.py") + skip_tests.add("float/true_value.py") + skip_tests.add("float/types.py") if not has_coverage: - skip_tests.add('cmdline/cmd_parsetree.py') + skip_tests.add("cmdline/cmd_parsetree.py") # Some tests shouldn't be run on a PC - if args.target == 'unix': + if args.target == "unix": # unix build does not have the GIL so can't run thread mutation tests for t in tests: - if t.startswith('thread/mutate_'): + if t.startswith("thread/mutate_"): skip_tests.add(t) # Some tests shouldn't be run on pyboard - if args.target != 'unix': - skip_tests.add('basics/exception_chain.py') # warning is not printed - skip_tests.add('micropython/meminfo.py') # output is very different to PC output - skip_tests.add('extmod/machine_mem.py') # raw memory access not supported - - if args.target == 'wipy': - skip_tests.add('misc/print_exception.py') # requires error reporting full - skip_tests.update({'extmod/uctypes_%s.py' % t for t in 'bytearray le native_le ptr_le ptr_native_le sizeof sizeof_native array_assign_le array_assign_native_le'.split()}) # requires uctypes - skip_tests.add('extmod/zlibd_decompress.py') # requires zlib - skip_tests.add('extmod/uheapq1.py') # uheapq not supported by WiPy - skip_tests.add('extmod/urandom_basic.py') # requires urandom - skip_tests.add('extmod/urandom_extra.py') # requires urandom - elif args.target == 'esp8266': - skip_tests.add('misc/rge_sm.py') # too large - elif args.target == 'minimal': - skip_tests.add('basics/class_inplace_op.py') # all special methods not supported - skip_tests.add('basics/subclass_native_init.py')# native subclassing corner cases not support - skip_tests.add('misc/rge_sm.py') # too large - skip_tests.add('micropython/opt_level.py') # don't assume line numbers are stored - elif args.target == 'nrf': - skip_tests.add('basics/memoryview1.py') # no item assignment for memoryview - skip_tests.add('extmod/urandom_basic.py') # unimplemented: urandom.seed - skip_tests.add('micropython/opt_level.py') # no support for line numbers - skip_tests.add('misc/non_compliant.py') # no item assignment for bytearray + if args.target != "unix": + skip_tests.add("basics/exception_chain.py") # warning is not printed + skip_tests.add( + "micropython/meminfo.py" + ) # output is very different to PC output + skip_tests.add("extmod/machine_mem.py") # raw memory access not supported + + if args.target == "wipy": + skip_tests.add("misc/print_exception.py") # requires error reporting full + skip_tests.update( + { + "extmod/uctypes_%s.py" % t + for t in "bytearray le native_le ptr_le ptr_native_le sizeof sizeof_native array_assign_le array_assign_native_le".split() + } + ) # requires uctypes + skip_tests.add("extmod/zlibd_decompress.py") # requires zlib + skip_tests.add("extmod/uheapq1.py") # uheapq not supported by WiPy + skip_tests.add("extmod/urandom_basic.py") # requires urandom + skip_tests.add("extmod/urandom_extra.py") # requires urandom + elif args.target == "esp8266": + skip_tests.add("misc/rge_sm.py") # too large + elif args.target == "minimal": + skip_tests.add( + "basics/class_inplace_op.py" + ) # all special methods not supported + skip_tests.add( + "basics/subclass_native_init.py" + ) # native subclassing corner cases not support + skip_tests.add("misc/rge_sm.py") # too large + skip_tests.add( + "micropython/opt_level.py" + ) # don't assume line numbers are stored + elif args.target == "nrf": + skip_tests.add("basics/memoryview1.py") # no item assignment for memoryview + skip_tests.add("extmod/urandom_basic.py") # unimplemented: urandom.seed + skip_tests.add("micropython/opt_level.py") # no support for line numbers + skip_tests.add("misc/non_compliant.py") # no item assignment for bytearray for t in tests: - if t.startswith('basics/io_'): + if t.startswith("basics/io_"): skip_tests.add(t) - elif args.target == 'qemu-arm': - skip_tests.add('misc/print_exception.py') # requires sys stdfiles + elif args.target == "qemu-arm": + skip_tests.add("misc/print_exception.py") # requires sys stdfiles # Some tests are known to fail on 64-bit machines - if pyb is None and platform.architecture()[0] == '64bit': + if pyb is None and platform.architecture()[0] == "64bit": pass # Some tests use unsupported features on Windows - if os.name == 'nt': - skip_tests.add('import/import_file.py') # works but CPython prints forward slashes + if os.name == "nt": + skip_tests.add( + "import/import_file.py" + ) # works but CPython prints forward slashes # Some tests are known to fail with native emitter # Remove them from the below when they work - if args.emit == 'native': - skip_tests.update({'basics/%s.py' % t for t in 'gen_yield_from_close generator_name'.split()}) # require raise_varargs, generator name - skip_tests.update({'basics/async_%s.py' % t for t in 'with with2 with_break with_return'.split()}) # require async_with - skip_tests.update({'basics/%s.py' % t for t in 'try_reraise try_reraise2'.split()}) # require raise_varargs - skip_tests.add('basics/annotate_var.py') # requires checking for unbound local - skip_tests.add('basics/del_deref.py') # requires checking for unbound local - skip_tests.add('basics/del_local.py') # requires checking for unbound local - skip_tests.add('basics/exception_chain.py') # raise from is not supported - skip_tests.add('basics/scope_implicit.py') # requires checking for unbound local - skip_tests.add('basics/try_finally_return2.py') # requires raise_varargs - skip_tests.add('basics/unboundlocal.py') # requires checking for unbound local - skip_tests.add('extmod/uasyncio_event.py') # unknown issue - skip_tests.add('extmod/uasyncio_lock.py') # requires async with - skip_tests.add('extmod/uasyncio_micropython.py') # unknown issue - skip_tests.add('extmod/uasyncio_wait_for.py') # unknown issue - skip_tests.add('misc/features.py') # requires raise_varargs - skip_tests.add('misc/print_exception.py') # because native doesn't have proper traceback info - skip_tests.add('misc/sys_exc_info.py') # sys.exc_info() is not supported for native - skip_tests.add('micropython/emg_exc.py') # because native doesn't have proper traceback info - skip_tests.add('micropython/heapalloc_traceback.py') # because native doesn't have proper traceback info - skip_tests.add('micropython/opt_level_lineno.py') # native doesn't have proper traceback info - skip_tests.add('micropython/schedule.py') # native code doesn't check pending events + if args.emit == "native": + skip_tests.update( + {"basics/%s.py" % t for t in "gen_yield_from_close generator_name".split()} + ) # require raise_varargs, generator name + skip_tests.update( + { + "basics/async_%s.py" % t + for t in "with with2 with_break with_return".split() + } + ) # require async_with + skip_tests.update( + {"basics/%s.py" % t for t in "try_reraise try_reraise2".split()} + ) # require raise_varargs + skip_tests.add("basics/annotate_var.py") # requires checking for unbound local + skip_tests.add("basics/del_deref.py") # requires checking for unbound local + skip_tests.add("basics/del_local.py") # requires checking for unbound local + skip_tests.add("basics/exception_chain.py") # raise from is not supported + skip_tests.add( + "basics/scope_implicit.py" + ) # requires checking for unbound local + skip_tests.add("basics/try_finally_return2.py") # requires raise_varargs + skip_tests.add("basics/unboundlocal.py") # requires checking for unbound local + skip_tests.add("extmod/uasyncio_event.py") # unknown issue + skip_tests.add("extmod/uasyncio_lock.py") # requires async with + skip_tests.add("extmod/uasyncio_micropython.py") # unknown issue + skip_tests.add("extmod/uasyncio_wait_for.py") # unknown issue + skip_tests.add("misc/features.py") # requires raise_varargs + skip_tests.add( + "misc/print_exception.py" + ) # because native doesn't have proper traceback info + skip_tests.add( + "misc/sys_exc_info.py" + ) # sys.exc_info() is not supported for native + skip_tests.add( + "micropython/emg_exc.py" + ) # because native doesn't have proper traceback info + skip_tests.add( + "micropython/heapalloc_traceback.py" + ) # because native doesn't have proper traceback info + skip_tests.add( + "micropython/opt_level_lineno.py" + ) # native doesn't have proper traceback info + skip_tests.add( + "micropython/schedule.py" + ) # native code doesn't check pending events for test_file in tests: - test_file = test_file.replace('\\', '/') + test_file = test_file.replace("\\", "/") if args.filters: # Default verdict is the opposit of the first action @@ -458,12 +574,18 @@ def run_tests(pyb, tests, args, result_dir): if verdict == "exclude": continue - test_basename = test_file.replace('..', '_').replace('./', '').replace('/', '_') + test_basename = test_file.replace("..", "_").replace("./", "").replace("/", "_") test_name = os.path.splitext(os.path.basename(test_file))[0] - is_native = test_name.startswith("native_") or test_name.startswith("viper_") or args.emit == "native" + is_native = ( + test_name.startswith("native_") + or test_name.startswith("viper_") + or args.emit == "native" + ) is_endian = test_name.endswith("_endian") is_int_big = test_name.startswith("int_big") or test_name.endswith("_intbig") - is_bytearray = test_name.startswith("bytearray") or test_name.endswith("_bytearray") + is_bytearray = test_name.startswith("bytearray") or test_name.endswith( + "_bytearray" + ) is_set_type = test_name.startswith("set_") or test_name.startswith("frozenset") is_slice = test_name.find("slice") != -1 or test_name in misc_slice_tests is_async = test_name.startswith(("async_", "uasyncio_")) @@ -493,23 +615,26 @@ def run_tests(pyb, tests, args, result_dir): continue # get expected output - test_file_expected = test_file + '.exp' + test_file_expected = test_file + ".exp" if os.path.isfile(test_file_expected): # expected output given by a file, so read that in - with open(test_file_expected, 'rb') as f: + with open(test_file_expected, "rb") as f: output_expected = f.read() else: # run CPython to work out expected output try: - output_expected = subprocess.check_output(CPYTHON3_CMD + [test_file]) + with create_working_dir(test_file) as working_dir: + output_expected = subprocess.check_output( + CPYTHON3_CMD + [os.path.abspath(test_file)], cwd=working_dir + ) if args.write_exp: - with open(test_file_expected, 'wb') as f: + with open(test_file_expected, "wb") as f: f.write(output_expected) except subprocess.CalledProcessError: - output_expected = b'CPYTHON3 CRASH' + output_expected = b"CPYTHON3 CRASH" # canonical form for all host platforms is to use \n for end-of-line - output_expected = output_expected.replace(b'\r\n', b'\n') + output_expected = output_expected.replace(b"\r\n", b"\n") if args.write_exp: continue @@ -517,7 +642,7 @@ def run_tests(pyb, tests, args, result_dir): # run MicroPython output_mupy = run_micropython(pyb, args, test_file) - if output_mupy == b'SKIP\n': + if output_mupy == b"SKIP\n": print("skip ", test_file) skipped_tests.append(test_name) continue @@ -545,13 +670,19 @@ def run_tests(pyb, tests, args, result_dir): if args.list_tests: return True - print("{} tests performed ({} individual testcases)".format(test_count, testcase_count)) + print( + "{} tests performed ({} individual testcases)".format( + test_count, testcase_count + ) + ) print("{} tests passed".format(passed_count)) if len(skipped_tests) > 0: - print("{} tests skipped: {}".format(len(skipped_tests), ' '.join(skipped_tests))) + print( + "{} tests skipped: {}".format(len(skipped_tests), " ".join(skipped_tests)) + ) if len(failed_tests) > 0: - print("{} tests failed: {}".format(len(failed_tests), ' '.join(failed_tests))) + print("{} tests failed: {}".format(len(failed_tests), " ".join(failed_tests))) return False # all tests succeeded @@ -559,7 +690,6 @@ def run_tests(pyb, tests, args, result_dir): class append_filter(argparse.Action): - def __init__(self, option_strings, dest, **kwargs): super().__init__(option_strings, dest, default=[], **kwargs) @@ -576,7 +706,7 @@ class append_filter(argparse.Action): def main(): cmd_parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description='''Run and manage tests for MicroPython. + description="""Run and manage tests for MicroPython. Tests are discovered by scanning test directories for .py files or using the specified test files. If test files nor directories are specified, the script @@ -587,34 +717,99 @@ produced by running the test through CPython unless a .exp file is found, case it is used as comparison. If a test fails, run-tests produces a pair of .out and .exp files in the result directory with the MicroPython output and the expectations, respectively. -''', - epilog='''\ +""", + epilog="""\ Options -i and -e can be multiple and processed in the order given. Regex "search" (vs "match") operation is used. An action (include/exclude) of the last matching regex is used: run-tests -i async - exclude all, then include tests containing "async" anywhere run-tests -e '/big.+int' - include all, then exclude by regex run-tests -e async -i async_foo - include all, exclude async, yet still include async_foo -''') - cmd_parser.add_argument('--target', default='unix', help='the target platform') - cmd_parser.add_argument('--device', default='/dev/ttyACM0', help='the serial device or the IP address of the pyboard') - cmd_parser.add_argument('-b', '--baudrate', default=115200, help='the baud rate of the serial device') - cmd_parser.add_argument('-u', '--user', default='micro', help='the telnet login username') - cmd_parser.add_argument('-p', '--password', default='python', help='the telnet login password') - cmd_parser.add_argument('-d', '--test-dirs', nargs='*', help='input test directories (if no files given)') - cmd_parser.add_argument('-r', '--result-dir', default=base_path('results'), help='directory for test results') - cmd_parser.add_argument('-e', '--exclude', action=append_filter, metavar='REGEX', dest='filters', help='exclude test by regex on path/name.py') - cmd_parser.add_argument('-i', '--include', action=append_filter, metavar='REGEX', dest='filters', help='include test by regex on path/name.py') - cmd_parser.add_argument('--write-exp', action='store_true', help='use CPython to generate .exp files to run tests w/o CPython') - cmd_parser.add_argument('--list-tests', action='store_true', help='list tests instead of running them') - cmd_parser.add_argument('--emit', default='bytecode', help='MicroPython emitter to use (bytecode or native)') - cmd_parser.add_argument('--heapsize', help='heapsize to use (use default if not specified)') - cmd_parser.add_argument('--via-mpy', action='store_true', help='compile .py files to .mpy first') - cmd_parser.add_argument('--mpy-cross-flags', default='-mcache-lookup-bc', help='flags to pass to mpy-cross') - cmd_parser.add_argument('--keep-path', action='store_true', help='do not clear MICROPYPATH when running tests') - cmd_parser.add_argument('files', nargs='*', help='input test files') - cmd_parser.add_argument('--print-failures', action='store_true', help='print the diff of expected vs. actual output for failed tests and exit') - cmd_parser.add_argument('--clean-failures', action='store_true', help='delete the .exp and .out files from failed tests and exit') +""", + ) + cmd_parser.add_argument("--target", default="unix", help="the target platform") + cmd_parser.add_argument( + "--device", + default="/dev/ttyACM0", + help="the serial device or the IP address of the pyboard", + ) + cmd_parser.add_argument( + "-b", "--baudrate", default=115200, help="the baud rate of the serial device" + ) + cmd_parser.add_argument( + "-u", "--user", default="micro", help="the telnet login username" + ) + cmd_parser.add_argument( + "-p", "--password", default="python", help="the telnet login password" + ) + cmd_parser.add_argument( + "-d", + "--test-dirs", + nargs="*", + help="input test directories (if no files given)", + ) + cmd_parser.add_argument( + "-r", + "--result-dir", + default=base_path("results"), + help="directory for test results", + ) + cmd_parser.add_argument( + "-e", + "--exclude", + action=append_filter, + metavar="REGEX", + dest="filters", + help="exclude test by regex on path/name.py", + ) + cmd_parser.add_argument( + "-i", + "--include", + action=append_filter, + metavar="REGEX", + dest="filters", + help="include test by regex on path/name.py", + ) + cmd_parser.add_argument( + "--write-exp", + action="store_true", + help="use CPython to generate .exp files to run tests w/o CPython", + ) + cmd_parser.add_argument( + "--list-tests", action="store_true", help="list tests instead of running them" + ) + cmd_parser.add_argument( + "--emit", + default="bytecode", + help="MicroPython emitter to use (bytecode or native)", + ) + cmd_parser.add_argument( + "--heapsize", help="heapsize to use (use default if not specified)" + ) + cmd_parser.add_argument( + "--via-mpy", action="store_true", help="compile .py files to .mpy first" + ) + cmd_parser.add_argument( + "--mpy-cross-flags", + default="-mcache-lookup-bc", + help="flags to pass to mpy-cross", + ) + cmd_parser.add_argument( + "--keep-path", + action="store_true", + help="do not clear MICROPYPATH when running tests", + ) + cmd_parser.add_argument("files", nargs="*", help="input test files") + cmd_parser.add_argument( + "--print-failures", + action="store_true", + help="print the diff of expected vs. actual output for failed tests and exit", + ) + cmd_parser.add_argument( + "--clean-failures", + action="store_true", + help="delete the .exp and .out files from failed tests and exit", + ) args = cmd_parser.parse_args() if args.print_failures: @@ -627,55 +822,84 @@ the last matching regex is used: sys.exit(0) if args.clean_failures: - for f in glob(os.path.join(args.result_dir, "*.exp")) + glob(os.path.join(args.result_dir, "*.out")): + for f in glob(os.path.join(args.result_dir, "*.exp")) + glob( + os.path.join(args.result_dir, "*.out") + ): os.remove(f) sys.exit(0) - LOCAL_TARGETS = ('unix', 'qemu-arm',) - EXTERNAL_TARGETS = ('pyboard', 'wipy', 'esp8266', 'esp32', 'minimal', 'nrf') + LOCAL_TARGETS = ( + "unix", + "qemu-arm", + ) + EXTERNAL_TARGETS = ("pyboard", "wipy", "esp8266", "esp32", "minimal", "nrf") if args.target in LOCAL_TARGETS or args.list_tests: pyb = None elif args.target in EXTERNAL_TARGETS: global pyboard - sys.path.append(base_path('../tools')) + sys.path.append(base_path("../tools")) import pyboard + pyb = pyboard.Pyboard(args.device, args.baudrate, args.user, args.password) pyb.enter_raw_repl() else: - raise ValueError('target must be one of %s' % ", ".join(LOCAL_TARGETS + EXTERNAL_TARGETS)) + raise ValueError( + "target must be one of %s" % ", ".join(LOCAL_TARGETS + EXTERNAL_TARGETS) + ) if len(args.files) == 0: if args.test_dirs is None: - test_dirs = ('basics', 'micropython', 'misc', 'extmod',) - if args.target == 'pyboard': + test_dirs = ( + "basics", + "micropython", + "misc", + "extmod", + ) + if args.target == "pyboard": # run pyboard tests - test_dirs += ('float', 'stress', 'pyb', 'pybnative', 'inlineasm') - elif args.target in ('esp8266', 'esp32', 'minimal', 'nrf'): - test_dirs += ('float',) - elif args.target == 'wipy': + test_dirs += ("float", "stress", "pyb", "pybnative", "inlineasm") + elif args.target in ("esp8266", "esp32", "minimal", "nrf"): + test_dirs += ("float",) + elif args.target == "wipy": # run WiPy tests - test_dirs += ('wipy',) - elif args.target == 'unix': + test_dirs += ("wipy",) + elif args.target == "unix": # run PC tests - test_dirs += ('float', 'import', 'io', 'stress', 'unicode', 'unix', 'cmdline',) - elif args.target == 'qemu-arm': + test_dirs += ( + "float", + "import", + "io", + "stress", + "unicode", + "unix", + "cmdline", + ) + elif args.target == "qemu-arm": if not args.write_exp: - raise ValueError('--target=qemu-arm must be used with --write-exp') + raise ValueError("--target=qemu-arm must be used with --write-exp") # Generate expected output files for qemu run. # This list should match the test_dirs tuple in tinytest-codegen.py. - test_dirs += ('float', 'inlineasm', 'qemu-arm',) + test_dirs += ( + "float", + "inlineasm", + "qemu-arm", + ) else: # run tests from these directories test_dirs = args.test_dirs - tests = sorted(test_file for test_files in (glob('{}/*.py'.format(dir)) for dir in test_dirs) for test_file in test_files) + tests = sorted( + test_file + for test_files in (glob("{}/*.py".format(dir)) for dir in test_dirs) + for test_file in test_files + ) else: # tests explicitly given tests = args.files if not args.keep_path: # clear search path to make sure tests use only builtin modules and those in extmod - os.environ['MICROPYPATH'] = os.pathsep + base_path('../extmod') + os.environ["MICROPYPATH"] = os.pathsep + base_path("../extmod") try: os.makedirs(args.result_dir, exist_ok=True) @@ -687,5 +911,6 @@ the last matching regex is used: if not res: sys.exit(1) + if __name__ == "__main__": main() diff --git a/tests/unicode/file1.py b/tests/unicode/file1.py index 08b7d25041943..6508b5f716501 100644 --- a/tests/unicode/file1.py +++ b/tests/unicode/file1.py @@ -1,4 +1,4 @@ -f = open("unicode/data/utf-8_1.txt", encoding="utf-8") +f = open("data/utf-8_1.txt", encoding="utf-8") l = f.readline() print(l) print(len(l)) diff --git a/tests/unicode/file2.py b/tests/unicode/file2.py index 1a5b933c89e48..c35c18789c51e 100644 --- a/tests/unicode/file2.py +++ b/tests/unicode/file2.py @@ -6,7 +6,7 @@ def do(mode): enc = None else: enc = "utf-8" - f = open("unicode/data/utf-8_2.txt", mode=mode, encoding=enc) + f = open("data/utf-8_2.txt", mode=mode, encoding=enc) print(f.read(1)) print(f.read(1)) print(f.read(2))