mirror of
https://github.com/xroche/httrack.git
synced 2026-08-13 03:02:19 +03:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8f0cec17d |
@@ -30,6 +30,11 @@ 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.
|
||||
- Each test runs under a 600s wall-clock guard that reports a wedge as 124. A test
|
||||
whose own work outlasts it raises the budget with a `# TEST_TIMEOUT_AT_LEAST: N`
|
||||
line, at column 0 within its first 40 lines, and paces itself with
|
||||
`skip_if_out_of_budget` so a host too slow to finish skips instead. The value only
|
||||
ever raises the budget: nothing can disarm the guard.
|
||||
- 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.
|
||||
|
||||
@@ -36,11 +36,43 @@ echo "marker: the wedged test started"
|
||||
sleep 300
|
||||
EOF
|
||||
|
||||
start=$SECONDS
|
||||
# Time the guard to its DUMP announcement, not to the driver's exit: the dump that
|
||||
# follows runs for minutes on an emulated host, and that is not what is under test.
|
||||
rc=0
|
||||
HTTRACK_PROGRESS_LOG="$tmp/progress" HTTRACK_TEST_TIMEOUT=5 \
|
||||
bash "$driver" "$tmp/90_wedged.test" >"$out" 2>&1 || rc=$?
|
||||
elapsed=$((SECONDS - start))
|
||||
fired=
|
||||
marked=
|
||||
total=
|
||||
run_wedged() { # run_wedged <budget> [VAR=VAL...]
|
||||
local budget=$1 start=$SECONDS pid
|
||||
shift
|
||||
: >"$tmp/progress"
|
||||
rc=0
|
||||
fired=
|
||||
marked=
|
||||
env "$@" HTTRACK_PROGRESS_LOG="$tmp/progress" HTTRACK_TEST_TIMEOUT="$budget" \
|
||||
bash "$driver" "$tmp/90_wedged.test" >"$out" 2>&1 &
|
||||
pid=$!
|
||||
while kill -0 "$pid" 2>/dev/null; do
|
||||
# Read with the shell: a fork per poll would blur the latency being measured.
|
||||
if read -r line <"$tmp/progress" 2>/dev/null && test "$line" = "DUMP 90_wedged.test"; then
|
||||
fired=$((SECONDS - start))
|
||||
marked=1
|
||||
break
|
||||
fi
|
||||
poll_wait 0.1 || sleep 1
|
||||
done
|
||||
wait "$pid" || rc=$?
|
||||
total=$((SECONDS - start))
|
||||
# Missed between two polls: the whole run then bounds the latency from above.
|
||||
test -n "$fired" || fired=$total
|
||||
}
|
||||
|
||||
# Retried once, because a dump short enough to fall between two polls is a race, not a
|
||||
# regression; announcing after the dump loses both attempts.
|
||||
run_wedged 5
|
||||
test -n "$marked" || run_wedged 5
|
||||
test -n "$marked" ||
|
||||
fail "the announcement was never seen while the guard ran, so its latency is unknown"
|
||||
|
||||
# Announced when the dump starts, which runs for minutes: a suite watchdog
|
||||
# reading that log would take the silence for a wedge and kill the step.
|
||||
@@ -49,8 +81,8 @@ grep -qx 'DUMP 90_wedged.test' "$tmp/progress" ||
|
||||
|
||||
test "$rc" -eq 124 || fail "wedged test reported $rc, want 124"
|
||||
# Never before the budget, or a slow-but-healthy test would be killed too.
|
||||
test "$elapsed" -ge 5 || fail "the guard fired early (${elapsed}s of a 5s budget)"
|
||||
test "$elapsed" -lt 30 || fail "the guard fired late (${elapsed}s)"
|
||||
test "$fired" -ge 5 || fail "the guard fired early (${fired}s of a 5s budget)"
|
||||
test "$fired" -lt 30 || fail "the guard fired late (${fired}s)"
|
||||
grep -q 'marker: the wedged test started' "$out" || fail "the test's own output was lost"
|
||||
# The header, not a bare name: the process list quotes the test's path too, so a
|
||||
# wrapper that named nothing would still match that.
|
||||
@@ -58,6 +90,20 @@ grep -q '^===== TIMEOUT: 90_wedged.test exceeded' "$out" ||
|
||||
fail "the diagnostics do not name the test"
|
||||
grep -q "own process tree" "$out" || fail "no process list in the diagnostics"
|
||||
|
||||
# Announced BEFORE the dump, not merely at some point during it: the watchdog reading
|
||||
# that log takes the silence of a dump for a wedge. Only a slow dump tells the two
|
||||
# orderings apart, and the dump's own ps is what this makes slow.
|
||||
if ! is_windows; then
|
||||
printf '#!/bin/sh\nsleep 3\nexec %s "$@"\n' "$(command -v ps)" >"$shim/ps"
|
||||
chmod +x "$shim/ps"
|
||||
run_wedged 5 "PATH=$shim:$PATH"
|
||||
rm -f "$shim/ps" # before the starve shim shares this directory
|
||||
test "$rc" -eq 124 || fail "the guard reported $rc under a slow dump, want 124"
|
||||
test -n "$marked" || fail "no announcement under a slow dump"
|
||||
test "$((total - fired))" -ge 2 ||
|
||||
fail "announced with the dump (${fired}s of ${total}s), not before it"
|
||||
fi
|
||||
|
||||
# The budget is read, not hard-coded: well under it, the same shape survives.
|
||||
printf 'sleep 3\necho "slow but healthy"\n' >"$tmp/92_slow.test"
|
||||
rc=0
|
||||
@@ -78,15 +124,11 @@ sleep 1
|
||||
# the loop it stretches is the same one poll_wait's fd tick drives.
|
||||
(
|
||||
starve_sleep "$shim" 4 || fail "could not install the slow sleep"
|
||||
start=$SECONDS
|
||||
rc=0
|
||||
HTTRACK_POLL_SLEEP=1 HTTRACK_TEST_TIMEOUT=1 \
|
||||
bash "$driver" "$tmp/90_wedged.test" >"$out" 2>&1 || rc=$?
|
||||
elapsed=$((SECONDS - start))
|
||||
run_wedged 1 HTTRACK_POLL_SLEEP=1
|
||||
test "$rc" -eq 124 || fail "starved guard reported $rc, want 124"
|
||||
# Generous: the diagnostics dump runs inside this window too.
|
||||
test "$elapsed" -lt 25 ||
|
||||
fail "budget counted polls, not seconds: ${elapsed}s for a 1s budget"
|
||||
# 10 stretched polls would be 40s; a handful of them is the whole margin here.
|
||||
test "$fired" -lt 25 ||
|
||||
fail "budget counted polls, not seconds: ${fired}s for a 1s budget"
|
||||
)
|
||||
|
||||
# --- exit status and output of a healthy test pass straight through ----------
|
||||
@@ -127,6 +169,84 @@ saw_budget 45 "an explicit budget"
|
||||
HTTRACK_TEST_TIMEOUT=0 bash "$driver" "$tmp/95_budget.test" >"$out" 2>&1
|
||||
saw_budget 0 "a disabled guard"
|
||||
|
||||
# --- a test may raise the budget, never lower it ----------------------------
|
||||
# 269's header sweep is n^2 compiles, real work that outlasts the wedge budget on an
|
||||
# emulated host; anything else asking would be disarming the guard.
|
||||
raiser() { # raiser <asked for>
|
||||
# shellcheck disable=SC2016 # the fixture has to read the variable, not us
|
||||
printf '# TEST_TIMEOUT_AT_LEAST: %s\necho "budget=${HTTRACK_TEST_TIMEOUT-unset}"\n' \
|
||||
"$1" >"$tmp/97_raise.test"
|
||||
}
|
||||
# 0900 must not read as octal, and a value past intmax must not reach `test`, which
|
||||
# errors on it rather than comparing and would leave the guard unarmed.
|
||||
for want in 900:900 5:600 garbage:600 0900:900 99999999999999999999:600 ' 900':600; do
|
||||
raiser "${want%%:*}"
|
||||
HTTRACK_TEST_TIMEOUT=600 bash "$driver" "$tmp/97_raise.test" >"$out" 2>&1
|
||||
saw_budget "${want##*:}" "a test asking for '${want%%:*}'"
|
||||
done
|
||||
raiser 900
|
||||
HTTRACK_TEST_TIMEOUT=0 bash "$driver" "$tmp/97_raise.test" >"$out" 2>&1
|
||||
saw_budget 0 "a raise under a disabled guard"
|
||||
# Read from the header only, or a test's own data would be one: 151 writes this line.
|
||||
window() { # window <lines of padding> <budget wanted>
|
||||
{
|
||||
i=0
|
||||
while test "$i" -lt "$1"; do
|
||||
i=$((i + 1))
|
||||
echo "# pad $i"
|
||||
done
|
||||
cat "$tmp/97_raise.test"
|
||||
} >"$tmp/97_deep.test"
|
||||
HTTRACK_TEST_TIMEOUT=600 bash "$driver" "$tmp/97_deep.test" >"$out" 2>&1
|
||||
saw_budget "$2" "a raise on line $(($1 + 1))"
|
||||
}
|
||||
raiser 900
|
||||
window 39 900 # the last line the header reaches
|
||||
window 40 600
|
||||
|
||||
# Enforced, not merely exported: the number the guard uses is the one it kills on.
|
||||
printf '# TEST_TIMEOUT_AT_LEAST: 900\nsleep 4\necho "outlived the default"\n' \
|
||||
>"$tmp/97_raise.test"
|
||||
rc=0
|
||||
HTTRACK_TEST_TIMEOUT=2 bash "$driver" "$tmp/97_raise.test" >"$out" 2>&1 || rc=$?
|
||||
test "$rc" -eq 0 || fail "a 4s test that raised the budget to 900 reported $rc"
|
||||
grep -q 'outlived the default' "$out" || fail "the raised budget killed the test anyway"
|
||||
printf '# TEST_TIMEOUT_AT_LEAST: 1\nsleep 3\necho "not shrunk"\n' >"$tmp/97_raise.test"
|
||||
rc=0
|
||||
HTTRACK_TEST_TIMEOUT=600 bash "$driver" "$tmp/97_raise.test" >"$out" 2>&1 || rc=$?
|
||||
test "$rc" -eq 0 || fail "a 3s test asking for a 1s budget reported $rc"
|
||||
grep -q 'not shrunk' "$out" || fail "the header shrank the budget and killed the test"
|
||||
|
||||
# --- one budget parser, and what is left of it ------------------------------
|
||||
secs() { # secs <value in the environment> <seconds it must read as>
|
||||
local got
|
||||
got=$(HTTRACK_TEST_TIMEOUT=$1 bash -c '. "$1"; budget_secs' _ "${testdir}/testlib.sh" 2>&1)
|
||||
test "$got" = "$2" || fail "budget_secs read '$1' as '$got', want $2"
|
||||
}
|
||||
secs 45 45
|
||||
secs 0900 900 # decimal, or $((...)) and test disagree on the same string
|
||||
secs garbage 600
|
||||
secs 99999999999999999999 600 # past intmax, where test errors instead of comparing
|
||||
secs 0 0
|
||||
|
||||
# budget_left hands a child what is left of it, and keeps 0 meaning "no guard".
|
||||
# shellcheck disable=SC2016 # the fixture has to call the helper, not us
|
||||
printf '. "%s"\nsleep 2\necho "left=$(budget_left) at=$SECONDS"\n' "${testdir}/testlib.sh" \
|
||||
>"$tmp/98_left.test"
|
||||
# Exact, against the clock the child itself read: a tolerance would pass a wrong epoch.
|
||||
HTTRACK_TEST_TIMEOUT=60 bash "$driver" "$tmp/98_left.test" >"$out" 2>&1
|
||||
got=$(sed -n 's/^left=\([0-9][0-9]*\) .*/\1/p' "$out")
|
||||
at=$(sed -n 's/^left=[0-9][0-9]* at=\([0-9][0-9]*\)$/\1/p' "$out")
|
||||
case "$got$at" in '' | *[!0-9]*) fail "a 60s budget printed '$(cat "$out")'" ;; esac
|
||||
test "$got" -eq "$((60 - at))" ||
|
||||
fail "a 60s budget left $got with $at gone, want $((60 - at))"
|
||||
HTTRACK_TEST_TIMEOUT=0 bash "$driver" "$tmp/98_left.test" >"$out" 2>&1
|
||||
grep -q '^left=0 ' "$out" || fail "a disabled guard left '$(cat "$out")', want 0"
|
||||
# Never 0 on an exhausted budget: a child would read that as the guard being off.
|
||||
HTTRACK_TEST_TIMEOUT=1 bash -c '. "$1"; sleep 2; echo "left=$(budget_left)"' \
|
||||
_ "${testdir}/testlib.sh" >"$out" 2>&1
|
||||
grep -qx 'left=1' "$out" || fail "an exhausted budget left '$(cat "$out")', want 1"
|
||||
|
||||
# --- a test too slow to finish skips instead of being killed ----------------
|
||||
# hppa spends ~150s on one configure run, and 124 takes the build down where 77
|
||||
# does not.
|
||||
|
||||
@@ -8,6 +8,8 @@ set -euo pipefail
|
||||
|
||||
# shellcheck source=tests/testlib.sh
|
||||
. "$(dirname "$0")/testlib.sh"
|
||||
# shellcheck source=tests/proclib.sh
|
||||
. "$(dirname "$0")/proclib.sh"
|
||||
|
||||
sh=${BASH_SHELL:-}
|
||||
test -n "$sh" || {
|
||||
@@ -51,11 +53,21 @@ chmod 755 "$tmp/fakebin/bash"
|
||||
mkfifo "$tmp/fifo"
|
||||
chmod 755 "$tmp/fifo"
|
||||
|
||||
# Sampled rather than polled per second: the size read is a fork, and an emulated
|
||||
# host pays for it. SILENCE clears the slowest single configure probe there.
|
||||
SAMPLE=5
|
||||
SILENCE=${HTTRACK_CONFIGURE_SILENCE:-120}
|
||||
RESERVE=15 # what killing the run and skipping still needs of the budget
|
||||
|
||||
n=0
|
||||
cases=16 # reject/accept calls below; pinned again once they have all run
|
||||
status=0
|
||||
log=
|
||||
rundir=
|
||||
took=0
|
||||
# What run() launches, so the checks below can hand it a child that hangs or one that
|
||||
# only crawls; nothing else may override it.
|
||||
configure_cmd=(bash "$tmp/src/configure" --disable-https)
|
||||
run() { # run <label> <env argument>...
|
||||
local label=$1 began=$SECONDS
|
||||
shift
|
||||
@@ -65,23 +77,56 @@ run() { # run <label> <env argument>...
|
||||
status=0
|
||||
# Capped: configure executes the candidate, and a hang wedges "make check" with no output
|
||||
# at all. Polled, not a backgrounded "sleep" watchdog, which outlives the run it guards.
|
||||
(cd "$rundir" && env "$@" bash "$tmp/src/configure" --disable-https) \
|
||||
local had_m=
|
||||
case "$-" in *m*) had_m=1 ;; esac
|
||||
# Own process group, so the kills below reach what configure spawned: bash 3.2 keeps
|
||||
# the subshell it runs in, and killing that alone leaves the child running (macOS).
|
||||
set -m
|
||||
(cd "$rundir" && env "$@" "${configure_cmd[@]}") \
|
||||
>"$rundir/log" 2>&1 &
|
||||
local pid=$! waited=0
|
||||
while test "$waited" -lt 300 && kill -0 "$pid" 2>/dev/null; do
|
||||
local pid=$! waited=0 quiet=0 size=0 now left
|
||||
test -n "$had_m" || set +m
|
||||
# A hang is silence, not slowness: configure writes a line per probe,
|
||||
# but hppa's emulated run can take longer overall than a runner's whole budget (#1146).
|
||||
while kill -0 "$pid" 2>/dev/null; do
|
||||
sleep 1
|
||||
waited=$((waited + 1))
|
||||
test "$((waited % SAMPLE))" -eq 0 || continue
|
||||
now=$(wc -c <"$rundir/log")
|
||||
if test "$now" -gt "$size"; then
|
||||
size=$now
|
||||
quiet=0
|
||||
else
|
||||
quiet=$((quiet + SAMPLE))
|
||||
fi
|
||||
test "$quiet" -lt "$SILENCE" || {
|
||||
kill_tree "$pid"
|
||||
echo "configure wrote nothing for ${quiet}s of ${waited}s for $label" >&2
|
||||
tail -5 "$rundir/log" >&2
|
||||
exit 1
|
||||
}
|
||||
# Still writing but out of time: skip, where the harness would kill the whole test
|
||||
# and take the build down with it. Only while writing, and only with a full silence
|
||||
# window still affordable, or a hang would reach this before the check above fires
|
||||
# and a wedge would report a skip. 0 is the guard off.
|
||||
left=$(budget_left)
|
||||
if test "$quiet" -eq 0 && test "$left" -ne 0 &&
|
||||
test "$left" -le "$((RESERVE + SILENCE))"; then
|
||||
kill_tree "$pid"
|
||||
echo "$label was still configuring ${waited}s in and the budget is out; skipping" >&2
|
||||
exit 77
|
||||
fi
|
||||
done
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
kill -9 "$pid" 2>/dev/null
|
||||
echo "configure did not return within ${waited}s for $label" >&2
|
||||
tail -5 "$rundir/log" >&2
|
||||
exit 1
|
||||
fi
|
||||
wait "$pid" || status=$?
|
||||
log=$(cat "$rundir/log")
|
||||
took=$((SECONDS - began))
|
||||
echo "run $n ($label): exit $status"
|
||||
skip_if_out_of_budget "$((cases - n))" "$((SECONDS - began))"
|
||||
}
|
||||
|
||||
# Pace here rather than in run(), which returns with the answer still unjudged: a
|
||||
# skip between the two would bury a configure that answered wrongly.
|
||||
paced() {
|
||||
skip_if_out_of_budget "$((cases - n))" "$took"
|
||||
}
|
||||
|
||||
reject() { # reject <label> <expected message> <env argument>...
|
||||
@@ -97,6 +142,7 @@ reject() { # reject <label> <expected message> <env argument>...
|
||||
tail -5 <<<"$log" >&2
|
||||
exit 1
|
||||
}
|
||||
paced
|
||||
}
|
||||
|
||||
# accept <label> <expected $(BASH_SHELL), "" for any> <expected message, "" for none> <env argument>...
|
||||
@@ -125,8 +171,69 @@ accept() {
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
paced
|
||||
}
|
||||
|
||||
# --- what run() does to a child that hangs, and to one that is merely slow -------
|
||||
# Driven through configure_cmd, since the real configure can do neither on demand.
|
||||
probe() { # probe <run number> <seconds of budget left> <command>...
|
||||
local want_n=$1 left=$2 rc=0
|
||||
shift 2
|
||||
(
|
||||
# shellcheck disable=SC2030 # the isolation is the point: the real count is next door
|
||||
n=$want_n SAMPLE=1 SILENCE=2
|
||||
# shellcheck disable=SC2030,SC2031 # likewise: the budget here is the probe's own
|
||||
export HTTRACK_TEST_TIMEOUT=$((SECONDS + left))
|
||||
configure_cmd=("$@")
|
||||
run probe
|
||||
) >"$tmp/probe.log" 2>&1 || rc=$?
|
||||
echo "$rc"
|
||||
}
|
||||
# A wedge must fail even with the budget gone, or #922 comes back as a skip.
|
||||
rc=$(probe 90 6 sleep 999)
|
||||
test "$rc" -eq 1 || fail "a silent configure with 6s of budget reported $rc, want 1"
|
||||
grep -q 'wrote nothing' "$tmp/probe.log" || fail "the hang was not named: $(cat "$tmp/probe.log")"
|
||||
# Slow but talking is the emulated buildd, and a skip there beats the harness kill.
|
||||
rc=$(probe 91 6 bash -c 'while :; do echo tick; sleep 1; done')
|
||||
test "$rc" -eq 77 || fail "a slow but writing configure with 6s of budget reported $rc, want 77"
|
||||
# The kill has to reach what configure spawned. bash 3.2 keeps the subshell around the
|
||||
# child, so killing that alone leaves a live configure behind: it outlives "make check"
|
||||
# and holds the CI step open to its own timeout, with the suite reporting no failure.
|
||||
rc=$(probe 92 6 bash -c 'sleep 987 & wait')
|
||||
test "$rc" -eq 1 || fail "a silent configure with a child of its own reported $rc, want 1"
|
||||
sleep 1
|
||||
! ps_snapshot | grep -q '[s]leep 987' || fail "the killed run left its child running"
|
||||
# The pacer must not fire before the case is judged: run() returns with the verdict
|
||||
# still unread, and a skip there would bury a configure that answered wrongly. Through
|
||||
# the real run(), since a stub cannot see a pacer left inside the one it replaced.
|
||||
verdict() { # verdict <accept|reject> <run number> <status the child exits with>
|
||||
local rc=0
|
||||
(
|
||||
# shellcheck disable=SC2030,SC2031 # the isolation is the point: the real run is next door
|
||||
# Cases still to come, or the pacer this is looking for would decline to fire.
|
||||
n=$2 cases=$(($2 + 5)) SAMPLE=1
|
||||
# Spent by the time the run ends, so a pacer anywhere after it would fire.
|
||||
# shellcheck disable=SC2030,SC2031 # likewise: the budget here is the probe's own
|
||||
export HTTRACK_TEST_TIMEOUT=$((SECONDS + 4))
|
||||
configure_cmd=(bash -c "sleep 3; exit $3")
|
||||
# Their arities differ, and an extra argument would reach run() as an env
|
||||
# assignment: the child would then fail to exec and answer the wrong question.
|
||||
case "$1" in
|
||||
accept) accept probe-verdict '' '' ;;
|
||||
*) reject probe-verdict '' ;;
|
||||
esac
|
||||
) >/dev/null 2>&1 || rc=$?
|
||||
test "$rc" -eq 1 || fail "$1 of a wrong answer with the budget spent reported $rc, want 1"
|
||||
}
|
||||
verdict accept 80 1 # configure rejected what it must accept
|
||||
verdict reject 81 0 # configure accepted what it must reject
|
||||
# The probes ran in subshells, so the real cases below start from a clean count.
|
||||
cases=16
|
||||
n=0
|
||||
status=0
|
||||
log=
|
||||
took=0
|
||||
|
||||
# The four that configure to completion run first. A reject stops at the
|
||||
# BASH_SHELL check and costs a fraction of one, and the pacer projects the step it
|
||||
# just timed: behind the cheap ones it read far too low and 151 met the harness
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
# a break needing three headers, or a macro the consumer defined first, is out
|
||||
# of reach here. The sweep is shared with the MSVC job, which has no automake to
|
||||
# install with and so stages the same list out of DevIncludes_DATA (#1153).
|
||||
#
|
||||
# n^2 compiles is real work, not a wedge: emulated, it needs more than the suite's
|
||||
# default budget, and the sweep paces itself against whatever is left of this one.
|
||||
# TEST_TIMEOUT_AT_LEAST: 900
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
@@ -69,7 +73,10 @@ done
|
||||
|
||||
sweep_argv=(--headers-dir "$tmp/include/httrack" --cc "${CC:-cc}" --cxx "$cxx")
|
||||
[ "${#cpp_argv[@]}" -eq 0 ] || sweep_argv+=(-- "${cpp_argv[@]}")
|
||||
bash "$testdir/install-headers-sweep.sh" "${sweep_argv[@]}" ||
|
||||
fail "installed headers do not survive every include order"
|
||||
rc=0
|
||||
bash "$testdir/install-headers-sweep.sh" --budget "$(budget_left)" "${sweep_argv[@]}" || rc=$?
|
||||
# 77 is the sweep giving up on a host too slow to finish it, not a broken header.
|
||||
[ "$rc" -ne 77 ] || exit 77
|
||||
[ "$rc" -eq 0 ] || fail "installed headers do not survive every include order"
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -14,7 +14,8 @@ set -euo pipefail
|
||||
|
||||
usage() {
|
||||
echo "usage: ${0##*/} {--srcdir DIR [--builddir DIR] | --headers-dir DIR}" \
|
||||
"[--backend cl|cc] [--cc CMD] [--cxx CMD] [--self-test] [-- CPPFLAGS...]" >&2
|
||||
"[--backend cl|cc] [--cc CMD] [--cxx CMD] [--budget SECONDS] [--self-test]" \
|
||||
"[-- CPPFLAGS...]" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
@@ -26,6 +27,7 @@ cc_cmd=""
|
||||
cxx_cmd=""
|
||||
cxx_set=0
|
||||
selftest=0
|
||||
budget=
|
||||
extra=()
|
||||
while [ $# -gt 0 ]; do
|
||||
case $1 in
|
||||
@@ -33,6 +35,10 @@ while [ $# -gt 0 ]; do
|
||||
selftest=1
|
||||
shift
|
||||
;;
|
||||
--budget)
|
||||
budget=${2-}
|
||||
shift 2 || usage
|
||||
;;
|
||||
--srcdir)
|
||||
srcdir=${2-}
|
||||
shift 2 || usage
|
||||
@@ -237,15 +243,45 @@ fi
|
||||
|
||||
began=$SECONDS
|
||||
bad=0
|
||||
# Sliced only for a caller that gave a budget: one call per batch cannot be given up on,
|
||||
# and an emulated compiler needs more time for it than the harness allows a test (#1146).
|
||||
# Unsliced elsewhere, so the Windows job keeps paying one compiler spawn per batch.
|
||||
if [ -n "$budget" ] && [ "$budget" -gt 0 ]; then
|
||||
export HTTRACK_TEST_TIMEOUT=$budget
|
||||
slices=8
|
||||
else
|
||||
slices=1
|
||||
fi
|
||||
slice=$(((${#units[@]} + slices - 1) / slices))
|
||||
# From the slice size, not from $slices: they differ whenever the units do not divide
|
||||
# evenly, and a step count that outlives the loop leaves the pacer projecting forever.
|
||||
per=$(((${#units[@]} + slice - 1) / slice))
|
||||
left=$((${#langs[@]} * ${#modes[@]} * per))
|
||||
swept=0
|
||||
for lang in "${langs[@]}"; do
|
||||
for mode in "${modes[@]}"; do
|
||||
compile "$lang" "$mode" "${units[@]}" || {
|
||||
head -40 "$sweep_log" >&2
|
||||
echo "the headers do not compile as $lang standalone and pairwise ($mode)" >&2
|
||||
bad=1
|
||||
}
|
||||
i=0
|
||||
while [ "$i" -lt "${#units[@]}" ]; do
|
||||
step=$SECONDS
|
||||
chunk=("${units[@]:i:slice}")
|
||||
swept=$((swept + ${#chunk[@]}))
|
||||
compile "$lang" "$mode" "${chunk[@]}" || {
|
||||
head -40 "$sweep_log" >&2
|
||||
echo "the headers do not compile as $lang standalone and pairwise ($mode)" >&2
|
||||
bad=1
|
||||
}
|
||||
i=$((i + slice))
|
||||
left=$((left - 1))
|
||||
# Only while nothing has failed: a skip past a real break would bury it.
|
||||
[ "$bad" -ne 0 ] || [ -z "$budget" ] ||
|
||||
skip_if_out_of_budget "$left" "$((SECONDS - step))"
|
||||
done
|
||||
done
|
||||
done
|
||||
# What reached the compiler, not what was generated: a slice loop that steps past a unit
|
||||
# would otherwise report the full set and pass.
|
||||
want=$((${#langs[@]} * ${#modes[@]} * ${#units[@]}))
|
||||
[ "$swept" -eq "$want" ] || fail "compiled $swept units of $want, the slicing lost some"
|
||||
echo "swept $n headers standalone and pairwise x ${#modes[@]} bytecode modes x ${langs[*]}" \
|
||||
"= $((${#modes[@]} * ${#langs[@]} * ${#units[@]})) units in $((SECONDS - began))s with $backend"
|
||||
[ "$bad" -eq 0 ] || exit 1
|
||||
|
||||
@@ -22,19 +22,39 @@ testdir=$(cd "$(dirname "$0")" && pwd)
|
||||
# (CRAWL_DEADLINE, 180s a pass) -- budget below that and a slow-but-legitimate
|
||||
# run would be killed. The slowest healthy test measures 39s. A non-numeric or
|
||||
# absurd value falls back; 0 disables the guard, for use under a debugger.
|
||||
budget=${HTTRACK_TEST_TIMEOUT:-600}
|
||||
case "$budget" in
|
||||
'' | *[!0-9]*) budget=600 ;;
|
||||
esac
|
||||
budget=$(budget_secs)
|
||||
|
||||
# The test script is the last argument; automake passes no others today.
|
||||
for path in "$@"; do :; done
|
||||
name=$(basename "$path")
|
||||
|
||||
# A test whose work legitimately outlasts the wedge budget says so in its header
|
||||
# (269 sweeps n^2 compiles and paces itself inside it). The name carries the rule the
|
||||
# reader cannot see: it raises the budget, so no test can disarm the guard. Read with
|
||||
# the shell to keep it off the per-test fork bill, and bounded, since bash's `test`
|
||||
# errors rather than compares past intmax and would leave the guard unarmed.
|
||||
if test "$budget" -gt 0 && test -r "$path"; then
|
||||
read_lines=0
|
||||
while test "$read_lines" -lt 40 && IFS= read -r line; do
|
||||
read_lines=$((read_lines + 1))
|
||||
case "$line" in
|
||||
'# TEST_TIMEOUT_AT_LEAST: '*)
|
||||
want=${line#'# TEST_TIMEOUT_AT_LEAST: '}
|
||||
case "$want" in
|
||||
'' | *[!0-9]* | ???????*) ;;
|
||||
*) test "$((10#$want))" -le "$budget" || budget=$((10#$want)) ;;
|
||||
esac
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done <"$path"
|
||||
fi
|
||||
|
||||
# Exported so a test can pace itself against the same number (skip_if_out_of_budget)
|
||||
# instead of being killed halfway.
|
||||
export HTTRACK_TEST_TIMEOUT="$budget"
|
||||
test "$budget" -gt 0 || exec "$BASH" "$@"
|
||||
|
||||
# The test script is the last argument; automake passes no others today.
|
||||
for name in "$@"; do :; done
|
||||
name=$(basename "$name")
|
||||
|
||||
# Give the test its own TMPDIR, so the hang dump can salvage exactly this test's
|
||||
# crawl logs instead of racing (and deleting) a sibling's under "make check -j".
|
||||
tmproot=${TMPDIR:-/tmp}
|
||||
|
||||
@@ -539,16 +539,39 @@ EOF
|
||||
# one step is slower than its neighbours. It asks an ordering of the callers
|
||||
# instead, expensive steps first, so no step left can outrun the reserve the one
|
||||
# before it set (#1146).
|
||||
skip_if_out_of_budget() { # skip_if_out_of_budget <steps left> <seconds the last took>
|
||||
local budget=${HTTRACK_TEST_TIMEOUT:-600} need=$(($2 + $2 / 2))
|
||||
# The budget test-timeout.sh enforces, in seconds, 0 being the guard off. The one
|
||||
# parser: a value bash arithmetic or test would choke on falls back to the default,
|
||||
# and a leading zero would otherwise read as octal in one place and decimal in the next.
|
||||
budget_secs() {
|
||||
local budget=${HTTRACK_TEST_TIMEOUT:-600}
|
||||
case "$budget" in '' | *[!0-9]* | ???????*) budget=600 ;; esac
|
||||
echo "$((10#$budget))"
|
||||
}
|
||||
|
||||
case "$budget" in '' | *[!0-9]*) budget=600 ;; esac
|
||||
skip_if_out_of_budget() { # skip_if_out_of_budget <steps left> <seconds the last took>
|
||||
local budget need=$(($2 + $2 / 2))
|
||||
|
||||
budget=$(budget_secs)
|
||||
test "$1" -gt 0 && test "$budget" -gt 0 || return 0
|
||||
test "$((SECONDS + need))" -ge "$budget" || return 0
|
||||
echo "$1 steps left, the last took ${2}s and the budget is ${budget}s; skipping" >&2
|
||||
exit 77
|
||||
}
|
||||
|
||||
# Seconds left of the budget, for a child pacing itself against it (269 hands it to
|
||||
# the sweep). Never below 1 unless the guard is off, when it stays 0.
|
||||
budget_left() {
|
||||
local budget left
|
||||
budget=$(budget_secs)
|
||||
test "$budget" -gt 0 || {
|
||||
echo 0
|
||||
return 0
|
||||
}
|
||||
left=$((budget - SECONDS))
|
||||
test "$left" -ge 1 || left=1
|
||||
echo "$left"
|
||||
}
|
||||
|
||||
# Collect a killed job, giving up after REAP_GRACE seconds. kill_tree can fail to
|
||||
# reap a native Windows descendant -- the very case these watchdogs exist for --
|
||||
# and a bare `wait` then blocks the watchdog itself forever, so the timeout it was
|
||||
|
||||
@@ -48,7 +48,12 @@ cd /bld
|
||||
bash "${GITHUB_WORKSPACE:-/src}/configure"
|
||||
make -j"$(nproc)"
|
||||
# The buildd's own invocation, so a failure here is the one it would report.
|
||||
make check -j"$(nproc)"
|
||||
rc=0
|
||||
make check -j"$(nproc)" || rc=$?
|
||||
# Always, not only where automake prints it: this leg exists to say what an emulated
|
||||
# host does. A paced-out skip must not read as coverage with no reason given.
|
||||
cat tests/test-suite.log || true
|
||||
test "$rc" -eq 0 || exit "$rc"
|
||||
|
||||
# make check exits 0 for an all-SKIP run, and this leg skips a lot by design, so
|
||||
# a container that quietly lost a dependency would report a green covering
|
||||
|
||||
Reference in New Issue
Block a user