Files
ConformalLabpp/scripts/quality/sanitizers.sh
Tarik Moussa 1aa3493e7d
Some checks failed
C++ Tests / test-fast (pull_request) Successful in 1m57s
API Docs / doc-build (pull_request) Successful in 48s
Markdown link check / check (pull_request) Successful in 51s
C++ Tests / test-cgal (pull_request) Failing after 12m23s
quality: 4 more gates + dependency audit; full --fast sweep 10/10 green
This commit closes the structural-tests work on PR #18.  Every gate
in `run-all.sh --fast` now passes end-to-end on the canonical dev
machine.

New gates
─────────
1. shellcheck (scripts/quality/shellcheck.sh)
   * Scans every `scripts/**/*.sh` at severity=warning+
   * 16 scripts inspected; cleanup pass took the tree from 7 findings
     (SC2164 + SC2034) to 0 findings.

2. cppcheck (scripts/quality/cppcheck.sh)
   * Complementary static analyser to clang-tidy; different heuristics,
     fewer false-positives on heavy CGAL/Eigen templates.
   * Default severity warning+, --strict adds style, --all = everything.
   * Suppresses 4 noise classes (missingIncludeSystem, etc.) explicitly.

3. .editorconfig
   * Cross-IDE fallback for editors that don't honour clang-format.
   * Covers Markdown (preserve trailing whitespace), Python, YAML,
     JSON, shell, Makefile (tabs) — the file types clang-format
     doesn't cover.

4. CONFORMALLAB_WARNINGS_AS_ERRORS CMake option
   * Off by default → regular builds don't break on new GCC warnings.
   * `-DCONFORMALLAB_WARNINGS_AS_ERRORS=ON` adds `-Werror`, intended for
     CI promotion-track and sanitizer runs.

Dependency audit  (doc/architecture/dependencies.md)
────────────────────────────────────────────────────
New single-source-of-truth document listing:
  * what the library requires (Eigen + CGAL + Boost — all header-only)
  * what tests require (auto-fetched GTest, no system install)
  * what each quality tool is for, install command per OS, and
    behaviour when missing (each gate exits 2 = SKIP, run-all
    recognises this and continues)
  * a verification recipe that strips PATH down and shows the
    library still configures + builds + tests cleanly with zero
    quality tools installed.

run-all.sh enhanced
───────────────────
* Recognises "tool not in PATH" → SKIP (not FAIL).
* Summary now reports `passed / skipped / failed` separately.

Bug fixes uncovered by the sweep
────────────────────────────────
* sanitizers.sh: gtest_discover_tests ran the ASan-instrumented
  binary at build time and aborted → added
  `-DCMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE=PRE_TEST` to defer
  discovery to ctest invocation.  Now 23/23 sanitizer-instrumented
  tests pass.

* clang-tidy.sh on macOS: brew-installed clang-tidy couldn't find
  Apple SDK system headers (<cmath>, <complex>, …) → added
  `--extra-arg=-isysroot $(xcrun --show-sdk-path)` on Darwin.

* clang-tidy.sh: needed `-DWITH_CGAL_TESTS=ON` in compile_commands
  generation so CGAL include paths are part of at least one
  compile entry.  Now resolves CGAL/Surface_mesh.h etc.

* clang-tidy.sh: viewer-only headers (`viewer_utils.h`, `mesh_utils.hpp`)
  excluded — they need `WITH_VIEWER=ON` + system GLFW/libigl that the
  lint build doesn't drag in.

* `.codespellrc`: extended ignore list (recognise, signalled, modelled,
  travelled, …) for British-English consistency across own writing.

Final state — local quality block on this commit, this branch:

     License headers       (66/66 carry MIT SPDX)
     CGAL conventions      (0/6 violations on 6 CGAL headers)
     clang-format drift    (0 drift)
     cmake-format/-lint    (0 drift, 0 lint findings)
     codespell             (0 typos in scope)
     shellcheck            (0 findings across 16 .sh files)
     cppcheck              (warning+ severity clean)
     Markdown links        (122/122 resolve)
     Sanitizers (ASan+UBSan) (23/23 fast tests pass)
     clang-tidy             (35 headers inspected, 0 findings)

