Skip to content

Commit d65ea99

Browse files
authored
Merge pull request adafruit#729 from jepler/tests-parallel-circuitpython
Optionally parallelize the testsuite
2 parents 1489450 + f8e0baa commit d65ea99

4 files changed

Lines changed: 59 additions & 32 deletions

File tree

.travis.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,24 +78,24 @@ script:
7878
- echo -en 'travis_fold:end:qemu\\r'
7979

8080
# run tests without coverage info
81-
#- (cd tests && MICROPY_CPYTHON3=python3.4 ./run-tests)
82-
#- (cd tests && MICROPY_CPYTHON3=python3.4 ./run-tests --emit native)
81+
#- (cd tests && MICROPY_CPYTHON3=python3.4 ./run-tests --auto-jobs)
82+
#- (cd tests && MICROPY_CPYTHON3=python3.4 ./run-tests --auto-jobs --emit native)
8383

8484
# run tests with coverage info
8585
- echo 'Test all' && echo -en 'travis_fold:start:test_all\\r'
86-
- ([[ $TRAVIS_TEST != "unix" ]] || (cd tests && MICROPY_CPYTHON3=python3.4 MICROPY_MICROPYTHON=../ports/unix/micropython_coverage ./run-tests))
86+
- ([[ $TRAVIS_TEST != "unix" ]] || (cd tests && MICROPY_CPYTHON3=python3.4 MICROPY_MICROPYTHON=../ports/unix/micropython_coverage ./run-tests --auto-jobs))
8787
- echo -en 'travis_fold:end:test_all\\r'
8888

8989
- echo 'Test threads' && echo -en 'travis_fold:start:test_threads\\r'
90-
- ([[ $TRAVIS_TEST != "unix" ]] || (cd tests && MICROPY_CPYTHON3=python3.4 MICROPY_MICROPYTHON=../ports/unix/micropython_coverage ./run-tests -d thread))
90+
- ([[ $TRAVIS_TEST != "unix" ]] || (cd tests && MICROPY_CPYTHON3=python3.4 MICROPY_MICROPYTHON=../ports/unix/micropython_coverage ./run-tests --auto-jobs -d thread))
9191
- echo -en 'travis_fold:end:test_threads\\r'
9292

9393
- echo 'Testing with native' && echo -en 'travis_fold:start:test_native\\r'
94-
- ([[ $TRAVIS_TEST != "unix" ]] || (cd tests && MICROPY_CPYTHON3=python3.4 MICROPY_MICROPYTHON=../ports/unix/micropython_coverage ./run-tests --emit native))
94+
- ([[ $TRAVIS_TEST != "unix" ]] || (cd tests && MICROPY_CPYTHON3=python3.4 MICROPY_MICROPYTHON=../ports/unix/micropython_coverage ./run-tests --auto-jobs --emit native))
9595
- echo -en 'travis_fold:end:test_native\\r'
9696

9797
- (echo 'Testing with mpy' && echo -en 'travis_fold:start:test_mpy\\r')
98-
- ([[ $TRAVIS_TEST != "unix" ]] || (cd tests && MICROPY_CPYTHON3=python3.4 MICROPY_MICROPYTHON=../ports/unix/micropython_coverage ./run-tests --via-mpy -d basics float))
98+
- ([[ $TRAVIS_TEST != "unix" ]] || (cd tests && MICROPY_CPYTHON3=python3.4 MICROPY_MICROPYTHON=../ports/unix/micropython_coverage ./run-tests -j1 --via-mpy -d basics float))
9999
- echo -en 'travis_fold:end:test_mpy\\r'
100100

101101
- (echo 'Building docs' && echo -en 'travis_fold:start:build_docs\\r')

ports/unix/Makefile

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ include $(TOP)/py/mkrules.mk
189189

190190
test: $(PROG) $(TOP)/tests/run-tests
191191
$(eval DIRNAME=ports/$(notdir $(CURDIR)))
192-
cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(PROG) ./run-tests
192+
cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(PROG) ./run-tests --auto-jobs
193193

194194
# install micropython in /usr/local/bin
195195
TARGET = micropython
@@ -254,10 +254,10 @@ coverage:
254254

