# WoWee Testing Guide This document covers everything needed to build, run, lint, and extend the WoWee test suite. --- ## Table of Contents 1. [Overview](#overview) 2. [Prerequisites](#prerequisites) 3. [Test Suite Layout](#test-suite-layout) 4. [Building the Tests](#building-the-tests) - [Release Build (normal)](#release-build-normal) - [Debug + ASAN/UBSan Build](#debug--asanubsan-build) 5. [Running Tests](#running-tests) - [test.sh — the unified entry point](#testsh--the-unified-entry-point) - [Running directly with ctest](#running-directly-with-ctest) 6. [Lint (clang-tidy)](#lint-clang-tidy) - [Running lint](#running-lint) - [Applying auto-fixes](#applying-auto-fixes) - [Configuration (.clang-tidy)](#configuration-clang-tidy) 7. [ASAN / UBSan](#asan--ubsan) 8. [Adding New Tests](#adding-new-tests) 9. [CI Reference](#ci-reference) --- ## Overview WoWee uses **Catch2 v3** (amalgamated) for unit testing and **clang-tidy** for static analysis. The `test.sh` script is the single entry point for both. | Command | What it does | |---|---| | `./test.sh` | Runs both unit tests (Release) and lint | | `./test.sh --test` | Runs unit tests only (Release build) | | `./test.sh --lint` | Runs clang-tidy only | | `./test.sh --asan` | Runs unit tests under ASAN + UBSan (Debug build) | | `FIX=1 ./test.sh --lint` | Applies clang-tidy auto-fixes in-place | All commands exit non-zero on any failure. --- ## Prerequisites The test suite requires the same base toolchain used to build the project. See [BUILD_INSTRUCTIONS.md](BUILD_INSTRUCTIONS.md) for platform-specific dependency installation. ### Linux (Ubuntu / Debian) ```bash sudo apt update sudo apt install -y \ build-essential cmake pkg-config git \ libssl-dev \ clang-tidy ``` ### Linux (Arch) ```bash sudo pacman -S --needed base-devel cmake pkgconf git openssl clang ``` ### macOS ```bash brew install cmake openssl@3 llvm # Add LLVM tools to PATH so clang-tidy is found: export PATH="$(brew --prefix llvm)/bin:$PATH" ``` ### Windows (MSYS2) Install the full toolchain as described in `BUILD_INSTRUCTIONS.md`, then add: ```bash pacman -S --needed mingw-w64-x86_64-clang-tools-extra ``` --- ## Test Suite Layout ``` tests/ CMakeLists.txt — CMake test configuration test_packet.cpp — Network packet encode/decode test_srp.cpp — SRP-6a authentication math (requires OpenSSL) test_opcode_table.cpp — Opcode registry lookup test_entity.cpp — ECS entity basics test_dbc_loader.cpp — DBC binary file parsing test_m2_structs.cpp — M2 model struct layout / alignment test_blp_loader.cpp — BLP texture file parsing test_frustum.cpp — View-frustum culling math ``` The Catch2 v3 amalgamated source lives at: ``` extern/catch2/ catch_amalgamated.hpp catch_amalgamated.cpp ``` --- ## Building the Tests Tests are _not_ built by default. Enable them with `-DWOWEE_BUILD_TESTS=ON`. ### Release Build (normal) > **Note:** Per project rules, always use `rebuild.sh` for a full clean build. Direct `cmake --build` is fine for test-only incremental builds. ```bash # Configure (only needed once) cmake -B build -DCMAKE_BUILD_TYPE=Release -DWOWEE_BUILD_TESTS=ON # Build test targets (fast — only compiles tests and Catch2) cmake --build build --target \ test_packet test_srp test_opcode_table test_entity \ test_dbc_loader test_m2_structs test_blp_loader test_frustum ``` Or simply run a full rebuild (builds everything including the main binary): ```bash ./rebuild.sh # ~10 minutes — see BUILD_INSTRUCTIONS.md ``` ### Debug + ASAN/UBSan Build A separate CMake build directory is used so ASAN flags do not pollute the Release binary. ```bash cmake -B build_asan \ -DCMAKE_BUILD_TYPE=Debug \ -DWOWEE_ENABLE_ASAN=ON \ -DWOWEE_BUILD_TESTS=ON cmake --build build_asan --target \ test_packet test_srp test_opcode_table test_entity \ test_dbc_loader test_m2_structs test_blp_loader test_frustum ``` CMake will print: `Test targets: ASAN + UBSan ENABLED` when configured correctly. --- ## Running Tests ### test.sh — the unified entry point `test.sh` is the recommended way to run tests and/or lint. It handles build-directory discovery, dependency checking, and exit-code aggregation across both steps. ```bash # Run everything (tests + lint) — default when no flags are given ./test.sh # Tests only (Release build) ./test.sh --test # Tests only under ASAN+UBSan (Debug build — requires build_asan/) ./test.sh --asan # Lint only ./test.sh --lint # Both tests and lint explicitly ./test.sh --test --lint # Usage summary ./test.sh --help ``` **Exit codes:** | Outcome | Exit code | |---|---| | All tests passed, lint clean | `0` | | Any test failed | `1` | | Any lint diagnostic | `1` | | Both test failure and lint issues | `1` | ### Running directly with ctest ```bash # Release build cd build ctest --output-on-failure # ASAN build cd build_asan ctest --output-on-failure # Run one specific test suite by name ctest --output-on-failure -R srp # Verbose output (shows every SECTION and REQUIRE) ctest --output-on-failure -V ``` You can also run a test binary directly for detailed Catch2 output: ```bash ./build/bin/test_srp ./build/bin/test_srp --reporter console ./build/bin/test_srp "[authentication]" # run only tests tagged [authentication] ``` --- ## Lint (clang-tidy) The project uses clang-tidy to enforce C++20 best practices on all first-party sources under `src/`. Third-party code (anything in `extern/`) and generated files are excluded. ### Running lint ```bash ./test.sh --lint ``` Under the hood the script: 1. Locates `clang-tidy` (tries versions 14–18, then `clang-tidy`). 2. Uses `run-clang-tidy` for parallel execution when available; falls back to sequential. 3. Reads `build/compile_commands.json` (generated by CMake) for compiler flags. 4. Feeds GCC stdlib include paths as `-isystem` extras so clang-tidy can resolve ``, ``, etc. when the compile-commands were generated with GCC. `compile_commands.json` is regenerated automatically by any CMake configure step. If you only want to update it without rebuilding: ```bash cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ``` ### Applying auto-fixes Some clang-tidy checks can apply fixes automatically (e.g. `modernize-*`, `readability-*`): ```bash FIX=1 ./test.sh --lint ``` > **Caution:** Review the diff before committing — automatic fixes occasionally produce non-idiomatic results in complex template code. ### Configuration (.clang-tidy) The active check set is defined in [.clang-tidy](.clang-tidy) at the repository root. **Enabled check categories:** | Category | What it catches | |---|---| | `bugprone-*` | Common bug patterns (signed overflow, misplaced `=`, etc.) | | `clang-analyzer-*` | Deep flow-analysis: null dereferences, memory leaks, dead stores | | `performance-*` | Unnecessary copies, inefficient STL usage | | `modernize-*` (subset) | Pre-C++11 patterns that should use modern equivalents | | `readability-*` (subset) | Control-flow simplification, redundant code | **Notable suppressions** (see `.clang-tidy` for details): | Suppressed check | Reason | |---|---| | `bugprone-easily-swappable-parameters` | High false-positive rate in graphics/math APIs | | `clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling` | Intentional low-level buffer code in rendering | | `performance-avoid-endl` | `std::endl` is used intentionally for logger flushing | To suppress a specific warning inline, use: ```cpp // NOLINT(bugprone-narrowing-conversions) uint8_t byte = static_cast(value); // NOLINT ``` --- ## ASAN / UBSan AddressSanitizer (ASAN) and Undefined Behaviour Sanitizer (UBSan) are applied to all test targets when `WOWEE_ENABLE_ASAN=ON`. Both the test executables **and** the `catch2_main` static library are recompiled with: ``` -fsanitize=address,undefined -fno-omit-frame-pointer ``` This means any heap overflow, stack buffer overflow, use-after-free, null dereference, signed integer overflow, or misaligned access detected during a test will abort the process and print a human-readable report to stderr. ### Workflow ```bash # 1. Configure once (only needs to be re-run when CMakeLists.txt changes) cmake -B build_asan \ -DCMAKE_BUILD_TYPE=Debug \ -DWOWEE_ENABLE_ASAN=ON \ -DWOWEE_BUILD_TESTS=ON # 2. Build test binaries (fast incremental after the first build) cmake --build build_asan --target test_packet test_srp # etc. # 3. Run ./test.sh --asan ``` ### Interpreting ASAN output A failing ASAN report looks like: ``` ==12345==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000010 READ of size 4 at 0x602000000010 thread T0 #0 0x... in PacketBuffer::read_uint32 src/network/packet.cpp:42 #1 0x... in test_packet tests/test_packet.cpp:88 ``` Address the issue in the source file and re-run. Do **not** suppress ASAN reports without a code fix. --- ## Adding New Tests 1. **Create** `tests/test_.cpp` with a standard Catch2 v3 structure: ```cpp #include "catch_amalgamated.hpp" TEST_CASE("SomeFeature does X", "[tag]") { REQUIRE(1 + 1 == 2); } ``` 2. **Register** the test in `tests/CMakeLists.txt` following the existing pattern: ```cmake # ── test_ ────────────────────────────────────────────── add_executable(test_ test_.cpp ${TEST_COMMON_SOURCES} ${CMAKE_SOURCE_DIR}/src//.cpp # source under test ) target_include_directories(test_ PRIVATE ${TEST_INCLUDE_DIRS}) target_include_directories(test_ SYSTEM PRIVATE ${TEST_SYSTEM_INCLUDE_DIRS}) target_link_libraries(test_ PRIVATE catch2_main) add_test(NAME COMMAND test_) register_test_target(test_) # required — enables ASAN propagation ``` 3. **Build** and verify: ```bash cmake --build build --target test_ ./test.sh --test ``` The `register_test_target()` macro call is **mandatory** — without it the new test will not receive ASAN/UBSan flags when `WOWEE_ENABLE_ASAN=ON`. --- ## CI Reference The following commands map to typical CI jobs: | Job | Command | |---|---| | Unit tests (Release) | `./test.sh --test` | | Unit tests (ASAN+UBSan) | `./test.sh --asan` | | Lint | `./test.sh --lint` | | Full check (tests + lint) | `./test.sh` | **Configuring the ASAN job in CI:** ```yaml - name: Configure ASAN build run: | cmake -B build_asan \ -DCMAKE_BUILD_TYPE=Debug \ -DWOWEE_ENABLE_ASAN=ON \ -DWOWEE_BUILD_TESTS=ON - name: Build test targets run: | cmake --build build_asan --target \ test_packet test_srp test_opcode_table test_entity \ test_dbc_loader test_m2_structs test_blp_loader test_frustum - name: Run ASAN tests run: ./test.sh --asan ``` > See [BUILD_INSTRUCTIONS.md](BUILD_INSTRUCTIONS.md) for full platform dependency installation steps required before any CI job.