- add new tests: - test_blp_loader.cpp - test_dbc_loader.cpp - test_entity.cpp - test_frustum.cpp - test_m2_structs.cpp - test_opcode_table.cpp - test_packet.cpp - test_srp.cpp - CMakeLists.txt - add docs and progress tracking: - TESTING.md - perf_baseline.md - update project config/build: - .gitignore - CMakeLists.txt - test.sh - core engine updates: - application.cpp - game_handler.cpp - world_socket.cpp - adt_loader.cpp - asset_manager.cpp - m2_renderer.cpp - post_process_pipeline.cpp - renderer.cpp - terrain_manager.cpp - game_screen.cpp - add profiler header: - profiler.hpp
11 KiB
WoWee Testing Guide
This document covers everything needed to build, run, lint, and extend the WoWee test suite.
Table of Contents
- Overview
- Prerequisites
- Test Suite Layout
- Building the Tests
- Running Tests
- Lint (clang-tidy)
- ASAN / UBSan
- Adding New Tests
- 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 for platform-specific dependency installation.
Linux (Ubuntu / Debian)
sudo apt update
sudo apt install -y \
build-essential cmake pkg-config git \
libssl-dev \
clang-tidy
Linux (Arch)
sudo pacman -S --needed base-devel cmake pkgconf git openssl clang
macOS
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:
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.shfor a full clean build. Directcmake --buildis fine for test-only incremental builds.
# 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):
./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.
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.
# 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
# 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:
./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
./test.sh --lint
Under the hood the script:
- Locates
clang-tidy(tries versions 14–18, thenclang-tidy). - Uses
run-clang-tidyfor parallel execution when available; falls back to sequential. - Reads
build/compile_commands.json(generated by CMake) for compiler flags. - Feeds GCC stdlib include paths as
-isystemextras so clang-tidy can resolve<vector>,<string>, 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:
cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
Applying auto-fixes
Some clang-tidy checks can apply fixes automatically (e.g. modernize-*, readability-*):
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 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:
// NOLINT(bugprone-narrowing-conversions)
uint8_t byte = static_cast<uint8_t>(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
# 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
- Create
tests/test_<name>.cppwith a standard Catch2 v3 structure:
#include "catch_amalgamated.hpp"
TEST_CASE("SomeFeature does X", "[tag]") {
REQUIRE(1 + 1 == 2);
}
- Register the test in
tests/CMakeLists.txtfollowing the existing pattern:
# ── test_<name> ──────────────────────────────────────────────
add_executable(test_<name>
test_<name>.cpp
${TEST_COMMON_SOURCES}
${CMAKE_SOURCE_DIR}/src/<module>/<file>.cpp # source under test
)
target_include_directories(test_<name> PRIVATE ${TEST_INCLUDE_DIRS})
target_include_directories(test_<name> SYSTEM PRIVATE ${TEST_SYSTEM_INCLUDE_DIRS})
target_link_libraries(test_<name> PRIVATE catch2_main)
add_test(NAME <name> COMMAND test_<name>)
register_test_target(test_<name>) # required — enables ASAN propagation
- Build and verify:
cmake --build build --target test_<name>
./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:
- 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 for full platform dependency installation steps required before any CI job.