diff options
Diffstat (limited to 'test')
14 files changed, 188 insertions, 26 deletions
diff --git a/test/integration-test-wrapper.py b/test/integration-test-wrapper.py index a3f90dc9fa..09dcda92e1 100755 --- a/test/integration-test-wrapper.py +++ b/test/integration-test-wrapper.py @@ -4,12 +4,15 @@ """Test wrapper command for driving integration tests.""" import argparse +import base64 +import dataclasses import json import os import re import shlex import subprocess import sys +import tempfile import textwrap from pathlib import Path @@ -33,6 +36,47 @@ ExecStart=false """ +def sandbox(args: argparse.Namespace) -> list[str]: + return [ + args.mkosi, + '--directory', os.fspath(args.meson_source_dir), + '--extra-search-path', os.fspath(args.meson_build_dir), + 'sandbox', + ] # fmt: skip + + +@dataclasses.dataclass(frozen=True) +class Summary: + distribution: str + release: str + architecture: str + builddir: Path + environment: dict[str, str] + + @classmethod + def get(cls, args: argparse.Namespace) -> 'Summary': + j = json.loads( + subprocess.run( + [ + args.mkosi, + '--directory', os.fspath(args.meson_source_dir), + '--json', + 'summary', + ], + stdout=subprocess.PIPE, + text=True, + ).stdout + ) # fmt: skip + + return Summary( + distribution=j['Images'][-1]['Distribution'], + release=j['Images'][-1]['Release'], + architecture=j['Images'][-1]['Architecture'], + builddir=Path(j['Images'][-1]['BuildDirectory']), + environment=j['Images'][-1]['Environment'], + ) + + def process_coredumps(args: argparse.Namespace, journal_file: Path) -> bool: # Collect executable paths of all coredumps and filter out the expected ones. @@ -42,11 +86,7 @@ def process_coredumps(args: argparse.Namespace, journal_file: Path) -> bool: exclude_regex = None result = subprocess.run( - [ - args.mkosi, - '--directory', os.fspath(args.meson_source_dir), - '--extra-search-path', os.fspath(args.meson_build_dir), - 'sandbox', + sandbox(args) + [ 'coredumpctl', '--file', journal_file, '--json=short', @@ -69,11 +109,7 @@ def process_coredumps(args: argparse.Namespace, journal_file: Path) -> bool: return False subprocess.run( - [ - args.mkosi, - '--directory', os.fspath(args.meson_source_dir), - '--extra-search-path', os.fspath(args.meson_build_dir), - 'sandbox', + sandbox(args) + [ 'coredumpctl', '--file', journal_file, '--no-pager', @@ -86,6 +122,119 @@ def process_coredumps(args: argparse.Namespace, journal_file: Path) -> bool: return True +def process_coverage(args: argparse.Namespace, summary: Summary, name: str, journal_file: Path) -> None: + coverage = subprocess.run( + sandbox(args) + [ + 'journalctl', + '--file', journal_file, + '--field=COVERAGE_TAR', + ], + stdout=subprocess.PIPE, + text=True, + check=True, + ).stdout # fmt: skip + + (args.meson_build_dir / 'test/coverage').mkdir(exist_ok=True) + + initial = args.meson_build_dir / 'test/coverage/initial.coverage-info' + output = args.meson_build_dir / f'test/coverage/{name}.coverage-info' + + for b64 in coverage.splitlines(): + tarball = base64.b64decode(b64) + + with tempfile.TemporaryDirectory(prefix='coverage-') as tmp: + subprocess.run( + sandbox(args) + [ + 'tar', + '--extract', + '--file', '-', + '--directory', tmp, + '--keep-directory-symlink', + '--no-overwrite-dir', + '--zstd', + ], + input=tarball, + check=True, + ) # fmt: skip + + for p in Path(tmp).iterdir(): + if not p.name.startswith('#'): + continue + + dst = Path(tmp) / p.name.replace('#', '/').lstrip('/') + dst.parent.mkdir(parents=True, exist_ok=True) + p.rename(dst) + + subprocess.run( + sandbox(args) + [ + 'find', + tmp, + '-name', '*.gcda', + '-size', '0', + '-delete', + ], + input=tarball, + check=True, + ) # fmt: skip + + subprocess.run( + sandbox(args) + + [ + 'rsync', + '--archive', + '--prune-empty-dirs', + '--include=*/', + '--include=*.gcno', + '--exclude=*', + f'{os.fspath(args.meson_build_dir / summary.builddir)}/', + os.fspath(Path(tmp) / 'work/build'), + ], + check=True, + ) + + subprocess.run( + sandbox(args) + + [ + 'lcov', + *( + [ + '--gcov-tool', 'llvm-cov', + '--gcov-tool', 'gcov', + ] + if summary.environment.get('LLVM', '0') == '1' + else [] + ), + '--directory', tmp, + '--base-directory', 'src/', + '--capture', + '--exclude', '*.gperf', + '--output-file', f'{output}.new', + '--ignore-errors', 'inconsistent,inconsistent,source,negative', + '--substitute', 's#src/src#src#g', + '--no-external', + '--quiet', + ], + check=True, + ) # fmt: skip + + subprocess.run( + sandbox(args) + + [ + 'lcov', + '--ignore-errors', 'inconsistent,inconsistent,format,corrupt,empty', + '--add-tracefile', output if output.exists() else initial, + '--add-tracefile', f'{output}.new', + '--output-file', output, + '--quiet', + ], + check=True, + ) # fmt: skip + + Path(f'{output}.new').unlink() + + print(f'Wrote coverage report for {name} to {output}', file=sys.stderr) + + def main() -> None: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('--mkosi', required=True) @@ -127,6 +276,7 @@ def main() -> None: keep_journal = os.getenv('TEST_SAVE_JOURNAL', 'fail') shell = bool(int(os.getenv('TEST_SHELL', '0'))) + summary = Summary.get(args) if shell and not sys.stderr.isatty(): print( @@ -250,6 +400,13 @@ def main() -> None: coredumps = process_coredumps(args, journal_file) + if ( + summary.environment.get('COVERAGE', '0') == '1' + and result.returncode in (args.exit_code, 77) + and not coredumps + ): + process_coverage(args, summary, name, journal_file) + if keep_journal == '0' or ( keep_journal == 'fail' and result.returncode in (args.exit_code, 77) and not coredumps ): @@ -262,22 +419,11 @@ def main() -> None: if os.getenv('GITHUB_ACTIONS'): id = os.environ['GITHUB_RUN_ID'] + workflow = os.environ['GITHUB_WORKFLOW'] iteration = os.environ['GITHUB_RUN_ATTEMPT'] - j = json.loads( - subprocess.run( - [ - args.mkosi, - '--directory', os.fspath(args.meson_source_dir), - '--json', - 'summary', - ], - stdout=subprocess.PIPE, - text=True, - ).stdout - ) # fmt: skip - distribution = j['Images'][-1]['Distribution'] - release = j['Images'][-1]['Release'] - artifact = f'ci-mkosi-{id}-{iteration}-{distribution}-{release}-failed-test-journals' + artifact = ( + f'ci-{workflow}-{id}-{iteration}-{summary.distribution}-{summary.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') diff --git a/test/test-execute/exec-ambientcapabilities-dynuser.service b/test/test-execute/exec-ambientcapabilities-dynuser.service index ab815f39a3..b927c7dbca 100644 --- a/test/test-execute/exec-ambientcapabilities-dynuser.service +++ b/test/test-execute/exec-ambientcapabilities-dynuser.service @@ -9,3 +9,4 @@ AmbientCapabilities=CAP_CHOWN CAP_SETUID CAP_NET_RAW DynamicUser=yes PrivateUsers=yes EnvironmentFile=-/usr/lib/systemd/systemd-asan-env +ReadWritePaths=-/coverage diff --git a/test/test-execute/exec-dynamicuser-fixeduser-adm.service b/test/test-execute/exec-dynamicuser-fixeduser-adm.service index 1b7f232cd1..3a7f8aef60 100644 --- a/test/test-execute/exec-dynamicuser-fixeduser-adm.service +++ b/test/test-execute/exec-dynamicuser-fixeduser-adm.service @@ -10,3 +10,4 @@ ExecStart=sh -x -c 'test "$$(id -nG)" = "adm" && test "$$(id -ng)" = "adm" && te ExecStart=sh -x -c 'test "$$(id -nG)" = "adm" && test "$$(id -ng)" = "adm" && test "$$(id -nu)" = "adm"' DynamicUser=yes User=adm +ReadWritePaths=-/coverage diff --git a/test/test-execute/exec-dynamicuser-fixeduser-games.service b/test/test-execute/exec-dynamicuser-fixeduser-games.service index b13c23a74d..40048d27a8 100644 --- a/test/test-execute/exec-dynamicuser-fixeduser-games.service +++ b/test/test-execute/exec-dynamicuser-fixeduser-games.service @@ -10,3 +10,4 @@ ExecStart=sh -x -c 'test "$$(id -nG)" = "games" && test "$$(id -ng)" = "games" & ExecStart=sh -x -c 'test "$$(id -nG)" = "games" && test "$$(id -ng)" = "games" && test "$$(id -nu)" = "games"' DynamicUser=yes User=games +ReadWritePaths=-/coverage diff --git a/test/test-execute/exec-dynamicuser-fixeduser-one-supplementarygroup.service b/test/test-execute/exec-dynamicuser-fixeduser-one-supplementarygroup.service index e494c33551..e58b524033 100644 --- a/test/test-execute/exec-dynamicuser-fixeduser-one-supplementarygroup.service +++ b/test/test-execute/exec-dynamicuser-fixeduser-one-supplementarygroup.service @@ -9,3 +9,4 @@ Type=oneshot User=1 DynamicUser=yes SupplementaryGroups=1 +ReadWritePaths=-/coverage diff --git a/test/test-execute/exec-dynamicuser-fixeduser.service b/test/test-execute/exec-dynamicuser-fixeduser.service index 4ebfc20cde..8e5244d891 100644 --- a/test/test-execute/exec-dynamicuser-fixeduser.service +++ b/test/test-execute/exec-dynamicuser-fixeduser.service @@ -8,3 +8,4 @@ ExecStart=sh -x -c 'test "$$(id -g)" = "1" && test "$$(id -u)" = "1"' Type=oneshot User=1 DynamicUser=yes +ReadWritePaths=-/coverage diff --git a/test/test-execute/exec-dynamicuser-runtimedirectory1.service b/test/test-execute/exec-dynamicuser-runtimedirectory1.service index 59d3bf0884..671b316736 100644 --- a/test/test-execute/exec-dynamicuser-runtimedirectory1.service +++ b/test/test-execute/exec-dynamicuser-runtimedirectory1.service @@ -11,3 +11,4 @@ RuntimeDirectory=test-exec_runtimedirectorypreserve RuntimeDirectoryPreserve=yes DynamicUser=yes EnvironmentFile=-/usr/lib/systemd/systemd-asan-env +ReadWritePaths=-/coverage diff --git a/test/test-execute/exec-dynamicuser-runtimedirectory2.service b/test/test-execute/exec-dynamicuser-runtimedirectory2.service index 6ff9d7503a..cdb80848e3 100644 --- a/test/test-execute/exec-dynamicuser-runtimedirectory2.service +++ b/test/test-execute/exec-dynamicuser-runtimedirectory2.service @@ -12,3 +12,4 @@ RuntimeDirectory=test-exec_runtimedirectorypreserve RuntimeDirectoryPreserve=yes DynamicUser=yes EnvironmentFile=-/usr/lib/systemd/systemd-asan-env +ReadWritePaths=-/coverage diff --git a/test/test-execute/exec-dynamicuser-runtimedirectory3.service b/test/test-execute/exec-dynamicuser-runtimedirectory3.service index cebb819476..51a9e44c6f 100644 --- a/test/test-execute/exec-dynamicuser-runtimedirectory3.service +++ b/test/test-execute/exec-dynamicuser-runtimedirectory3.service @@ -11,3 +11,4 @@ Type=oneshot RuntimeDirectory=test-exec_runtimedirectorypreserve DynamicUser=yes EnvironmentFile=-/usr/lib/systemd/systemd-asan-env +ReadWritePaths=-/coverage diff --git a/test/test-execute/exec-dynamicuser-statedir-migrate-step2.service b/test/test-execute/exec-dynamicuser-statedir-migrate-step2.service index 7261f4a174..f22862378c 100644 --- a/test/test-execute/exec-dynamicuser-statedir-migrate-step2.service +++ b/test/test-execute/exec-dynamicuser-statedir-migrate-step2.service @@ -25,3 +25,4 @@ Type=oneshot DynamicUser=yes StateDirectory=test-dynamicuser-migrate test-dynamicuser-migrate2/hoge EnvironmentFile=-/usr/lib/systemd/systemd-asan-env +ReadWritePaths=-/coverage diff --git a/test/test-execute/exec-dynamicuser-statedir.service b/test/test-execute/exec-dynamicuser-statedir.service index 636a70259c..1e4fe818ac 100644 --- a/test/test-execute/exec-dynamicuser-statedir.service +++ b/test/test-execute/exec-dynamicuser-statedir.service @@ -84,3 +84,4 @@ Type=oneshot DynamicUser=yes StateDirectory=waldo quux/pief aaa/bbb aaa aaa/ccc xxx/yyy:aaa/111 xxx:aaa/222 xxx/zzz:aaa/333 abc:d\:ef EnvironmentFile=-/usr/lib/systemd/systemd-asan-env +ReadWritePaths=-/coverage diff --git a/test/test-execute/exec-dynamicuser-supplementarygroups.service b/test/test-execute/exec-dynamicuser-supplementarygroups.service index be1b8f76f2..fd88a790e4 100644 --- a/test/test-execute/exec-dynamicuser-supplementarygroups.service +++ b/test/test-execute/exec-dynamicuser-supplementarygroups.service @@ -9,3 +9,4 @@ Type=oneshot DynamicUser=yes SupplementaryGroups=1 2 EnvironmentFile=-/usr/lib/systemd/systemd-asan-env +ReadWritePaths=-/coverage diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 215f3cb1cc..1fd1b2290f 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -8655,7 +8655,7 @@ if __name__ == '__main__': asan_options = ns.asan_options lsan_options = ns.lsan_options ubsan_options = ns.ubsan_options - with_coverage = ns.with_coverage + with_coverage = ns.with_coverage or "COVERAGE_BUILD_DIR" in os.environ show_journal = ns.show_journal if use_valgrind: diff --git a/test/units/TEST-38-FREEZER.sh b/test/units/TEST-38-FREEZER.sh index 07597843e2..4c483df46a 100755 --- a/test/units/TEST-38-FREEZER.sh +++ b/test/units/TEST-38-FREEZER.sh @@ -7,6 +7,11 @@ set -o pipefail # shellcheck source=test/units/test-control.sh . "$(dirname "$0")"/test-control.sh +if [[ -n "${COVERAGE_BUILD_DIR:-}" ]]; then + echo "TEST-38-FREEZER freezes when systemd is built with coverage enabled" >/skipped + exit 77 +fi + systemd-analyze log-level debug unit=TEST-38-FREEZER-sleep.service |