diff --git a/test/TEST-21-DFUZZER/meson.build b/test/TEST-21-DFUZZER/meson.build index e107b85561..5a27fb2f3f 100644 --- a/test/TEST-21-DFUZZER/meson.build +++ b/test/TEST-21-DFUZZER/meson.build @@ -3,4 +3,5 @@ test_params += { 'timeout' : 3600, 'priority' : -50, + 'slow' : true, } diff --git a/test/integration-test-wrapper.py b/test/integration-test-wrapper.py index 575403344e..c41ca8e55a 100755 --- a/test/integration-test-wrapper.py +++ b/test/integration-test-wrapper.py @@ -9,6 +9,7 @@ with the expectation that as part of formally defining the API it will be tidy. ''' import argparse +import json import os import shlex import subprocess @@ -38,10 +39,6 @@ ExecStart=false def main(): - if not bool(int(os.getenv("SYSTEMD_INTEGRATION_TESTS", "0"))): - print("SYSTEMD_INTEGRATION_TESTS=1 not found in environment, skipping", file=sys.stderr) - exit(77) - parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('--meson-source-dir', required=True, type=Path) parser.add_argument('--meson-build-dir', required=True, type=Path) @@ -49,9 +46,18 @@ def main(): parser.add_argument('--test-number', required=True) parser.add_argument('--storage', required=True) parser.add_argument('--firmware', required=True) + parser.add_argument('--slow', action=argparse.BooleanOptionalAction) parser.add_argument('mkosi_args', nargs="*") args = parser.parse_args() + if not bool(int(os.getenv("SYSTEMD_INTEGRATION_TESTS", "0"))): + print(f"SYSTEMD_INTEGRATION_TESTS=1 not found in environment, skipping {args.test_name}", file=sys.stderr) + exit(77) + + if args.slow and not bool(int(os.getenv("SYSTEMD_SLOW_TESTS", "0"))): + print(f"SYSTEMD_SLOW_TESTS=1 not found in environment, skipping {args.test_name}", file=sys.stderr) + exit(77) + name = args.test_name + (f"-{i}" if (i := os.getenv("MESON_TEST_ITERATION")) else "") test_unit = f"testsuite-{args.test_number}.service" @@ -144,24 +150,47 @@ def main(): ] result = subprocess.run(cmd) - # Return code 123 is the expected success code - if result.returncode != 123: - if result.returncode != 77 and journal_file: - cmd = [ - 'journalctl', - '--no-hostname', - '-o', 'short-monotonic', - '--file', journal_file, - '-u', test_unit, - '-p', 'info', - ] - print("Test failed, relevant logs can be viewed with: \n\n" - f"{shlex.join(str(a) for a in cmd)}\n", file=sys.stderr) - exit(result.returncode or 1) - # Do not keep journal files for tests that don't fail. + # Return code 123 is the expected success code + if result.returncode in (123, 77): + # Do not keep journal files for tests that don't fail. + if journal_file: + journal_file.unlink(missing_ok=True) + + exit(0 if result.returncode == 123 else 77) + if journal_file: - journal_file.unlink(missing_ok=True) + ops = [] + + if os.getenv("GITHUB_ACTIONS"): + id = os.environ["GITHUB_RUN_ID"] + iteration = os.environ["GITHUB_RUN_ATTEMPT"] + j = json.loads( + subprocess.run( + [ + "mkosi", + "--directory", os.fspath(args.meson_source_dir), + "--json", + "summary", + ], + stdout=subprocess.PIPE, + text=True, + ).stdout + ) + images = {image["Image"]: image for image in j["Images"]} + distribution = images["system"]["Distribution"] + release = images["system"]["Release"] + artifact = f"ci-mkosi-{id}-{iteration}-{distribution}-{release}-failed-test-journals" + ops += [f"gh run download {id} --name {artifact} -D ci/{artifact}"] + journal_file = Path(f"ci/{artifact}/test/journal/{name}.journal") + + ops += [f"journalctl --file {journal_file} --no-hostname -o short-monotonic -u {test_unit} -p info"] + + print("Test failed, relevant logs can be viewed with: \n\n" + f"{(' && '.join(ops))}\n", file=sys.stderr) + + # 0 also means we failed so translate that to a non-zero exit code to mark the test as failed. + exit(result.returncode or 1) if __name__ == '__main__': diff --git a/test/meson.build b/test/meson.build index 63b94b1c7a..30a0461f0d 100644 --- a/test/meson.build +++ b/test/meson.build @@ -416,6 +416,7 @@ foreach test_number, dirname : integration_tests 'storage' : 'volatile', 'priority' : 0, 'firmware' : 'linux', + 'slow' : false, } # TODO: This fs.exists call isn't included in rebuild logic @@ -432,13 +433,22 @@ foreach test_number, dirname : integration_tests '--test-number', test_number, '--storage', test_params['storage'], '--firmware', test_params['firmware'], - '--', - ] + test_params['mkosi_args'] + ] + + if test_params['slow'] + args += ['--slow'] + endif + + args += ['--'] + test_params['mkosi_args'] integration_test_env = {} if want_integration_tests - integration_test_env = {'SYSTEMD_INTEGRATION_TESTS': '1'} + integration_test_env += {'SYSTEMD_INTEGRATION_TESTS': '1'} + endif + + if want_slow_tests + integration_test_env += {'SYSTEMD_SLOW_TESTS': '1'} endif # We don't explicitly depend on the "mkosi" target because that means the image is rebuilt