#####################################################################
+if get_option('integration-tests') != false
+ system_mkosi = custom_target('system_mkosi',
+ build_always_stale : true,
+ output : 'system',
+ console : true,
+ command : ['mkosi', '-C', meson.project_source_root(), '--image=system', '--format=disk', '--output-dir', meson.project_build_root() / '@OUTPUT@', '--without-tests', '-fi', 'build'],
+ depends : [executables_by_name['bootctl'], executables_by_name['systemd-measure'], executables_by_name['systemd-repart'], ukify],
+ )
+endif
+
+############################################################
+
subdir('rules.d')
subdir('test')
description : 'install test executables')
option('log-message-verification', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'do fake printf() calls to verify format strings')
+option('integration-tests', type : 'boolean', value : false,
+ description : 'run the integration tests')
option('ok-color', type : 'combo',
choices : ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan',
# SPDX-License-Identifier: LGPL-2.1-or-later
[Config]
-Images=system
+@Images=system
MinimumVersion=23~devel
[Output]
@Incremental=yes
@RuntimeSize=8G
@RuntimeBuildSources=yes
+@QemuSmp=2
ToolsTreePackages=virtiofsd
KernelCommandLineExtra=systemd.crash_shell
systemd.log_level=debug,console:info
[Content]
Autologin=yes
+ExtraTrees=
+ %D/mkosi.crt:/usr/lib/verity.d/mkosi.crt # sysext verification key
+
Packages=
acl
bash-completion
--- /dev/null
+[Service]
+PassEnvironment=SYSTEMD_UNIT_PATH
$ sudo make -C test/TEST-01-BASIC clean setup run
+To run the meson-based integration test config
+enable integration tests and options for required commands with the following:
+
+$ meson configure build -Dintegration-tests=true -Dremote=enabled -Dopenssl=enabled -Dblkid=enabled -Dtpm2=enabled
+
+Once enabled the integration tests can be run with:
+
+$ sudo meson test -C build/ --suite integration-tests --num-processes "$((nproc / 2))"
+
+As usual, specific tests can be run in meson by appending the name of the test
+which is usually the name of the directory e.g.
+
+$ sudo meson test -C build/ --suite integration-tests --num-processes "$((nproc / 2))" TEST-01-BASIC
+
+See `meson introspect build --tests` for a list of tests.
+
Specifying the build directory
==============================
--- /dev/null
+test_params += {
+ 'mkosi_args': test_params['mkosi_args'] + [
+ '--kernel-command-line-extra=' + ' '.join([
+ '''
+frobnicate!
+
+systemd.setenv=TEST_CMDLINE_NEWLINE=foo
+systemd.setenv=TEST_CMDLINE_NEWLINE=bar
+
+''',
+ ]),
+ ],
+}
--- /dev/null
+#!/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 logging
+import os
+from pathlib import Path
+import shlex
+import subprocess
+
+
+TEST_EXIT_DROPIN = """\
+[Unit]
+SuccessAction=exit
+FailureAction=exit
+"""
+
+
+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
+"""
+
+
+parser = argparse.ArgumentParser(description=__doc__)
+parser.add_argument('--test-name', required=True)
+parser.add_argument('--mkosi-image-name', required=True)
+parser.add_argument('--mkosi-output-path', required=True, type=Path)
+parser.add_argument('--test-number', required=True)
+parser.add_argument('--no-emergency-exit',
+ dest='emergency_exit', default=True, action='store_false',
+ help="Disable emergency exit drop-ins for interactive debugging")
+parser.add_argument('mkosi_args', nargs="*")
+
+def main():
+ logging.basicConfig(level=logging.DEBUG)
+ args = parser.parse_args()
+
+ test_unit_name = f"testsuite-{args.test_number}.service"
+ # Machine names shouldn't have / since it's used as a file name
+ # and it must be a valid hostname so 64 chars max
+ machine_name = args.test_name.replace('/', '_')[:64]
+
+ logging.debug(f"test name: {args.test_name}\n"
+ f"test number: {args.test_number}\n"
+ f"image: {args.mkosi_image_name}\n"
+ f"mkosi output path: {args.mkosi_output_path}\n"
+ f"mkosi args: {args.mkosi_args}\n"
+ f"emergency exit: {args.emergency_exit}")
+
+ journal_file = Path(f"{machine_name}.journal").absolute()
+ logging.info(f"Capturing journal to {journal_file}")
+
+ mkosi_args = [
+ 'mkosi',
+ '--directory', Path('..').resolve(),
+ '--output-dir', args.mkosi_output_path.absolute(),
+ '--machine', machine_name,
+ '--image', args.mkosi_image_name,
+ '--format=disk',
+ '--runtime-build-sources=no',
+ '--ephemeral',
+ '--forward-journal', journal_file,
+ *(
+ [
+ '--credential',
+ f"systemd.extra-unit.emergency-exit.service={shlex.quote(EMERGENCY_EXIT_SERVICE)} "
+ f"systemd.unit-dropin.emergency.target={shlex.quote(EMERGENCY_EXIT_DROPIN)}",
+ ]
+ if args.emergency_exit
+ else []
+ ),
+ f"--credential=systemd.unit-dropin.{test_unit_name}={shlex.quote(TEST_EXIT_DROPIN)}",
+ '--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:",
+ 'systemd.unit=testsuite.target',
+ f"systemd.wants={test_unit_name}",
+ ]),
+ *args.mkosi_args,
+ ]
+
+ mkosi_args += ['qemu']
+
+ logging.debug(f"Running {shlex.join(os.fspath(a) for a in mkosi_args)}")
+
+ try:
+ subprocess.run(mkosi_args, check=True)
+ except subprocess.CalledProcessError as e:
+ if e.returncode not in (0, 77):
+ suggested_command = [
+ 'journalctl',
+ '--all',
+ '--no-hostname',
+ '-o', 'short-monotonic',
+ '--file', journal_file,
+ f"_SYSTEMD_UNIT={test_unit_name}",
+ '+', f"SYSLOG_IDENTIFIER=testsuite-{args.test_number}.sh",
+ '+', 'PRIORITY=4',
+ '+', 'PRIORITY=3',
+ '+', 'PRIORITY=2',
+ '+', 'PRIORITY=1',
+ '+', 'PRIORITY=0',
+ ]
+ logging.info("Test failed, relevant logs can be viewed with: "
+ f"{shlex.join(os.fspath(a) for a in suggested_command)}")
+ exit(e.returncode)
+
+
+if __name__ == '__main__':
+ main()
depends : deps,
suite : 'kernel-install')
endif
+
+############################################################
+
+if get_option('integration-tests') != false
+ integration_test_wrapper = find_program('integration_test_wrapper.py')
+ integration_tests = {
+ '01': 'TEST-01-BASIC',
+ '02': 'TEST-02-UNITTESTS',
+ }
+ foreach test_number, dirname : integration_tests
+ test_unit_name = f'testsuite-@test_number@.service'
+ test_params = {
+ 'test_name' : dirname,
+ 'mkosi_image_name' : 'system',
+ 'mkosi_output_path' : system_mkosi,
+ 'test_number' : test_number,
+ 'mkosi_args' : [],
+ 'depends' : [system_mkosi],
+ 'timeout' : 600,
+ }
+
+ # TODO: This fs.exists call isn't included in rebuild logic
+ # so if you add a new meson.build in a subdir
+ # you need to touch another build file to get it to reparse.
+ if fs.exists(dirname / 'meson.build')
+ subdir(dirname)
+ endif
+ args = ['--test-name', test_params['test_name'],
+ '--mkosi-image-name', test_params['mkosi_image_name'],
+ '--mkosi-output-path', test_params['mkosi_output_path'],
+ '--test-number', test_params['test_number']]
+ args += ['--'] + test_params['mkosi_args']
+ test(test_params['test_name'],
+ integration_test_wrapper,
+ env: test_env,
+ args : args,
+ depends : test_params['depends'],
+ timeout : test_params['timeout'],
+ suite : 'integration-tests')
+ endforeach
+endif