Skip to content

Commit

Permalink
tests/run-tests.py: Parallelize running tests by default.
Browse files Browse the repository at this point in the history
This significantly reduces the time taken to run the test suite (on the
unix port).  Use `-j1` to disable this feature.

Signed-off-by: Jeff Epler <[email protected]>
  • Loading branch information
jepler authored and dpgeorge committed May 14, 2021
1 parent 43e7e5f commit 94a3f8a
Showing 1 changed file with 71 additions and 21 deletions.
92 changes: 71 additions & 21 deletions tests/run-tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
import inspect
import re
from glob import glob
import multiprocessing
from multiprocessing.pool import ThreadPool
import threading
import tempfile

# See stackoverflow.com/questions/2632199: __file__ nor sys.argv[0]
# are guaranteed to always work, this one should though.
Expand Down Expand Up @@ -157,12 +161,14 @@ def send_get(what):

# if running via .mpy, first compile the .py file
if args.via_mpy:
mpy_modname = tempfile.mktemp(dir="")
mpy_filename = mpy_modname + ".mpy"
subprocess.check_output(
[MPYCROSS]
+ args.mpy_cross_flags.split()
+ ["-o", "mpytest.mpy", "-X", "emit=" + args.emit, test_file]
+ ["-o", mpy_filename, "-X", "emit=" + args.emit, test_file]
)
cmdlist.extend(["-m", "mpytest"])
cmdlist.extend(["-m", mpy_modname])
else:
cmdlist.append(test_file)

Expand All @@ -175,7 +181,7 @@ def send_get(what):

# clean up if we had an intermediate .mpy file
if args.via_mpy:
rm_f("mpytest.mpy")
rm_f(mpy_filename)

else:
# run on pyboard
Expand Down Expand Up @@ -247,12 +253,32 @@ def run_feature_check(pyb, args, base_path, test_file):
return run_micropython(pyb, args, base_path("feature_check", test_file), is_special=True)


def run_tests(pyb, tests, args, result_dir):
test_count = 0
testcase_count = 0
passed_count = 0
failed_tests = []
skipped_tests = []
class ThreadSafeCounter:
def __init__(self, start=0):
self._value = start
self._lock = threading.Lock()

def increment(self):
self.add(1)

def add(self, to_add):
with self._lock:
self._value += to_add

def append(self, arg):
self.add([arg])

@property
def value(self):
return self._value


def run_tests(pyb, tests, args, result_dir, num_threads=1):
test_count = ThreadSafeCounter()
testcase_count = ThreadSafeCounter()
passed_count = ThreadSafeCounter()
failed_tests = ThreadSafeCounter([])
skipped_tests = ThreadSafeCounter([])

skip_tests = set()
skip_native = False
Expand Down Expand Up @@ -490,7 +516,7 @@ def run_tests(pyb, tests, args, result_dir):
) # 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:
def run_one_test(test_file):
test_file = test_file.replace("\\", "/")

if args.filters:
Expand All @@ -500,7 +526,7 @@ def run_tests(pyb, tests, args, result_dir):
if pat.search(test_file):
verdict = action
if verdict == "exclude":
continue
return

test_basename = test_file.replace("..", "_").replace("./", "").replace("/", "_")
test_name = os.path.splitext(os.path.basename(test_file))[0]
Expand Down Expand Up @@ -533,12 +559,12 @@ def run_tests(pyb, tests, args, result_dir):
if args.list_tests:
if not skip_it:
print(test_file)
continue
return

if skip_it:
print("skip ", test_file)
skipped_tests.append(test_name)
continue
return

# get expected output
test_file_expected = test_file + ".exp"
Expand All @@ -560,24 +586,24 @@ def run_tests(pyb, tests, args, result_dir):
output_expected = output_expected.replace(b"\r\n", b"\n")

if args.write_exp:
continue
return

# run MicroPython
output_mupy = run_micropython(pyb, args, test_file)

if output_mupy == b"SKIP\n":
print("skip ", test_file)
skipped_tests.append(test_name)
continue
return

testcase_count += len(output_expected.splitlines())
testcase_count.add(len(output_expected.splitlines()))

filename_expected = os.path.join(result_dir, test_basename + ".exp")
filename_mupy = os.path.join(result_dir, test_basename + ".out")

if output_expected == output_mupy:
print("pass ", test_file)
passed_count += 1
passed_count.increment()
rm_f(filename_expected)
rm_f(filename_mupy)
else:
Expand All @@ -588,16 +614,32 @@ def run_tests(pyb, tests, args, result_dir):
print("FAIL ", test_file)
failed_tests.append(test_name)

test_count += 1
test_count.increment()

if pyb or args.list_tests:
num_threads = 1

if num_threads > 1:
pool = ThreadPool(num_threads)
pool.map(run_one_test, tests)
else:
for test in tests:
run_one_test(test)

if args.list_tests:
return True

print("{} tests performed ({} individual testcases)".format(test_count, testcase_count))
print("{} tests passed".format(passed_count))
print(
"{} tests performed ({} individual testcases)".format(
test_count.value, testcase_count.value
)
)
print("{} tests passed".format(passed_count.value))

skipped_tests = sorted(skipped_tests.value)
if len(skipped_tests) > 0:
print("{} tests skipped: {}".format(len(skipped_tests), " ".join(skipped_tests)))
failed_tests = sorted(failed_tests.value)
if len(failed_tests) > 0:
print("{} tests failed: {}".format(len(failed_tests), " ".join(failed_tests)))
return False
Expand Down Expand Up @@ -698,6 +740,14 @@ def main():
cmd_parser.add_argument(
"--keep-path", action="store_true", help="do not clear MICROPYPATH when running tests"
)
cmd_parser.add_argument(
"-j",
"--jobs",
default=multiprocessing.cpu_count(),
metavar="N",
type=int,
help="Number of tests to run simultaneously",
)
cmd_parser.add_argument("files", nargs="*", help="input test files")
cmd_parser.add_argument(
"--print-failures",
Expand Down Expand Up @@ -800,7 +850,7 @@ def main():

try:
os.makedirs(args.result_dir, exist_ok=True)
res = run_tests(pyb, tests, args, args.result_dir)
res = run_tests(pyb, tests, args, args.result_dir, args.jobs)
finally:
if pyb:
pyb.close()
Expand Down

0 comments on commit 94a3f8a

Please sign in to comment.