1#! /usr/bin/env python3
2
3import os
4import subprocess
5import sys
6import argparse
7import re
8from glob import glob
9from collections import defaultdict
10
11# Tests require at least CPython 3.3. If your default python3 executable
12# is of lower version, you can point MICROPY_CPYTHON3 environment var
13# to the correct executable.
14if os.name == "nt":
15    CPYTHON3 = os.getenv("MICROPY_CPYTHON3", "python3.exe")
16    MICROPYTHON = os.getenv("MICROPY_MICROPYTHON", "../ports/windows/micropython.exe")
17else:
18    CPYTHON3 = os.getenv("MICROPY_CPYTHON3", "python3")
19    MICROPYTHON = os.getenv("MICROPY_MICROPYTHON", "../ports/unix/micropython")
20
21
22def run_tests(pyb, test_dict):
23    test_count = 0
24    testcase_count = 0
25
26    for base_test, tests in sorted(test_dict.items()):
27        print(base_test + ":")
28        for test_file in tests:
29
30            # run MicroPython
31            if pyb is None:
32                # run on PC
33                try:
34                    output_mupy = subprocess.check_output(
35                        [MICROPYTHON, "-X", "emit=bytecode", test_file[0]]
36                    )
37                except subprocess.CalledProcessError:
38                    output_mupy = b"CRASH"
39            else:
40                # run on pyboard
41                pyb.enter_raw_repl()
42                try:
43                    output_mupy = pyb.execfile(test_file).replace(b"\r\n", b"\n")
44                except pyboard.PyboardError:
45                    output_mupy = b"CRASH"
46
47            output_mupy = float(output_mupy.strip())
48            test_file[1] = output_mupy
49            testcase_count += 1
50
51        test_count += 1
52        baseline = None
53        for t in tests:
54            if baseline is None:
55                baseline = t[1]
56            print("    %.3fs (%+06.2f%%) %s" % (t[1], (t[1] * 100 / baseline) - 100, t[0]))
57
58    print("{} tests performed ({} individual testcases)".format(test_count, testcase_count))
59
60    # all tests succeeded
61    return True
62
63
64def main():
65    cmd_parser = argparse.ArgumentParser(description="Run tests for MicroPython.")
66    cmd_parser.add_argument("--pyboard", action="store_true", help="run the tests on the pyboard")
67    cmd_parser.add_argument("files", nargs="*", help="input test files")
68    args = cmd_parser.parse_args()
69
70    # Note pyboard support is copied over from run-tests, not testes, and likely needs revamping
71    if args.pyboard:
72        import pyboard
73
74        pyb = pyboard.Pyboard("/dev/ttyACM0")
75        pyb.enter_raw_repl()
76    else:
77        pyb = None
78
79    if len(args.files) == 0:
80        if pyb is None:
81            # run PC tests
82            test_dirs = ("internal_bench",)
83        else:
84            # run pyboard tests
85            test_dirs = ("basics", "float", "pyb")
86        tests = sorted(
87            test_file
88            for test_files in (glob("{}/*.py".format(dir)) for dir in test_dirs)
89            for test_file in test_files
90        )
91    else:
92        # tests explicitly given
93        tests = sorted(args.files)
94
95    test_dict = defaultdict(lambda: [])
96    for t in tests:
97        m = re.match(r"(.+?)-(.+)\.py", t)
98        if not m:
99            continue
100        test_dict[m.group(1)].append([t, None])
101
102    if not run_tests(pyb, test_dict):
103        sys.exit(1)
104
105
106if __name__ == "__main__":
107    main()
108