name: C++ Tests on: push: branches: - main - dev - "claude/**" - "feature/**" pull_request: # /test-cgal comment on any PR triggers the CGAL test suite manually. issue_comment: types: [created] # ───────────────────────────────────────────────────────────────────────────── # Job 1 — test-fast # Pure-math tests (Clausen, ImLi₂, Hyper-ideal geometry). # No CGAL, no Boost. Eigen + GTest only. Runs on ALL branches. # ───────────────────────────────────────────────────────────────────────────── jobs: test-fast: runs-on: eulernest container: image: git.eulernest.eu/conformallab/ci-cpp:latest steps: - uses: actions/checkout@v4 - name: Configure (tests-only) run: cmake -S code -B build -DCMAKE_BUILD_TYPE=Release - name: Build run: nice -n 19 cmake --build build --target conformallab_tests -j$(nproc) - name: Run tests run: > ctest --test-dir build --output-on-failure --output-junit test-results.xml - name: Summary if: always() run: | if [ -f test-results.xml ]; then total=$(grep -o 'tests="[0-9]*"' test-results.xml | grep -o '[0-9]*' | head -1) failed=$(grep -o 'failures="[0-9]*"' test-results.xml | grep -o '[0-9]*' | head -1) skipped=$(grep -o 'skipped="[0-9]*"' test-results.xml | grep -o '[0-9]*' | head -1) passed=$(( ${total:-0} - ${failed:-0} - ${skipped:-0} )) echo "FAST ▸ TOTAL ${total:-0} | PASSED $passed | FAILED ${failed:-0} | SKIPPED ${skipped:-0}" fi # ───────────────────────────────────────────────────────────────────────────── # Job 2 — test-cgal # Full CGAL test suite (Phase 3–7, 158 tests). # Runs ONLY on pull requests (not on direct pushes to dev/main). # Starts only after test-fast succeeds. # # Uses -DWITH_CGAL_TESTS=ON (not -DWITH_CGAL=ON) to avoid building # Viewer/GLFW — the CI container has no wayland-scanner. # # Boost (libboost-dev) is already present in the container since the image rebuild. # ───────────────────────────────────────────────────────────────────────────── # ─── CGAL tests: manual trigger via PR comment ─────────────────────────── # # Trigger: write "/test-cgal" as a comment on any pull request. # # Why comment-triggered (not automatic on every PR push): # The Pi runner has 3-4 GB RAM total with swap constantly loaded. # Even with LOW_MEMORY_BUILD the CGAL build takes ~5 min and stresses # the runner. Triggering on every push would queue builds faster than # the Pi can drain them. A manual trigger gives full control: run the # 277-test suite when a PR is ready for review, not on every WIP commit. # # How it works: # - `issue_comment` fires on all PR + issue comments. # - The `if:` condition checks: # 1. Event is a comment (not a push or PR sync) # 2. The comment is on a PR (issue.pull_request != null) # 3. The comment body contains "/test-cgal" # - The checkout uses refs/pull/N/head to get the PR branch, because # `issue_comment` does not set GITHUB_REF to the PR branch by default. # # LOW_MEMORY_BUILD applies four RAM-saving measures so the build fits in # 2000 MB: -O0, PCH off, unity batch 1, --no-keep-memory linker. # See code/tests/cgal/CMakeLists.txt for the full explanation. test-cgal: needs: test-fast if: | github.event_name == 'issue_comment' && github.event.issue.pull_request != null && contains(github.event.comment.body, '/test-cgal') runs-on: eulernest container: image: git.eulernest.eu/conformallab/ci-cpp:latest # 2000 MB hard limit; 1000 MB swap headroom (memory-swap = RAM + swap). # Uses ~half of the Pi's 3-4 GB, leaving margin for OS + runner daemon. # With LOW_MEMORY_BUILD peak per TU is ~150-200 MB → well within limit. options: "--memory=2000m --memory-swap=3000m" steps: # Check out the PR branch, not the default branch. # issue_comment events set GITHUB_REF to the default branch; we need # refs/pull/N/head to get the actual PR code. - uses: actions/checkout@v4 with: ref: refs/pull/${{ github.event.issue.number }}/head - name: Configure (LOW_MEMORY_BUILD — -O0, no PCH, unity batch 1) run: | cmake -S code -B build \ -DWITH_CGAL_TESTS=ON \ -DCMAKE_BUILD_TYPE=Release \ -DCONFORMALLAB_LOW_MEMORY_BUILD=ON - name: Build CGAL-Tests run: nice -n 19 cmake --build build --target conformallab_cgal_tests -j1 - name: Run CGAL-Tests run: > ctest --test-dir build -R "^cgal\." --output-on-failure --output-junit cgal-results.xml - name: Summary if: always() run: | if [ -f cgal-results.xml ]; then total=$(grep -o 'tests="[0-9]*"' cgal-results.xml | grep -o '[0-9]*' | head -1) failed=$(grep -o 'failures="[0-9]*"' cgal-results.xml | grep -o '[0-9]*' | head -1) skipped=$(grep -o 'skipped="[0-9]*"' cgal-results.xml | grep -o '[0-9]*' | head -1) passed=$(( ${total:-0} - ${failed:-0} - ${skipped:-0} )) echo "CGAL ▸ TOTAL ${total:-0} | PASSED $passed | FAILED ${failed:-0} | SKIPPED ${skipped:-0}" fi # ── Structural gate: doc/api/tests.md totals match ctest reality ─── # Single source of truth for test counts (see doc/release-policy.md). # Reuses the already-built ./build dir via BUILD_DIR env var, so this # adds ~5 s on top of the existing CGAL job. - name: Verify test-count consistency (doc/api/tests.md) run: BUILD_DIR=build bash scripts/check-test-counts.sh # ── Structural gate: end-to-end smoke (try_it.sh) ────────────────── # The user-facing quick-start script: configure + build + run the # full ctest + run the Euclidean example on a bundled mesh. If # this regresses, README quick-start instructions are broken. # try_it.sh creates its own build-try/ — accept the ~3 min cost as # the price of guaranteeing the documented workflow stays working. - name: End-to-end smoke test (scripts/try_it.sh) run: bash scripts/try_it.sh # ───────────────────────────────────────────────────────────────────────────── # Job 3 — quality-gates (style + convention block) # # Trigger: write "/quality-gates" as a comment on any pull request. # # Cheap, deterministic checks (~30 s): license headers, CGAL conventions, # codespell, shellcheck. Runs independently of test-fast when comment- # triggered (no `needs:` — the caller decides when to invoke it). # ───────────────────────────────────────────────────────────────────────────── quality-gates: if: | github.event_name == 'issue_comment' && github.event.issue.pull_request != null && contains(github.event.comment.body, '/quality-gates') runs-on: eulernest container: image: git.eulernest.eu/conformallab/ci-cpp:latest steps: - uses: actions/checkout@v4 with: ref: refs/pull/${{ github.event.issue.number }}/head - name: Install codespell + shellcheck (job-local) run: | apt-get update -qq apt-get install -y --no-install-recommends \ codespell shellcheck - name: License headers (every C++ source carries MIT SPDX) run: bash scripts/quality/license-headers.sh - name: CGAL conventions (6 rules over CGAL public headers) run: python3 scripts/quality/cgal-conventions.py - name: codespell (docs + source comments + script messages) run: bash scripts/quality/codespell.sh - name: shellcheck (scripts/**/*.sh, severity=warning, strict) run: bash scripts/quality/shellcheck.sh --strict - name: Summary if: always() run: | echo "QUALITY ▸ all four gates passed." echo " see scripts/quality/README.md for the full catalogue" echo " (sanitizers, clang-tidy, coverage, etc. are local-only)"