Some checks failed
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (push) Successful in 10s
wai-audit collector and player integration / does this change touch it (push) Successful in 43s
joule-moq-web browser client / joule-moq-web · does this change touch it (push) Successful in 1m0s
wai-fleet service / does this change touch it (push) Successful in 1m0s
wai-app and Create / wai-app · does this change touch it (push) Successful in 1m1s
joule-moq-room room service / joule-moq-room · does this change touch it (push) Successful in 1m3s
WAI operation catalogue / does this change touch it (push) Successful in 1m2s
joule-moq-remote host agent / joule-moq-remote · does this change touch it (push) Successful in 1m8s
wai-ledger service / does this change touch it (push) Successful in 1m2s
wai-fleet service / service · macos (push) Successful in 45s
wai-quantum for JavaScript / does this change touch it (push) Successful in 1m2s
wai-sink-android native sink library / does this change touch it (push) Successful in 48s
wai-sink-swift native sink package / does this change touch it (push) Successful in 46s
WAI Studio / does this change touch it (push) Successful in 46s
wai-quantum studio and desktop / does this change touch it (push) Successful in 49s
wai-audit collector and player integration / collector · linux (push) Successful in 1m20s
wai-ledger service / service · macos (push) Successful in 1m2s
joule-moq-remote host agent / joule-moq-remote, every target (macos) (push) Successful in 1m2s
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (push) Successful in 1m26s
wai-app and Create / wai-app · tests, clippy, the line language's verifier (linux) (push) Successful in 1m21s
wai-ledger service / service · linux (push) Failing after 1m50s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (push) Successful in 3m15s
joule-moq-remote host agent / joule-moq-remote, every target (linux-arm64) (push) Successful in 2m9s
WAI Studio / the catalogue's actions, the unit tests and the core · linux (push) Successful in 1m54s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (push) Successful in 3m44s
wai-fleet service / service · linux (push) Successful in 3m1s
wai-quantum studio and desktop / desktop app · macos (push) Successful in 3m12s
wai-quantum studio and desktop / studio · browser (push) Successful in 4m9s
wai-quantum for JavaScript / javascript · linux (push) Successful in 4m29s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (push) Successful in 4m28s
joule-moq-room room service / joule-moq-room, every target (push) Successful in 5m26s
wai-app and Create / wai-app · Create, browser end-to-end (macos) (push) Successful in 6m10s
wai-quantum studio and desktop / desktop app · linux (push) Successful in 5m45s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (push) Successful in 8m3s
WAI operation catalogue / the catalogue and every door · linux (push) Successful in 7m22s
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (push) Successful in 7m23s
wai-quantum studio and desktop / desktop app · windows (push) Successful in 9m59s
WAI Studio / Studio in Chromium and Firefox · macos (push) Successful in 10m52s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (push) Successful in 13m34s
WAI Studio / Studio in WebKit · linux (push) Successful in 12m47s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (push) Successful in 15m58s
Every MUST test could pass on a first GET, which a cache relays from the origin, so a cache that broke §9 rule 3 only on what it stored passed the suite. C14 asks again, a second after the first GET, for an object, a head proof, an alias, a live-edge part, a receipt file, a playlist receipt, a key log and a parameter set, and holds each answer to the origin's bytes, ETag, Content-Type, Cache-Control and Content-Length, with no Vary. Every 206 is held to the origin's headers too. Three negative controls show the driver catches those faults: stored changes the headers of every answer after a path's first, ranged those of every 206, and reflip a byte of every answer after a path's first. S3 says how many of C14's repeats the cache answered without reaching the origin and S4 how many of the run's requests reached it; the summary marks a labelled cache whose store answered none as a relay. A WAI_CACHE_DIR whose labels file is empty fails the run as an absent one does, and a labelled cache that does not start has a row in the summary. wai http origin sends no Date while its clock reads before the epoch (RFC 9110 §6.6.1). Co-Authored-By: Claude Opus 5.5 <[email protected]>
103 lines
5.6 KiB
Shell
Executable file
103 lines
5.6 KiB
Shell
Executable file
#!/usr/bin/env bash
|
|
# Cache conformance (extensions/http-delivery.md §9): stage the corpus's objects, start
|
|
# the reference origin, and run driver.py
|
|
#
|
|
# 1. against the origin itself, which must pass every MUST test (the positive control);
|
|
# 2. against each negative-control proxy in mutators.py, which must fail the tests its
|
|
# defect reaches (the driver's own falsification);
|
|
# 3. against each labelled cache an environment provides: <dir>/labels lists the
|
|
# labels, and <dir>/<label>/start.sh <port> <origin URL> starts one in front of the
|
|
# origin (with version and config files beside it). <dir> is /opt/wai-cache, or
|
|
# WAI_CACHE_DIR where that is set. The public repository holds no cache
|
|
# configuration; without labels in /opt/wai-cache only 1 and 2 run, and the summary
|
|
# says so. A WAI_CACHE_DIR that names no label is an error, since it was asked for.
|
|
# The summary says how many repeat GETs each cache answered from its store, and marks
|
|
# one that answered none as a relay.
|
|
#
|
|
# WAI=<path to a `wai` built with http> [WAI_CACHE_DIR=<dir>] \
|
|
# bash wai/cache-conformance/run.sh <out-dir>
|
|
#
|
|
# Reports go to <out-dir>/reports: report-<label>.json and requirements-<label>.md.
|
|
# Exit status 1 when a control does not behave as stated or a labelled cache fails a
|
|
# MUST test.
|
|
set -uo pipefail
|
|
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
OUT="${1:?usage: run.sh <out-dir>}"
|
|
WAI="${WAI:?set WAI to a wai binary built with the http feature}"
|
|
CACHES="${WAI_CACHE_DIR:-/opt/wai-cache}"
|
|
mkdir -p "$OUT/reports"
|
|
ROOT="$OUT/root"
|
|
python3 "$HERE/driver.py" stage "$HERE/../http-conformance" "$ROOT" || exit 1
|
|
|
|
pids=()
|
|
cleanup() { for p in "${pids[@]}"; do kill "$p" 2>/dev/null; done; }
|
|
trap cleanup EXIT
|
|
|
|
free_port() { python3 -c 'import socket; s=socket.socket(); s.bind(("127.0.0.1",0)); print(s.getsockname()[1]); s.close()'; }
|
|
wait_port() { for _ in $(seq 1 100); do python3 -c "import socket,sys; socket.create_connection(('127.0.0.1',$1),1)" 2>/dev/null && return 0; sleep 0.1; done; return 1; }
|
|
|
|
OP="$(free_port)"
|
|
"$WAI" http origin --root "$ROOT" --port "$OP" --count-hits --live-prefix live/ --live-max-age 30 > "$OUT/origin.log" 2>&1 &
|
|
pids+=($!)
|
|
wait_port "$OP" || { echo "::error::the origin did not start"; cat "$OUT/origin.log"; exit 1; }
|
|
ORIGIN="http://127.0.0.1:$OP"
|
|
fail=0
|
|
summary="| run | expectation | MUST passed | result |\n|---|---|---|---|\n"
|
|
|
|
run_driver() { # label base expect-pass [expected failing ids]
|
|
local label="$1" base="$2" expect="$3"; shift 3
|
|
python3 "$HERE/driver.py" run --base "$base" --root "$ROOT" --origin "$ORIGIN" --label "$label" \
|
|
--report "$OUT/reports/report-$label.json" > "$OUT/reports/$label.log" 2>&1
|
|
local rc=$?
|
|
local passed; passed="$(tail -1 "$OUT/reports/$label.log" | sed 's/.*: //')"
|
|
if [ "$expect" = pass ]; then
|
|
if [ $rc -eq 0 ]; then summary+="| $label | every MUST passes | $passed | ok |\n"
|
|
else summary+="| $label | every MUST passes | $passed | **failed** |\n"; echo "::error::$label failed a MUST test"; cat "$OUT/reports/$label.log"; fail=1; fi
|
|
else
|
|
local missing=""
|
|
for id in "$@"; do grep -q "^FAIL $id:" "$OUT/reports/$label.log" || missing="$missing $id"; done
|
|
if [ $rc -ne 0 ] && [ -z "$missing" ]; then summary+="| $label | fails $* | $passed | caught |\n"
|
|
else summary+="| $label | fails $* | $passed | **not caught:$missing** |\n"; echo "::error::$label: the driver did not catch$missing (exit $rc)"; cat "$OUT/reports/$label.log"; fail=1; fi
|
|
fi
|
|
}
|
|
|
|
# 1. The origin, directly.
|
|
run_driver origin-direct "$ORIGIN" pass
|
|
|
|
# 2. The negative controls.
|
|
expect_of() { case "$1" in flip) echo "C1 C3 C11 C14" ;; gzip) echo "C7" ;; truncate) echo "C1 C2 C14" ;; vary) echo "C13" ;; stored) echo "C14" ;; ranged) echo "C3 C5 C8" ;; reflip) echo "C1 C14" ;; esac; }
|
|
for m in flip gzip truncate vary stored ranged reflip; do
|
|
MP="$(free_port)"
|
|
python3 "$HERE/mutators.py" --mode "$m" --listen "$MP" --upstream "$ORIGIN" > "$OUT/mutator-$m.log" 2>&1 &
|
|
pids+=($!)
|
|
wait_port "$MP" || { echo "::error::mutator $m did not start"; fail=1; continue; }
|
|
# shellcheck disable=SC2086
|
|
run_driver "control-$m" "http://127.0.0.1:$MP" fail $(expect_of "$m")
|
|
done
|
|
|
|
# 3. Labelled caches, where the environment provides them.
|
|
labels="$(cat "$CACHES/labels" 2>/dev/null | xargs)"
|
|
if [ -n "$labels" ]; then
|
|
for label in $labels; do
|
|
CP="$(free_port)"
|
|
"$CACHES/$label/start.sh" "$CP" "$ORIGIN" > "$OUT/cache-$label.log" 2>&1 &
|
|
pids+=($!)
|
|
wait_port "$CP" || { summary+="| $label | every MUST passes | — | **did not start** |\n"; echo "::error::cache $label did not start"; fail=1; continue; }
|
|
python3 "$HERE/driver.py" run --base "http://127.0.0.1:$CP" --root "$ROOT" --origin "$ORIGIN" --label "$label" \
|
|
--version "$(cat "$CACHES/$label/version" 2>/dev/null || echo unknown)" \
|
|
--config "$CACHES/$label/config" --report "$OUT/reports/report-$label.json" > "$OUT/reports/$label.log" 2>&1
|
|
rc=$?
|
|
stored="$(sed -n 's/^info S3: //p' "$OUT/reports/$label.log")"
|
|
case "$stored" in 0\ of*) stored="**a relay:** $stored" ;; esac
|
|
summary+="| $label | every MUST passes | $(tail -1 "$OUT/reports/$label.log" | sed 's/.*: //') | $([ $rc -eq 0 ] && echo ok || echo '**failed**'); $stored |\n"
|
|
[ $rc -eq 0 ] || fail=1
|
|
done
|
|
elif [ -n "${WAI_CACHE_DIR:-}" ]; then
|
|
summary+="| labelled caches | — | — | **no labels in $CACHES/labels** |\n"; echo "::error::WAI_CACHE_DIR names no label: $CACHES/labels"; fail=1
|
|
else
|
|
summary+="| labelled caches | — | — | none in this environment (no labels in $CACHES/labels): controls only |\n"
|
|
fi
|
|
|
|
printf "%b" "$summary" | tee "$OUT/reports/summary.md"
|
|
[ -n "${GITHUB_STEP_SUMMARY:-}" ] && printf "%b" "$summary" >> "$GITHUB_STEP_SUMMARY"
|
|
exit $fail
|