circuitpython/tests/run-internalbench.py
Anson Mansfield 88cb6bc818 tests/run-internalbench.py: Allow running internalbench on hardware.
Signed-off-by: Anson Mansfield <amansfield@mantaro.com>
2025-07-29 11:49:10 +10:00

158 lines
4.6 KiB
Python
Executable file

#! /usr/bin/env python3
import os
import subprocess
import sys
import argparse
import re
from glob import glob
from collections import defaultdict
run_tests_module = __import__("run-tests")
sys.path.append(run_tests_module.base_path("../tools"))
import pyboard
if os.name == "nt":
MICROPYTHON = os.getenv(
"MICROPY_MICROPYTHON", "../ports/windows/build-standard/micropython.exe"
)
else:
MICROPYTHON = os.getenv("MICROPY_MICROPYTHON", "../ports/unix/build-standard/micropython")
injected_bench_code = b"""
import time
class bench_class:
ITERS = 20000000
@staticmethod
def run(test):
t = time.ticks_us()
test(bench_class.ITERS)
t = time.ticks_diff(time.ticks_us(), t)
s, us = divmod(t, 1_000_000)
print("{}.{:06}".format(s, us))
import sys
sys.modules['bench'] = bench_class
"""
def execbench(pyb, filename, iters):
with open(filename, "rb") as f:
pyfile = f.read()
code = (injected_bench_code + pyfile).replace(b"20000000", str(iters).encode("utf-8"))
return pyb.exec(code).replace(b"\r\n", b"\n")
def run_tests(pyb, test_dict, iters):
test_count = 0
testcase_count = 0
for base_test, tests in sorted(test_dict.items()):
print(base_test + ":")
baseline = None
for test_file in tests:
# run MicroPython
if pyb is None:
# run on PC
try:
output_mupy = subprocess.check_output(
[MICROPYTHON, "-X", "emit=bytecode", test_file[0]]
)
except subprocess.CalledProcessError:
output_mupy = b"CRASH"
else:
# run on pyboard
pyb.enter_raw_repl()
try:
output_mupy = execbench(pyb, test_file[0], iters)
except pyboard.PyboardError:
output_mupy = b"CRASH"
try:
output_mupy = float(output_mupy.strip())
except ValueError:
output_mupy = -1
test_file[1] = output_mupy
testcase_count += 1
if baseline is None:
baseline = test_file[1]
print(
" %.3fs (%+06.2f%%) %s"
% (test_file[1], (test_file[1] * 100 / baseline) - 100, test_file[0])
)
test_count += 1
print("{} tests performed ({} individual testcases)".format(test_count, testcase_count))
# all tests succeeded
return True
def main():
cmd_parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description=f"""Run and manage tests for MicroPython.
{run_tests_module.test_instance_description}
{run_tests_module.test_directory_description}
""",
epilog=run_tests_module.test_instance_epilog,
)
cmd_parser.add_argument(
"-t", "--test-instance", default="unix", help="the MicroPython instance to test"
)
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(
"-I",
"--iters",
type=int,
default=200_000,
help="number of test iterations, only for remote instances (default 200,000)",
)
cmd_parser.add_argument("files", nargs="*", help="input test files")
args = cmd_parser.parse_args()
# Note pyboard support is copied over from run-tests.py, not tests, and likely needs revamping
pyb = run_tests_module.get_test_instance(
args.test_instance, args.baudrate, args.user, args.password
)
if len(args.files) == 0:
if args.test_dirs:
test_dirs = tuple(args.test_dirs)
else:
test_dirs = ("internal_bench",)
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 = sorted(args.files)
test_dict = defaultdict(lambda: [])
for t in tests:
m = re.match(r"(.+?)-(.+)\.py", t)
if not m:
continue
test_dict[m.group(1)].append([t, None])
if not run_tests(pyb, test_dict, args.iters):
sys.exit(1)
if __name__ == "__main__":
main()