Files
yaze/docs/internal/testing/overview.md
2025-12-22 14:50:57 -05:00

500 lines
12 KiB
Markdown

# CI/CD and Testing Infrastructure
This document describes YAZE's continuous integration and testing systems, including how to understand and manage test suites.
## Table of Contents
1. [CI/CD Pipeline Overview](#cicd-pipeline-overview)
2. [Test Structure](#test-structure)
3. [GitHub Workflows](#github-workflows)
4. [Test Execution](#test-execution)
5. [Adding Tests](#adding-tests)
6. [Maintenance & Troubleshooting](#maintenance--troubleshooting)
## CI/CD Pipeline Overview
YAZE uses GitHub Actions with a **tiered testing strategy** for continuous integration:
### PR/Push CI (`ci.yml`) - Fast Feedback Loop
- **Trigger**: Every PR and push to master/develop
- **Duration**: ~5-10 minutes per platform
- **Tests**: Stable suite + GUI smoke tests (ONLY)
- **Result**: Must pass before merging
### Nightly CI (`nightly.yml`) - Comprehensive Coverage
- **Trigger**: Daily at 3 AM UTC or manual dispatch
- **Duration**: ~30-60 minutes total
- **Tests**: All suites including ROM-dependent, experimental, benchmarks
- **Result**: Alerts on failure but non-blocking
### Build Targets
- **Debug/AI/Dev presets**: Always include test targets
- **Release presets**: No test targets (focused on distribution)
## Test Structure
### Default (Stable) Tests - Run in PR/Push CI
These tests are always available and ALWAYS run in PR/Push CI (blocking merges):
**Characteristics:**
- No external dependencies (no ROM files required)
- Fast execution (~5 seconds total for stable)
- Safe to run in any environment
- Must pass in all PR/Push builds
- Included in all debug/dev/AI presets
**Included:**
- **Unit tests**: Core, ROM, graphics, Zelda3 functionality (21 test files)
- **Integration tests**: Editor, ASAR, dungeon system (10 test files)
- **GUI smoke tests**: Framework validation, editor basics (3 test files)
**Run with:**
```bash
ctest --test-dir build -L stable # All stable tests
ctest --test-dir build -L "stable|gui" # Stable + GUI
ctest --test-dir build -L headless_gui # GUI in headless mode (CI)
```
### Optional Test Suites - Run in Nightly CI Only
These tests are disabled in PR/Push CI but run in nightly builds for comprehensive coverage.
#### ROM-Dependent Tests
**Purpose:** Full ROM editing workflows, version upgrades, data integrity
**CI Execution:** Nightly only (non-blocking)
**Requirements to Run Locally:**
- CMake flag: `-DYAZE_ENABLE_ROM_TESTS=ON`
- ROM path: `-DYAZE_TEST_ROM_VANILLA_PATH=/path/to/alttp_vanilla.sfc`
- Use `mac-dev`, `lin-dev`, `win-dev` presets or configure manually
**Contents:**
- ASAR ROM patching (`integration/asar_rom_test.cc`)
- Complete ROM workflows (`e2e/rom_dependent/e2e_rom_test.cc`)
- ZSCustomOverworld upgrades (`e2e/zscustomoverworld/zscustomoverworld_upgrade_test.cc`)
**Run with:**
```bash
cmake --preset mac-dev -DYAZE_TEST_ROM_VANILLA_PATH=~/roms/alttp_vanilla.sfc
ctest --test-dir build -L rom_dependent
```
#### Experimental AI Tests
**Purpose:** AI runtime features, vision models, agent automation
**CI Execution:** Nightly only (non-blocking)
**Requirements to Run Locally:**
- CMake flag: `-DYAZE_ENABLE_AI_RUNTIME=ON`
- Use `mac-ai`, `lin-ai`, `win-ai` presets
**Contents:**
- AI tile placement (`integration/ai/test_ai_tile_placement.cc`)
- Vision model integration (`integration/ai/test_gemini_vision.cc`)
- GUI controller tests (`integration/ai/ai_gui_controller_test.cc`)
**Run with:**
```bash
cmake --preset mac-ai
ctest --test-dir build -L experimental
```
#### Benchmark Tests
**Purpose:** Performance profiling and optimization validation
**CI Execution:** Nightly only (non-blocking)
**Contents:**
- Graphics optimization benchmarks (`benchmarks/gfx_optimization_benchmarks.cc`)
**Run with:**
```bash
ctest --test-dir build -L benchmark
```
## GitHub Workflows
### Primary Workflows
#### 1. CI Pipeline (`ci.yml`)
**Trigger:** Push to master/develop, pull requests, manual dispatch
**Matrix:**
```
- Ubuntu 22.04 (GCC-12)
- macOS 14 (Clang)
- Windows 2022 (Visual Studio)
```
**Jobs:**
```
build
├── Checkout
├── Setup build environment
├── Build project
└── Upload artifacts (Windows)
test
├── Checkout
├── Setup build environment
├── Build project
├── Run stable tests
├── Run GUI smoke tests
├── Run ROM tests (if available)
└── Artifacts upload (on failure)
```
**Test Execution in CI:**
```yaml
# Default: stable tests always run
- name: Run stable tests
uses: ./.github/actions/run-tests
with:
test-type: stable
# Always: GUI headless smoke tests
- name: Run GUI smoke tests (headless)
uses: ./.github/actions/run-tests
with:
test-type: gui-headless
# Conditional: ROM tests on develop branch
- name: Run ROM-dependent tests
if: github.ref == 'refs/heads/develop'
uses: ./.github/actions/run-tests
with:
test-type: rom
```
#### 2. Code Quality (`code-quality.yml`)
**Trigger:** Push to master/develop, pull requests
**Checks:**
- `clang-format`: Code formatting validation
- `cppcheck`: Static analysis
- `clang-tidy`: Linting and best practices
#### 3. Release Pipeline (`release.yml`)
**Trigger:** Manual dispatch or tag push
**Outputs:**
- Cross-platform binaries
- Installer packages (Windows)
- Disk images (macOS)
#### 4. Matrix Test Pipeline (`matrix-test.yml`)
**Purpose:** Extended testing on multiple compiler versions
**Configuration:**
- GCC 12, 13 (Linux)
- Clang 14, 15, 16 (macOS, Linux)
- MSVC 193 (Windows)
### Composite Actions
Located in `.github/actions/`:
#### `setup-build`
Prepares build environment with:
- Dependency caching (CPM)
- Compiler cache (sccache/ccache)
- Platform-specific tools
#### `build-project`
Builds with:
- CMake preset configuration
- Optimal compiler settings
- Build artifact staging
#### `run-tests`
Executes tests with:
- CTest label filtering
- Test result uploads
- Failure artifact collection
## Test Execution
### Local Test Runs
#### Stable Tests (Recommended for Development)
```bash
# Fast iteration
ctest --test-dir build -L stable -j4
# With output on failure
ctest --test-dir build -L stable --output-on-failure
# With GUI tests
ctest --test-dir build -L "stable|gui" -j4
```
#### ROM-Dependent Tests
```bash
# Configure with ROM
cmake --preset mac-dbg \
-DYAZE_ENABLE_ROM_TESTS=ON \
-DYAZE_TEST_ROM_VANILLA_PATH=~/roms/alttp_vanilla.sfc
# Build ROM test suite
cmake --build --preset mac-dbg --target yaze_test_rom_dependent
# Run ROM tests
ctest --test-dir build -L rom_dependent -v
```
#### All Available Tests
```bash
# Runs all enabled test suites
ctest --test-dir build --output-on-failure
```
### Test Organization by Label
Tests are organized with ctest labels for flexible filtering:
```
Labels:
stable → Core unit/integration tests (default)
gui → GUI smoke tests
experimental → AI runtime features
rom_dependent → Zelda3 ROM workflows
benchmark → Performance tests
headless_gui → GUI tests in headless mode
```
**Usage:**
```bash
ctest --test-dir build -L stable # Single label
ctest --test-dir build -L "stable|gui" # Multiple labels (OR)
ctest --test-dir build -L "^stable$" # Exact match
ctest --test-dir build -L "^(?!benchmark)" # Exclude benchmarks
```
### CTest vs Gtest Filtering
Both approaches work, but differ in flexibility:
```bash
# CTest approach (recommended - uses CMake labels)
ctest --test-dir build -L stable
ctest --test-dir build -R "Dungeon"
# Gtest approach (direct binary execution)
./build/bin/yaze_test_stable --gtest_filter="*Dungeon*"
./build/bin/yaze_test_stable --show-gui
```
## Adding Tests
### File Organization Rules
```
test/
├── unit/ → Fast, no ROM dependency
├── integration/ → Component integration
├── e2e/ → End-to-end workflows
├── benchmarks/ → Performance tests
└── integration/ai/ → AI-specific (requires AI runtime)
```
### Adding Unit Test
1. Create file: `test/unit/new_feature_test.cc`
2. Include headers and use `gtest_add_tests()`
3. File auto-discovered by CMakeLists.txt
4. Automatically labeled as `stable`
```cpp
#include <gtest/gtest.h>
#include "app/new_feature.h"
TEST(NewFeatureTest, BasicFunctionality) {
EXPECT_TRUE(NewFeature::Work());
}
```
### Adding Integration Test
1. Create file: `test/integration/new_feature_test.cc`
2. Same pattern as unit tests
3. May access ROM files via `YAZE_TEST_ROM_VANILLA` or `TestRomManager`
4. Automatically labeled as `stable` (unless in special subdirectory)
### Adding ROM-Dependent Test
1. Create file: `test/e2e/rom_dependent/my_rom_test.cc`
2. Wrap ROM access in `#ifdef YAZE_ENABLE_ROM_TESTS`
3. Access ROM path via environment variable or CMake define
4. Automatically labeled as `rom_dependent`
```cpp
#ifdef YAZE_ENABLE_ROM_TESTS
TEST(MyRomTest, EditAndSave) {
const std::string rom_path =
yaze::test::TestRomManager::GetRomPath(yaze::test::RomRole::kVanilla);
// ... ROM testing code
}
#endif
```
### Adding AI/Experimental Test
1. Create file: `test/integration/ai/my_ai_test.cc`
2. Wrap code in `#ifdef YAZE_ENABLE_AI_RUNTIME`
3. Only included when `-DYAZE_ENABLE_AI_RUNTIME=ON`
4. Automatically labeled as `experimental`
### Adding GUI Test
1. Create file: `test/e2e/my_gui_test.cc`
2. Use ImGui Test Engine API
3. Register test in `test/yaze_test.cc`
4. Automatically labeled as `gui;experimental`
```cpp
#include "test_utils.h"
#include "imgui_te_engine.h"
void E2ETest_MyGuiWorkflow(ImGuiTestContext* ctx) {
const std::string rom_path =
yaze::test::TestRomManager::GetRomPath(yaze::test::RomRole::kVanilla);
yaze::test::gui::LoadRomInTest(ctx, rom_path);
// ... GUI test code
}
// In yaze_test.cc RunGuiMode():
ImGuiTest* my_test = IM_REGISTER_TEST(engine, "E2ETest", "MyGuiWorkflow");
my_test->TestFunc = E2ETest_MyGuiWorkflow;
```
## CMakeLists.txt Test Configuration
The test configuration in `test/CMakeLists.txt` follows this pattern:
```cmake
if(YAZE_BUILD_TESTS)
# Define test suites with labels
yaze_add_test_suite(yaze_test_stable "stable" OFF ${STABLE_SOURCES})
if(YAZE_ENABLE_ROM_TESTS)
yaze_add_test_suite(yaze_test_rom_dependent "rom_dependent" OFF ${ROM_SOURCES})
endif()
yaze_add_test_suite(yaze_test_gui "gui;experimental" ON ${GUI_SOURCES})
if(YAZE_ENABLE_AI_RUNTIME)
yaze_add_test_suite(yaze_test_experimental "experimental" OFF ${AI_SOURCES})
endif()
yaze_add_test_suite(yaze_test_benchmark "benchmark" OFF ${BENCH_SOURCES})
endif()
```
**Key function:** `yaze_add_test_suite(name label is_gui_test sources...)`
- Creates executable
- Links test dependencies
- Discovers tests with gtest_discover_tests()
- Assigns ctest label
## Maintenance & Troubleshooting
### Test Flakiness
If tests intermittently fail:
1. Check for race conditions in parallel execution
2. Look for timing-dependent operations
3. Verify test isolation (no shared state)
4. Check for environment-dependent behavior
**Fix strategies:**
- Use `ctest -j1` to disable parallelization
- Add explicit synchronization points
- Use test fixtures for setup/teardown
### ROM Test Failures
If ROM tests fail:
```bash
# Verify ROM path is correct
echo $YAZE_TEST_ROM_VANILLA
file ~/roms/alttp_vanilla.sfc
# Check ROM-dependent tests are enabled
cmake . | grep YAZE_ENABLE_ROM_TESTS
# Rebuild ROM test suite
cmake --build . --target yaze_test_rom_dependent
ctest --test-dir build -L rom_dependent -vv
```
### GUI Test Failures
If GUI tests crash:
```bash
# Check display available
echo $DISPLAY # Linux/macOS
# Run headlessly
ctest --test-dir build -L headless_gui -vv
# Check test registration
grep -r "IM_REGISTER_TEST" test/e2e/
```
### Test Not Discovered
If new tests aren't found:
```bash
# Rebuild CMake
rm -rf build && cmake --preset mac-dbg
# Check file is included in CMakeLists.txt
grep "my_feature_test.cc" test/CMakeLists.txt
# Verify test definitions
ctest --test-dir build -N # List all tests
```
### Performance Degradation
If tests run slowly:
```bash
# Run with timing
ctest --test-dir build -T performance
# Identify slow tests
ctest --test-dir build -T performance | grep "Wall Time"
# Profile specific test
time ./build/bin/yaze_test_stable "*SlowTest*"
```
## References
- **Test Documentation**: `test/README.md`
- **Quick Build Reference**: `docs/public/build/quick-reference.md`
- **CI Workflows**: `.github/workflows/ci.yml`, `matrix-test.yml`
- **Test Utilities**: `test/test_utils.h`
- **ImGui Test Engine**: `ext/imgui_test_engine/imgui_te_engine.h`
- **CMake Test Configuration**: `test/CMakeLists.txt`