Skip to content

Commit 9e9b48d

Browse files
committed
Merge #13105: [qa] Add --failfast option to functional test runner
58f9a0a Use --failfast when running functional tests on Travis (James O'Beirne) bf720c1 Add --failfast option to functional test runner (James O'Beirne) Pull request description: Add the option (`--failfast`) to stop the functional test runner's execution when it encounters the first failure. Also cleans up run_test's arguments list ([no more mutable default for `args`](http://docs.python-guide.org/en/latest/writing/gotchas/#mutable-default-arguments)) and call site. Tree-SHA512: e854b1b1634bf613ae8ae88e715df1460982fa68db9d785aafeb5eccf5bf324c7f20dded2ca6840ebf18a28347ecac2138d6c7592507b34939b02609ef55e1b3
2 parents 19fadd9 + 58f9a0a commit 9e9b48d

File tree

2 files changed

+35
-3
lines changed

2 files changed

+35
-3
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ script:
8080
- export LD_LIBRARY_PATH=$TRAVIS_BUILD_DIR/depends/$HOST/lib
8181
- if [ "$RUN_TESTS" = "true" ]; then travis_wait 50 make $MAKEJOBS check VERBOSE=1; fi
8282
- if [ "$TRAVIS_EVENT_TYPE" = "cron" ]; then extended="--extended --exclude feature_pruning,feature_dbcrash"; fi
83-
- if [ "$RUN_TESTS" = "true" ]; then test/functional/test_runner.py --combinedlogslen=4000 --coverage --quiet ${extended}; fi
83+
- if [ "$RUN_TESTS" = "true" ]; then test/functional/test_runner.py --combinedlogslen=4000 --coverage --quiet --failfast ${extended}; fi
8484
after_script:
8585
- echo $TRAVIS_COMMIT_RANGE
8686
- echo $TRAVIS_COMMIT_LOG

test/functional/test_runner.py

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ def main():
201201
parser.add_argument('--keepcache', '-k', action='store_true', help='the default behavior is to flush the cache directory on startup. --keepcache retains the cache from the previous testrun.')
202202
parser.add_argument('--quiet', '-q', action='store_true', help='only print results summary and failure logs')
203203
parser.add_argument('--tmpdirprefix', '-t', default=tempfile.gettempdir(), help="Root directory for datadirs")
204+
parser.add_argument('--failfast', action='store_true', help='stop execution after the first test failure')
204205
args, unknown_args = parser.parse_known_args()
205206

206207
# args to be passed on always start with two dashes; tests are the remaining unknown args
@@ -283,9 +284,21 @@ def main():
283284
if not args.keepcache:
284285
shutil.rmtree("%s/test/cache" % config["environment"]["BUILDDIR"], ignore_errors=True)
285286

286-
run_tests(test_list, config["environment"]["SRCDIR"], config["environment"]["BUILDDIR"], tmpdir, args.jobs, args.coverage, passon_args, args.combinedlogslen)
287+
run_tests(
288+
test_list,
289+
config["environment"]["SRCDIR"],
290+
config["environment"]["BUILDDIR"],
291+
tmpdir,
292+
jobs=args.jobs,
293+
enable_coverage=args.coverage,
294+
args=passon_args,
295+
combined_logs_len=args.combinedlogslen,
296+
failfast=args.failfast,
297+
)
298+
299+
def run_tests(test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=False, args=None, combined_logs_len=0, failfast=False):
300+
args = args or []
287301

288-
def run_tests(test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=False, args=[], combined_logs_len=0):
289302
# Warn if bitcoind is already running (unix only)
290303
try:
291304
if subprocess.check_output(["pidof", "bitcoind"]) is not None:
@@ -346,6 +359,10 @@ def run_tests(test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=Fal
346359
combined_logs, _ = subprocess.Popen([sys.executable, os.path.join(tests_dir, 'combine_logs.py'), '-c', testdir], universal_newlines=True, stdout=subprocess.PIPE).communicate()
347360
print("\n".join(deque(combined_logs.splitlines(), combined_logs_len)))
348361

362+
if failfast:
363+
logging.debug("Early exiting after test failure")
364+
break
365+
349366
print_results(test_results, max_len_name, (int(time.time() - start_time)))
350367

351368
if coverage:
@@ -360,6 +377,10 @@ def run_tests(test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=Fal
360377

361378
all_passed = all(map(lambda test_result: test_result.was_successful, test_results))
362379

380+
# This will be a no-op unless failfast is True in which case there may be dangling
381+
# processes which need to be killed.
382+
job_queue.kill_and_join()
383+
363384
sys.exit(not all_passed)
364385

365386
def print_results(test_results, max_len_name, runtime):
@@ -450,6 +471,17 @@ def get_next(self):
450471
return TestResult(name, status, int(time.time() - start_time)), testdir, stdout, stderr
451472
print('.', end='', flush=True)
452473

474+
def kill_and_join(self):
475+
"""Send SIGKILL to all jobs and block until all have ended."""
476+
procs = [i[2] for i in self.jobs]
477+
478+
for proc in procs:
479+
proc.kill()
480+
481+
for proc in procs:
482+
proc.wait()
483+
484+
453485
class TestResult():
454486
def __init__(self, name, status, time):
455487
self.name = name

0 commit comments

Comments
 (0)