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