127 lines
5.1 KiB
Bash
Executable File
127 lines
5.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
SUBJECT="$ROOT/tools/verify-greenfield-case-coverage.sh"
|
|
WORKFLOW="$ROOT/.woodpecker/greenfield-install.yml"
|
|
TMP="$(mktemp -d)"
|
|
trap 'rm -rf "$TMP"' EXIT
|
|
|
|
manifest="$TMP/expected-red.tsv"
|
|
state_root="$TMP/state"
|
|
run_id="pipeline-1"
|
|
printf '%s\n' \
|
|
'# representative manifest comment' \
|
|
$'# case\tkind\tkey/value' \
|
|
'' \
|
|
$'next-git-present\texit\t1' \
|
|
$'main-git-present\texit\t1' \
|
|
$'next-git-absent\texit\t1' > "$manifest"
|
|
|
|
bash "$SUBJECT" init "$manifest" "$state_root" "$run_id"
|
|
|
|
# Positive firing control: a skipped expected arm makes the final gate red.
|
|
bash "$SUBJECT" mark "$manifest" "$state_root" "$run_id" next-git-present
|
|
bash "$SUBJECT" mark "$manifest" "$state_root" "$run_id" main-git-present
|
|
set +e
|
|
skipped_output="$(bash "$SUBJECT" check "$manifest" "$state_root" "$run_id" 2>&1)"
|
|
skipped_status=$?
|
|
set -e
|
|
[[ "$skipped_status" -eq 1 ]]
|
|
grep -qF '[fixture-suite] cases_defined=3 cases_executed=2' <<<"$skipped_output"
|
|
grep -qF '[fixture-suite] missing_case=next-git-absent' <<<"$skipped_output"
|
|
|
|
# Count inflation cannot pass: equal counts with one missing and one unexpected remain red.
|
|
: > "$state_root/$run_id/unexpected-case.ran"
|
|
set +e
|
|
inflated_output="$(bash "$SUBJECT" check "$manifest" "$state_root" "$run_id" 2>&1)"
|
|
inflated_status=$?
|
|
set -e
|
|
[[ "$inflated_status" -eq 1 ]]
|
|
grep -qF '[fixture-suite] cases_defined=3 cases_executed=3' <<<"$inflated_output"
|
|
grep -qF '[fixture-suite] missing_case=next-git-absent' <<<"$inflated_output"
|
|
grep -qF '[fixture-suite] unexpected_case=unexpected-case' <<<"$inflated_output"
|
|
|
|
# Re-initializing the same run clears stale markers instead of certifying a later run.
|
|
bash "$SUBJECT" init "$manifest" "$state_root" "$run_id"
|
|
set +e
|
|
stale_output="$(bash "$SUBJECT" check "$manifest" "$state_root" "$run_id" 2>&1)"
|
|
stale_status=$?
|
|
set -e
|
|
[[ "$stale_status" -eq 1 ]]
|
|
grep -qF '[fixture-suite] cases_defined=3 cases_executed=0' <<<"$stale_output"
|
|
|
|
# Exact set equality is the sole green disposition.
|
|
for case_name in next-git-present main-git-present next-git-absent; do
|
|
bash "$SUBJECT" mark "$manifest" "$state_root" "$run_id" "$case_name"
|
|
done
|
|
complete_output="$(bash "$SUBJECT" check "$manifest" "$state_root" "$run_id")"
|
|
grep -qF '[fixture-suite] cases_defined=3 cases_executed=3' <<<"$complete_output"
|
|
|
|
# The repository's production manifest, including its comments, initializes and checks.
|
|
production_manifest="$ROOT/tools/fixtures/greenfield-expected-red.tsv"
|
|
production_state="$TMP/production-state"
|
|
bash "$SUBJECT" init "$production_manifest" "$production_state" production-1
|
|
for case_name in next-git-present main-git-present next-git-absent; do
|
|
bash "$SUBJECT" mark "$production_manifest" "$production_state" production-1 "$case_name"
|
|
done
|
|
production_output="$(bash "$SUBJECT" check "$production_manifest" "$production_state" production-1)"
|
|
grep -qF '[fixture-suite] cases_defined=3 cases_executed=3' <<<"$production_output"
|
|
|
|
# A future manifest case changes the derived set and cannot silently escape coverage.
|
|
printf '%s\n' $'future-case\texit\t1' >> "$manifest"
|
|
set +e
|
|
future_output="$(bash "$SUBJECT" check "$manifest" "$state_root" "$run_id" 2>&1)"
|
|
future_status=$?
|
|
set -e
|
|
[[ "$future_status" -eq 1 ]]
|
|
grep -qF '[fixture-suite] cases_defined=4 cases_executed=3' <<<"$future_output"
|
|
grep -qF '[fixture-suite] missing_case=future-case' <<<"$future_output"
|
|
|
|
# The workflow writes each marker only after that case's per-case verifier succeeds,
|
|
# and the final check is eligible after either success or failure.
|
|
python3 - "$WORKFLOW" <<'PY'
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
text = Path(sys.argv[1]).read_text(encoding="utf-8")
|
|
steps = {
|
|
match.group(1): match.group(2)
|
|
for match in re.finditer(r"(?ms)^ ([A-Za-z0-9_-]+):\n(.*?)(?=^ [A-Za-z0-9_-]+:\n|\Z)", text)
|
|
}
|
|
case_steps = (
|
|
("greenfield-git-present", "next-git-present"),
|
|
("greenfield-main-git-present", "main-git-present"),
|
|
("greenfield-git-absent", "next-git-absent"),
|
|
)
|
|
for step, case in case_steps:
|
|
block = steps[step]
|
|
verify = block.index("bash tools/verify-greenfield-expected-red.sh")
|
|
mark = block.index(f"mark \\\n tools/fixtures/greenfield-expected-red.tsv .greenfield-case-state \\\n \"$coverage_run\" {case}")
|
|
assert verify < mark, f"{step} marks execution before successful verification"
|
|
|
|
for step in (
|
|
"greenfield-git-present",
|
|
"greenfield-main-git-present",
|
|
"greenfield-remote-installer-contract",
|
|
"greenfield-git-absent",
|
|
):
|
|
assert re.search(
|
|
r"(?m)^ depends_on:\n - greenfield-case-denominator-init$", steps[step]
|
|
), f"{step} can race marker initialization"
|
|
|
|
final = steps["greenfield-case-denominator"]
|
|
assert "status: [success, failure]" in final
|
|
assert "bash tools/verify-greenfield-case-coverage.sh check" in final
|
|
for dependency in (
|
|
"greenfield-git-present",
|
|
"greenfield-main-git-present",
|
|
"greenfield-remote-installer-contract",
|
|
"greenfield-git-absent",
|
|
):
|
|
assert f" - {dependency}\n" in final, f"final gate can race {dependency}"
|
|
PY
|
|
|
|
printf 'greenfield case coverage tests passed\n'
|