Files
httrack/tests/151_bash-shell-validate.test
Xavier Roche a6315d04a3 Two suite tests fail on Debian's emulated hppa buildd (#1147)
* The hppa buildd kills 151 and hides the engine from 105

Debian's hppa is a qemu-user chroot, and two suite tests fail there in 3.49.20-1.

105 read /proc/<pid>/cmdline's first field as the program name, which binfmt_misc
gives to the interpreter, so its own probe never saw the fake engine start.
proclib already knew that shift; hoist the two regexes it matches on and reach
them from the shell too.

151 was killed at the 600s budget with four cases to go. The pacer reserved 1.5x
the last step for one more step, and its first twelve cases are cheap rejects
while the last four run configure to completion. Reserve for every step left
instead, past the first, which 196 spends warming a shared config.cache.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>

* Order 151's expensive cases first instead of projecting the pacer

Reserving every remaining step at the last one's cost skips a run that fits
whenever one step is slower than its neighbours: a 2x outlier in 196 case 2
projects over four remaining cases and skips a run that ends at 562s of 600.
Keep the one-step-ahead reserve and give 151 the ordering it needs instead --
its four full configures first, so no step left can outrun the reserve the one
before it set.

Also fold proc_program_name onto the awk prologue so the interpreter shift has
one implementation, keep qemu-img-static and friends out of the shift (they feed
kill -9), and pin the reserve's half-step margin, which no fixture covered.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>

---------

Signed-off-by: Xavier Roche <roche@httrack.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 07:06:51 +00:00

176 lines
6.3 KiB
Bash

