126 lines
6.0 KiB
Bash
Executable File
126 lines
6.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Verify that the detector found exactly the pinned C1 phase verdicts. The
|
|
# fixture is expected to exit non-zero; this verifier is the green CI contract.
|
|
set -euo pipefail
|
|
|
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
MANIFEST="${MOSAIC_EXPECTED_RED_MANIFEST:-$ROOT/tools/fixtures/greenfield-expected-red.tsv}"
|
|
CASE="${1:?usage: verify-greenfield-expected-red.sh <case> <log> <fixture-exit>}"
|
|
LOG="${2:?usage: verify-greenfield-expected-red.sh <case> <log> <fixture-exit>}"
|
|
FIXTURE_EXIT="${3:?usage: verify-greenfield-expected-red.sh <case> <log> <fixture-exit>}"
|
|
|
|
[[ -r "$MANIFEST" ]] || { echo "expected-RED manifest is unreadable: $MANIFEST" >&2; exit 2; }
|
|
[[ -r "$LOG" ]] || { echo "fixture log is unreadable: $LOG" >&2; exit 2; }
|
|
[[ "$FIXTURE_EXIT" =~ ^[0-9]+$ ]] || { echo "fixture exit is not numeric: $FIXTURE_EXIT" >&2; exit 2; }
|
|
|
|
# Validate the entire pinned contract before selecting one case. Otherwise a
|
|
# deleted case/phase silently disappears from the gate and a one-row manifest
|
|
# can certify any exit-1 transcript.
|
|
expected_cases=(next-git-present main-git-present next-git-absent)
|
|
declare -A allowed_case=(
|
|
[next-git-present]=1 [main-git-present]=1 [next-git-absent]=1
|
|
)
|
|
declare -A expected_requires=(
|
|
[next-git-present]=6 [main-git-present]=6 [next-git-absent]=4
|
|
)
|
|
declare -A row_count=() exit_count=() require_count=() forbid_count=() phase_count=() unique_rows=()
|
|
while IFS= read -r raw; do
|
|
[[ -n "$raw" && "${raw:0:1}" != "#" ]] || continue
|
|
field_count="$(awk -F '\t' '{print NF}' <<<"$raw")"
|
|
[[ "$field_count" -eq 3 ]] || { echo "invalid expected-RED manifest row (expected exactly 3 tab fields): $raw" >&2; exit 2; }
|
|
IFS=$'\t' read -r case_name kind expectation <<<"$raw"
|
|
[[ -n "${allowed_case[$case_name]:-}" ]] || { echo "invalid expected-RED manifest case: $case_name" >&2; exit 2; }
|
|
unique_key="$case_name|$kind|$expectation"
|
|
[[ -z "${unique_rows[$unique_key]:-}" ]] || { echo "duplicate expected-RED manifest row: $raw" >&2; exit 2; }
|
|
unique_rows[$unique_key]=1
|
|
row_count[$case_name]=$((${row_count[$case_name]:-0} + 1))
|
|
case "$kind" in
|
|
exit)
|
|
[[ "$expectation" == 1 ]] || { echo "invalid expected-RED exit contract: case=$case_name expected=$expectation" >&2; exit 2; }
|
|
exit_count[$case_name]=$((${exit_count[$case_name]:-0} + 1))
|
|
;;
|
|
phase)
|
|
[[ "$expectation" =~ ^(P[0-9])=(PASS|FAIL)$ ]] \
|
|
|| { echo "invalid expected-RED phase disposition: case=$case_name value=$expectation" >&2; exit 2; }
|
|
phase="${BASH_REMATCH[1]}"
|
|
phase_key="$case_name|$phase"
|
|
phase_count[$phase_key]=$((${phase_count[$phase_key]:-0} + 1))
|
|
;;
|
|
require)
|
|
[[ -n "$expectation" ]] || { echo "empty expected-RED require row: case=$case_name" >&2; exit 2; }
|
|
require_count[$case_name]=$((${require_count[$case_name]:-0} + 1))
|
|
;;
|
|
forbid)
|
|
[[ -n "$expectation" ]] || { echo "empty expected-RED forbid row: case=$case_name" >&2; exit 2; }
|
|
forbid_count[$case_name]=$((${forbid_count[$case_name]:-0} + 1))
|
|
;;
|
|
*) echo "invalid expected-RED manifest kind: case=$case_name kind=$kind" >&2; exit 2 ;;
|
|
esac
|
|
done < "$MANIFEST"
|
|
|
|
for case_name in "${expected_cases[@]}"; do
|
|
[[ "${exit_count[$case_name]:-0}" -eq 1 ]] \
|
|
|| { echo "expected-RED manifest requires exactly one exit row for case=$case_name" >&2; exit 2; }
|
|
for phase in P0 P1 P2 P3 P4 P5 P6 P7 P8 P9; do
|
|
[[ "${phase_count[$case_name|$phase]:-0}" -eq 1 ]] \
|
|
|| { echo "expected-RED manifest requires exactly one $phase disposition for case=$case_name" >&2; exit 2; }
|
|
done
|
|
[[ "${require_count[$case_name]:-0}" -eq "${expected_requires[$case_name]}" ]] \
|
|
|| { echo "expected-RED manifest require-row population changed for case=$case_name" >&2; exit 2; }
|
|
[[ "${forbid_count[$case_name]:-0}" -eq 1 ]] \
|
|
|| { echo "expected-RED manifest requires exactly one forbid row for case=$case_name" >&2; exit 2; }
|
|
expected_total=$((1 + 10 + expected_requires[$case_name] + 1))
|
|
[[ "${row_count[$case_name]:-0}" -eq "$expected_total" ]] \
|
|
|| { echo "expected-RED manifest row population changed for case=$case_name" >&2; exit 2; }
|
|
done
|
|
[[ -n "${allowed_case[$CASE]:-}" ]] || { echo "unknown expected-RED verification case: $CASE" >&2; exit 2; }
|
|
|
|
checks=0
|
|
failures=0
|
|
while IFS=$'\t' read -r case_name kind expectation; do
|
|
[[ -n "$case_name" && "${case_name:0:1}" != "#" ]] || continue
|
|
[[ "$case_name" == "$CASE" ]] || continue
|
|
checks=$((checks + 1))
|
|
case "$kind" in
|
|
exit)
|
|
if [[ "$FIXTURE_EXIT" != "$expectation" ]]; then
|
|
echo "expected-RED mismatch: case=$CASE fixture_exit=$FIXTURE_EXIT expected=$expectation" >&2
|
|
failures=$((failures + 1))
|
|
fi
|
|
;;
|
|
phase)
|
|
phase="${expectation%%=*}"
|
|
expected_verdict="${expectation#*=}"
|
|
last_row="$(grep -E "^\[$phase\] (PASS|FAIL):" "$LOG" | tail -n 1 || true)"
|
|
actual_verdict="$(printf '%s\n' "$last_row" | sed -n "s/^\[$phase\] \(PASS\|FAIL\):.*/\1/p")"
|
|
if [[ "$actual_verdict" != "$expected_verdict" ]]; then
|
|
echo "expected-RED mismatch: case=$CASE phase=$phase got=${actual_verdict:-missing} expected=$expected_verdict" >&2
|
|
failures=$((failures + 1))
|
|
fi
|
|
;;
|
|
require)
|
|
if ! grep -Eq -- "$expectation" "$LOG"; then
|
|
echo "expected-RED missing required evidence: case=$CASE regex=$expectation" >&2
|
|
failures=$((failures + 1))
|
|
fi
|
|
;;
|
|
forbid)
|
|
if grep -Eq -- "$expectation" "$LOG"; then
|
|
echo "expected-RED found forbidden evidence: case=$CASE regex=$expectation" >&2
|
|
failures=$((failures + 1))
|
|
fi
|
|
;;
|
|
*)
|
|
echo "invalid expected-RED manifest kind: case=$case_name kind=$kind" >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
done < "$MANIFEST"
|
|
|
|
[[ "$checks" -gt 0 ]] || { echo "expected-RED manifest has no checks for case=$CASE" >&2; exit 2; }
|
|
if [[ "$failures" -ne 0 ]]; then
|
|
echo "expected-RED verification failed: case=$CASE failures=$failures checks=$checks" >&2
|
|
exit 1
|
|
fi
|
|
printf 'expected-RED verification passed: case=%s checks=%d\n' "$CASE" "$checks"
|