check / check (push) Waiting to run
internal/database was the slowest test package, and three retention tests were nearly all of it. Most of their time went to seeding thousands of rows 500 per INSERT: the SQLite driver finds each parameter's value by scanning the statement's arguments from the first until it reaches that parameter's, so binding grows with the square of the parameter count, and a 500-row insert carries several thousand. Seeding 50 rows per insert stores the same rows. No test case or assertion changes. The header of script/test records the new figures; its 90s timeout stays. Model: opus-5-5
85 lines
3.9 KiB
Bash
Executable File
85 lines
3.9 KiB
Bash
Executable File
#!/bin/sh
|
|
# script/test: run the test suite.
|
|
#
|
|
# -timeout is applied by `go test` per package, not to the run as a whole, so
|
|
# it only has to clear the slowest single package. When this budget was set
|
|
# that was internal/handlers, measured in a cache-defeated builder stage on the
|
|
# 48-core shared build host (2026-08-18); load- and host-dependent, not
|
|
# invariants:
|
|
#
|
|
# 16.9s host load 5-20, GOMAXPROCS 48
|
|
# 45.9s / 47.3s / 49.0s three runs at deliberate host load 31-73
|
|
# 30.6s / 39.7s host load 5-20, GOMAXPROCS 6 / 4
|
|
# 67.3s / 97.5s host load 5-20, GOMAXPROCS 2 / 1
|
|
# 67.3s GOMAXPROCS 4 at deliberate host load 52-68
|
|
#
|
|
# The old 30s budget was breached by every loaded run and by every GOMAXPROCS
|
|
# at or below 6; at GOMAXPROCS 4 it failed outright ("panic: test timed out
|
|
# after 30s"), reproduced on 33e4fa4 with no other change.
|
|
#
|
|
# 90s matches the org-wide backstop in REPO_POLICIES.md and is sized here
|
|
# against the figures above: the worst case under native parallelism is 49.0s,
|
|
# and the compound GOMAXPROCS-4-under-load case at 67.3s sits at 75% of it.
|
|
# The one figure above 90s is GOMAXPROCS 1, a synthetic core floor rather than
|
|
# a condition CI runs under. If a CPU-limited runner ever puts a real run near
|
|
# 67s, that is the datum to revisit the org figure with.
|
|
#
|
|
# Those figures predate tests hashing the admin password at 1 MB instead of
|
|
# 64 MB (https://git.eeqj.de/sneak/webhooker/pulls/404). After that change, in
|
|
# a cache-defeated build at host load 44-109 (2026-10-02), internal/handlers
|
|
# took 8.5s and the slowest package was internal/database at 15.8s. Once its
|
|
# retention tests seeded 50 rows per insert instead of 500
|
|
# (https://git.eeqj.de/sneak/webhooker/issues/198), internal/database took
|
|
# 7.3s and the slowest package was internal/handlers at 8.1s to 10.0s, at host
|
|
# load 25-48 (2026-10-02).
|
|
#
|
|
# -p 4 -parallel 8 keep the run under 2 GB of memory: at most four test
|
|
# binaries build or run at once, each with at most eight parallel tests. Under
|
|
# -race every test binary and every link costs a few hundred MB, so the
|
|
# defaults (one per core) add up to several GB on a many-core host.
|
|
#
|
|
# The first run has no -v: go test then prints one result line per package,
|
|
# with its coverage, and for a package that fails, everything its tests wrote,
|
|
# application log lines included. Verbose output from the whole suite passes
|
|
# the 2 MiB at which the Docker build cuts off each step's log, so on a failure
|
|
# only the tests that failed run again, with -v. The script exits 1 after that
|
|
# rerun whatever its result: the first run already showed the suite is broken.
|
|
set -eu
|
|
|
|
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
|
|
|
main() {
|
|
cd "$ROOT"
|
|
"$ROOT/script/assets"
|
|
|
|
log="$(mktemp -t webhooker-test.XXXXXXXX)"
|
|
rcfile="$(mktemp -t webhooker-test-rc.XXXXXXXX)"
|
|
trap 'rm -f "$log" "$rcfile"' EXIT INT TERM
|
|
|
|
# The pipeline's status is tee's, and POSIX sh has no pipefail, so go
|
|
# test's status travels via a file. Output still streams live.
|
|
{
|
|
go test -race -cover -p 4 -parallel 8 -timeout 90s ./... 2>&1 \
|
|
&& echo 0 >"$rcfile" || echo $? >"$rcfile"
|
|
} | tee "$log"
|
|
if [ "$(cat "$rcfile")" -eq 0 ]; then
|
|
return
|
|
fi
|
|
|
|
# go test reports a failed test as a line starting "--- FAIL: TestName"
|
|
# (a failed subtest's line is indented, and reruns with its parent), and
|
|
# a failed package as "FAIL<tab>package/path<tab>...". A failure that
|
|
# names no test, such as a build error or a timeout, is already shown in
|
|
# full above, so there is nothing to rerun.
|
|
tests="$(awk '/^--- FAIL: / { print $3 }' "$log" | paste -s -d '|' -)"
|
|
packages="$(awk '/^FAIL\t/ { print $2 }' "$log")"
|
|
if [ -n "$tests" ]; then
|
|
echo "--- Rerunning the failed tests with -v for details ---"
|
|
go test -race -v -p 4 -parallel 8 -timeout 90s \
|
|
-run "^($tests)\$" $packages || true
|
|
fi
|
|
exit 1
|
|
}
|
|
|
|
main "$@"
|