From 7d7a3d47871b0be4864363e5064e98360cba5e2b Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Mon, 8 Apr 2024 20:32:10 +0200 Subject: [PATCH 1/8] test: handle NULL correctly when passed to ASSERT_EQ() strcmp() doesn't handle NULLs nicely, so switch to streq_ptr(). --- src/shared/tests.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/shared/tests.h b/src/shared/tests.h index 85f9463f9b..a8fa4a6622 100644 --- a/src/shared/tests.h +++ b/src/shared/tests.h @@ -249,11 +249,10 @@ static inline int run_test_table(void) { #define ASSERT_STREQ(expr1, expr2) \ ({ \ - const char* _expr1 = (expr1); \ - const char* _expr2 = (expr2); \ - if (strcmp(_expr1, _expr2) != 0) { \ + const char *_expr1 = (expr1), *_expr2 = (expr2); \ + if (!streq_ptr(_expr1, _expr2)) { \ log_error("%s:%i: Assertion failed: expected \"%s == %s\", but \"%s != %s\"", \ - PROJECT_FILE, __LINE__, #expr1, #expr2, _expr1, _expr2); \ + PROJECT_FILE, __LINE__, #expr1, #expr2, strnull(_expr1), strnull(_expr2)); \ abort(); \ } \ }) From 8c33218e9ef78e6501ebb103c6265e167d1d84ce Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Mon, 8 Apr 2024 18:17:48 +0200 Subject: [PATCH 2/8] test: add a couple of sanity tests for ASSERT_*() macros Also, introduce ASSERT_SIGNAL() macro that should help us test failing cases of mentioned macros (which usually end with calling abort()). --- src/shared/tests.h | 27 ++++++++++++++++++++ src/test/test-macro.c | 57 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/src/shared/tests.h b/src/shared/tests.h index a8fa4a6622..07e05bf267 100644 --- a/src/shared/tests.h +++ b/src/shared/tests.h @@ -2,11 +2,15 @@ #pragma once #include +#include #include "sd-daemon.h" #include "argv-util.h" #include "macro.h" +#include "process-util.h" +#include "rlimit-util.h" +#include "signal-util.h" #include "static-destruct.h" #include "strv.h" @@ -349,3 +353,26 @@ static inline int run_test_table(void) { abort(); \ } \ }) + +#define ASSERT_SIGNAL(expr, signal) \ + ({ \ + ASSERT_TRUE(SIGNAL_VALID(signal)); \ + siginfo_t _siginfo = {}; \ + int _pid = fork(); \ + ASSERT_OK(_pid); \ + if (_pid == 0) { \ + /* Speed things up by never even attempting to generate a coredump */ \ + (void) prctl(PR_SET_DUMPABLE, 0); \ + /* But still set an rlimit just in case */ \ + (void) setrlimit(RLIMIT_CORE, &RLIMIT_MAKE_CONST(0)); \ + expr; \ + _exit(EXIT_SUCCESS); \ + } \ + (void) wait_for_terminate(_pid, &_siginfo); \ + if (_siginfo.si_status != signal) { \ + log_error("%s:%i: Assertion failed: \"%s\" died with signal %s, but %s was expected", \ + PROJECT_FILE, __LINE__, #expr, signal_to_string(_siginfo.si_status), \ + signal_to_string(signal)); \ + abort(); \ + } \ + }) diff --git a/src/test/test-macro.c b/src/test/test-macro.c index 78503a5660..017a20f540 100644 --- a/src/test/test-macro.c +++ b/src/test/test-macro.c @@ -1105,4 +1105,61 @@ TEST(u64_multiply_safe) { assert_se(u64_multiply_safe(UINT64_MAX, UINT64_MAX) == 0); } +TEST(ASSERT) { + char *null = NULL; + + ASSERT_OK(0); + ASSERT_OK(255); + ASSERT_OK(printf("Hello world\n")); + ASSERT_SIGNAL(ASSERT_OK(-1), SIGABRT); + ASSERT_SIGNAL(ASSERT_OK(-ENOANO), SIGABRT); + + ASSERT_TRUE(true); + ASSERT_TRUE(255); + ASSERT_TRUE(getpid()); + ASSERT_SIGNAL(ASSERT_TRUE(1 == 0), SIGABRT); + + ASSERT_FALSE(false); + ASSERT_FALSE(1 == 0); + ASSERT_SIGNAL(ASSERT_FALSE(1 > 0), SIGABRT); + + ASSERT_NULL(NULL); + ASSERT_SIGNAL(ASSERT_NULL(signal_to_string(SIGINT)), SIGABRT); + + ASSERT_NOT_NULL(signal_to_string(SIGTERM)); + ASSERT_SIGNAL(ASSERT_NOT_NULL(NULL), SIGABRT); + + ASSERT_STREQ(NULL, null); + ASSERT_STREQ("foo", "foo"); + ASSERT_SIGNAL(ASSERT_STREQ(null, "bar"), SIGABRT); + ASSERT_SIGNAL(ASSERT_STREQ("foo", "bar"), SIGABRT); + + ASSERT_EQ(0, 0); + ASSERT_EQ(-1, -1); + ASSERT_SIGNAL(ASSERT_EQ(255, -1), SIGABRT); + + ASSERT_GE(0, 0); + ASSERT_GE(1, -1); + ASSERT_SIGNAL(ASSERT_GE(-1, 1), SIGABRT); + + ASSERT_LE(0, 0); + ASSERT_LE(-1, 1); + ASSERT_SIGNAL(ASSERT_LE(1, -1), SIGABRT); + + ASSERT_NE(0, (int64_t) UINT_MAX); + ASSERT_NE(-1, 1); + ASSERT_SIGNAL(ASSERT_NE(0, 0), SIGABRT); + ASSERT_SIGNAL(ASSERT_NE(-1, -1), SIGABRT); + + ASSERT_GT(1, 0); + ASSERT_GT(1, -1); + ASSERT_SIGNAL(ASSERT_GT(0, 0), SIGABRT); + ASSERT_SIGNAL(ASSERT_GT(-1, 1), SIGABRT); + + ASSERT_LT(0, 1); + ASSERT_LT(-1, 1); + ASSERT_SIGNAL(ASSERT_LT(0, 0), SIGABRT); + ASSERT_SIGNAL(ASSERT_LT(1, -1), SIGABRT); +} + DEFINE_TEST_MAIN(LOG_INFO); From d4e11f30bcfb9c781c7c4e4415b5542347cdeada Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Tue, 9 Apr 2024 14:04:28 +0200 Subject: [PATCH 3/8] test: forward journal to console in TEST-24-CRYPTSETUP If we fail to mount the encrypted /var during boot we're left with nothing to debug, so let's do the same thing we do for TEST-08-INITRD and forward journal to the console. --- test/TEST-24-CRYPTSETUP/test.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/TEST-24-CRYPTSETUP/test.sh b/test/TEST-24-CRYPTSETUP/test.sh index 757c39a449..93b447f583 100755 --- a/test/TEST-24-CRYPTSETUP/test.sh +++ b/test/TEST-24-CRYPTSETUP/test.sh @@ -18,6 +18,9 @@ KERNEL_OPTIONS=( "luks.name=$PART_UUID=$DM_NAME" "luks.key=$PART_UUID=/keyfile:LABEL=varcrypt_keydev" "luks.options=$PART_UUID=x-initrd.attach" + # Forward journal to console to make debugging easier (or possible at all) if we fail to bring the + # encrypted /var up during boot + "systemd.journald.forward_to_console=1" ) KERNEL_APPEND+=" ${KERNEL_OPTIONS[*]}" QEMU_OPTIONS+=" -drive format=raw,cache=unsafe,file=${STATEDIR:?}/keydev.img" From bfd30e8af64a0d39ad110258dca69be919f6df34 Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Tue, 9 Apr 2024 17:04:50 +0200 Subject: [PATCH 4/8] test: let curl show a potential error in silent mode I collected a couple of fails in this particular test, but without any output they're impossible to debug. Let's make this slightly less annoying and let curl show an error (if any) even in silent mode. This patch uncovers that curl has been (silently) complaining about not being able to write to the output destination, because `grep -q` short-circuits on the first match and doesn't bother reading the rest, so replace `grep -q` with `grep ... >/dev/null` to force grep to always read the whole thing from curl. --- test/units/testsuite-04.journal-gatewayd.sh | 74 ++++++++++----------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/test/units/testsuite-04.journal-gatewayd.sh b/test/units/testsuite-04.journal-gatewayd.sh index a58d76e2ee..4bd3c79a4d 100755 --- a/test/units/testsuite-04.journal-gatewayd.sh +++ b/test/units/testsuite-04.journal-gatewayd.sh @@ -27,37 +27,37 @@ systemctl start systemd-journal-gatewayd.socket # /browse # We should get redirected to /browse by default -curl -Lfs http://localhost:19531 | grep -qF "Journal" -curl -Lfs http://localhost:19531/browse | grep -qF "Journal" -(! curl -Lfs http://localhost:19531/foo/bar/baz) -(! curl -Lfs http://localhost:19531/foo/../../../bar/../baz) +curl -LSfs http://localhost:19531 | grep -F "Journal" >/dev/null +curl -LSfs http://localhost:19531/browse | grep -F "Journal" >/dev/null +(! curl -LSfs http://localhost:19531/foo/bar/baz) +(! curl -LSfs http://localhost:19531/foo/../../../bar/../baz) # /entries # Accept: text/plain should be the default -curl -Lfs http://localhost:19531/entries | \ - grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" -curl -Lfs --header "Accept: text/plain" http://localhost:19531/entries | \ - grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" -curl -Lfs --header "Accept: application/json" http://localhost:19531/entries | \ +curl -LSfs http://localhost:19531/entries | \ + grep -E " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" >/dev/null +curl -LSfs --header "Accept: text/plain" http://localhost:19531/entries | \ + grep -E " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" >/dev/null +curl -LSfs --header "Accept: application/json" http://localhost:19531/entries | \ jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" -curl -Lfs --header "Accept: application/json" http://localhost:19531/entries?boot | \ +curl -LSfs --header "Accept: application/json" http://localhost:19531/entries?boot | \ jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" -curl -Lfs --header "Accept: application/json" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \ +curl -LSfs --header "Accept: application/json" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \ jq -se "length == 1 and select(.[].MESSAGE == \"$TEST_MESSAGE\")" # Show 10 entries starting from $BOOT_CURSOR, skip the first 5 -curl -Lfs --header "Accept: application/json" --header "Range: entries=$BOOT_CURSOR:5:10" http://localhost:19531/entries | \ +curl -LSfs --header "Accept: application/json" --header "Range: entries=$BOOT_CURSOR:5:10" http://localhost:19531/entries | \ jq -se "length == 10" # Check if the specified cursor refers to an existing entry and return just that entry -curl -Lfs --header "Accept: application/json" --header "Range: entries=$TEST_CURSOR" http://localhost:19531/entries?discrete | \ +curl -LSfs --header "Accept: application/json" --header "Range: entries=$TEST_CURSOR" http://localhost:19531/entries?discrete | \ jq -se "length == 1 and select(.[].MESSAGE == \"$TEST_MESSAGE\")" # Check entry is present (resp. absent) when filtering by timestamp -curl -Lfs --header "Range: realtime=$BEFORE_TIMESTAMP:" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \ - grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" -curl -Lfs --header "Range: realtime=:$AFTER_TIMESTAMP" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \ - grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" -curl -Lfs --header "Accept: application/json" --header "Range: realtime=:$BEFORE_TIMESTAMP" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \ +curl -LSfs --header "Range: realtime=$BEFORE_TIMESTAMP:" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \ + grep -E " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" >/dev/null +curl -LSfs --header "Range: realtime=:$AFTER_TIMESTAMP" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \ + grep -E " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" >/dev/null +curl -LSfs --header "Accept: application/json" --header "Range: realtime=:$BEFORE_TIMESTAMP" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \ jq -se "length == 0" -curl -Lfs --header "Accept: application/json" --header "Range: realtime=$AFTER_TIMESTAMP:" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \ +curl -LSfs --header "Accept: application/json" --header "Range: realtime=$AFTER_TIMESTAMP:" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \ jq -se "length == 0" # Check positive and negative skip when filtering by timestamp echo "-= This is a second test message =-" | systemd-cat -t "$TEST_TAG" @@ -67,18 +67,18 @@ echo "-= This is a third test message =-" | systemd-cat -t "$TEST_TAG" journalctl --sync sleep 1 END_TIMESTAMP="$(date +%s)" -curl -Lfs --header "Accept: application/json" --header "Range: realtime=$BEFORE_TIMESTAMP::1:1" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \ +curl -LSfs --header "Accept: application/json" --header "Range: realtime=$BEFORE_TIMESTAMP::1:1" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \ jq -se "length == 1 and select(.[].__CURSOR == \"$TEST2_CURSOR\")" -curl -Lfs --header "Accept: application/json" --header "Range: realtime=$END_TIMESTAMP::-1:1" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \ +curl -LSfs --header "Accept: application/json" --header "Range: realtime=$END_TIMESTAMP::-1:1" http://localhost:19531/entries?SYSLOG_IDENTIFIER="$TEST_TAG" | \ jq -se "length == 1 and select(.[].__CURSOR == \"$TEST2_CURSOR\")" # No idea how to properly parse this (jq won't cut it), so let's at least do some sanity checks that every # line is either empty or begins with data: -curl -Lfs --header "Accept: text/event-stream" http://localhost:19531/entries | \ +curl -LSfs --header "Accept: text/event-stream" http://localhost:19531/entries | \ awk '!/^(data: \{.+\}|)$/ { exit 1; }' # Same thing as journalctl --output=export mkdir /tmp/remote-journal -curl -Lfs --header "Accept: application/vnd.fdo.journal" http://localhost:19531/entries | \ +curl -LSfs --header "Accept: application/vnd.fdo.journal" http://localhost:19531/entries | \ /usr/lib/systemd/systemd-journal-remote --output=/tmp/remote-journal/system.journal --split-mode=none - journalctl --directory=/tmp/remote-journal -t "$TEST_TAG" --grep "$TEST_MESSAGE" rm -rf /tmp/remote-journal/* @@ -90,13 +90,13 @@ journalctl --directory=/tmp/remote-journal -t "$TEST_TAG" --grep "$TEST_MESSAGE" rm -rf /tmp/remote-journal # /machine -curl -Lfs http://localhost:19531/machine | jq +curl -LSfs http://localhost:19531/machine | jq # /fields -curl -Lfs http://localhost:19531/fields/MESSAGE | grep -qE -- "$TEST_MESSAGE" -curl -Lfs http://localhost:19531/fields/_TRANSPORT -(! curl -Lfs http://localhost:19531/fields) -(! curl -Lfs http://localhost:19531/fields/foo-bar-baz) +curl -LSfs http://localhost:19531/fields/MESSAGE | grep -E -- "$TEST_MESSAGE" >/dev/null +curl -LSfs http://localhost:19531/fields/_TRANSPORT +(! curl -LSfs http://localhost:19531/fields) +(! curl -LSfs http://localhost:19531/fields/foo-bar-baz) systemctl stop systemd-journal-gatewayd.{socket,service} @@ -133,13 +133,13 @@ GATEWAYD_PID=$! sleep 1 # Do a limited set of tests, since the underlying code should be the same past the HTTPS transport -curl -Lfsk https://localhost:19531 | grep -qF "Journal" -curl -Lfsk https://localhost:19531/entries | \ - grep -qE " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" -curl -Lfsk --header "Accept: application/json" https://localhost:19531/entries | \ +curl -LSfsk https://localhost:19531 | grep -F "Journal" >/dev/null +curl -LSfsk https://localhost:19531/entries | \ + grep -E " $TEST_TAG\[[0-9]+\]: $TEST_MESSAGE" >/dev/null +curl -LSfsk --header "Accept: application/json" https://localhost:19531/entries | \ jq -se ".[] | select(.MESSAGE == \"$TEST_MESSAGE\")" -curl -Lfsk https://localhost:19531/machine | jq -curl -Lfsk https://localhost:19531/fields/_TRANSPORT +curl -LSfsk https://localhost:19531/machine | jq +curl -LSfsk https://localhost:19531/fields/_TRANSPORT kill "$GATEWAYD_PID" @@ -161,18 +161,18 @@ curl --fail-with-body --upload-file "$GATEWAYD_FILE" -L http://localhost:19531/u (! grep '[^[:print:]]' "$OUT") rm -rf "$OUT" -curl -Lfs http://localhost:19531/browse | grep -qF "Journal" +curl -LSfs http://localhost:19531/browse | grep -F "Journal" >/dev/null # Nuke the file behind the /browse endpoint mv /usr/share/systemd/gatewayd/browse.html /usr/share/systemd/gatewayd/browse.html.bak (! curl --fail-with-body -L http://localhost:19531/browse) mv /usr/share/systemd/gatewayd/browse.html.bak /usr/share/systemd/gatewayd/browse.html -curl -Lfs http://localhost:19531/browse | grep -qF "Journal" +curl -LSfs http://localhost:19531/browse | grep -F "Journal" >/dev/null # Nuke the journal file mv "$GATEWAYD_FILE" "$GATEWAYD_FILE.bak" (! curl --fail-with-body -L http://localhost:19531/fields/_PID) mv "$GATEWAYD_FILE.bak" "$GATEWAYD_FILE" -curl -Lfs http://localhost:19531/fields/_PID +curl -LSfs http://localhost:19531/fields/_PID systemctl stop test-gatewayd.{socket,service} rm -f "$GATEWAYD_FILE" From 721e78ff7315739e9083913fc13c0864acb80213 Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Tue, 9 Apr 2024 20:15:36 +0200 Subject: [PATCH 5/8] test: give sd-resolved enough time to timeout The timeout on sd-resolved's side is 5-10s (UDP or TCP), but dig's default timeout is 5s. Let's give sd-resolved enough time to timeout before either giving up or checking if it served stale data on dig's side. Resolves: #31639 --- test/units/testsuite-75.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/units/testsuite-75.sh b/test/units/testsuite-75.sh index 199a23964d..fc29e642fb 100755 --- a/test/units/testsuite-75.sh +++ b/test/units/testsuite-75.sh @@ -719,7 +719,9 @@ if command -v nft >/dev/null; then sleep 2 drop_dns_outbound_traffic set +e - run dig stale1.unsigned.test -t A + # Make sure we give sd-resolved enough time to timeout (5-10s) before giving up + # See: https://github.com/systemd/systemd/issues/31639#issuecomment-2009152617 + run dig +tries=1 +timeout=15 stale1.unsigned.test -t A set -eux grep -qE "no servers could be reached" "$RUN_OUT" nft flush ruleset @@ -738,7 +740,8 @@ if command -v nft >/dev/null; then grep -qE "NOERROR" "$RUN_OUT" sleep 2 drop_dns_outbound_traffic - run dig stale1.unsigned.test -t A + # Make sure we give sd-resolved enough time to timeout (5-10s) and serve the stale data (see above) + run dig +tries=1 +timeout=15 stale1.unsigned.test -t A grep -qE "NOERROR" "$RUN_OUT" grep -qE "10.0.0.112" "$RUN_OUT" From 04f0c6752c10d152bc1884b2cc92b1f0b2df5de5 Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Tue, 9 Apr 2024 20:55:48 +0200 Subject: [PATCH 6/8] test: set pexpect's logfile early So we capture the container's boot as well. --- test/test-shutdown.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/test/test-shutdown.py b/test/test-shutdown.py index f496122f80..b83487c5d4 100755 --- a/test/test-shutdown.py +++ b/test/test-shutdown.py @@ -15,9 +15,16 @@ def run(args): ret = 1 logger = logging.getLogger("test-shutdown") + logfile = None + + if args.logfile: + logger.debug("Logging pexpect IOs to %s", args.logfile) + logfile = open(args.logfile, 'w') + elif args.verbose: + logfile = sys.stdout logger.info("spawning test") - console = pexpect.spawn(args.command, args.arg, env={ + console = pexpect.spawn(args.command, args.arg, logfile=logfile, env={ "TERM": "linux", }, encoding='utf-8', timeout=60) @@ -27,12 +34,6 @@ def run(args): logger.info("waiting for login prompt") console.expect('H login: ', 10) - if args.logfile: - logger.debug("Logging pexpect IOs to %s", args.logfile) - console.logfile = open(args.logfile, 'w') - elif args.verbose: - console.logfile = sys.stdout - logger.info("log in and start screen") console.sendline('root') console.expect('bash.*# ', 10) From 8d4bfd38ed941aa8003d7007145eccc01f52a5f6 Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Tue, 9 Apr 2024 21:11:26 +0200 Subject: [PATCH 7/8] test: disable background color for nspawn containers As it makes the logs really messy in certain tests. --- test/test-functions | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test-functions b/test/test-functions index c62cf3d521..33575d3b3a 100644 --- a/test/test-functions +++ b/test/test-functions @@ -662,6 +662,7 @@ run_nspawn() { "--directory=${1:?}" "--setenv=SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/testsuite-$2.units:/usr/lib/systemd/tests/testdata/units:" "--machine=TEST-$TESTID" + "--background=''" ) local kernel_params=( "$PATH_TO_INIT" From 515eb678a057099ee7e462ec83f71b199a368f8a Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Tue, 9 Apr 2024 21:16:41 +0200 Subject: [PATCH 8/8] test: wait until the test container is fully booted up TEST-69 still occasionally times out in CI and appears to be stuck on boot, so let's see if this helps the situation a bit. --- test/test-shutdown.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/test-shutdown.py b/test/test-shutdown.py index b83487c5d4..870c1e269f 100755 --- a/test/test-shutdown.py +++ b/test/test-shutdown.py @@ -43,6 +43,10 @@ def run(args): console.send('c') console.expect('screen1 ', 10) + logger.info('wait for the machine to fully boot') + console.sendline('systemctl is-system-running --wait') + console.expect(r'\b(running|degraded)\b', 60) + # console.interact() console.sendline('tty')