mirror of
https://github.com/systemd/systemd.git
synced 2024-12-26 03:22:00 +03:00
0ea6e6a526
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.
150 lines
4.2 KiB
Bash
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
|
|
}
|