Library standalone-ness verified:
    env -i PATH=... cmake -S code -B /tmp/build-standalone
    cmake --build /tmp/build-standalone --target conformallab_tests
    ctest -E '^cgal\.'    →  all green

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 09:56:40 +02:00

96 lines
4.2 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# scripts/quality/sanitizers.sh
#
# Build the fast test suite with AddressSanitizer + UndefinedBehaviorSanitizer
# and run it. Catches:
# * use-after-free, double-free, heap-buffer-overflow (ASan)
# * signed integer overflow, NaN propagation, alignment violations (UBSan)
# * Eigen / CGAL template-induced UB that escapes the regular build
#
# Local-only (not in CI): the sanitizer build is ~3× slower and brittle
# against system libraries. Run it before every release tag, after
# touching any Newton/Hessian code, or when investigating intermittent
# test failures.
#
# Usage:
# bash scripts/quality/sanitizers.sh # default: ASan + UBSan
# ASAN_OPTIONS=... UBSAN_OPTIONS=... bash scripts/quality/sanitizers.sh
#
# Exit codes:
# 0 every test passes under sanitizer instrumentation
# 1 a sanitizer report was triggered (test failure or runtime error)
# 2 prerequisite missing (no clang/gcc with sanitizer support)
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
cd "$ROOT"
BUILD_DIR="build-sanitizers"
SAN_FLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer -O1 -g"
# Default ASan/UBSan runtime options — print stack on first error,
# abort on first issue (so CI logs make the cause obvious).
export ASAN_OPTIONS="${ASAN_OPTIONS:-detect_leaks=1:abort_on_error=1:print_stacktrace=1}"
export UBSAN_OPTIONS="${UBSAN_OPTIONS:-print_stacktrace=1:halt_on_error=1}"
echo "========================================"
echo " Sanitizer build (ASan + UBSan)"
echo " flags : $SAN_FLAGS"
echo " ASAN : $ASAN_OPTIONS"
echo " UBSAN : $UBSAN_OPTIONS"
echo "========================================"
# ── Pick a compiler with sanitizer support ──────────────────────────────────
# Prefer clang (better diagnostics); fall back to gcc.
CXX_BIN=""
for cand in clang++-17 clang++-16 clang++-15 clang++ g++; do
if command -v "$cand" >/dev/null 2>&1; then
CXX_BIN="$cand"
break
fi
done
if [ -z "$CXX_BIN" ]; then
echo "FAIL: no clang++ / g++ found in PATH" >&2
exit 2
fi
echo "Using CXX = $CXX_BIN ($("$CXX_BIN" --version | head -1))"
echo
# ── Configure ────────────────────────────────────────────────────────────────
# CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE=PRE_TEST: without this,
# gtest_discover_tests runs the (sanitizer-instrumented) test binary at
# *build* time to enumerate test cases. ASan aborts that subprocess
# the moment it sees any allocation in static-init, which fails the
# build before we can even get to ctest. PRE_TEST defers discovery to
# `ctest` invocation, which is exactly what we want.
cmake -S code -B "$BUILD_DIR" \
-DCMAKE_CXX_COMPILER="$CXX_BIN" \
-DCMAKE_CXX_FLAGS="$SAN_FLAGS" \
-DCMAKE_EXE_LINKER_FLAGS="$SAN_FLAGS" \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE=PRE_TEST \
-Wno-dev
# ── Build the fast (non-CGAL) tests only ────────────────────────────────────
# CGAL tests would 45× the build time under sanitizers and have a
# higher false-positive surface (CGAL's expression-template trickery).
# Use the fast suite as the sanitizer canary; full coverage of the CGAL
# layer is covered by coverage.sh + the regular Release build.
nice -n 19 cmake --build "$BUILD_DIR" --target conformallab_tests \
-j"$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 2)"
# ── Run ──────────────────────────────────────────────────────────────────────
cd "$BUILD_DIR"
if ctest -E "^cgal\." --output-on-failure --output-junit san-results.xml; then
cd "$ROOT"
echo
echo "OK: all sanitizer-instrumented tests passed."
exit 0
else
cd "$ROOT"
echo
echo "FAIL: sanitizer-instrumented tests reported issues."
echo " See: $BUILD_DIR/Testing/Temporary/LastTest.log"
exit 1
fi