Files
codebase-memory-mcp/scripts/test.sh
T
Martin Vogel bd939fc49a perf(test): run suites as parallel processes — same gates, ~2.5x faster
The runner executed all 104 suites sequentially (~10 min locally, the bulk
of every CI test leg). The suites are process-isolated already (per-process
mkdtemp HOME sentinel; two full parallel runs produced zero cross-suite
failures), so the serialization was pure convention.

- test-runner --list-suites: prints every registered suite, one per line,
  emitted by the SAME macro table that executes suites — the list cannot
  drift from the run set by construction.
- scripts/run-tests-parallel.sh: runs each suite as its own process
  (jobs = CPU count; CBM_TEST_PAR_JOBS overrides) under a ZERO-LOSS
  CONTRACT: a union guard fails the gate if the set of suites that
  produced a result differs from --list-suites (nothing can be silently
  dropped, a newly added suite is picked up automatically); per-suite
  pass/fail/skip are summed into the sequential runner's exact summary
  format; any suite crash, failure, or omission exits nonzero. Per-suite
  wall times are printed for balance tracking.
- make test-par: the parallel target. make test (sequential) is unchanged
  and remains the escape hatch (CBM_TEST_SEQUENTIAL=1 in test.sh).
- scripts/test.sh: builds, then routes through test-par — every CI test
  leg gets the speedup with zero workflow-topology change (same jobs,
  same gates, same billing; the legs just finish sooner).
- The stack_overflow suite is split into a/b/c (7+6+7 of its 20 tests,
  pure re-registration): as one suite it was the wall-clock critical path
  of any parallel run — every other suite finished underneath its ~4
  minutes.

Measured locally (Apple Silicon, ASan runner): sequential ~10 min vs
parallel 232 s, totals identical (6,361 passed / 0 failed / 1 skipped),
union guard clean. The TSan leg keeps its dedicated subset runner
(sequential) — out of scope here.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-18 11:05:17 +02:00

100 lines
3.5 KiB
Bash
Executable File

#!/usr/bin/env bash
# test.sh — Clean build + run all C tests with ASan + UBSan.
#
# Usage:
# scripts/test.sh # Auto-detect everything
# scripts/test.sh --arch x86_64 # Force x86_64 build
# scripts/test.sh CC=gcc-14 CXX=g++-14 # Override compiler
#
# This script is the SINGLE source of truth for running tests.
# Used identically in local development and CI workflows.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$ROOT"
# Parse --arch flag before sourcing env.sh
for arg in "$@"; do
case "$arg" in
--arch) :;; # next arg is the value, handled below
arm64|x86_64)
# Check if previous arg was --arch
if [[ "${prev_arg:-}" == "--arch" ]]; then
export CBM_ARCH="$arg"
fi
;;
esac
prev_arg="$arg"
done
# Also support --arch=value
for arg in "$@"; do
case "$arg" in
--arch=*) export CBM_ARCH="${arg#--arch=}" ;;
esac
done
# shellcheck source=env.sh
source "$ROOT/scripts/env.sh"
# Forward CC/CXX and collect make-passthrough args
MAKE_ARGS=""
for arg in "$@"; do
case "$arg" in
CC=*|CXX=*) export "${arg}" ;;
--arch|--arch=*) ;; # already handled
arm64|x86_64) ;; # already handled
*=*) MAKE_ARGS="$MAKE_ARGS $arg" ;; # forward any VAR=VAL to make
esac
done
print_env "test.sh"
# Verify compiler supports target arch
verify_compiler "$CC"
# Step 1: Clean
scripts/clean.sh
# Step 2 + 3: Build, then run every suite as parallel processes (identical
# gate quality — see the ZERO-LOSS CONTRACT in scripts/run-tests-parallel.sh:
# the suite set is enumerated from the runner itself and union-guarded, and
# pass/fail/skip totals aggregate to the same numbers as the sequential run).
# CBM_TEST_SEQUENTIAL=1 restores the single-process runner.
make -j"$NPROC" -f Makefile.cbm build/c/test-runner $MAKE_ARGS
if [ "${CBM_TEST_SEQUENTIAL:-0}" = "1" ]; then
make -f Makefile.cbm test $MAKE_ARGS
else
make -f Makefile.cbm test-par $MAKE_ARGS
fi
# Step 4: C++ large-TU index-hang regression guard (#410). Runs the PROD binary
# in a subprocess with a wall-clock timeout — a hang must fail, not block the run.
# Opt-in via CBM_RUN_HANG_TEST=1 (it needs the prod binary, which the ASan unit
# run above does not build). Skipped by default so the fast unit run stays fast.
if [ "${CBM_RUN_HANG_TEST:-0}" = "1" ]; then
echo "=== Step 4: C++ index-hang regression (#410) ==="
bash "$ROOT/tests/test_cpp_index_hang.sh"
fi
# Step 5: Parent-death watchdog regression (#406/#407). Builds the prod stdio
# binary and verifies it self-exits when its launching parent is killed.
echo "=== Step 5: parent-death watchdog regression (#406/#407) ==="
make -j"$NPROC" -f Makefile.cbm cbm $MAKE_ARGS
bash "$ROOT/tests/test_parent_watchdog.sh"
# Step 5b: worker-mode parent-death watchdog (#845). A supervised index worker
# (`cli --index-worker …`) whose supervisor dies must self-exit instead of
# indexing on as an orphan. Reuses the prod binary built in Step 5.
echo "=== Step 5b: worker-mode watchdog regression (#845) ==="
bash "$ROOT/tests/test_worker_watchdog.sh"
# Step 6: security-strings URL allow-list regression. The MSYS2 CLANG64 toolchain
# bakes its package-tracker URL into the static Windows .exe; the binary string
# audit must allow-list it (Windows-only — Linux smoke never saw it).
echo "=== Step 6: security-strings allow-list regression ==="
bash "$ROOT/tests/test_security_strings_allowlist.sh"
echo "=== All tests passed ==="