mirror of
https://github.com/xroche/httrack.git
synced 2026-08-22 11:19:00 +03:00
The offline suite waited on each httrack crawl with a bare wait bounded only by the engine's --max-time; a fetch that wedges past that (a Windows socket stall the engine misses) blocked wait forever and ran the test step to its 45-minute cap. wait_bounded attaches the #595 kill_tree reaper to the crawl pid so an overrun is reaped in seconds, stop_server now reaps the server's native tree, and 72_watchdog-crawl proves the fail-fast against an always-stall endpoint.
126 lines
4.2 KiB
Bash
126 lines
4.2 KiB
Bash
#!/bin/bash
|
|
#
|
|
# Helpers shared by the crawl tests. Sourced, not run.
|
|
|
|
# Python 3 interpreter, or empty: Windows only installs python.exe, and a bare
|
|
# "python" may be 2.x or the Store stub.
|
|
find_python() {
|
|
local py
|
|
for py in "${PYTHON:-}" python3 python; do
|
|
test -n "$py" || continue
|
|
"$py" -c 'import sys; sys.exit(sys.version_info[0] != 3)' 2>/dev/null || continue
|
|
printf '%s\n' "$py"
|
|
return 0
|
|
done
|
|
return 1
|
|
}
|
|
|
|
# Native form of a path: a non-MSYS binary cannot resolve Git Bash's /d/a/... ones.
|
|
nativepath() {
|
|
if is_windows && command -v cygpath >/dev/null 2>&1; then
|
|
cygpath -m "$1"
|
|
else
|
|
printf '%s\n' "$1"
|
|
fi
|
|
}
|
|
|
|
is_windows() {
|
|
case "$(uname -s)" in
|
|
MINGW* | MSYS* | CYGWIN*) return 0 ;;
|
|
*) return 1 ;;
|
|
esac
|
|
}
|
|
|
|
# On Windows MSYS can't signal a native python.exe, so kill_tree ends the whole
|
|
# tree (a bare kill -9 leaves children). "|| true" throughout: callers run under
|
|
# set -e and the reap makes wait return 143.
|
|
stop_server() {
|
|
test -n "${1:-}" || return 0
|
|
kill "$1" 2>/dev/null || true
|
|
if is_windows; then kill_tree "$1"; fi
|
|
wait "$1" 2>/dev/null || true
|
|
return 0
|
|
}
|
|
|
|
# Dump and clear the crawl logs a hard-killed test leaves in TMPDIR (its cleanup
|
|
# trap never ran): hts-log.txt alone records "More than N seconds passed.. giving
|
|
# up", so a wedge past --max-time is undiagnosable without it (#605).
|
|
dump_crawl_logs() {
|
|
local d f
|
|
for d in "${TMPDIR:-/tmp}"/httrack_local.*; do
|
|
test -d "$d" || continue
|
|
for f in "$d/crawl/hts-log.txt" "$d/log" "$d/log.2"; do
|
|
test -f "$f" || continue
|
|
# Leading newline: the killed test's last line has no terminator.
|
|
printf '\n--- %s (last 200 lines)\n' "$f"
|
|
tail -n 200 "$f"
|
|
done
|
|
# so a later test's dump cannot re-report this one; never fatal, the
|
|
# caller is already handling a failure and Windows may still hold a file
|
|
rm -rf "$d" || true
|
|
done
|
|
}
|
|
|
|
# Kill a backgrounded job and its whole descendant tree. POSIX: the caller must
|
|
# have put the job in its own process group (run_with_timeout does) so we signal
|
|
# the group; a bare kill would orphan the grandchildren. Windows: the tree is
|
|
# native processes MSYS can't signal, so taskkill /T ends it by Windows PID.
|
|
# Single-slash switches: the workflow sets MSYS_NO_PATHCONV/MSYS2_ARG_CONV_EXCL,
|
|
# so args pass verbatim and a //T would reach taskkill unfolded and be rejected.
|
|
kill_tree() {
|
|
local pid=$1
|
|
if is_windows; then
|
|
local winpid=
|
|
test -r "/proc/$pid/winpid" && winpid=$(cat "/proc/$pid/winpid" 2>/dev/null)
|
|
if test -n "$winpid"; then
|
|
taskkill /F /T /PID "$winpid" >/dev/null 2>&1 || true
|
|
else
|
|
# The offline suite runs serially, so no wanted process races this.
|
|
taskkill /F /IM httrack.exe >/dev/null 2>&1 || true
|
|
taskkill /F /IM python.exe >/dev/null 2>&1 || true
|
|
fi
|
|
fi
|
|
kill -9 -"$pid" 2>/dev/null || kill -9 "$pid" 2>/dev/null || true
|
|
}
|
|
|
|
# Run "$@" under a wall-clock deadline of $1 seconds; return its exit status, or
|
|
# 124 if it overran and was killed. timeout(1) is unusable here: it's absent on
|
|
# macOS and its signals can't reap httrack.exe on Windows. We poll and kill_tree.
|
|
run_with_timeout() {
|
|
local secs=$1
|
|
shift
|
|
local had_m=
|
|
case "$-" in *m*) had_m=1 ;; esac
|
|
is_windows || set -m # own process group, so kill_tree can signal the group
|
|
"$@" &
|
|
local pid=$!
|
|
test -n "$had_m" || is_windows || set +m
|
|
local waited=0
|
|
while kill -0 "$pid" 2>/dev/null; do
|
|
if test "$waited" -ge "$secs"; then
|
|
kill_tree "$pid"
|
|
wait "$pid" 2>/dev/null || true
|
|
return 124
|
|
fi
|
|
sleep 1
|
|
waited=$((waited + 1))
|
|
done
|
|
wait "$pid"
|
|
}
|
|
|
|
# Bound an already-backgrounded crawl (pid $1) at $2s, reaping it and returning 124
|
|
# on overrun: a wedge past --max-time would else block wait() forever and hang the CI step.
|
|
wait_bounded() {
|
|
local pid=$1 secs=$2 waited=0
|
|
while kill -0 "$pid" 2>/dev/null; do
|
|
if test "$waited" -ge "$secs"; then
|
|
kill_tree "$pid"
|
|
wait "$pid" 2>/dev/null || true
|
|
return 124
|
|
fi
|
|
sleep 1
|
|
waited=$((waited + 1))
|
|
done
|
|
wait "$pid"
|
|
}
|