# Integration Test Guide This guide explains how to use yaze's integration test suite to validate ROM loading, overworld functionality, and ensure compatibility between vanilla and ZSCustomOverworld ROMs. ## Table of Contents 1. [Overview](#overview) 2. [Test Structure](#test-structure) 3. [Running Tests](#running-tests) 4. [Understanding Results](#understanding-results) 5. [Adding New Tests](#adding-new-tests) 6. [Debugging Failed Tests](#debugging-failed-tests) 7. [Best Practices](#best-practices) ## Overview The integration test suite validates that yaze correctly loads and processes ROM data by comparing against known values from vanilla ROMs and ensuring that ZSCustomOverworld features work as expected. ### Key Components - **Vanilla ROM Tests**: Validate loading of original Zelda 3 ROMs - **ZSCustomOverworld Tests**: Validate v2/v3 feature compatibility - **Sprite Position Tests**: Verify sprite coordinate systems - **Overworld Map Tests**: Test map loading and property access ## Test Structure ### Test Files ``` test/zelda3/ ├── overworld_integration_test.cc # Integration tests with real ROMs ├── comprehensive_integration_test.cc # Full ROM validation ├── dungeon_integration_test.cc # Dungeon system integration tests ├── dungeon_editor_system_integration_test.cc # Dungeon editor system tests └── extract_vanilla_values.cc # Utility to extract test values test/integration/ ├── editor_integration_test.h/cc # Base editor integration test framework ├── dungeon_editor_test.h # Dungeon editor integration tests └── test_editor.h/cc # Test editor framework ``` ### Test Categories #### 1. Integration Tests (`overworld_integration_test.cc`) Test with real ROM files and validate overworld functionality: ```cpp TEST_F(OverworldIntegrationTest, VanillaROMLoading) { // Load vanilla ROM auto rom = LoadVanillaROM(); Overworld overworld(rom.get()); // Test specific map properties auto* map = overworld.overworld_map(0); EXPECT_EQ(map->area_graphics(), expected_graphics); EXPECT_EQ(map->area_palette(), expected_palette); } ``` #### 2. Comprehensive Integration Tests (`comprehensive_integration_test.cc`) Full ROM validation with multiple ROM types: ```cpp TEST_F(ComprehensiveIntegrationTest, VanillaVsV3Comparison) { // Compare vanilla and v3 ROM features EXPECT_NE(vanilla_overworld_, nullptr); EXPECT_NE(v3_overworld_, nullptr); // Test feature differences TestFeatureDifferences(); } ``` #### 3. Dungeon Integration Tests (`dungeon_integration_test.cc`) Test dungeon system functionality: ```cpp TEST_F(DungeonIntegrationTest, DungeonRoomLoading) { // Test loading dungeon rooms for (int i = 0; i < kNumTestRooms; i++) { // Test room loading and properties } } ``` #### 4. Dungeon Editor System Tests (`dungeon_editor_system_integration_test.cc`) Test the complete dungeon editor system: ```cpp TEST_F(DungeonEditorSystemIntegrationTest, BasicInitialization) { EXPECT_NE(dungeon_editor_system_, nullptr); EXPECT_EQ(dungeon_editor_system_->GetROM(), rom_.get()); EXPECT_FALSE(dungeon_editor_system_->IsDirty()); } ``` ## Running Tests ### Prerequisites 1. **Build yaze**: Ensure yaze is built with test support 2. **ROM Files**: Have test ROM files available 3. **Test Data**: Generated test values from vanilla ROMs ### Basic Test Execution ```bash # Run all tests cd /Users/scawful/Code/yaze/build ./bin/yaze_test # Run specific test suite ./bin/yaze_test --gtest_filter="OverworldTest.*" # Run specific test ./bin/yaze_test --gtest_filter="SpritePositionTest.SpriteCoordinateSystem" # Run with verbose output ./bin/yaze_test --gtest_filter="OverworldIntegrationTest.*" --gtest_output=xml:test_results.xml ``` ### Test Filtering Use Google Test filters to run specific tests: ```bash # Run only overworld tests ./bin/yaze_test --gtest_filter="Overworld*" # Run only integration tests ./bin/yaze_test --gtest_filter="*Integration*" # Run tests matching pattern ./bin/yaze_test --gtest_filter="*Sprite*" # Exclude specific tests ./bin/yaze_test --gtest_filter="OverworldTest.*:-OverworldTest.OverworldMapDestroy" ``` ### Parallel Execution ```bash # Run tests in parallel (faster) ./bin/yaze_test --gtest_parallel=4 # Run with specific number of workers ./bin/yaze_test --gtest_workers=8 ``` ## Understanding Results ### Test Output Format ``` [==========] Running 3 tests from 1 test suite. [----------] Global test environment set-up. [----------] 3 tests from OverworldTest [ RUN ] OverworldTest.OverworldMapInitialization [ OK ] OverworldTest.OverworldMapInitialization (2 ms) [ RUN ] OverworldTest.AreaSizeEnumValues [ OK ] OverworldTest.AreaSizeEnumValues (1 ms) [ RUN ] OverworldTest.OverworldMapDestroy [ OK ] OverworldTest.OverworldMapDestroy (1 ms) [----------] 3 tests from OverworldTest (4 ms total) [----------] Global test environment tear-down. [==========] 3 tests from 1 test suite ran. (4 ms total) [ PASSED ] 3 tests. ``` ### Success Indicators - `[ OK ]`: Test passed - `[ PASSED ]`: All tests in suite passed - No error messages or stack traces ### Failure Indicators - `[ FAILED ]`: Test failed - Error messages with expected vs actual values - Stack traces showing failure location ### Example Failure Output ``` [ RUN ] OverworldTest.OverworldMapInitialization test/zelda3/overworld_test.cc:45: Failure Expected equality of these values: map.area_graphics() Which is: 5 0 [ FAILED ] OverworldTest.OverworldMapInitialization (2 ms) ``` ## Adding New Tests ### 1. Unit Test Example ```cpp // Add to overworld_test.cc TEST_F(OverworldTest, VanillaOverlayLoading) { OverworldMap map(0, rom_.get()); // Test vanilla overlay loading RETURN_IF_ERROR(map.LoadVanillaOverlay()); // Verify overlay data EXPECT_TRUE(map.has_vanilla_overlay()); EXPECT_GT(map.vanilla_overlay_data().size(), 0); } ``` ### 2. Integration Test Example ```cpp // Add to overworld_integration_test.cc TEST_F(OverworldIntegrationTest, ZSCustomOverworldV3Features) { // Load ZSCustomOverworld v3 ROM auto rom = LoadZSCustomOverworldV3ROM(); Overworld overworld(rom.get()); // Test v3 features auto* map = overworld.overworld_map(0); EXPECT_GT(map->subscreen_overlay(), 0); EXPECT_GT(map->animated_gfx(), 0); // Test custom tile graphics for (int i = 0; i < 8; i++) { EXPECT_GE(map->custom_tileset(i), 0); } } ``` ### 3. Performance Test Example ```cpp TEST_F(OverworldPerformanceTest, LargeMapLoading) { auto start = std::chrono::high_resolution_clock::now(); // Load large number of maps for (int i = 0; i < 100; i++) { OverworldMap map(i % 160, rom_.get()); } auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(end - start); // Ensure loading is reasonably fast EXPECT_LT(duration.count(), 1000); // Less than 1 second } ``` ## Debugging Failed Tests ### 1. Enable Debug Output ```cpp // Add debug output to tests TEST_F(OverworldTest, DebugTest) { OverworldMap map(0, rom_.get()); // Print debug information std::cout << "Map index: " << map.index() << std::endl; std::cout << "Area graphics: " << static_cast(map.area_graphics()) << std::endl; std::cout << "Area palette: " << static_cast(map.area_palette()) << std::endl; // Your assertions here } ``` ### 2. Use GDB for Debugging ```bash # Run test with GDB gdb --args ./bin/yaze_test --gtest_filter="OverworldTest.DebugTest" # Set breakpoints (gdb) break overworld_test.cc:45 (gdb) run # Inspect variables (gdb) print map.area_graphics() (gdb) print map.area_palette() ``` ### 3. Memory Debugging ```bash # Run with Valgrind (Linux) valgrind --leak-check=full ./bin/yaze_test --gtest_filter="OverworldTest.*" # Run with AddressSanitizer export ASAN_OPTIONS=detect_leaks=1 ./bin/yaze_test --gtest_filter="OverworldTest.*" ``` ### 4. Common Debugging Scenarios #### ROM Loading Issues ```cpp // Check ROM version detection uint8_t asm_version = (*rom_)[zelda3::OverworldCustomASMHasBeenApplied]; std::cout << "ASM Version: 0x" << std::hex << static_cast(asm_version) << std::endl; // Verify ROM size std::cout << "ROM Size: " << rom_->size() << " bytes" << std::endl; ``` #### Map Property Issues ```cpp // Check map loading auto* map = overworld.overworld_map(current_map_); std::cout << "Map " << current_map_ << " properties:" << std::endl; std::cout << " Area Graphics: 0x" << std::hex << static_cast(map->area_graphics()) << std::endl; std::cout << " Area Palette: 0x" << std::hex << static_cast(map->area_palette()) << std::endl; std::cout << " Main Palette: 0x" << std::hex << static_cast(map->main_palette()) << std::endl; ``` #### Sprite Issues ```cpp // Check sprite loading for (int game_state = 0; game_state < 3; game_state++) { auto& sprites = *overworld.mutable_sprites(game_state); std::cout << "Game State " << game_state << ": " << sprites.size() << " sprites" << std::endl; for (size_t i = 0; i < std::min(sprites.size(), size_t(5)); i++) { auto& sprite = sprites[i]; std::cout << " Sprite " << i << ": Map=" << sprite.map_id() << ", X=" << sprite.x_ << ", Y=" << sprite.y_ << std::endl; } } ``` ## Best Practices ### 1. Test Organization - **Group related tests**: Use descriptive test suite names - **One concept per test**: Each test should verify one specific behavior - **Descriptive names**: Test names should clearly indicate what they're testing ```cpp // Good TEST_F(OverworldTest, VanillaOverlayLoadingForMap00) { // Test specific map's overlay loading } // Bad TEST_F(OverworldTest, Test1) { // Unclear what this tests } ``` ### 2. Test Data Management ```cpp class OverworldTest : public ::testing::Test { protected: void SetUp() override { // Initialize test data once rom_ = std::make_unique(); // ... setup code } void TearDown() override { // Clean up after each test rom_.reset(); } std::unique_ptr rom_; }; ``` ### 3. Error Handling in Tests ```cpp TEST_F(OverworldTest, ErrorHandling) { // Test error conditions OverworldMap map(999, rom_.get()); // Invalid map index // Verify error handling EXPECT_FALSE(map.is_initialized()); } ``` ### 4. Performance Considerations ```cpp // Use fixtures for expensive setup class ExpensiveTest : public ::testing::Test { protected: void SetUp() override { // Expensive setup here LoadLargeROM(); ProcessAllMaps(); } }; // Run expensive tests separately TEST_F(ExpensiveTest, FullOverworldProcessing) { // Test that requires expensive setup } ``` ### 5. Continuous Integration Add tests to CI pipeline: ```yaml # .github/workflows/tests.yml name: Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Build and Test run: | cd build make -j4 yaze_test ./bin/yaze_test --gtest_output=xml:test_results.xml - name: Upload Test Results uses: actions/upload-artifact@v2 with: name: test-results path: build/test_results.xml ``` ## Test Results Interpretation ### Viewing Results Test results are typically displayed in the terminal, but you can also generate XML reports: ```bash # Generate XML report ./bin/yaze_test --gtest_output=xml:test_results.xml # View in browser (if you have a test result viewer) open test_results.xml ``` ### Key Metrics - **Test Count**: Number of tests run - **Pass Rate**: Percentage of tests that passed - **Execution Time**: How long tests took to run - **Memory Usage**: Peak memory consumption during tests ### Performance Benchmarks Track performance over time: ```bash # Run with timing time ./bin/yaze_test --gtest_filter="OverworldPerformanceTest.*" # Profile with gprof gprof ./bin/yaze_test gmon.out > profile.txt ``` ## Conclusion The integration test suite is essential for maintaining yaze's reliability and ensuring compatibility with different ROM types. By following this guide, you can effectively run tests, debug issues, and add new test cases to improve the overall quality of the codebase. Remember to: - Run tests regularly during development - Add tests for new features - Debug failures promptly - Keep tests fast and focused - Use appropriate test data and fixtures