mirror of
https://github.com/systemd/systemd.git
synced 2024-12-23 21:35:11 +03:00
d91bb1cbf0
When we want to get an interactive shell in a test that fails because of a race condition, we might need to run the test a few times with --repeat before it fails. However, currently, when -i is used, the VM needs to be shut down manually each time before the next run can start. Let's always shut down the VM if the test succeeds so that --repeat can be used with -i to run the test until it fails and then get an interactive shell in the VM.
167 lines
4.9 KiB
Python
Executable File
167 lines
4.9 KiB
Python
Executable File
#!/usr/bin/python3
|
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
|
|
|
'''Test wrapper command for driving integration tests.
|
|
|
|
Note: This is deliberately rough and only intended to drive existing tests
|
|
with the expectation that as part of formally defining the API it will be tidy.
|
|
|
|
'''
|
|
|
|
import argparse
|
|
import os
|
|
import shlex
|
|
import subprocess
|
|
import sys
|
|
import textwrap
|
|
from pathlib import Path
|
|
|
|
|
|
EMERGENCY_EXIT_DROPIN = """\
|
|
[Unit]
|
|
Wants=emergency-exit.service
|
|
"""
|
|
|
|
|
|
EMERGENCY_EXIT_SERVICE = """\
|
|
[Unit]
|
|
DefaultDependencies=no
|
|
Conflicts=shutdown.target
|
|
Conflicts=rescue.service
|
|
Before=shutdown.target
|
|
Before=rescue.service
|
|
FailureAction=exit
|
|
|
|
[Service]
|
|
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)
|
|
parser.add_argument('--test-name', required=True)
|
|
parser.add_argument('--test-number', required=True)
|
|
parser.add_argument('--storage', required=True)
|
|
parser.add_argument('mkosi_args', nargs="*")
|
|
args = parser.parse_args()
|
|
|
|
name = args.test_name + (f"-{i}" if (i := os.getenv("MESON_TEST_ITERATION")) else "")
|
|
test_unit = f"testsuite-{args.test_number}.service"
|
|
|
|
dropin = textwrap.dedent(
|
|
"""\
|
|
[Unit]
|
|
After=multi-user.target network.target
|
|
Requires=multi-user.target
|
|
SuccessAction=exit
|
|
SuccessActionExitStatus=123
|
|
|
|
[Service]
|
|
StandardOutput=journal+console
|
|
"""
|
|
)
|
|
|
|
if os.getenv("TEST_MATCH_SUBTEST"):
|
|
dropin += textwrap.dedent(
|
|
f"""
|
|
[Service]
|
|
Environment=TEST_MATCH_SUBTEST={os.environ["TEST_MATCH_SUBTEST"]}
|
|
"""
|
|
)
|
|
|
|
if os.getenv("TEST_MATCH_TESTCASE"):
|
|
dropin += textwrap.dedent(
|
|
f"""
|
|
[Service]
|
|
Environment=TEST_MATCH_TESTCASE={os.environ["TEST_MATCH_TESTCASE"]}
|
|
"""
|
|
)
|
|
|
|
if not sys.stderr.isatty():
|
|
dropin += textwrap.dedent(
|
|
"""
|
|
[Unit]
|
|
FailureAction=exit
|
|
"""
|
|
)
|
|
|
|
journal_file = (args.meson_build_dir / (f"test/journal/{name}.journal")).absolute()
|
|
journal_file.unlink(missing_ok=True)
|
|
else:
|
|
journal_file = None
|
|
|
|
cmd = [
|
|
'mkosi',
|
|
'--directory', os.fspath(args.meson_source_dir),
|
|
'--output-dir', os.fspath(args.meson_build_dir / 'mkosi.output'),
|
|
'--extra-search-path', os.fspath(args.meson_build_dir),
|
|
'--machine', name,
|
|
'--ephemeral',
|
|
*(['--forward-journal', journal_file] if journal_file else []),
|
|
*(
|
|
[
|
|
'--credential',
|
|
f"systemd.extra-unit.emergency-exit.service={shlex.quote(EMERGENCY_EXIT_SERVICE)}",
|
|
'--credential',
|
|
f"systemd.unit-dropin.emergency.target={shlex.quote(EMERGENCY_EXIT_DROPIN)}",
|
|
]
|
|
if not sys.stderr.isatty()
|
|
else []
|
|
),
|
|
'--credential',
|
|
f"systemd.unit-dropin.{test_unit}={shlex.quote(dropin)}",
|
|
'--runtime-network=none',
|
|
'--runtime-scratch=no',
|
|
'--append',
|
|
'--kernel-command-line-extra',
|
|
' '.join([
|
|
'systemd.hostname=H',
|
|
f"SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/testsuite-{args.test_number}.units:/usr/lib/systemd/tests/testdata/units:",
|
|
f"systemd.unit={test_unit}",
|
|
'systemd.mask=systemd-networkd-wait-online.service',
|
|
*(
|
|
[
|
|
"systemd.mask=serial-getty@.service",
|
|
"systemd.show_status=no",
|
|
"systemd.crash_shell=0",
|
|
"systemd.crash_action=poweroff",
|
|
]
|
|
if not sys.stderr.isatty()
|
|
else []
|
|
),
|
|
]),
|
|
'--credential', f"journal.storage={'persistent' if sys.stderr.isatty() else args.storage}",
|
|
*args.mkosi_args,
|
|
'qemu',
|
|
]
|
|
|
|
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.
|
|
if journal_file:
|
|
journal_file.unlink(missing_ok=True)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|