name: C++ Tests # Trigger keywords in commit message (checked via head_commit.message): # /test-cgal — full CGAL test suite (277 tests, ~5 min build) # /quality-gates — license, codespell, shellcheck, CGAL conventions # # ⚠ /ci-all was removed: the Pi runner (3-4 GB RAM) cannot sustain # multiple Docker containers simultaneously. Use one keyword per commit. # # Examples: # git commit -m "fix: correct angle formula /test-cgal" # git commit -m "chore: update headers /quality-gates" # # test-fast always runs on every push — it is fast (< 5 s) and cheap. on: push: branches: - main - "claude/**" - "feature/**" - "review/**" pull_request: # ───────────────────────────────────────────────────────────────────────────── # Job 1 — test-fast # Pure-math tests (Clausen, ImLi₂, Hyper-ideal geometry). # No CGAL, no Boost. Eigen + GTest only. Runs on EVERY push. # ───────────────────────────────────────────────────────────────────────────── jobs: test-fast: runs-on: eulernest container: image: git.eulernest.eu/conformallab/ci-cpp:latest options: "--memory=800m --memory-swap=1200m" steps: - uses: actions/checkout@v4 - name: Configure (tests-only) run: cmake -S code -B build -DCMAKE_BUILD_TYPE=Release - name: Build # Serial build (-j1): the 800m-limited container OOM-kills cc1plus when # several Eigen+GoogleTest translation units compile in parallel # (`-j$(nproc)`), failing test-fast non-deterministically regardless of # branch content. Mirrors the Pi-protection already used by test-cgal. run: nice -n 19 cmake --build build --target conformallab_tests -j1 - 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 # # Trigger: include "/test-cgal" anywhere in the commit message. # # git commit -m "fix: correct angle formula /test-cgal" # # Why keyword-triggered (not automatic on every push): # The Pi runner (3-4 GB RAM, swap heavily loaded) cannot sustain a # CGAL build on every WIP commit. Adding the keyword to a commit # message explicitly signals "this commit is ready for full testing". # # LOW_MEMORY_BUILD applies four RAM-saving measures so the build fits in # a 2000 MB container: -O0, no PCH, unity batch 1, --no-keep-memory. # See code/tests/cgal/CMakeLists.txt for the full explanation. # ───────────────────────────────────────────────────────────────────────────── test-cgal: needs: test-fast if: contains(github.event.head_commit.message, '/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). # With LOW_MEMORY_BUILD peak per TU is ~150-200 MB → well within limit. options: "--memory=2000m --memory-swap=3000m" steps: - uses: actions/checkout@v4 - 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 - name: Verify test-count consistency (doc/api/tests.md) run: BUILD_DIR=build bash scripts/check-test-counts.sh - name: End-to-end smoke test (scripts/try_it.sh) run: bash scripts/try_it.sh # ───────────────────────────────────────────────────────────────────────────── # Job 3 — quality-gates # # Trigger: include "/quality-gates" anywhere in the commit message. # # git commit -m "chore: update docs /quality-gates" # # Cheap (~30 s): license headers, CGAL conventions, codespell, shellcheck. # ───────────────────────────────────────────────────────────────────────────── quality-gates: if: contains(github.event.head_commit.message, '/quality-gates') runs-on: eulernest container: image: git.eulernest.eu/conformallab/ci-cpp:latest options: "--memory=600m --memory-swap=900m" steps: - uses: actions/checkout@v4 - 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: Install lcov (coverage gate) run: apt-get install -y --no-install-recommends lcov - name: Coverage gate (fast-test suite, ramp-up mode) # SKIP_COVERAGE_GATE=1: reports numbers but does not fail on threshold. # Remove once I5 is resolved (coverage is wired to the full CGAL suite, # not just the 26 fast tests). Threshold: 80% line / 70% branch / 90% func. run: SKIP_COVERAGE_GATE=1 bash scripts/quality/coverage.sh - name: Summary if: always() run: | echo "QUALITY ▸ all gates passed." echo " see scripts/quality/README.md for the full catalogue" echo " (sanitizers, clang-tidy, coverage report in build-coverage/)"