2024-04-19 10:17:58 +03:00
#!/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 ( ) :
2024-04-24 22:18:27 +03:00
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 )
2024-04-19 10:17:58 +03:00
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 )
2024-04-30 20:06:00 +03:00
parser . add_argument ( ' --storage ' , required = True )
2024-04-19 10:17:58 +03:00
parser . add_argument ( ' mkosi_args ' , nargs = " * " )
args = parser . parse_args ( )
2024-05-03 11:57:50 +03:00
name = args . test_name + ( f " - { i } " if ( i := os . getenv ( " MESON_TEST_ITERATION " ) ) else " " )
2024-04-19 10:17:58 +03:00
test_unit = f " testsuite- { args . test_number } .service "
dropin = textwrap . dedent (
""" \
[ Unit ]
After = multi - user . target network . target
Requires = multi - user . target
2024-05-05 12:38:09 +03:00
SuccessAction = exit
SuccessActionExitStatus = 123
2024-04-19 10:17:58 +03:00
[ Service ]
StandardOutput = journal + console
"""
)
2024-05-03 11:27:58 +03:00
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 " ] }
"""
)
2024-04-19 10:17:58 +03:00
if not sys . stderr . isatty ( ) :
dropin + = textwrap . dedent (
"""
[ Unit ]
FailureAction = exit
"""
)
2024-05-03 11:57:50 +03:00
journal_file = ( args . meson_build_dir / ( f " test/journal/ { name } .journal " ) ) . absolute ( )
2024-04-19 10:17:58 +03:00
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 ) ,
2024-05-03 11:57:50 +03:00
' --machine ' , name ,
2024-04-19 10:17:58 +03:00
' --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 ) } " ,
2024-04-25 12:00:15 +03:00
' --runtime-network=none ' ,
2024-04-28 21:46:14 +03:00
' --runtime-scratch=no ' ,
2024-04-19 10:17:58 +03:00
' --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 } " ,
2024-04-28 23:51:41 +03:00
' systemd.mask=systemd-networkd-wait-online.service ' ,
2024-04-28 20:28:37 +03:00
* (
[
" systemd.mask=serial-getty@.service " ,
" systemd.show_status=no " ,
" systemd.crash_shell=0 " ,
2024-04-29 11:47:25 +03:00
" systemd.crash_action=poweroff " ,
2024-04-28 20:28:37 +03:00
]
if not sys . stderr . isatty ( )
else [ ]
) ,
2024-04-19 10:17:58 +03:00
] ) ,
2024-05-02 09:52:50 +03:00
' --credential ' , f " journal.storage= { ' persistent ' if sys . stderr . isatty ( ) else args . storage } " ,
2024-04-19 10:17:58 +03:00
* args . mkosi_args ,
' qemu ' ,
]
2024-04-23 16:13:22 +03:00
result = subprocess . run ( cmd )
# Return code 123 is the expected success code
2024-05-05 12:38:09 +03:00
if result . returncode != 123 :
2024-04-23 16:13:22 +03:00
if result . returncode != 77 and journal_file :
2024-04-19 10:17:58 +03:00
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 )
2024-04-23 16:13:22 +03:00
exit ( result . returncode or 1 )
2024-04-19 10:17:58 +03:00
# Do not keep journal files for tests that don't fail.
if journal_file :
journal_file . unlink ( missing_ok = True )
if __name__ == ' __main__ ' :
main ( )