From 47664038ed253f30518f434fb2967286c9382e7f Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Wed, 10 May 2023 21:12:01 +0200 Subject: [PATCH] test: abstract the common test parts into a utility script Also, instead of bailing out on the first failed subtest, always run all subtests and print a summary at the end (with an appropriate exit code). (cherry picked from commit 15bbc0c1071c439d83e711ef7786d401b6c0a0d4) Related: #2213521 --- test/units/test-control.sh | 126 +++++++++++++++++++++++++++++++++++++ test/units/testsuite-07.sh | 8 +-- test/units/testsuite-17.sh | 7 ++- test/units/testsuite-22.sh | 7 ++- test/units/testsuite-23.sh | 59 ++--------------- test/units/testsuite-74.sh | 8 +-- 6 files changed, 146 insertions(+), 69 deletions(-) create mode 100644 test/units/test-control.sh diff --git a/test/units/test-control.sh b/test/units/test-control.sh new file mode 100644 index 0000000000..dd28939cbf --- /dev/null +++ b/test/units/test-control.sh @@ -0,0 +1,126 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# shellcheck shell=bash + +if [[ "${BASH_SOURCE[0]}" -ef "$0" ]]; then + echo >&2 "This file should not be executed directly" + exit 1 +fi + +declare -i CHILD_PID=0 +PASSED_TESTS=() +FAILED_TESTS=() + +# Like trap, but passes the signal name as the first argument +trap_with_sig() { + local fun="${1:?}" + local sig + shift + + for sig in "$@"; do + # shellcheck disable=SC2064 + trap "$fun $sig" "$sig" + done +} + +# Propagate the caught signal to the current child process +handle_signal() { + local sig="${1:?}" + + if [[ $CHILD_PID -gt 0 ]]; then + echo "Propagating signal $sig to child process $CHILD_PID" + kill -s "$sig" "$CHILD_PID" + fi +} + +# In order to make the handle_signal() stuff above work, we have to execute +# each script asynchronously, since bash won't execute traps until the currently +# executed command finishes. This, however, introduces another issue regarding +# how bash's wait works. Quoting: +# +# When bash is waiting for an asynchronous command via the wait builtin, +# the reception of a signal for which a trap has been set will cause the wait +# builtin to return immediately with an exit status greater than 128, +# immediately after which the trap is executed. +# +# In other words - every time we propagate a signal, wait returns with +# 128+signal, so we have to wait again - repeat until the process dies. +wait_harder() { + local pid="${1:?}" + + while kill -0 "$pid" &>/dev/null; do + wait "$pid" || : + done + + wait "$pid" +} + +# Like run_subtests, but propagate specified signals to the subtest script +run_subtests_with_signals() { + local subtests=("${0%.sh}".*.sh) + local subtest + + if [[ "${#subtests[@]}" -eq 0 ]]; then + echo >&2 "No subtests found for file $0" + exit 1 + fi + + if [[ "$#" -eq 0 ]]; then + echo >&2 "No signals to propagate were specified" + exit 1 + fi + + trap_with_sig handle_signal "$@" + + for subtest in "${subtests[@]}"; do + : "--- $subtest BEGIN ---" + "./$subtest" & + CHILD_PID=$! + wait_harder "$CHILD_PID" && PASSED_TESTS+=("$subtest") || FAILED_TESTS+=("$subtest") + : "--- $subtest END ---" + done + + show_summary +} + +run_subtests() { + local subtests=("${0%.sh}".*.sh) + local subtest + + if [[ "${#subtests[@]}" -eq 0 ]]; then + echo >&2 "No subtests found for file $0" + exit 1 + fi + + for subtest in "${subtests[@]}"; do + : "--- $subtest BEGIN ---" + "./$subtest" && PASSED_TESTS+=("$subtest") || FAILED_TESTS+=("$subtest") + : "--- $subtest END ---" + done + + show_summary +} + +show_summary() {( + set +x + + if [[ ${#PASSED_TESTS[@]} -eq 0 && ${#FAILED_TESTS[@]} -eq 0 ]]; then + echo >&2 "No tests were executed, this is most likely an error" + exit 1 + fi + + printf "PASSED TESTS: %3d:\n" "${#PASSED_TESTS[@]}" + echo "------------------" + for t in "${PASSED_TESTS[@]}"; do + echo "$t" + done + + if [[ "${#FAILED_TESTS[@]}" -ne 0 ]]; then + printf "FAILED TESTS: %3d:\n" "${#FAILED_TESTS[@]}" + echo "------------------" + for t in "${FAILED_TESTS[@]}"; do + echo "$t" + done + fi + + [[ "${#FAILED_TESTS[@]}" -eq 0 ]] +)} diff --git a/test/units/testsuite-07.sh b/test/units/testsuite-07.sh index 8c004a72e5..58d278e1f1 100755 --- a/test/units/testsuite-07.sh +++ b/test/units/testsuite-07.sh @@ -3,16 +3,16 @@ set -eux set -o pipefail +# shellcheck source=test/units/test-control.sh +. "$(dirname "$0")"/test-control.sh + : >/failed # Issue: https://github.com/systemd/systemd/issues/2730 # See TEST-07-PID1/test.sh for the first "half" of the test mountpoint /issue2730 -for script in "${0%.sh}".*.sh; do - echo "Running $script" - "./$script" -done +run_subtests touch /testok rm /failed diff --git a/test/units/testsuite-17.sh b/test/units/testsuite-17.sh index b389875ef1..72040f69d8 100755 --- a/test/units/testsuite-17.sh +++ b/test/units/testsuite-17.sh @@ -3,13 +3,14 @@ set -eux set -o pipefail +# shellcheck source=test/units/test-control.sh +. "$(dirname "$0")"/test-control.sh + : >/failed udevadm settle -for t in "${0%.sh}".*.sh; do - echo "Running $t"; ./"$t" -done +run_subtests touch /testok rm /failed diff --git a/test/units/testsuite-22.sh b/test/units/testsuite-22.sh index 43823f1d46..5a07e7b78c 100755 --- a/test/units/testsuite-22.sh +++ b/test/units/testsuite-22.sh @@ -3,11 +3,12 @@ set -eux set -o pipefail +# shellcheck source=test/units/test-control.sh +. "$(dirname "$0")"/test-control.sh + : >/failed -for t in "${0%.sh}".*.sh; do - echo "Running $t"; ./"$t" -done +run_subtests touch /testok rm /failed diff --git a/test/units/testsuite-23.sh b/test/units/testsuite-23.sh index a6b8da34c9..3be645e20a 100755 --- a/test/units/testsuite-23.sh +++ b/test/units/testsuite-23.sh @@ -5,62 +5,11 @@ set -o pipefail : >/failed -declare -i CHILD_PID=0 +# shellcheck source=test/units/test-control.sh +. "$(dirname "$0")"/test-control.sh -# Note: all the signal shenanigans are necessary for the Upholds= tests - -# Like trap, but passes the signal name as the first argument -trap_with_sig() { - local fun="${1:?}" - local sig - shift - - for sig in "$@"; do - # shellcheck disable=SC2064 - trap "$fun $sig" "$sig" - done -} - -# Propagate the caught signal to the current child process -handle_signal() { - local sig="${1:?}" - - if [[ $CHILD_PID -gt 0 ]]; then - echo "Propagating signal $sig to child process $CHILD_PID" - kill -s "$sig" "$CHILD_PID" - fi -} - -# In order to make the handle_signal() stuff above work, we have to execute -# each script asynchronously, since bash won't execute traps until the currently -# executed command finishes. This, however, introduces another issue regarding -# how bash's wait works. Quoting: -# -# When bash is waiting for an asynchronous command via the wait builtin, -# the reception of a signal for which a trap has been set will cause the wait -# builtin to return immediately with an exit status greater than 128, -# immediately after which the trap is executed. -# -# In other words - every time we propagate a signal, wait returns with -# 128+signal, so we have to wait again - repeat until the process dies. -wait_harder() { - local pid="${1:?}" - - while kill -0 "$pid"; do - wait "$pid" || : - done - - wait "$pid" -} - -trap_with_sig handle_signal SIGUSR1 SIGUSR2 SIGRTMIN+1 - -for script in "${0%.sh}".*.sh; do - echo "Running $script" - "./$script" & - CHILD_PID=$! - wait_harder "$CHILD_PID" -done +# Note: the signal shenanigans are necessary for the Upholds= tests +run_subtests_with_signals SIGUSR1 SIGUSR2 SIGRTMIN+1 touch /testok rm /failed diff --git a/test/units/testsuite-74.sh b/test/units/testsuite-74.sh index 13c767e490..5a07e7b78c 100755 --- a/test/units/testsuite-74.sh +++ b/test/units/testsuite-74.sh @@ -3,12 +3,12 @@ set -eux set -o pipefail +# shellcheck source=test/units/test-control.sh +. "$(dirname "$0")"/test-control.sh + : >/failed -for script in "${0%.sh}".*.sh; do - echo "Running $script" - "./$script" -done +run_subtests touch /testok rm /failed