Skip to content

Commit e6fe553

Browse files
committed
run_tests: Parallelism improvements, partial sync with micropython
Take changes from micropython#3694 (expected to be merged soon) as well as other accumulated stuff from upstream that we want. Leave our desired differences, including: * silencing warnings in python3 * renaming the file descriptors returned by openpty() * adding ulab tests * Adding "." to the import path for skip_if This speeds up `make test_full` and should also reduce the time in CI a little bit.
1 parent 47947dc commit e6fe553

File tree

2 files changed

+43
-55
lines changed

2 files changed

+43
-55
lines changed

ports/unix/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ include $(TOP)/py/mkrules.mk
228228

229229
test: $(PROG) $(TOP)/tests/run-tests.py
230230
$(eval DIRNAME=ports/$(notdir $(CURDIR)))
231-
cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(PROG) ./run-tests.py --auto-jobs
231+
cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(PROG) ./run-tests.py
232232

233233
test_full: $(PROG) $(TOP)/tests/run-tests.py
234234
$(eval DIRNAME=ports/$(notdir $(CURDIR)))

tests/run-tests.py

Lines changed: 42 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@
77
import argparse
88
import inspect
99
import re
10-
import threading
10+
from glob import glob
1111
import multiprocessing
1212
from multiprocessing.pool import ThreadPool
13-
from glob import glob
13+
import threading
14+
import tempfile
1415

1516
# See stackoverflow.com/questions/2632199: __file__ nor sys.argv[0]
1617
# are guaranteed to always work, this one should though.
@@ -169,26 +170,27 @@ def send_get(what):
169170

170171
# if running via .mpy, first compile the .py file
171172
if args.via_mpy:
173+
mpy_modname = tempfile.mktemp(dir="")
174+
mpy_filename = mpy_modname + ".mpy"
172175
subprocess.check_output(
173176
[MPYCROSS]
174177
+ args.mpy_cross_flags.split()
175-
+ ["-o", "mpytest.mpy", "-X", "emit=" + args.emit, test_file]
178+
+ ["-o", mpy_filename, "-X", "emit=" + args.emit, test_file]
176179
)
177-
cmdlist.extend(["-m", "mpytest"])
180+
cmdlist.extend(["-m", mpy_modname])
178181
else:
179182
cmdlist.append(test_file)
180183

181184
# run the actual test
182-
e = {"LANG": "en_US.UTF-8", "MICROPYPATH": os.environ["MICROPYPATH"]}
183185
try:
184-
output_mupy = subprocess.check_output(cmdlist, env=e, stderr=subprocess.STDOUT)
185-
except subprocess.CalledProcessError as error:
186+
output_mupy = subprocess.check_output(cmdlist, stderr=subprocess.STDOUT)
187+
except subprocess.CalledProcessError as er:
186188
had_crash = True
187-
output_mupy = error.output + b"CRASH"
189+
output_mupy = er.output + b"CRASH"
188190

189191
# clean up if we had an intermediate .mpy file
190192
if args.via_mpy:
191-
rm_f("mpytest.mpy")
193+
rm_f(mpy_filename)
192194

193195
else:
194196
# run on pyboard
@@ -200,7 +202,7 @@ def send_get(what):
200202
if not is_special and e.args[0] == "exception":
201203
output_mupy = e.args[1] + e.args[2] + b"CRASH"
202204
else:
203-
output_mupy = b"CRASH"
205+
output_mupy = bytes(e.args[0], "ascii") + b"\nCRASH"
204206

205207
# canonical form for all ports/platforms is to use \n for end-of-line
206208
output_mupy = output_mupy.replace(b"\r\n", b"\n")
@@ -241,10 +243,9 @@ def send_get(what):
241243
else:
242244
# a regex
243245
if lines_exp[i][1].match(lines_mupy[i_mupy]):
244-
# print("match", lines_exp[i][0], lines_mupy[i_mupy])
245246
lines_mupy[i_mupy] = lines_exp[i][0]
246247
else:
247-
# print("don't match: %r %s" % (lines_exp[i][0], lines_mupy[i_mupy])) # DEBUG
248+
# print("don't match: %r %s" % (lines_exp[i][1], lines_mupy[i_mupy])) # DEBUG
248249
pass
249250
i_mupy += 1
250251
if i_mupy >= len(lines_mupy):
@@ -266,6 +267,9 @@ def __init__(self, start=0):
266267
self._value = start
267268
self._lock = threading.Lock()
268269

270+
def increment(self):
271+
self.add(1)
272+
269273
def add(self, to_add):
270274
with self._lock:
271275
self._value += to_add
@@ -306,11 +310,11 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
306310
if not (args.list_tests or args.write_exp):
307311
# Even if we run completely different tests in a different directory,
308312
# we need to access feature_checks from the same directory as the
309-
# run-tests script itself so use base_path.
313+
# run-tests.py script itself so use base_path.
310314

311315
# Check if micropython.native is supported, and skip such tests if it's not
312316
output = run_feature_check(pyb, args, base_path, "native_check.py")
313-
if output.endswith(b"CRASH"):
317+
if output != b"native\n":
314318
skip_native = True
315319

316320
# Check if arbitrary-precision integers are supported, and skip such tests if it's not
@@ -325,7 +329,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
325329

326330
# Check if set type (and set literals) is supported, and skip such tests if it's not
327331
output = run_feature_check(pyb, args, base_path, "set_check.py")
328-
if output.endswith(b"CRASH"):
332+
if output != b"{1}\n":
329333
skip_set_type = True
330334

