]> git.ipfire.org Git - thirdparty/systemd.git/blame - test/integration-test-wrapper.py
test: Ignore configure scripts in minimal images
[thirdparty/systemd.git] / test / integration-test-wrapper.py
CommitLineData
b85e5496
DDM
1#!/usr/bin/python3
2# SPDX-License-Identifier: LGPL-2.1-or-later
3
4'''Test wrapper command for driving integration tests.
5
6Note: This is deliberately rough and only intended to drive existing tests
7with the expectation that as part of formally defining the API it will be tidy.
8
9'''
10
11import argparse
a77f65d0 12import json
b85e5496
DDM
13import os
14import shlex
15import subprocess
16import sys
17import textwrap
18from pathlib import Path
19
20
21EMERGENCY_EXIT_DROPIN = """\
22[Unit]
23Wants=emergency-exit.service
24"""
25
26
27EMERGENCY_EXIT_SERVICE = """\
28[Unit]
29DefaultDependencies=no
30Conflicts=shutdown.target
31Conflicts=rescue.service
32Before=shutdown.target
33Before=rescue.service
34FailureAction=exit
35
36[Service]
37ExecStart=false
38"""
39
40
41def main():
42 parser = argparse.ArgumentParser(description=__doc__)
43 parser.add_argument('--meson-source-dir', required=True, type=Path)
44 parser.add_argument('--meson-build-dir', required=True, type=Path)
f483e083 45 parser.add_argument('--name', required=True)
348f5017 46 parser.add_argument('--unit', required=True)
9a69900a 47 parser.add_argument('--storage', required=True)
1f2c9bda 48 parser.add_argument('--firmware', required=True)
f1f87f3b 49 parser.add_argument('--slow', action=argparse.BooleanOptionalAction)
b85e5496
DDM
50 parser.add_argument('mkosi_args', nargs="*")
51 args = parser.parse_args()
52
f1f87f3b 53 if not bool(int(os.getenv("SYSTEMD_INTEGRATION_TESTS", "0"))):
f483e083 54 print(f"SYSTEMD_INTEGRATION_TESTS=1 not found in environment, skipping {args.name}", file=sys.stderr)
f1f87f3b
DDM
55 exit(77)
56
57 if args.slow and not bool(int(os.getenv("SYSTEMD_SLOW_TESTS", "0"))):
f483e083 58 print(f"SYSTEMD_SLOW_TESTS=1 not found in environment, skipping {args.name}", file=sys.stderr)
f1f87f3b
DDM
59 exit(77)
60
f483e083 61 name = args.name + (f"-{i}" if (i := os.getenv("MESON_TEST_ITERATION")) else "")
b85e5496
DDM
62
63 dropin = textwrap.dedent(
64 """\
65 [Unit]
d91bb1cb
DDM
66 SuccessAction=exit
67 SuccessActionExitStatus=123
b85e5496
DDM
68
69 [Service]
70 StandardOutput=journal+console
71 """
72 )
73
3cb61e0d
DDM
74 if os.getenv("TEST_MATCH_SUBTEST"):
75 dropin += textwrap.dedent(
76 f"""
77 [Service]
78 Environment=TEST_MATCH_SUBTEST={os.environ["TEST_MATCH_SUBTEST"]}
79 """
80 )
81
82 if os.getenv("TEST_MATCH_TESTCASE"):
83 dropin += textwrap.dedent(
84 f"""
85 [Service]
86 Environment=TEST_MATCH_TESTCASE={os.environ["TEST_MATCH_TESTCASE"]}
87 """
88 )
89
b85e5496
DDM
90 if not sys.stderr.isatty():
91 dropin += textwrap.dedent(
92 """
93 [Unit]
b85e5496
DDM
94 FailureAction=exit
95 """
96 )
97
0596237e 98 journal_file = (args.meson_build_dir / (f"test/journal/{name}.journal")).absolute()
b85e5496
DDM
99 journal_file.unlink(missing_ok=True)
100 else:
101 journal_file = None
102
103 cmd = [
104 'mkosi',
105 '--directory', os.fspath(args.meson_source_dir),
106 '--output-dir', os.fspath(args.meson_build_dir / 'mkosi.output'),
107 '--extra-search-path', os.fspath(args.meson_build_dir),
0596237e 108 '--machine', name,
b85e5496
DDM
109 '--ephemeral',
110 *(['--forward-journal', journal_file] if journal_file else []),
111 *(
112 [
113 '--credential',
114 f"systemd.extra-unit.emergency-exit.service={shlex.quote(EMERGENCY_EXIT_SERVICE)}",
115 '--credential',
116 f"systemd.unit-dropin.emergency.target={shlex.quote(EMERGENCY_EXIT_DROPIN)}",
b85e5496
DDM
117 ]
118 if not sys.stderr.isatty()
119 else []
120 ),
121 '--credential',
348f5017 122 f"systemd.unit-dropin.{args.unit}={shlex.quote(dropin)}",
ab5f60cb 123 '--runtime-network=none',
d99deaaa 124 '--runtime-scratch=no',
eb4c962a 125 *args.mkosi_args,
b85e5496 126 '--append',
1f2c9bda 127 '--qemu-firmware', args.firmware,
b85e5496
DDM
128 '--kernel-command-line-extra',
129 ' '.join([
130 'systemd.hostname=H',
f483e083 131 f"SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/{args.name}.units:/usr/lib/systemd/tests/testdata/units:",
348f5017 132 f"systemd.unit={args.unit}",
e911a335 133 'systemd.mask=systemd-networkd-wait-online.service',
06489e83
DDM
134 *(
135 [
136 "systemd.mask=serial-getty@.service",
137 "systemd.show_status=no",
138 "systemd.crash_shell=0",
27f166c5 139 "systemd.crash_action=poweroff",
06489e83
DDM
140 ]
141 if not sys.stderr.isatty()
142 else []
143 ),
b85e5496 144 ]),
bdade5f5 145 '--credential', f"journal.storage={'persistent' if sys.stderr.isatty() else args.storage}",
b85e5496
DDM
146 'qemu',
147 ]
148
2fd84901 149 result = subprocess.run(cmd)
ca2e19f2 150
2fd84901 151 # Return code 123 is the expected success code
ca2e19f2
DDM
152 if result.returncode in (123, 77):
153 # Do not keep journal files for tests that don't fail.
154 if journal_file:
155 journal_file.unlink(missing_ok=True)
156
157 exit(0 if result.returncode == 123 else 77)
b85e5496 158
b85e5496 159 if journal_file:
a77f65d0
DDM
160 ops = []
161
162 if os.getenv("GITHUB_ACTIONS"):
163 id = os.environ["GITHUB_RUN_ID"]
164 iteration = os.environ["GITHUB_RUN_ATTEMPT"]
165 j = json.loads(
166 subprocess.run(
167 [
168 "mkosi",
169 "--directory", os.fspath(args.meson_source_dir),
170 "--json",
171 "summary",
172 ],
173 stdout=subprocess.PIPE,
174 text=True,
175 ).stdout
176 )
177 images = {image["Image"]: image for image in j["Images"]}
178 distribution = images["system"]["Distribution"]
179 release = images["system"]["Release"]
180 artifact = f"ci-mkosi-{id}-{iteration}-{distribution}-{release}-failed-test-journals"
181 ops += [f"gh run download {id} --name {artifact} -D ci/{artifact}"]
182 journal_file = Path(f"ci/{artifact}/test/journal/{name}.journal")
183
348f5017 184 ops += [f"journalctl --file {journal_file} --no-hostname -o short-monotonic -u {args.unit} -p info"]
a77f65d0 185
ca2e19f2 186 print("Test failed, relevant logs can be viewed with: \n\n"
a77f65d0 187 f"{(' && '.join(ops))}\n", file=sys.stderr)
ca2e19f2
DDM
188
189 # 0 also means we failed so translate that to a non-zero exit code to mark the test as failed.
190 exit(result.returncode or 1)
b85e5496
DDM
191
192
193if __name__ == '__main__':
194 main()