Files
stack/tools/verify-greenfield-case-coverage.sh
T
be-coder-05 f33bd0da96
ci/woodpecker/pr/ci Pipeline was canceled
ci/woodpecker/pr/greenfield-install Pipeline was canceled
ci(installer): enforce greenfield case coverage
2026-08-06 02:52:14 -05:00

89 lines
2.9 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
usage() {
echo "usage: $0 <init|mark|check> <manifest> <state-root> <run-id> [case]" >&2
}
[[ "$#" -ge 4 ]] || { usage; exit 2; }
mode="$1"
manifest="$2"
state_root="$3"
run_id="$4"
case_name="${5:-}"
[[ -s "$manifest" ]] || { echo "[fixture-suite] manifest missing or empty: $manifest" >&2; exit 2; }
[[ -n "$state_root" && "$state_root" != / ]] \
|| { echo "[fixture-suite] unsafe state root" >&2; exit 2; }
[[ "$run_id" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ ]] \
|| { echo "[fixture-suite] invalid run id: $run_id" >&2; exit 2; }
run_dir="$state_root/$run_id"
expected_cases() {
awk -F '\t' 'NF && $0 !~ /^[[:space:]]*#/ && !seen[$1]++ { print $1 }' "$manifest" | LC_ALL=C sort
}
validate_expected_cases() {
local found=0 expected
while IFS= read -r expected; do
[[ -n "$expected" ]] || continue
found=1
[[ "$expected" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ ]] \
|| { echo "[fixture-suite] invalid manifest case name: $expected" >&2; return 1; }
done < <(expected_cases)
[[ "$found" -eq 1 ]] \
|| { echo "[fixture-suite] manifest defines no cases" >&2; return 1; }
}
validate_expected_cases
case "$mode" in
init)
[[ "$#" -eq 4 ]] || { usage; exit 2; }
mkdir -p "$run_dir"
find "$run_dir" -mindepth 1 -delete
;;
mark)
[[ "$#" -eq 5 ]] || { usage; exit 2; }
[[ "$case_name" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ ]] \
|| { echo "[fixture-suite] invalid case marker name: $case_name" >&2; exit 2; }
[[ -d "$run_dir" ]] \
|| { echo "[fixture-suite] run state was not initialized: $run_id" >&2; exit 1; }
expected_cases | grep -Fxq -- "$case_name" \
|| { echo "[fixture-suite] case is not defined by manifest: $case_name" >&2; exit 1; }
: > "$run_dir/$case_name.ran"
;;
check)
[[ "$#" -eq 4 ]] || { usage; exit 2; }
mkdir -p "$run_dir"
expected_file="$(mktemp "$run_dir/.expected.XXXXXX")"
actual_file="$(mktemp "$run_dir/.actual.XXXXXX")"
cleanup() { rm -f "$expected_file" "$actual_file"; }
trap cleanup EXIT
expected_cases > "$expected_file"
find "$run_dir" -mindepth 1 -maxdepth 1 -type f -name '*.ran' -printf '%f\n' \
| sed 's/\.ran$//' | LC_ALL=C sort -u > "$actual_file"
cases_defined="$(wc -l < "$expected_file" | tr -d ' ')"
cases_executed="$(wc -l < "$actual_file" | tr -d ' ')"
printf '[fixture-suite] cases_defined=%s cases_executed=%s\n' \
"$cases_defined" "$cases_executed"
if ! cmp -s "$expected_file" "$actual_file"; then
while IFS= read -r missing; do
[[ -n "$missing" ]] && printf '[fixture-suite] missing_case=%s\n' "$missing" >&2
done < <(comm -23 "$expected_file" "$actual_file")
while IFS= read -r unexpected; do
[[ -n "$unexpected" ]] && printf '[fixture-suite] unexpected_case=%s\n' "$unexpected" >&2
done < <(comm -13 "$expected_file" "$actual_file")
exit 1
fi
;;
*)
usage
exit 2
;;
esac