---
name: webpp-build-test
description: Configure, build, and run Web++ tests in this repo — derive test target names from tests/*_test.cpp, detect stale or missing focused CMake presets, build and run one test or the full suite without trusting the manually enumerated tests preset. Use whenever verifying a change with CMake/CTest.
---

# Build and test Web++ (CMake/CTest)

Presets live in `CMakePresets.json`; CI configures with `cmake --preset dev-default`.

## Test target naming (from `tests/CMakeLists.txt`)

- `tests/foo_bar_test.cpp` → target `test-foo-bar` (strip `_test.cpp`, `_` → `-`, prefix `test-`).
- `tests/foo_fuzz.cpp` → target `fuzz-foo` (Clang only, needs `FUZZ_TESTS=ON`).
- Every test source is also compiled into the aggregate `webpp-tests` executable.

Enumerate current targets:

```sh
ls tests/*_test.cpp | sed 's|.*/||; s|_test\.cpp$||; s|_|-|g; s|^|test-|'
```

## Preset drift (check before trusting a `test-*` preset)

`CMakePresets.json` enumerates focused build/test presets manually, so it lags behind `tests/`. As of the last
check: 5 focused build presets missing (`test-cgi-headers`, `test-cgi-request`, `test-dynamic-scoping`,
`test-header-fields`, `test-middlewares`) and 5 stale (`test-extensions`, `test-header-accept-encoding`,
`test-traits`, `test-unicode-algos`, `test-ustring`). Re-run detection instead of trusting that list:

```sh
python3 - <<'EOF'
import json, pathlib
build = {p["name"] for p in json.load(open("CMakePresets.json")).get("buildPresets", [])
         if not p.get("hidden") and p.get("name", "").startswith("test-")}
derived = {"test-" + p.stem[: -len("_test")].replace("_", "-")
           for p in pathlib.Path("tests").glob("*_test.cpp")}
print("missing focused build presets:", sorted(derived - build))
print("stale focused build presets:", sorted(build - derived))
EOF
```

If a focused preset is missing or stale, use the direct build/ctest commands below instead — never edit
`CMakePresets.json` just to run one test.

## Configure

```sh
cmake --preset dev-default
```

- `dev-default` inherits `default`: `binaryDir` = `${sourceDir}/build`, Debug, C++23, `WEBPP_DEV=ON`.
- **`WEBPP_DEV=ON` is required** — it gates `add_subdirectory(tests ...)` and the top-level `enable_testing()`.
  Symptom if the `build/` cache was configured without it: `ctest --test-dir build -N` reports `Total Tests: 0`
  and no `test-*` targets exist. Fix: re-run `cmake --preset dev-default`
  (check with `grep WEBPP_DEV build/CMakeCache.txt`).

## Run one focused test

When a focused build preset exists (verify with `ctest --list-presets` / `cmake --list-presets`):

```sh
cmake --build --preset test-uri
ctest --preset tests -R '^test-uri$' --output-on-failure
```

When it does not (or the preset is stale) — build the target directly in the preset's build dir, then filter
CTest by the same name (`build/` for `dev-default`):

```sh
cmake --build build --target test-uri -j $(nproc)
ctest --test-dir build -R '^test-uri$' --output-on-failure
```

Beware: plain `ctest -R` exits `0` when the filter matches nothing, so confirm the run printed the test name or
check `ctest --test-dir build -N -R '^test-uri$'`. Test presets set `noTestsAction=error`, so `ctest --preset ...`
exits `8` instead of passing silently.

## Run the full suite

The `tests` build preset's target list is manually enumerated and can be stale. Prefer deriving targets from the
sources; `-- -k` keeps make going past targets that fail to compile — report those separately instead of hiding them:

```sh
cmake --build build --target $(ls tests/*_test.cpp | sed 's|.*/||; s|_test\.cpp$||; s|_|-|g; s|^|test-|') \
  -j $(nproc) -- -k
ctest --test-dir build --output-on-failure
```

CI-style alternative (fine when preset drift was just checked):
`cmake --build --preset tests && ctest --preset tests --output-on-failure`.

## Discover what exists

```sh
cmake --list-presets    # configure presets
ctest --list-presets    # test presets (one per focused test)
```

## Rules

- Derive target names from `tests/*_test.cpp`, never from memory or a stale preset list.
- Report preset drift you find (missing/stale `test-*` presets) instead of silently working around it —
  `CMakePresets.json` preset lists are maintained manually.
- Never claim a build or test passed without its real exit status; on failure include the actual compiler/CTest
  output.
