1#!/usr/bin/env python3
2
3import os, sys
4from glob import glob
5from re import sub
6import argparse
7
8
9def escape(s):
10    s = s.decode()
11    lookup = {
12        "\0": "\\0",
13        "\t": "\\t",
14        "\n": '\\n"\n"',
15        "\r": "\\r",
16        "\\": "\\\\",
17        '"': '\\"',
18    }
19    return '""\n"{}"'.format("".join([lookup[x] if x in lookup else x for x in s]))
20
21
22def chew_filename(t):
23    return {"func": "test_{}_fn".format(sub(r"/|\.|-", "_", t)), "desc": t}
24
25
26def script_to_map(test_file):
27    r = {"name": chew_filename(test_file)["func"]}
28    with open(test_file, "rb") as f:
29        r["script"] = escape(f.read())
30    with open(test_file + ".exp", "rb") as f:
31        r["output"] = escape(f.read())
32    return r
33
34
35test_function = (
36    "void {name}(void* data) {{\n"
37    "  static const char pystr[] = {script};\n"
38    "  static const char exp[] = {output};\n"
39    '  printf("\\n");\n'
40    "  upytest_set_expected_output(exp, sizeof(exp) - 1);\n"
41    "  upytest_execute_test(pystr);\n"
42    '  printf("result: ");\n'
43    "}}"
44)
45
46testcase_struct = "struct testcase_t {name}_tests[] = {{\n{body}\n  END_OF_TESTCASES\n}};"
47testcase_member = '  {{ "{desc}", {func}, TT_ENABLED_, 0, 0 }},'
48
49testgroup_struct = "struct testgroup_t groups[] = {{\n{body}\n  END_OF_GROUPS\n}};"
50testgroup_member = '  {{ "{name}", {name}_tests }},'
51
52## XXX: may be we could have `--without <groups>` argument...
53# currently these tests are selected because they pass on qemu-arm
54test_dirs = (
55    "basics",
56    "micropython",
57    "misc",
58    "extmod",
59    "float",
60    "inlineasm",
61    "qemu-arm",
62)  # 'import', 'io',)
63exclude_tests = (
64    # pattern matching in .exp
65    "basics/bytes_compare3.py",
66    "extmod/ticks_diff.py",
67    "extmod/time_ms_us.py",
68    "extmod/uheapq_timeq.py",
69    # unicode char issue
70    "extmod/ujson_loads.py",
71    # doesn't output to python stdout
72    "extmod/ure_debug.py",
73    "extmod/vfs_basic.py",
74    "extmod/vfs_fat_ramdisk.py",
75    "extmod/vfs_fat_fileio.py",
76    "extmod/vfs_fat_fsusermount.py",
77    "extmod/vfs_fat_oldproto.py",
78    # rounding issues
79    "float/float_divmod.py",
80    # requires double precision floating point to work
81    "float/float2int_doubleprec_intbig.py",
82    "float/float_parse_doubleprec.py",
83    # inline asm FP tests (require Cortex-M4)
84    "inlineasm/asmfpaddsub.py",
85    "inlineasm/asmfpcmp.py",
86    "inlineasm/asmfpldrstr.py",
87    "inlineasm/asmfpmuldiv.py",
88    "inlineasm/asmfpsqrt.py",
89    # different filename in output
90    "micropython/emg_exc.py",
91    "micropython/heapalloc_traceback.py",
92    # don't have emergency exception buffer
93    "micropython/heapalloc_exc_compressed_emg_exc.py",
94    # pattern matching in .exp
95    "micropython/meminfo.py",
96    # needs sys stdfiles
97    "misc/print_exception.py",
98    # settrace .exp files are too large
99    "misc/sys_settrace_loop.py",
100    "misc/sys_settrace_generator.py",
101    "misc/sys_settrace_features.py",
102    # don't have f-string
103    "basics/string_fstring.py",
104    "basics/string_fstring_debug.py",
105)
106
107output = []
108tests = []
109
110argparser = argparse.ArgumentParser(
111    description="Convert native MicroPython tests to tinytest/upytesthelper C code"
112)
113argparser.add_argument("--stdin", action="store_true", help="read list of tests from stdin")
114argparser.add_argument("--exclude", action="append", help="exclude test by name")
115args = argparser.parse_args()
116
117if not args.stdin:
118    if args.exclude:
119        exclude_tests += tuple(args.exclude)
120    for group in test_dirs:
121        tests += [test for test in glob("{}/*.py".format(group)) if test not in exclude_tests]
122else:
123    for l in sys.stdin:
124        tests.append(l.rstrip())
125
126output.extend([test_function.format(**script_to_map(test)) for test in tests])
127testcase_members = [testcase_member.format(**chew_filename(test)) for test in tests]
128output.append(testcase_struct.format(name="", body="\n".join(testcase_members)))
129
130testgroup_members = [testgroup_member.format(name=group) for group in [""]]
131
132output.append(testgroup_struct.format(body="\n".join(testgroup_members)))
133
134## XXX: may be we could have `--output <filename>` argument...
135# Don't depend on what system locale is set, use utf8 encoding.
136sys.stdout.buffer.write("\n\n".join(output).encode("utf8"))
137