diff --git a/9pm.py b/9pm.py index bf37ba2..581be7b 100755 --- a/9pm.py +++ b/9pm.py @@ -313,10 +313,10 @@ def parse_suite(suite_path, parent_suite_path, options, settings, name=None): opts = lmerge(opts, options) else: opts = options.copy() - if 'name' in entry: - suite['suite'].append(parse_suite(next_suite_path, suite_path, opts, settings, entry['name'])) - else: - suite['suite'].append(parse_suite(next_suite_path, suite_path, opts, settings)) + sub = parse_suite(next_suite_path, suite_path, opts, settings, entry.get('name')) + if 'abort' in entry: + sub['abort'] = entry['abort'] + suite['suite'].append(sub) elif 'case' in entry: case = {} @@ -347,6 +347,9 @@ def parse_suite(suite_path, parent_suite_path, options, settings, name=None): if 'mask' in entry: case['mask'] = entry['mask'] + if 'abort' in entry: + case['abort'] = entry['abort'] + case['case'] = os.path.join(suite_dirname, entry['case']) if 'test-spec' in settings: @@ -545,22 +548,25 @@ def count_suite_tree(data): tests += 1 return suites, tests -def run_suite(args, data, skip_suite): +def run_suite(args, data, skip_suite, fatal): skip = False err = False + abort = False if data['name'] != "command-line": print(pcolor.blue + f"\nRunning suite {data['suite_num']}: {data['name']}" + pcolor.reset) for test in data['suite']: if 'suite' in test: - subskip, suberr = run_suite(args, test, skip_suite) + subskip, suberr, subabort = run_suite(args, test, skip_suite, + fatal or test.get('abort')) if subskip: skip = True if suberr: err = True - if err and args.abort: - break; + if subabort: + abort = True + break elif 'case' in test: if not os.path.isfile(test['case']): @@ -586,8 +592,9 @@ def run_suite(args, data, skip_suite): if 'onfail' in test: run_onfail(args, test) - if err and args.abort: + if err and (fatal or test.get('abort')): print("Aborting execution") + abort = True break elif subskip: if 'mask' in test and test['mask'] == "skip": @@ -606,7 +613,7 @@ def run_suite(args, data, skip_suite): else: data['result'] = "pass" - return skip, err + return skip, err, abort def get_first_existing_file(list, name): for f in list: @@ -848,7 +855,7 @@ def main(): setup_env(args) - skip, err = run_suite(args, suite, False) + skip, err, _ = run_suite(args, suite, False, args.abort) if err: cprint(pcolor.red, "\nx Execution") elif skip: diff --git a/self_test/run.py b/self_test/run.py index 0760367..c1760a3 100755 --- a/self_test/run.py +++ b/self_test/run.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import subprocess +import glob import os import json import re @@ -76,6 +77,13 @@ def run(self, workers, args=None, expected_return=0, grep=None): output = result.stdout + result.stderr # Combine both streams for searching assert grep in output, f"Expected text '{grep}' not found in output!" + return result + + def executed(self, name): + """Check if a named test case ran, by the log the worker writes""" + pattern = os.path.join(self.env["NINEPM_TEST_DIR"], f"*-{name}.log") + return bool(glob.glob(pattern)) + def check(self, expected, descr): """Check if the worker script wrote the expected JSON result and print colored results.""" file_name = f"{expected['name']}.log" @@ -282,6 +290,25 @@ def test_abort_flag(self): self.run(["cases/fail.sh", "cases/pass.sh"], ["--abort"], 1, "Aborting execution") print_green(f"[PASS] Abort flag works (-a --abort)") + def test_abort_key(self): + """Verify that the abort key works on cases and suites""" + + self.env["NINEPM_TEST_DIR"] = self.create_unique_subdir() + + self.run(["suites/abort-case.yaml"], [], 1, "Aborting execution") + assert not self.executed("after-fatal-case"), "Ran case after fatal case" + + self.run(["suites/abort-suite.yaml"], [], 1, "Aborting execution") + assert not self.executed("in-deep-suite"), "Ran rest of deepest suite" + assert not self.executed("in-inner-suite"), "Ran rest of nested suite" + assert not self.executed("after-fatal-suite"), "Ran case after fatal suite" + + result = self.run(["suites/abort-none.yaml"], [], 1) + assert "Aborting execution" not in result.stdout, "Aborted without abort key" + assert self.executed("after-plain-fail"), "Did not run case after failure" + + print_green(f"[PASS] Abort key works (abort: true)") + def test_proj_config(self): """Verify that that 9pm project config works (--proj)""" @@ -432,6 +459,7 @@ def cleanup(self): tester.test_config_file() tester.test_verbose_flag() tester.test_abort_flag() + tester.test_abort_key() tester.test_repeat_flag() tester.test_proj_config() tester.test_spec_json_format() diff --git a/self_test/suites/abort-case.yaml b/self_test/suites/abort-case.yaml new file mode 100644 index 0000000..0e6de15 --- /dev/null +++ b/self_test/suites/abort-case.yaml @@ -0,0 +1,5 @@ +--- +- case: "../cases/fail.sh" + abort: true +- case: "../cases/worker.py" + name: "after-fatal-case" diff --git a/self_test/suites/abort-none.yaml b/self_test/suites/abort-none.yaml new file mode 100644 index 0000000..2d8ebce --- /dev/null +++ b/self_test/suites/abort-none.yaml @@ -0,0 +1,4 @@ +--- +- case: "../cases/fail.sh" +- case: "../cases/worker.py" + name: "after-plain-fail" diff --git a/self_test/suites/abort-suite-deep.yaml b/self_test/suites/abort-suite-deep.yaml new file mode 100644 index 0000000..8e32c8e --- /dev/null +++ b/self_test/suites/abort-suite-deep.yaml @@ -0,0 +1,4 @@ +--- +- case: "../cases/fail.sh" +- case: "../cases/worker.py" + name: "in-deep-suite" diff --git a/self_test/suites/abort-suite-inner.yaml b/self_test/suites/abort-suite-inner.yaml new file mode 100644 index 0000000..d3d5253 --- /dev/null +++ b/self_test/suites/abort-suite-inner.yaml @@ -0,0 +1,4 @@ +--- +- suite: "abort-suite-deep.yaml" +- case: "../cases/worker.py" + name: "in-inner-suite" diff --git a/self_test/suites/abort-suite.yaml b/self_test/suites/abort-suite.yaml new file mode 100644 index 0000000..b715eda --- /dev/null +++ b/self_test/suites/abort-suite.yaml @@ -0,0 +1,5 @@ +--- +- suite: "abort-suite-inner.yaml" + abort: true +- case: "../cases/worker.py" + name: "after-fatal-suite"