tests/run-tests.py: Add an option for running only the failed tests.

Implement the typical 're-run the failed tests' most test runners have, for
convenience.  Accessible via the new --run-failures argument, and
implemented using a json file containing a list of the failed tests.

Signed-off-by: stijn <stijn@ignitron.net>
This commit is contained in:
stijn 2023-12-18 12:09:30 +01:00 committed by Damien George
parent 0c81ffd31a
commit 2b56bab226
1 changed files with 43 additions and 3 deletions

View File

@ -6,6 +6,7 @@ import sys
import platform import platform
import argparse import argparse
import inspect import inspect
import json
import re import re
from glob import glob from glob import glob
import multiprocessing import multiprocessing
@ -47,6 +48,8 @@ else:
# (not site packages which may clash with u-module names), and improve start up time. # (not site packages which may clash with u-module names), and improve start up time.
CPYTHON3_CMD = [CPYTHON3, "-BS"] CPYTHON3_CMD = [CPYTHON3, "-BS"]
# File with the test results.
RESULTS_FILE = "_results.json"
# For diff'ing test output # For diff'ing test output
DIFF = os.getenv("MICROPY_DIFF", "diff -u") DIFF = os.getenv("MICROPY_DIFF", "diff -u")
@ -770,7 +773,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
with open(filename_mupy, "wb") as f: with open(filename_mupy, "wb") as f:
f.write(output_mupy) f.write(output_mupy)
print("FAIL ", test_file) print("FAIL ", test_file)
failed_tests.append(test_name) failed_tests.append((test_name, test_file))
test_count.increment() test_count.increment()
@ -784,6 +787,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
for test in tests: for test in tests:
run_one_test(test) run_one_test(test)
# Leave RESULTS_FILE untouched here for future runs.
if args.list_tests: if args.list_tests:
return True return True
@ -798,8 +802,26 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
if len(skipped_tests) > 0: 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)))
failed_tests = sorted(failed_tests.value) failed_tests = sorted(failed_tests.value)
# Serialize regex added by append_filter.
def to_json(obj):
if isinstance(obj, re.Pattern):
return obj.pattern
return obj
with open(os.path.join(result_dir, RESULTS_FILE), "w") as f:
json.dump(
{"args": vars(args), "failed_tests": [test[1] for test in failed_tests]},
f,
default=to_json,
)
if len(failed_tests) > 0: if len(failed_tests) > 0:
print("{} tests failed: {}".format(len(failed_tests), " ".join(failed_tests))) print(
"{} tests failed: {}".format(
len(failed_tests), " ".join(test[0] for test in failed_tests)
)
)
return False return False
# all tests succeeded # all tests succeeded
@ -915,6 +937,11 @@ the last matching regex is used:
action="store_true", action="store_true",
help="delete the .exp and .out files from failed tests and exit", help="delete the .exp and .out files from failed tests and exit",
) )
cmd_parser.add_argument(
"--run-failures",
action="store_true",
help="re-run only the failed tests",
)
args = cmd_parser.parse_args() args = cmd_parser.parse_args()
if args.print_failures: if args.print_failures:
@ -931,6 +958,7 @@ the last matching regex is used:
os.path.join(args.result_dir, "*.out") os.path.join(args.result_dir, "*.out")
): ):
os.remove(f) os.remove(f)
rm_f(os.path.join(args.result_dir, RESULTS_FILE))
sys.exit(0) sys.exit(0)
@ -979,7 +1007,19 @@ the last matching regex is used:
else: 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.run_failures and (any(args.files) or args.test_dirs is not None):
raise ValueError(
"--run-failures cannot be used together with files or --test-dirs arguments"
)
if args.run_failures:
results_file = os.path.join(args.result_dir, RESULTS_FILE)
if os.path.exists(results_file):
with open(results_file, "r") as f:
tests = json.load(f)["failed_tests"]
else:
tests = []
elif len(args.files) == 0:
if args.test_dirs is None: if args.test_dirs is None:
test_dirs = ( test_dirs = (
"basics", "basics",