Kelsidavis-WoWee/TESTING.md
Kelsi 2f3a973444 docs: update documentation for PRs #59-63 refactors
- architecture.md: add chat system modules (src/ui/chat/), world map
  modules (src/rendering/world_map/), CatmullRomSpline (src/math/),
  transport decomposition, and updated namespace list
- status.md: update timestamp to 2026-04-14, add recent refactors
  section and world map known gaps
- CHANGELOG.md: add detailed entries for PRs #58-63 covering
  architecture, features, bug fixes, and 19 new test files
- TESTING.md: expand test suite layout from 8 to 27 files organized
  by category (core, animation, transport, world map, chat)
- CONTRIBUTING.md: update namespace table, testing section, and key
  files list to reflect new module directories
- README.md: update status timestamp to 2026-04-14
2026-04-14 03:42:46 -07:00

12 KiB
Raw Blame History

WoWee Testing Guide

This document covers everything needed to build, run, lint, and extend the WoWee test suite.


Table of Contents

  1. Overview
  2. Prerequisites
  3. Test Suite Layout
  4. Building the Tests
  5. Running Tests
  6. Lint (clang-tidy)
  7. ASAN / UBSan
  8. Adding New Tests
  9. 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

  # Core
  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

  # Animation
  test_animation_ids.cpp                  — Animation ID constants
  test_locomotion_fsm.cpp                 — Locomotion state machine transitions
  test_combat_fsm.cpp                     — Combat animation state machine
  test_activity_fsm.cpp                   — Activity state machine
  test_anim_capability.cpp                — Animation capability queries
  test_indoor_shadows.cpp                 — Indoor shadow rendering

  # Transport & Spline
  test_spline.cpp                         — CatmullRomSpline math (interpolation, binary search, looping)
  test_transport_components.cpp           — Transport clock sync and animator
  test_transport_path_repo.cpp            — TransportPathRepository (DBC loading, path inference)

  # World Map
  test_world_map.cpp                      — World map integration tests
  test_world_map_coordinate_projection.cpp — UV projection, zone/continent spatial lookups
  test_world_map_exploration_state.cpp    — Server exploration mask, local tracking
  test_world_map_map_resolver.cpp         — Cross-map navigation (Outland, Northrend)
  test_world_map_view_state_machine.cpp   — COSMIC→WORLD→CONTINENT→ZONE transitions
  test_world_map_zone_metadata.cpp        — Zone level ranges and faction labels

  # Chat
  test_chat_markup_parser.cpp             — Item link and markup parsing
  test_chat_tab_completer.cpp             — Tab-completion for names and commands
  test_gm_commands.cpp                    — GM command data table and dispatch
  test_macro_evaluator.cpp                — Macro conditional evaluation

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.

# Configure (only needed once)
cmake -B build -DCMAKE_BUILD_TYPE=Release -DWOWEE_BUILD_TESTS=ON

# Build all test targets
cmake --build build --parallel $(nproc)

# Or build specific test targets
cmake --build build --target test_packet test_spline test_world_map

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 --parallel $(nproc)

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:

  1. Locates clang-tidy (tries versions 1418, 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 <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

  1. Create tests/test_<name>.cpp with a standard Catch2 v3 structure:
#include "catch_amalgamated.hpp"

TEST_CASE("SomeFeature does X", "[tag]") {
    REQUIRE(1 + 1 == 2);
}
  1. Register the test in tests/CMakeLists.txt following 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
  1. 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 --parallel $(nproc)

- name: Run ASAN tests
  run: ./test.sh --asan

See BUILD_INSTRUCTIONS.md for full platform dependency installation steps required before any CI job.