255255
coverage_test: coverage
256256
$(eval DIRNAME=ports/$(notdir $(CURDIR)))
257-
cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/micropython_coverage ./run-tests
258-
cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/micropython_coverage ./run-tests -d thread
259-
cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/micropython_coverage ./run-tests --emit native
260-
cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/micropython_coverage ./run-tests --via-mpy -d basics float
257+
cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/micropython_coverage ./run-tests --auto-jobs
258+
cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/micropython_coverage ./run-tests --auto-jobs -d thread
259+
cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/micropython_coverage ./run-tests --auto-jobs --emit native
260+
cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/micropython_coverage ./run-tests -j1 --via-mpy -d basics float
261261
gcov -o build-coverage/py $(TOP)/py/*.c
262262
gcov -o build-coverage/extmod $(TOP)/extmod/*.c
263263

ports/windows/.appveyor.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ test_script:
2222
- cmd: >-
2323
cd tests
2424
25-
%MICROPY_CPYTHON3% run-tests
25+
%MICROPY_CPYTHON3% run-tests --auto-jobs
2626
2727
skip_tags: true
2828

tests/run-tests

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import sys
66
import platform
77
import argparse
88
import re
9+
import threading
10+
import multiprocessing
11+
from multiprocessing.pool import ThreadPool
912
from glob import glob
1013

1114
# Tests require at least CPython 3.3. If your default python3 executable
@@ -197,13 +200,27 @@ def run_micropython(pyb, args, test_file, is_special=False):
197200
def run_feature_check(pyb, args, base_path, test_file):
198201
return run_micropython(pyb, args, base_path + "/feature_check/" + test_file, is_special=True)
199202

203+
class ThreadSafeCounter:
204+
def __init__(self, start=0):
205+
self._value = start
206+
self._lock = threading.Lock()
200207

201-
def run_tests(pyb, tests, args, base_path="."):
202-
test_count = 0
203-
testcase_count = 0
204-
passed_count = 0
205-
failed_tests = []
206-
skipped_tests = []
208+
def add(self, to_add):
209+
with self._lock: self._value += to_add
210+
211+
def append(self, arg):
212+
self.add([arg])
213+
214+
@property
215+
def value(self):
216+
return self._value
217+
218+
def run_tests(pyb, tests, args, base_path=".", num_threads=1):
219+
test_count = ThreadSafeCounter()
220+
testcase_count = ThreadSafeCounter()
221+
passed_count = ThreadSafeCounter()
222+
failed_tests = ThreadSafeCounter([])
223+
skipped_tests = ThreadSafeCounter([])
207224

208225
skip_tests = set()
209226
skip_native = False
@@ -354,7 +371,7 @@ def run_tests(pyb, tests, args, base_path="."):
354371
skip_tests.add('micropython/heapalloc_iter.py') # requires generators
355372
skip_tests.add('micropython/schedule.py') # native code doesn't check pending events
356373

357-
for test_file in tests:
374+
def run_one_test(test_file):
358375
test_file = test_file.replace('\\', '/')
359376
test_basename = os.path.basename(test_file)
360377
test_name = os.path.splitext(test_basename)[0]
@@ -377,7 +394,7 @@ def run_tests(pyb, tests, args, base_path="."):
377394
if skip_it:
378395
print("skip ", test_file)
379396
skipped_tests.append(test_name)
380-
continue
397+
return
381398

382399
# get expected output
383400
test_file_expected = test_file + '.exp'
@@ -405,24 +422,24 @@ def run_tests(pyb, tests, args, base_path="."):
405422
output_expected = output_expected.replace(b'\r\n', b'\n')
406423

407424
if args.write_exp:
408-
continue
425+
return
409426

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

413430
if output_mupy == b'SKIP\n':
414431
print("skip ", test_file)
415432
skipped_tests.append(test_name)
416-
continue
433+
return
417434

418-
testcase_count += len(output_expected.splitlines())
435+
testcase_count.add(len(output_expected.splitlines()))
419436

420437
filename_expected = test_basename + ".exp"
421438
filename_mupy = test_basename + ".out"
422439

423440
if output_expected == output_mupy:
424441
print("pass ", test_file)
425-
passed_count += 1
442+
passed_count.add(1)
426443
rm_f(filename_expected)
427444
rm_f(filename_mupy)
428445
else:
@@ -437,15 +454,22 @@ def run_tests(pyb, tests, args, base_path="."):
437454
print("FAIL ", test_file)
438455
failed_tests.append(test_name)
439456

440-
test_count += 1
457+
test_count.add(1)
458+
459+
if num_threads > 1:
460+
pool = ThreadPool(num_threads)
461+
pool.map(run_one_test, tests)
462+
else:
463+
for test in tests:
464+
run_one_test(test)
441465

442-
print("{} tests performed ({} individual testcases)".format(test_count, testcase_count))
443-
print("{} tests passed".format(passed_count))
466+
print("{} tests performed ({} individual testcases)".format(test_count.value, testcase_count.value))
467+
print("{} tests passed".format(passed_count.value))
444468

445-
if len(skipped_tests) > 0:
446-
print("{} tests skipped: {}".format(len(skipped_tests), ' '.join(skipped_tests)))
447-
if len(failed_tests) > 0:
448-
print("{} tests failed: {}".format(len(failed_tests), ' '.join(failed_tests)))
469+
if len(skipped_tests.value) > 0:
470+
print("{} tests skipped: {}".format(len(skipped_tests.value), ' '.join(sorted(skipped_tests.value))))
471+
if len(failed_tests.value) > 0:
472+
print("{} tests failed: {}".format(len(failed_tests.value), ' '.join(sorted(failed_tests.value))))
449473
return False
450474

451475
# all tests succeeded
@@ -464,11 +488,14 @@ def main():
464488
cmd_parser.add_argument('--heapsize', help='heapsize to use (use default if not specified)')
465489
cmd_parser.add_argument('--via-mpy', action='store_true', help='compile .py files to .mpy first')
466490
cmd_parser.add_argument('--keep-path', action='store_true', help='do not clear MICROPYPATH when running tests')
491+
cmd_parser.add_argument('-j', '--jobs', default=1, metavar='N', type=int, help='Number of tests to run simultaneously')
492+
cmd_parser.add_argument('--auto-jobs', action='store_const', dest='jobs', const=multiprocessing.cpu_count(), help='Set the -j values to the CPU (thread) count')
467493
cmd_parser.add_argument('files', nargs='*', help='input test files')
468494
args = cmd_parser.parse_args()
469495

470496
EXTERNAL_TARGETS = ('pyboard', 'wipy', 'esp8266', 'minimal')
471497
if args.target in EXTERNAL_TARGETS:
498+
args.jobs = 1
472499
import pyboard
473500
pyb = pyboard.Pyboard(args.device, args.baudrate, args.user, args.password)
474501
pyb.enter_raw_repl()
@@ -507,7 +534,7 @@ def main():
507534
# run-tests script itself.
508535
base_path = os.path.dirname(sys.argv[0]) or "."
509536
try:
510-
res = run_tests(pyb, tests, args, base_path)
537+
res = run_tests(pyb, tests, args, base_path, args.jobs)
511538
finally:
512539
if pyb:
513540
pyb.close()

0 commit comments

Comments
 (0)