Teardown must not decide a test's verdict

Under set -e a failing command in an EXIT trap becomes the script's exit
status, so a cleanup hiccup fails a test whose assertions all passed. Run
teardown with errexit off, on EXIT only: sharing set +e with the signal
traps would leave errexit off for the rest of a signalled run, where a
torn-down test could still report success. A failing teardown command
still prints its own diagnostic, so nothing is silenced.

The return 0 that three cleanup() bodies ended with never protected
anything: errexit fires at the failing command before it is reached.

Closes #773

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <xroche@gmail.com>
This commit is contained in:
Xavier Roche
2026-07-27 10:30:36 +02:00
parent 56389103bf
commit 2fccb17058
74 changed files with 185 additions and 76 deletions

View File

@@ -26,6 +26,10 @@ the operational checklist: toolchain, invariants, and how to ship a change.
check`, or `PATH="<bld>/src:$PATH"` for a manual run.
- Give new `.test` scripts `set -e`: the older ones predate the rule, so several
`local-crawl.sh` calls with no `set -e` report PASS on any non-last failure.
- Run teardown with errexit off: `trap 'set +e; cleanup' EXIT`. Under `set -e` a
failing cleanup command becomes the test's exit status (#773). Keep the other
signals on their own `trap` line, or errexit stays off for the rest of the run.
The guard also resets `$?`, so save it first if teardown reads it.
- Never assert with `cmd | grep -q MARKER && fail`. Under `pipefail` the
pipeline is non-zero both when `cmd` fails and when `grep -q` matches early
and SIGPIPEs it, so the `&&` never fires and a probe that proved nothing reads

View File

@@ -4,7 +4,8 @@
set -euo pipefail
tmpdir=$(mktemp -d "${TMPDIR:-/tmp}/httrack_changes_st.XXXXXX") || exit 1
trap 'rm -rf "$tmpdir"' EXIT HUP INT QUIT PIPE TERM
trap 'set +e; rm -rf "$tmpdir"' EXIT
trap 'rm -rf "$tmpdir"' HUP INT QUIT PIPE TERM
# No pipe into grep: SIGPIPE would mask a failing exit status.
expect_ok() {

View File

@@ -11,7 +11,8 @@
set -euo pipefail
tmp=$(mktemp -d "${TMPDIR:-/tmp}/httrack_cmdline.XXXXXX") || exit 1
trap 'rm -rf "$tmp"' EXIT HUP INT QUIT PIPE TERM
trap 'set +e; rm -rf "$tmp"' EXIT
trap 'rm -rf "$tmp"' HUP INT QUIT PIPE TERM
echo '<html><body>hello</body></html>' >"$tmp/index.html"

View File

@@ -7,6 +7,6 @@ set -euo pipefail
# cookies *@*.txt) from a long, non-ASCII folder through the UTF-8/long-path
# file wrappers (#133,#630).
dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT
trap 'set +e; rm -rf "$dir"' EXIT
httrack -O /dev/null -#test=cookieimport "$dir" | grep -q "cookieimport:.*OK"

View File

@@ -6,6 +6,6 @@ set -euo pipefail
# Drives -#test=direnum: enumerate a long+non-ASCII directory through the
# opendir/readdir wrappers, checking each child round-trips as UTF-8 (#133,#630).
dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT
trap 'set +e; rm -rf "$dir"' EXIT
httrack -O /dev/null -#test=direnum "$dir" | grep -q "direnum:.*OK"

View File

@@ -28,7 +28,8 @@ case "$bin" in
esac
tmp=$(mktemp -d "${TMPDIR:-/tmp}/httrack_doitlog.XXXXXX") || exit 1
trap 'rm -rf "$tmp"' EXIT HUP INT QUIT PIPE TERM
trap 'set +e; rm -rf "$tmp"' EXIT
trap 'rm -rf "$tmp"' HUP INT QUIT PIPE TERM
site="$tmp/site"
out="$tmp/out"

View File

@@ -8,7 +8,8 @@
set -euo pipefail
tmp=$(mktemp -d "${TMPDIR:-/tmp}/httrack_filelist.XXXXXX") || exit 1
trap 'rm -rf "$tmp"' EXIT HUP INT QUIT PIPE TERM
trap 'set +e; rm -rf "$tmp"' EXIT
trap 'rm -rf "$tmp"' HUP INT QUIT PIPE TERM
echo '<html><body>hi</body></html>' >"$tmp/index.html"

View File

@@ -18,7 +18,7 @@ MINGW* | MSYS* | CYGWIN*) exit 77 ;;
esac
dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT
trap 'set +e; rm -rf "$dir"' EXIT
# A few-hundred-char file path (kept well under the URL length limit) makes the
# {path} field long.

View File

@@ -6,7 +6,7 @@
set -euo pipefail
dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT
trap 'set +e; rm -rf "$dir"' EXIT
rc=0
out=$(httrack -#test=fsize "$dir") || rc=$?

View File

@@ -10,7 +10,8 @@ echo "$out"
test "$out" == "growsize self-test OK"
tmp=$(mktemp -d "${TMPDIR:-/tmp}/httrack_growsize.XXXXXX")
trap 'rm -rf "$tmp"' EXIT HUP INT QUIT PIPE TERM
trap 'set +e; rm -rf "$tmp"' EXIT
trap 'rm -rf "$tmp"' HUP INT QUIT PIPE TERM
echo '<html><body>hi</body></html>' >"$tmp/index.html"
printf -- '-*/zzmarker*\n' >"$tmp/rules.txt"

View File

@@ -6,6 +6,6 @@ set -euo pipefail
# Drives -#test=longpath: a >MAX_PATH round trip exercising hts_pathToUCS2's
# \\?\ prefixing on Windows (#133); a positive control on POSIX.
dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT
trap 'set +e; rm -rf "$dir"' EXIT
httrack -O /dev/null -#test=longpath "$dir" | grep -q "longpath:.*OK"

View File

@@ -6,7 +6,7 @@ set -euo pipefail
# hts_finish_makeindex writes the footer and gates the refresh meta on a single
# first link (guards the macro->function extraction).
dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT
trap 'set +e; rm -rf "$dir"' EXIT
httrack -O /dev/null -#test=makeindex "$dir" run |
grep -q "makeindex self-test OK"

View File

@@ -7,6 +7,6 @@ set -euo pipefail
# (>MAX_PATH) and non-ASCII, exercising the mirror I/O wrappers the engine's
# raw file ops now route to on Windows (#133, #630). Positive control on POSIX.
dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT
trap 'set +e; rm -rf "$dir"' EXIT
httrack -O /dev/null -#test=mirrorio "$dir" | grep -q "mirrorio:.*OK"

View File

@@ -7,7 +7,8 @@
set -euo pipefail
tmp=$(mktemp -d "${TMPDIR:-/tmp}/httrack_parse.XXXXXX") || exit 1
trap 'rm -rf "$tmp"' EXIT HUP INT QUIT PIPE TERM
trap 'set +e; rm -rf "$tmp"' EXIT
trap 'rm -rf "$tmp"' HUP INT QUIT PIPE TERM
# a minimal valid 1x1 GIF, reused for every referenced asset
gif() {

View File

@@ -30,7 +30,8 @@ case "$bin" in
esac
tmp=$(mktemp -d "${TMPDIR:-/tmp}/httrack_rcfile.XXXXXX") || exit 1
trap 'rm -rf "$tmp"' EXIT HUP INT QUIT PIPE TERM
trap 'set +e; rm -rf "$tmp"' EXIT
trap 'rm -rf "$tmp"' HUP INT QUIT PIPE TERM
# HTS_HTTRACKRC is ".httrackrc" on POSIX but "httrackrc" on Windows: write both,
# each platform reads the one it knows.

View File

@@ -7,7 +7,7 @@
set -eu
dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT
trap 'set +e; rm -rf "$dir"' EXIT
out=$(httrack -#test=reconcile "$dir")

View File

@@ -12,7 +12,7 @@ httrack_bin=$(cd "$(dirname "$(command -v httrack)")" && pwd)/httrack
# scratch dir: body= and cached= write temp files (st-savename-body.tmp, hts-cache/)
scratch=$(mktemp -d)
trap 'rm -rf "$scratch"' EXIT
trap 'set +e; rm -rf "$scratch"' EXIT
cd "$scratch"
run() {

View File

@@ -6,7 +6,7 @@
set -euo pipefail
dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT
trap 'set +e; rm -rf "$dir"' EXIT
httrack -O /dev/null -#test=structcheck "$dir" |
grep -q "structcheck self-test OK"

View File

@@ -4,7 +4,8 @@
set -euo pipefail
tmpdir=$(mktemp -d "${TMPDIR:-/tmp}/httrack_threadwait_st.XXXXXX") || exit 1
trap 'rm -rf "$tmpdir"' EXIT HUP INT QUIT PIPE TERM
trap 'set +e; rm -rf "$tmpdir"' EXIT
trap 'rm -rf "$tmpdir"' HUP INT QUIT PIPE TERM
# No pipe into grep: SIGPIPE would mask a failing exit status.
expect_ok() {

View File

@@ -6,7 +6,7 @@ set -euo pipefail
# hts_buildtopindex takes a system-charset path but verif_backblue below it
# expects utf-8, mangling a non-ASCII project dir on Windows (#216, #217).
dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT
trap 'set +e; rm -rf "$dir"' EXIT
httrack -O /dev/null -#test=topindex "$dir" run |
grep -q "topindex self-test OK"

View File

@@ -5,7 +5,7 @@ set -euo pipefail
# Accept-Encoding (#450): advertise gzip+deflate; decode gzip/zlib/raw-deflate.
dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT
trap 'set +e; rm -rf "$dir"' EXIT
httrack -O /dev/null -#test=acceptencoding "$dir" run |
grep -q "acceptencoding self-test OK"

View File

@@ -7,7 +7,7 @@
set -eu
dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT
trap 'set +e; rm -rf "$dir"' EXIT
# the smashed-header case logs expected "Corrupted cache entry" warnings on
# stdout; the verdict is the last line

View File

@@ -27,7 +27,7 @@ test -e "$fixture/hts-cache/new.zip" || {
}
dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT
trap 'set +e; rm -rf "$dir"' EXIT
# Read against a private copy so the source tree is never touched (a read
# session does not write, but copying keeps the test hermetic). Create the dir

View File

@@ -6,7 +6,7 @@
set -eu
dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT
trap 'set +e; rm -rf "$dir"' EXIT
# the refusal errors land on stdout (no log file); pin them and the verdict
out=$(httrack -#test=cache-legacy "$dir" 2>/dev/null)

View File

@@ -11,7 +11,7 @@
set -eu
dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT
trap 'set +e; rm -rf "$dir"' EXIT
out=$(httrack -#test=cache-writefail "$dir")

View File

@@ -18,7 +18,7 @@
set -eu
dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT
trap 'set +e; rm -rf "$dir"' EXIT
# The working directory is a required argument; without it the test prints a
# usage line to stderr and returns non-zero.

View File

@@ -5,7 +5,7 @@ set -euo pipefail
# brotli/zstd decode, unknown codings, and the decoded-size budget.
dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT
trap 'set +e; rm -rf "$dir"' EXIT
httrack -O /dev/null -#test=contentcodings "$dir" run |
grep -q "contentcodings self-test OK"

View File

@@ -11,7 +11,7 @@
set -eu
dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT
trap 'set +e; rm -rf "$dir"' EXIT
out=$(httrack -#test=zip-repair-shift "$dir")

View File

@@ -10,7 +10,7 @@ set -euo pipefail
httrack_bin=$(cd "$(dirname "$(command -v httrack)")" && pwd)/httrack
scratch=$(mktemp -d)
trap 'rm -rf "$scratch"' EXIT
trap 'set +e; rm -rf "$scratch"' EXIT
cd "$scratch"
name() {

View File

@@ -9,7 +9,7 @@ set -euo pipefail
httrack_bin=$(cd "$(dirname "$(command -v httrack)")" && pwd)/httrack
scratch=$(mktemp -d)
trap 'rm -rf "$scratch"' EXIT
trap 'set +e; rm -rf "$scratch"' EXIT
out=$("$httrack_bin" -O /dev/null -#test=warc-cdx "$scratch/")
echo "$out"

View File

@@ -16,7 +16,7 @@ if ! "$httrack_bin" -#test 2>&1 | grep -q '^ warc-wacz'; then
fi
scratch=$(mktemp -d)
trap 'rm -rf "$scratch"' EXIT
trap 'set +e; rm -rf "$scratch"' EXIT
out=$("$httrack_bin" -O /dev/null -#test=warc-wacz "$scratch/")
echo "$out"

View File

@@ -10,7 +10,7 @@ set -euo pipefail
httrack_bin=$(cd "$(dirname "$(command -v httrack)")" && pwd)/httrack
scratch=$(mktemp -d)
trap 'rm -rf "$scratch"' EXIT
trap 'set +e; rm -rf "$scratch"' EXIT
for t in warc warc-trunc warc-ftp warc-rotate warc-verbatim; do
out=$("$httrack_bin" -O /dev/null "-#test=$t" "$scratch/")

View File

@@ -29,7 +29,7 @@ command -v httrack >/dev/null 2>&1 || {
tmp=$(mktemp) || exit 1
committed_clean=$(mktemp) || exit 1
generated_clean=$(mktemp) || exit 1
trap 'rm -f "$tmp" "$committed_clean" "$generated_clean"' EXIT
trap 'set +e; rm -f "$tmp" "$committed_clean" "$generated_clean"' EXIT
README="$top_srcdir/README" bash "$gen" httrack >"$tmp" 2>/dev/null || {
echo "makeman.sh failed" >&2

View File

@@ -14,7 +14,7 @@ set -euo pipefail
site=$(mktemp -d)
out=$(mktemp -d)
trap 'rm -rf "$site" "$out"' EXIT
trap 'set +e; rm -rf "$site" "$out"' EXIT
cat >"$site/index.html" <<EOF
<a href="a.html">a</a> <a href="sub/b.html">b</a>

View File

@@ -31,7 +31,7 @@ cleanup() {
done
rm -rf "$tmpdir"
}
trap cleanup EXIT
trap 'set +e; cleanup' EXIT
# self-signed cert for the local TLS origin (httrack does not verify certs)
openssl req -x509 -newkey rsa:2048 -keyout "$tmpdir/key.pem" \

View File

@@ -40,9 +40,8 @@ serverpid=
cleanup() {
stop_server "$serverpid"
rm -rf "$tmpdir"
return 0
}
trap cleanup EXIT
trap 'set +e; cleanup' EXIT
# bind the live server to 127.0.0.1 only, so 127.0.0.2 refuses the connect
"$python" "$server" --root "$root" --bind 127.0.0.1 >"$tmpdir/srv.out" 2>"$tmpdir/srv.err" &

View File

@@ -24,7 +24,8 @@ cleanup() {
stop_server "$serverpid"
rm -rf "$tmpdir"
}
trap cleanup EXIT HUP INT QUIT PIPE TERM
trap 'set +e; cleanup' EXIT
trap cleanup HUP INT QUIT PIPE TERM
# OVERLAP_COUNTER gets a byte per flaky.bin request so pass 1 knows when to interrupt.
serverlog="${tmpdir}/server.log"

View File

@@ -6,7 +6,7 @@ set -euo pipefail
# --why: report which +/- filter rule decides for a URL, without crawling.
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
trap 'set +e; rm -rf "$tmpdir"' EXIT
why() {
local want="$1" out

View File

@@ -34,7 +34,7 @@ cleanup() {
done
rm -rf "$tmpdir"
}
trap cleanup EXIT
trap 'set +e; cleanup' EXIT
openssl req -x509 -newkey rsa:2048 -keyout "$tmpdir/key.pem" \
-out "$tmpdir/cert.pem" -days 2 -nodes -subj "/CN=127.0.0.1" \

View File

@@ -5,7 +5,7 @@
set -euo pipefail
dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT
trap 'set +e; rm -rf "$dir"' EXIT
printf 'HTTP/1.1 404 Not Found Here At All\r\nContent-Type: text/html\r\nLast-Modified: Wed, 01 Jan 2025 00:00:00 GMT\r\nContent-Length: 5\r\n\r\n' >"$dir/hdr"
printf 'hello' >"$dir/body"

View File

@@ -5,7 +5,7 @@
set -euo pipefail
dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT
trap 'set +e; rm -rf "$dir"' EXIT
# The first length-prefixed field declares far more than firstline[256] holds;
# cache_brstr must clamp the copy to the destination, not the declared length.

View File

@@ -29,9 +29,8 @@ serverpid=
cleanup() {
stop_server "$serverpid"
rm -rf "$tmpdir"
return 0
}
trap cleanup EXIT
trap 'set +e; cleanup' EXIT
# decoy origin: it must stay silent. LOCAL_SERVER_VERBOSE logs any request it gets.
LOCAL_SERVER_VERBOSE=1 "$python" "$server" --root "$root" --bind 127.0.0.1 \

View File

@@ -29,7 +29,7 @@ cleanup() {
done
rm -rf "$tmpdir"
}
trap cleanup EXIT
trap 'set +e; cleanup' EXIT
# start_server <logdir> <mode>: launches a proxy+origin pair, sets $origin_port
# and $proxy_port from its announced ephemeral ports.

View File

@@ -12,7 +12,7 @@ testdir=$(cd "$(dirname "$0")" && pwd)
. "${testdir}/testlib.sh"
tmp=$(mktemp -d "${TMPDIR:-/tmp}/httrack_wd.XXXXXX")
trap 'rm -rf "$tmp"' EXIT
trap 'set +e; rm -rf "$tmp"' EXIT
fail() {
echo "FAIL: $*" >&2
exit 1

View File

@@ -27,9 +27,8 @@ serverpid=
cleanup() {
stop_server "$serverpid"
rm -rf "$tmpdir"
return 0
}
trap cleanup EXIT
trap 'set +e; cleanup' EXIT
# start_stall_server <tag> <mode-args...>: sets $port from the announced one.
start_stall_server() {

View File

@@ -12,7 +12,7 @@ testdir=$(cd "$(dirname "$0")" && pwd)
TMPDIR=$(mktemp -d "${TMPDIR:-/tmp}/httrack_salv.XXXXXX")
export TMPDIR
trap 'rm -rf "$TMPDIR"' EXIT
trap 'set +e; rm -rf "$TMPDIR"' EXIT
fail() {
echo "FAIL: $*" >&2
exit 1

View File

@@ -19,7 +19,7 @@ for f in "$eng" "$def"; do
done
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT
trap 'set +e; rm -rf "$tmp"' EXIT
keys="$tmp/english.keys"
# Byte-wise: each file is in its own declared legacy charset, lang.def is CRLF.

View File

@@ -25,10 +25,11 @@ srv=
cleanup() {
test -z "${srv}" || kill -9 "${srv}" 2>/dev/null || true
}
trap cleanup EXIT HUP INT QUIT PIPE TERM
trap 'set +e; cleanup' EXIT
trap cleanup HUP INT QUIT PIPE TERM
log=$(mktemp)
trap 'cleanup; rm -f "${log}"' EXIT
trap 'set +e; cleanup; rm -f "${log}"' EXIT
# Ask htsserver for its rendered default base path, under the given $HOME
# ("-" = leave $HOME unset).

View File

@@ -14,7 +14,8 @@ testdir=$(cd "$(dirname "$0")" && pwd)
. "${testdir}/testlib.sh"
tmp=$(mktemp -d "${TMPDIR:-/tmp}/httrack_portsib.XXXXXX") || exit 1
trap 'rm -rf "$tmp"' EXIT HUP INT QUIT PIPE TERM
trap 'set +e; rm -rf "$tmp"' EXIT
trap 'rm -rf "$tmp"' HUP INT QUIT PIPE TERM
# A port the old code accepted only by wrapping: 4295009395 = 2^32 + 42099 and
# 4294967376 = 2^32 + 80 both fold to a plausible listen port. They are the

View File

@@ -14,7 +14,7 @@ set -euo pipefail
httrack_bin=$(cd "$(dirname "$(command -v httrack)")" && pwd)/httrack
scratch=$(mktemp -d)
trap 'rm -rf "$scratch"' EXIT
trap 'set +e; rm -rf "$scratch"' EXIT
cd "$scratch"
hexseg=$(printf 'a1b2c3d4e5f60718%.0s' {1..8}) # 128 hex chars

View File

@@ -36,7 +36,8 @@ cleanup() {
wait "${srv}" 2>/dev/null || true # absorb bash's async "Killed" notice
rm -rf "${work}" "${srvlog}"
}
trap cleanup EXIT HUP INT QUIT PIPE TERM
trap 'set +e; cleanup' EXIT
trap cleanup HUP INT QUIT PIPE TERM
# webhttrack server on a pre-picked port; an isolated HOME keeps a stray
# ~/.httrack.ini out of it.

View File

@@ -9,7 +9,7 @@ set -euo pipefail
httrack_bin=$(cd "$(dirname "$(command -v httrack)")" && pwd)/httrack
scratch=$(mktemp -d)
trap 'rm -rf "$scratch"' EXIT
trap 'set +e; rm -rf "$scratch"' EXIT
cd "$scratch"
# ~300 chars: over the old 236 ceiling, well under any POSIX PATH_MAX. The

View File

@@ -23,7 +23,8 @@ cleanup() {
stop_server "$serverpid"
rm -rf "$tmpdir"
}
trap cleanup EXIT HUP INT QUIT PIPE TERM
trap 'set +e; cleanup' EXIT
trap cleanup HUP INT QUIT PIPE TERM
serverlog="${tmpdir}/server.log"
: >"$serverlog"

View File

@@ -32,7 +32,8 @@ cleanup() {
stop
rm -f "${log}"
}
trap cleanup EXIT HUP INT QUIT PIPE TERM
trap 'set +e; cleanup' EXIT
trap cleanup HUP INT QUIT PIPE TERM
freeport() {
python3 -c 'import socket

View File

@@ -26,7 +26,8 @@ cleanup() {
test -z "${srv}" || kill -9 "${srv}" 2>/dev/null || true
rm -f "${log}"
}
trap cleanup EXIT HUP INT QUIT PIPE TERM
trap 'set +e; cleanup' EXIT
trap cleanup HUP INT QUIT PIPE TERM
freeport() {
python3 -c 'import socket

View File

@@ -30,7 +30,7 @@ cleanup() {
stop_server "$ptpid"
rm -rf "$dir"
}
trap cleanup EXIT
trap 'set +e; cleanup' EXIT
# Neither header is present, so file->contenttype and ->lastmodified stay "".
printf 'HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\n' >"$dir/hdr"

View File

@@ -17,7 +17,8 @@ command -v httrack >/dev/null || {
}
tmpdir=$(mktemp -d "${TMPDIR:-/tmp}/httrack_symbolize.XXXXXX") || exit 1
trap 'rm -rf "$tmpdir"' EXIT HUP INT QUIT PIPE TERM
trap 'set +e; rm -rf "$tmpdir"' EXIT
trap 'rm -rf "$tmpdir"' HUP INT QUIT PIPE TERM
out="${tmpdir}/trace"
raw="${tmpdir}/trace-optout"
overflow="this string is far too long for the buffer"

View File

@@ -32,7 +32,8 @@ cleanup() {
srv=
rm -rf "${work}" "${srvlog}"
}
trap cleanup EXIT HUP INT QUIT PIPE TERM
trap 'set +e; cleanup' EXIT
trap cleanup HUP INT QUIT PIPE TERM
# webhttrack server on a pre-picked port; an isolated HOME keeps a stray
# ~/.httrack.ini out of it.

View File

@@ -38,7 +38,8 @@ cleanup() {
wait "${srv}" 2>/dev/null || true # absorb bash's async "Killed" notice
rm -rf "${work}" "${srvlog}"
}
trap cleanup EXIT HUP INT QUIT PIPE TERM
trap 'set +e; cleanup' EXIT
trap cleanup HUP INT QUIT PIPE TERM
# Stand in for a finished mirror: the crawl itself is not under test.
proj="${work}/websites/proj"

View File

@@ -27,7 +27,8 @@ cleanup() {
test -z "${srv}" || kill -9 "${srv}" 2>/dev/null || true
rm -f "${log}"
}
trap cleanup EXIT HUP INT QUIT PIPE TERM
trap 'set +e; cleanup' EXIT
trap cleanup HUP INT QUIT PIPE TERM
freeport() {
python3 -c 'import socket

View File

@@ -34,7 +34,8 @@ cleanup() {
wait "${csrv}" 2>/dev/null || true # absorb bash's async "Killed" notice
rm -rf "${log}" "${work}"
}
trap cleanup EXIT HUP INT QUIT PIPE TERM
trap 'set +e; cleanup' EXIT
trap cleanup HUP INT QUIT PIPE TERM
freeport() {
python3 -c 'import socket

View File

@@ -28,7 +28,8 @@ cleanup() {
rm -f "${log}"
rm -rf "${base}"
}
trap cleanup EXIT HUP INT QUIT PIPE TERM
trap 'set +e; cleanup' EXIT
trap cleanup HUP INT QUIT PIPE TERM
freeport() {
python3 -c 'import socket

View File

@@ -9,7 +9,7 @@ testdir=$(cd "$(dirname "$0")" && pwd)
. "${testdir}/testlib.sh"
dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT
trap 'set +e; rm -rf "$dir"' EXIT
pad() { printf "%${1}s" '' | tr ' ' "$2"; }

View File

@@ -5,7 +5,7 @@
set -euo pipefail
dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT
trap 'set +e; rm -rf "$dir"' EXIT
# $1 Last-Modified value (empty = omit the header), $2 expected archive date,
# $3 label. The date is asserted exactly: a guard that fires unconditionally

View File

@@ -14,7 +14,7 @@ python=$(find_python) || {
}
dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT
trap 'set +e; rm -rf "$dir"' EXIT
rc=0
"$python" - "$dir/in.zip" <<'EOF' || rc=$?

View File

@@ -33,7 +33,8 @@ cleanup() {
rm -f "${log}"
rm -rf "${base}"
}
trap cleanup EXIT HUP INT QUIT PIPE TERM
trap 'set +e; cleanup' EXIT
trap cleanup HUP INT QUIT PIPE TERM
freeport() {
python3 -c 'import socket

View File

@@ -33,7 +33,8 @@ cleanup() {
wait "${srv}" 2>/dev/null || true # absorb bash's async "Killed" notice
rm -rf "${work}" "${srvlog}"
}
trap cleanup EXIT HUP INT QUIT PIPE TERM
trap 'set +e; cleanup' EXIT
trap cleanup HUP INT QUIT PIPE TERM
# An isolated HOME keeps a stray ~/.httrack.ini out of the served settings.
sport=$("${python}" -c 'import socket

View File

@@ -28,7 +28,8 @@ cleanup() {
rm -f "${log}"
rm -rf "${base}"
}
trap cleanup EXIT HUP INT QUIT PIPE TERM
trap 'set +e; cleanup' EXIT
trap cleanup HUP INT QUIT PIPE TERM
# First line only. A "| head -1" would close the pipe early and, under pipefail,
# SIGPIPE the producer into a spurious failure.

View File

@@ -15,7 +15,7 @@ python=$(find_python) || {
}
dir=$(mktemp -d)
trap 'rm -rf "$dir"' EXIT
trap 'set +e; rm -rf "$dir"' EXIT
# each field fills binput's own bound, so together they run one past line[]
"$python" - "$dir/foo.ndx" <<'EOF'

View File

@@ -19,7 +19,8 @@ cleanup() {
stop_server "$serverpid"
rm -rf "$tmpdir"
}
trap cleanup EXIT HUP INT QUIT PIPE TERM
trap 'set +e; cleanup' EXIT
trap cleanup HUP INT QUIT PIPE TERM
serverlog="${tmpdir}/server.log"
: >"$serverlog"

View File

@@ -22,7 +22,8 @@ cleanup() {
htssrv=
rm -rf "$tmpdir"
}
trap cleanup EXIT HUP INT QUIT PIPE TERM
trap 'set +e; cleanup' EXIT
trap cleanup HUP INT QUIT PIPE TERM
# --- the site ---------------------------------------------------------------
doc="${tmpdir}/doc"

View File

@@ -0,0 +1,81 @@
#!/bin/bash
#
# Issue #773: a test must fail only when an assertion fails. Under "set -e" a
# failing command in an EXIT trap becomes the script's exit status, so a
# teardown hiccup turned a test whose assertions all passed into a red CI leg.
set -euo pipefail
: "${top_srcdir:=..}"
tmp=$(mktemp -d)
trap 'set +e; rm -rf "$tmp"' EXIT
fail() {
echo "FAIL: $1" >&2
exit 1
}
# run <trap-body> <script-body>: exit status and stderr of a subject script.
run() {
{
echo "set -euo pipefail"
# printf, so the scanner below does not read this as a trap of our own
printf 'trap %s EXIT\n' "$1"
echo "$2"
} >"$tmp/subject"
rc=0
err=$(bash "$tmp/subject" 2>&1 >/dev/null) || rc=$?
}
# 1. the shape in the tree: teardown fails, every assertion passed -> pass
run "'set +e; false'" "true"
test "$rc" -eq 0 || fail "guarded teardown failure exited $rc, want 0"
# 2. teeth for case 1: unguarded, the same teardown is what went red
run "'false'" "true"
test "$rc" -eq 1 || fail "unguarded teardown failure exited $rc, want 1"
# 3. the guard must not swallow a real failure, nor a skip
run "'set +e; false'" "exit 1"
test "$rc" -eq 1 || fail "assertion failure exited $rc, want 1"
run "'set +e; false'" "exit 77"
test "$rc" -eq 77 || fail "skip exited $rc, want 77"
# 4. not failing must not mean going quiet: teardown still reports why
run "'set +e; rm /nonexistent-teardown-probe'" "true"
test "$rc" -eq 0 || fail "guarded teardown failure exited $rc, want 0"
case "$err" in
*nonexistent-teardown-probe*) ;;
*) fail "teardown error was silenced (stderr: '$err')" ;;
esac
echo "OK: teardown cannot decide the verdict, and still reports"
# 5. no test may reintroduce the shape case 2 pins. Two spellings neutralise
# errexit for teardown: "set +e" first, or "|| true" over the whole body.
scanned=0
for f in "$top_srcdir"/tests/*.test "$top_srcdir"/tests/*.sh; do
test -r "$f" || continue
scanned=$((scanned + 1))
done
# an unexpanded glob would report no offender having read nothing
test "$scanned" -ge 50 || fail "scanned $scanned scripts in $top_srcdir/tests, want the whole suite"
bad=$(awk '
FNR == 1 { errexit = 0 }
/^[[:space:]]*set[[:space:]]+(-[a-z]*e|-o[[:space:]]+errexit)/ { errexit = 1 }
!errexit { next }
!/^[[:space:]]*trap[[:space:]]+.*[[:space:]]EXIT([[:space:]]|;|$)/ { next }
{
guarded = /^[[:space:]]*trap[[:space:]]+.set \+e; / || /\|\|[[:space:]]+true.[[:space:]]+EXIT/
if (!guarded)
print FILENAME ":" FNR " unguarded"
# "set +e" on a trap shared with a signal leaves errexit off for the
# rest of the run, so a torn-down test could still report success
else if (/^[[:space:]]*trap[[:space:]]+.set \+e; / && /[[:space:]]EXIT[[:space:]]+[A-Z]/)
print FILENAME ":" FNR " set +e shared with a signal trap"
}
' "$top_srcdir"/tests/*.test "$top_srcdir"/tests/*.sh)
test -z "$bad" || fail "EXIT trap can decide the verdict:
$bad"
echo "OK: every EXIT trap under set -e runs teardown with errexit off"

View File

@@ -200,6 +200,7 @@ TESTS = \
95_local-sitemap.test \
96_local-refetch-keep.test \
97_local-warc-update-keep.test \
98_local-warc-segments.test
98_local-warc-segments.test \
99_teardown-status.test
CLEANFILES = check-network_sh.cache

View File

@@ -13,7 +13,7 @@ test -x "$wht" || {
work="$(mktemp -d)"
# webhttrack backgrounds htsserver, which outlives it; reap any stray one (scoped
# to this prefix) so a lingering server can never hold the CI step open.
trap 'pkill -f "$prefix/bin/htsserver" 2>/dev/null || true; rm -rf "$work"' EXIT
trap 'set +e; pkill -f "$prefix/bin/htsserver" 2>/dev/null || true; rm -rf "$work"' EXIT
export HOME="$work/home"
mkdir -p "$HOME/websites"
marker="$work/marker"