diff --git a/script/test b/script/test index 39bfcb7..9095510 100755 --- a/script/test +++ b/script/test @@ -28,10 +28,12 @@ # -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. # -# No -v: the Docker build cuts each step's log off at 2 MiB, and verbose output -# from the whole suite passes that before a failure is printed. Without it, go -# test prints one result line per package and, for a package that fails, -# everything its tests wrote, application log lines included. +# 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)" @@ -39,7 +41,34 @@ ROOT="$(cd "$(dirname "$0")/.." && pwd -P)" main() { cd "$ROOT" "$ROOT/script/assets" - go test -race -p 4 -parallel 8 -timeout 90s ./... + + 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 "FAILpackage/path...". 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 "$@"