1
0
mirror of https://github.com/systemd/systemd.git synced 2024-12-26 03:22:00 +03:00
systemd/test/units/test-control.sh
Frantisek Sumsal 0ea6e6a526 test: exit on first failing subtest
Let's take a step back and revert back to the original behavior where we
exit on a first failing subtest. The current behavior makes fishing out the
failing test details quite unpleasant, and in certain situations the
journal may even be rotated away so we end up with no actionable logs.
2023-06-14 20:28:20 +02:00

150 lines
4.2 KiB
Bash

# 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=()
# 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"
}
_show_summary() {(
set +x
if [[ ${#_PASSED_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
)}
# 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") || return 1
: "--- $subtest END ---"
done
_show_summary
}
# Run all subtests (i.e. files named as testsuite-<testid>.<subtest_name>.sh)
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
if [[ -n "${TEST_MATCH_SUBTEST:-}" ]] && ! [[ "$subtest" =~ $TEST_MATCH_SUBTEST ]]; then
echo "Skipping $subtest (not matching '$TEST_MATCH_SUBTEST')"
continue
fi
: "--- $subtest BEGIN ---"
"./$subtest" && _PASSED_TESTS+=("$subtest") || return 1
: "--- $subtest END ---"
done
_show_summary
}
# Run all test cases (i.e. functions prefixed with testcase_ in the current namespace)
run_testcases() {
local testcase testcases
# Create a list of all functions prefixed with testcase_
mapfile -t testcases < <(declare -F | awk '$3 ~ /^testcase_/ {print $3;}')
if [[ "${#testcases[@]}" -eq 0 ]]; then
echo >&2 "No test cases found, this is most likely an error"
exit 1
fi
for testcase in "${testcases[@]}"; do
if [[ -n "${TEST_MATCH_TESTCASE:-}" ]] && ! [[ "$testcase" =~ $TEST_MATCH_TESTCASE ]]; then
echo "Skipping $testcase (not matching '$TEST_MATCH_TESTCASE')"
continue
fi
: "+++ $testcase BEGIN +++"
# Note: the subshell here is used purposefully, otherwise we might
# unexpectedly inherit a RETURN trap handler from the called
# function and call it for the second time once we return,
# causing a "double-free"
("$testcase")
: "+++ $testcase END +++"
done
}