#!/bin/bash
#
# An absolute BASH_SHELL is taken verbatim by AC_PATH_PROGS and a relative one is dropped
# for the search result, so configure has to check what it ends up with: BASH_SHELL=/bin/sh
# otherwise puts #895 back and only shows up at "make check" or "make deb" (#908).
set -euo pipefail
# shellcheck source=tests/testlib.sh
. "$(dirname "$0")/testlib.sh"
sh=${BASH_SHELL:-}
test -n "$sh" || {
echo "BASH_SHELL is empty; tests/Makefile.am must export the configured value" >&2
exit 1
}
configure="${abs_top_srcdir:-}/configure"
test -r "$configure" || {
echo "no configure script at $configure; tests/Makefile.am must export abs_top_srcdir" >&2
exit 1
}
tmp=$(mktemp -d)
cleanup() {
# O_RDWR never blocks, and it releases whatever is left stuck opening the FIFO for read.
exec 9<>"$tmp/fifo" && exec 9>&-
rm -rf "$tmp"
}
cleanup_push cleanup
# Symlink farm, not the real srcdir: an in-tree config.status makes autoconf refuse it.
mkdir "$tmp/src"
for f in "$abs_top_srcdir"/*; do
case "${f##*/}" in
config.status | config.log | config.h | stamp-h1 | Makefile) continue ;;
esac
ln -s "$f" "$tmp/src/"
done
mkdir "$tmp/notexec" "$tmp/with space" "$tmp/fakebin"
: >"$tmp/notexec/bash"
chmod 644 "$tmp/notexec/bash"
ln -s "$sh" "$tmp/mybash"
ln -s "$sh" "$tmp/with space/bash"
ln -s "$sh" "$tmp/sh" # bash invoked as "sh" enters POSIX mode
# A "bash" the PATH search will find first, and that answers -c like any other shell.
printf '#!/bin/sh\nexec /bin/sh "$@"\n' >"$tmp/fakebin/bash"
chmod 755 "$tmp/fakebin/bash"
# Executable to "test -x", but bash blocks in open() on one it failed to exec.
mkfifo "$tmp/fifo"
chmod 755 "$tmp/fifo"
n=0
cases=16 # reject/accept calls below; pinned again once they have all run
status=0
log=
rundir=
run() { # run <label> <env argument>...
local label=$1 began=$SECONDS
shift
n=$((n + 1))
rundir="$tmp/run$n"
mkdir "$rundir"
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) \
>"$rundir/log" 2>&1 &
local pid=$! waited=0
while test "$waited" -lt 300 && kill -0 "$pid" 2>/dev/null; do
sleep 1
waited=$((waited + 1))
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")
echo "run $n ($label): exit $status"
skip_if_out_of_budget "$((cases - n))" "$((SECONDS - began))"
}
reject() { # reject <label> <expected message> <env argument>...
local label=$1 want=$2
shift 2
run "$label" "$@"
test "$status" -ne 0 || {
echo "configure accepted $label" >&2
exit 1
}
grep -q "$want" <<<"$log" || {
echo "$label rejected without '$want':" >&2
tail -5 <<<"$log" >&2
exit 1
}
}
# accept <label> <expected $(BASH_SHELL), "" for any> <expected message, "" for none> <env argument>...
accept() {
local label=$1 path=$2 want=$3
shift 3
run "$label" "$@"
test "$status" -eq 0 || {
echo "configure rejected $label (exit $status):" >&2
tail -10 <<<"$log" >&2
exit 1
}
got=$(sed -n 's/^BASH_SHELL = //p' "$rundir/Makefile")
test -n "$got" || {
echo "$label configured, but the Makefile carries no BASH_SHELL" >&2
exit 1
}
if test -n "$path" && test "$got" != "$path"; then
echo "$label reached the Makefile as $got" >&2
exit 1
fi
if test -n "$want"; then
grep -q "$want" <<<"$log" || {
echo "$label configured without '$want':" >&2
tail -5 <<<"$log" >&2
exit 1
}
fi
}
# 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
# kill instead (#1146). Positive control first, as in 196.
accept override "$tmp/mybash" '' "BASH_SHELL=$tmp/mybash"
accept empty '' '' BASH_SHELL=
# No override: an unusable bash is a warning, so a box that has none still builds.
accept searched "$tmp/fakebin/bash" 'no usable bash found' -u BASH_SHELL "PATH=$tmp/fakebin:$PATH"
accept searched-posix-env '' 'POSIXLY_CORRECT or SHELLOPTS' -u BASH_SHELL POSIXLY_CORRECT=1
reject relative 'BASH_SHELL must be an absolute path' BASH_SHELL=relbash
notreg='is not an executable regular file'
reject missing "$notreg" "BASH_SHELL=$tmp/missing/bash"
reject non-executable "$notreg" "BASH_SHELL=$tmp/notexec/bash"
# "test -x" passes a FIFO, and configure then hangs forever instead of rejecting it (#922).
reject fifo "$notreg" "BASH_SHELL=$tmp/fifo"
reject sh-mode 'is a bash in POSIX sh-mode' "BASH_SHELL=$tmp/sh"
# The #908 case: dash on Linux, bash in sh-mode on macOS, unusable either way. The spoofed
# BASH_VERSION is what an ordinary shell echoes straight back, so it cannot be the probe.
reject /bin/sh 'BASH_SHELL=/bin/sh is ' BASH_SHELL=/bin/sh BASH_VERSION=9.9
# make splits the value on whitespace, cuts it at a '#' and expands a '$', and no Makefile
# quotes $(BASH_SHELL).
meta='must not contain shell or make metacharacters'
reject space "$meta" "BASH_SHELL=$tmp/with space/bash"
reject semicolon "$meta" 'BASH_SHELL=/opt/a;b/bash'
# shellcheck disable=SC2016 # the '$' has to reach configure unexpanded
reject dollar "$meta" 'BASH_SHELL=/opt/a$b/bash'
reject hash "$meta" 'BASH_SHELL=/opt/a#b/bash'
# An environment in POSIX mode leaves no path that could pass, so the message must blame it
# and not the shell.
reject posix-env 'POSIXLY_CORRECT or SHELLOPTS' "BASH_SHELL=$sh" POSIXLY_CORRECT=1
# Both at once: clearing the variable would still leave a bash invoked as sh, so the second
# probe has to decide which one to blame instead of reading the environment.
reject sh-mode-in-posix-env "BASH_SHELL=$tmp/sh is a bash in POSIX sh-mode" \
"BASH_SHELL=$tmp/sh" POSIXLY_CORRECT=1
if grep -q 'POSIXLY_CORRECT or SHELLOPTS' <<<"$log"; then
echo "a bash invoked as sh was blamed on the environment:" >&2
tail -5 <<<"$log" >&2
exit 1
fi
assert_steps_ran "$cases" "$n"
echo "configure validated $n BASH_SHELL values"