331335
# Check if slice is supported, and skip such tests if it's not
@@ -335,12 +339,12 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
335339

336340
# Check if async/await keywords are supported, and skip such tests if it's not
337341
output = run_feature_check(pyb, args, base_path, "async_check.py")
338-
if output.endswith(b"CRASH"):
342+
if output != b"async\n":
339343
skip_async = True
340344

341345
# Check if const keyword (MicroPython extension) is supported, and skip such tests if it's not
342346
output = run_feature_check(pyb, args, base_path, "const.py")
343-
if output.endswith(b"CRASH"):
347+
if output != b"1\n":
344348
skip_const = True
345349

346350
# Check if __rOP__ special methods are supported, and skip such tests if it's not
@@ -365,10 +369,10 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
365369

366370
upy_byteorder = run_feature_check(pyb, args, base_path, "byteorder.py")
367371
upy_float_precision = run_feature_check(pyb, args, base_path, "float.py")
368-
if upy_float_precision.endswith(b"CRASH"):
369-
upy_float_precision = 0
370-
else:
372+
try:
371373
upy_float_precision = int(upy_float_precision)
374+
except ValueError:
375+
upy_float_precision = 0
372376
has_complex = run_feature_check(pyb, args, base_path, "complex.py") == b"complex\n"
373377
has_coverage = run_feature_check(pyb, args, base_path, "coverage.py") == b"coverage\n"
374378
cpy_byteorder = subprocess.check_output(
@@ -545,7 +549,7 @@ def run_one_test(test_file):
545549
is_bytearray = test_name.startswith("bytearray") or test_name.endswith("_bytearray")
546550
is_set_type = test_name.startswith("set_") or test_name.startswith("frozenset")
547551
is_slice = test_name.find("slice") != -1 or test_name in misc_slice_tests
548-
is_async = test_name.startswith("async_")
552+
is_async = test_name.startswith(("async_", "uasyncio_"))
549553
is_const = test_name.startswith("const")
550554
is_io_module = test_name.startswith("io_")
551555

@@ -610,26 +614,22 @@ def run_one_test(test_file):
610614
filename_mupy = os.path.join(result_dir, test_basename + ".out")
611615

612616
if output_expected == output_mupy:
613-
# print("pass ", test_file)
614-
passed_count.add(1)
617+
print("pass ", test_file)
618+
passed_count.increment()
615619
rm_f(filename_expected)
616620
rm_f(filename_mupy)
617621
else:
618622
with open(filename_expected, "wb") as f:
619623
f.write(output_expected)
620624
with open(filename_mupy, "wb") as f:
621625
f.write(output_mupy)
622-
print("### Expected")
623-
print(output_expected)
624-
print("### Actual")
625-
print(output_mupy)
626626
print("FAIL ", test_file)
627-
failed_tests.append(test_file)
627+
failed_tests.append(test_name)
628628

629-
test_count.add(1)
629+
test_count.increment()
630630

631-
if args.list_tests:
632-
return True
631+
if pyb or args.list_tests:
632+
num_threads = 1
633633

634634
if num_threads > 1:
635635
pool = ThreadPool(num_threads)
@@ -638,25 +638,22 @@ def run_one_test(test_file):
638638
for test in tests:
639639
run_one_test(test)
640640

641+
if args.list_tests:
642+
return True
643+
641644
print(
642645
"{} tests performed ({} individual testcases)".format(
643646
test_count.value, testcase_count.value
644647
)
645648
)
646649
print("{} tests passed".format(passed_count.value))
647650

648-
if len(skipped_tests.value) > 0:
649-
print(
650-
"{} tests skipped: {}".format(
651-
len(skipped_tests.value), " ".join(sorted(skipped_tests.value))
652-
)
653-
)
654-
if len(failed_tests.value) > 0:
655-
print(
656-
"{} tests failed: {}".format(
657-
len(failed_tests.value), " ".join(sorted(failed_tests.value))
658-
)
659-
)
651+
skipped_tests = sorted(skipped_tests.value)
652+
if len(skipped_tests) > 0:
653+
print("{} tests skipped: {}".format(len(skipped_tests), " ".join(skipped_tests)))
654+
failed_tests = sorted(failed_tests.value)
655+
if len(failed_tests) > 0:
656+
print("{} tests failed: {}".format(len(failed_tests), " ".join(failed_tests)))
660657
return False
661658

662659
# all tests succeeded
@@ -758,18 +755,11 @@ def main():
758755
cmd_parser.add_argument(
759756
"-j",
760757
"--jobs",
761-
default=1,
758+
default=multiprocessing.cpu_count(),
762759
metavar="N",
763760
type=int,
764761
help="Number of tests to run simultaneously",
765762
)
766-
cmd_parser.add_argument(
767-
"--auto-jobs",
768-
action="store_const",
769-
dest="jobs",
770-
const=multiprocessing.cpu_count(),
771-
help="Set the -j values to the CPU (thread) count",
772-
)
773763
cmd_parser.add_argument("files", nargs="*", help="input test files")
774764
cmd_parser.add_argument(
775765
"--print-failures",
@@ -869,9 +859,7 @@ def main():
869859

870860
if not args.keep_path:
871861
# clear search path to make sure tests use only builtin modules and those in extmod
872-
os.environ["MICROPYPATH"] = (
873-
os.pathsep + base_path("../extmod") + os.pathsep + base_path(".")
874-
)
862+
os.environ["MICROPYPATH"] = os.pathsep + base_path("../extmod")
875863

876864
try:
877865
os.makedirs(args.result_dir, exist_ok=True)

0 commit comments

Comments
 (0)