From 82693960784427ad8583e6a7570c2f4834a6d397 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 25 Jul 2025 02:46:07 +0900 Subject: [PATCH 1/6] integration-test: show journalctl command for showing saved journal when TEST_SAVE_JOURNAL=1 --- test/integration-tests/integration-test-wrapper.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/test/integration-tests/integration-test-wrapper.py b/test/integration-tests/integration-test-wrapper.py index 999fef9edc..34908e7ed9 100755 --- a/test/integration-tests/integration-test-wrapper.py +++ b/test/integration-tests/integration-test-wrapper.py @@ -660,7 +660,12 @@ def main() -> None: journal_file = Path(shutil.move(journal_file, dst)) if shell or (result.returncode in (args.exit_code, 77) and not coredumps and not sanitizer): - exit(0 if shell or result.returncode == args.exit_code else 77) + exit_code = 0 if shell or result.returncode == args.exit_code else 77 + exit_str = 'succeeded' if exit_code == 0 else 'skipped' + else: + # 0 also means we failed so translate that to a non-zero exit code to mark the test as failed. + exit_code = result.returncode or 1 + exit_str = 'failed' if journal_file.exists(): ops = [] @@ -678,10 +683,11 @@ def main() -> None: ops += [f'journalctl --file {journal_file} --no-hostname -o short-monotonic -u {args.unit} -p info'] - print(f'Test failed, relevant logs can be viewed with: \n\n{(" && ".join(ops))}\n', file=sys.stderr) + print( + f'Test {exit_str}, relevant logs can be viewed with: \n\n{(" && ".join(ops))}\n', file=sys.stderr + ) - # 0 also means we failed so translate that to a non-zero exit code to mark the test as failed. - exit(result.returncode or 1) + exit(exit_code) if __name__ == '__main__': From 122779536254469ce55a31139985c7e5be434d79 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 25 Jul 2025 02:50:06 +0900 Subject: [PATCH 2/6] integration-test: mention TEST_SAVE_JOURNAL in README --- test/integration-tests/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/integration-tests/README.md b/test/integration-tests/README.md index ea3f4f5efb..4fea50660f 100644 --- a/test/integration-tests/README.md +++ b/test/integration-tests/README.md @@ -133,6 +133,10 @@ that make use of `run_testcases`. `TEST_SKIP_TESTCASE=testcase`: takes a space separated list of testcases to skip. +`TEST_SAVE_JOURNAL=0|1|fail`: When `0`, journal file will be removed on exit. +When `1`, journal file will be saved at `$BUILD_DIR/test/journal`. When `fail`, +journal file will be saved only when the test is failed. Defaults to `fail`. + `TEST_JOURNAL_USE_TMP=1`: Write test journal to `/tmp` while the test is in progress and only move the journal to its final location in the build directory (`$BUILD_DIR/test/journal`) when the test is finished. From 62c3f42debde313f42cafbce52562aa00d9aaed9 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 25 Jul 2025 00:14:59 +0900 Subject: [PATCH 3/6] test: move testcase_dependencies() to TEST-10-MOUNT TEST-60-MOUNT_RATELIMIT is run on nspawn by default, and currently run on vm only on arch mkosi. Let's move the test case to new TEST-10-MOUNT, which always run on vm. --- .../TEST-10-MOUNT/meson.build | 8 + test/integration-tests/meson.build | 1 + test/units/TEST-10-MOUNT.sh | 161 ++++++++++++++++++ test/units/TEST-60-MOUNT-RATELIMIT.sh | 148 ---------------- 4 files changed, 170 insertions(+), 148 deletions(-) create mode 100644 test/integration-tests/TEST-10-MOUNT/meson.build create mode 100755 test/units/TEST-10-MOUNT.sh diff --git a/test/integration-tests/TEST-10-MOUNT/meson.build b/test/integration-tests/TEST-10-MOUNT/meson.build new file mode 100644 index 0000000000..77370ce458 --- /dev/null +++ b/test/integration-tests/TEST-10-MOUNT/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +integration_tests += [ + integration_test_template + { + 'name' : fs.name(meson.current_source_dir()), + 'vm' : true, + }, +] diff --git a/test/integration-tests/meson.build b/test/integration-tests/meson.build index 19cd5b5310..08eb724dd2 100644 --- a/test/integration-tests/meson.build +++ b/test/integration-tests/meson.build @@ -43,6 +43,7 @@ foreach dirname : [ 'TEST-07-PID1', 'TEST-08-INITRD', 'TEST-09-REBOOT', + 'TEST-10-MOUNT', 'TEST-13-NSPAWN', 'TEST-15-DROPIN', 'TEST-16-EXTEND-TIMEOUT', diff --git a/test/units/TEST-10-MOUNT.sh b/test/units/TEST-10-MOUNT.sh new file mode 100755 index 0000000000..eedbd43cd4 --- /dev/null +++ b/test/units/TEST-10-MOUNT.sh @@ -0,0 +1,161 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +# shellcheck source=test/units/test-control.sh +. "$(dirname "$0")"/test-control.sh +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +teardown_test_dependencies() ( + set +eux + + if mountpoint /tmp/deptest; then + umount /tmp/deptest + fi + + if [[ -n "${LOOP}" ]]; then + losetup -d "${LOOP}" || : + fi + if [[ -n "${LOOP_0}" ]]; then + losetup -d "${LOOP_0}" || : + fi + if [[ -n "${LOOP_1}" ]]; then + losetup -d "${LOOP_1}" || : + fi + + rm -f /tmp/TEST-60-MOUNT-RATELIMIT-dependencies-0.img + rm -f /tmp/TEST-60-MOUNT-RATELIMIT-dependencies-1.img + + rm -f /run/systemd/system/tmp-deptest.mount + systemctl daemon-reload + + return 0 +) + +setup_loop() { + truncate -s 30m "/tmp/TEST-60-MOUNT-RATELIMIT-dependencies-${1?}.img" + sfdisk --wipe=always "/tmp/TEST-60-MOUNT-RATELIMIT-dependencies-${1?}.img" </run/systemd/system/tmp-deptest.mount </run/systemd/system/tmp-deptest.mount < Date: Fri, 25 Jul 2025 00:17:08 +0900 Subject: [PATCH 4/6] TEST-60-MOUNT-RATELIMIT: use reload to make new config applied --- test/units/TEST-60-MOUNT-RATELIMIT.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/units/TEST-60-MOUNT-RATELIMIT.sh b/test/units/TEST-60-MOUNT-RATELIMIT.sh index 85f2c7912a..028ad7e3a2 100755 --- a/test/units/TEST-60-MOUNT-RATELIMIT.sh +++ b/test/units/TEST-60-MOUNT-RATELIMIT.sh @@ -176,7 +176,7 @@ cat >/run/systemd/journald.conf.d/99-ratelimit.conf < Date: Fri, 25 Jul 2025 01:17:44 +0900 Subject: [PATCH 5/6] TEST-10-MOUNT: make cleanup function run on error --- test/units/TEST-10-MOUNT.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/units/TEST-10-MOUNT.sh b/test/units/TEST-10-MOUNT.sh index eedbd43cd4..77b0520a96 100755 --- a/test/units/TEST-10-MOUNT.sh +++ b/test/units/TEST-10-MOUNT.sh @@ -128,7 +128,7 @@ testcase_dependencies() { return fi - trap teardown_test_dependencies RETURN + trap teardown_test_dependencies RETURN EXIT ERR INT TERM setup_loop 0 LOOP_0="${LOOP}" From 3f6e7ac27a4ed431c50577c7983a4043c8d853b0 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 25 Jul 2025 04:12:10 +0900 Subject: [PATCH 6/6] TEST-10-MOUNT: wait for userspace mount options being loaded When a device is mounted with userspace options such as _netdev, even when the mount event source is triggered, only /proc/self/mountinfo may be updated, and /run/mount/utab may not be updated yet. Hence, the mount unit may be created/updated without the userspace options. In that case, the mount event source will be retriggered when /run/mount/utab is updated, and the mount unit will be updated again with the userspace options. Typically, the window between the two calls is very short, but when the mount event source is ratelimited after the first event, processing the second event may be delayed about 1 secound. Hence, here we need to wait for a while. By adding a debugging logs in mount_setup_unit(), the userspace mount is not obtained in the first event, and the second event is delayed by the ratelimit. ``` [ 20.023086] H TEST-10-MOUNT.sh[446]: + mount -t ext4 -o _netdev /dev/loop1p1 /tmp/deptest [ 20.026255] H kernel: EXT4-fs (loop1p1): mounted filesystem c1fa00ea-2ba8-46b2-9002-2ac997f4cda9 r/w with ordered data mode. Quota mode: none. [ 20.026537] H TEST-10-MOUNT.sh[446]: + timeout 10 bash -c 'until systemctl -q is-active tmp-deptest.mount; do sleep .1; done' [ 20.032293] H systemd[1]: tmp-deptest.mount: mount_setup_unit: proc: yes, netdev: no [ 20.035978] H systemd[1]: Unit blockdev@dev-loop1p1.target has alias blockdev@.target. [ 20.039765] H systemd[1]: tmp-deptest.mount: Changed dead -> mounted [ 20.046598] H systemd[1]: Event source 0x7c73093e05e0 (mount-monitor-dispatch) entered rate limit state. ``` Hopefully fixes #32712. --- test/units/TEST-10-MOUNT.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/units/TEST-10-MOUNT.sh b/test/units/TEST-10-MOUNT.sh index 77b0520a96..b4c867918f 100755 --- a/test/units/TEST-10-MOUNT.sh +++ b/test/units/TEST-10-MOUNT.sh @@ -82,6 +82,14 @@ check_dependencies() { # mount LOOP_1 (using fake _netdev option) mount -t ext4 -o _netdev "${LOOP_1}p1" /tmp/deptest timeout 10 bash -c 'until systemctl -q is-active tmp-deptest.mount; do sleep .1; done' + # When a device is mounted with userspace options such as _netdev, even when the mount event source is + # triggered, only /proc/self/mountinfo may be updated, and /run/mount/utab may not be updated yet. + # Hence, the mount unit may be created/updated without the userspace options. In that case, the mount + # event source will be retriggered when /run/mount/utab is updated, and the mount unit will be updated + # again with the userspace options. Typically, the window between the two calls is very short, but when + # the mount event source is ratelimited after the first event, processing the second event may be delayed + # about 1 secound. Hence, here we need to wait for a while. + timeout 10 bash -c 'until systemctl show --property=After --value tmp-deptest.mount | grep -q -F remote-fs-pre.target; do sleep .1; done' after=$(systemctl show --property=After --value tmp-deptest.mount) assert_not_in "local-fs-pre.target" "$after" assert_in "remote-fs-pre.target" "$after"