#!/usr/bin/env bash # scripts/check-test-counts.sh # # CI gate that verifies the totals in `doc/api/tests.md` match the # actual `ctest` output. Catches the canonical test-count document # going stale even if no other doc hardcodes the number. # # Usage: # bash scripts/check-test-counts.sh # # Exit codes: # 0 totals match # 1 totals diverge — prints diff and which file to fix # 2 prerequisites missing (cmake not found, tests.md missing, …) # # This script is meant to be cheap enough to run on every PR (~30 s on # a typical CI runner). It re-uses the existing build-cgal/ directory # if one is present; otherwise it builds a throwaway build-counts/. set -euo pipefail cd "$(dirname "${BASH_SOURCE[0]}")/.." # repo root TESTS_MD="doc/api/tests.md" if [ ! -f "$TESTS_MD" ]; then echo "FAIL: $TESTS_MD not found" >&2 exit 2 fi command -v cmake >/dev/null || { echo "FAIL: cmake not in PATH" >&2; exit 2; } command -v ctest >/dev/null || { echo "FAIL: ctest not in PATH" >&2; exit 2; } # ── Build ─────────────────────────────────────────────────────────────────── # Priority order: # 1. `BUILD_DIR` env var (CI sets it to the dir cpp-tests.yml just built). # 2. existing build-cgal/ if it has WITH_CGAL_TESTS=ON. # 3. throwaway build-counts/ — full build from scratch. if [ -n "${BUILD_DIR:-}" ]; then if [ ! -f "$BUILD_DIR/CMakeCache.txt" ]; then echo "FAIL: BUILD_DIR=$BUILD_DIR has no CMakeCache.txt" >&2 exit 2 fi if ! grep -q "WITH_CGAL_TESTS:.*=ON\|WITH_CGAL:.*=ON" "$BUILD_DIR/CMakeCache.txt"; then echo "FAIL: BUILD_DIR=$BUILD_DIR was not configured with WITH_CGAL_TESTS=ON" >&2 exit 2 fi elif [ -f build-cgal/CMakeCache.txt ] && grep -q "WITH_CGAL_TESTS:.*=ON\|WITH_CGAL:.*=ON" build-cgal/CMakeCache.txt; then BUILD_DIR=build-cgal cmake --build "$BUILD_DIR" -j"$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 2)" >/dev/null else BUILD_DIR=build-counts cmake -S code -B "$BUILD_DIR" -DWITH_CGAL_TESTS=ON -DCMAKE_BUILD_TYPE=Release >/dev/null cmake --build "$BUILD_DIR" -j"$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 2)" >/dev/null fi # ── Get actual counts ─────────────────────────────────────────────────────── cd "$BUILD_DIR" actual_cgal=$(ctest -R "^cgal" 2>&1 | grep "tests passed" | sed -E 's/.*out of ([0-9]+).*/\1/') actual_fast=$(ctest -E "^cgal" 2>&1 | grep "tests passed" | sed -E 's/.*out of ([0-9]+).*/\1/') cd - >/dev/null # ── Get claimed counts from tests.md ──────────────────────────────────────── # Expected format (in this order): # **Total: tests, 0 skipped.** (non-CGAL section) # ... # **Total: tests, 0 skipped.** (CGAL section) claimed_fast=$(grep -E "\*\*Total: [0-9]+ tests, 0 skipped\.\*\*" "$TESTS_MD" \ | head -n 1 | sed -E 's/.*Total: ([0-9]+) tests.*/\1/') claimed_cgal=$(grep -E "\*\*Total: [0-9]+ tests, 0 skipped\.\*\*" "$TESTS_MD" \ | tail -n 1 | sed -E 's/.*Total: ([0-9]+) tests.*/\1/') # ── Compare ──────────────────────────────────────────────────────────────── ok=1 if [ "${actual_fast}" != "${claimed_fast}" ]; then echo "MISMATCH: non-CGAL — $TESTS_MD claims ${claimed_fast}, ctest reports ${actual_fast}" ok=0 fi if [ "${actual_cgal}" != "${claimed_cgal}" ]; then echo "MISMATCH: CGAL — $TESTS_MD claims ${claimed_cgal}, ctest reports ${actual_cgal}" ok=0 fi if [ "$ok" -eq 0 ]; then cat <