/** * @file dungeon_layer_rendering_test.cc * @brief End-to-end tests for dungeon layer rendering system * * These tests verify the layer visibility controls in the DungeonEditorV2 * card-based architecture. Each room card has per-room layer visibility * settings for BG1, BG2, and object layers. * * The dungeon editor renders rooms with multiple background layers: * - BG1: Primary background tiles * - BG2: Secondary background tiles (with various blend modes) * - BG3: Additional layer (used for effects) * - Objects: Rendered on top of backgrounds * * Test Pattern: * 1. Load ROM and open Dungeon Editor * 2. Open room card(s) via Room Selector * 3. Interact with layer controls in the room card * 4. Verify canvas updates reflect layer visibility changes * * Created: November 2025 * Architecture: DungeonEditorV2 card-based system * Related: src/app/editor/dungeon/dungeon_canvas_viewer.cc */ #define IMGUI_DEFINE_MATH_OPERATORS #include "e2e/dungeon_layer_rendering_test.h" #include "app/controller.h" #include "imgui.h" #include "imgui_test_engine/imgui_te_context.h" #include "imgui_test_engine/imgui_te_engine.h" #include "test_utils.h" namespace yaze { namespace test { // ============================================================================= // Helper Functions // ============================================================================= namespace { /** * @brief Helper to set up dungeon editor with a room open * @param ctx ImGui test context * @param room_hex Room number in hex format (e.g., "0x00") * @return true if setup successful, false otherwise */ bool SetupDungeonEditorWithRoom(ImGuiTestContext* ctx, const char* room_hex = "0x00") { // Load ROM ctx->LogInfo("Loading ROM..."); gui::LoadRomInTest(ctx, "zelda3.sfc"); // Open Dungeon Editor ctx->LogInfo("Opening Dungeon Editor..."); gui::OpenEditorInTest(ctx, "Dungeon"); ctx->Yield(15); // Enable room selector via Dungeon Controls if (ctx->WindowInfo("Dungeon Controls").Window == nullptr) { ctx->LogWarning("Dungeon Controls window not found"); return false; } ctx->SetRef("Dungeon Controls"); ctx->ItemClick("Rooms"); ctx->Yield(5); // Open the specified room if (ctx->WindowInfo("Room Selector").Window == nullptr) { ctx->LogWarning("Room Selector window not found"); return false; } ctx->SetRef("Room Selector"); char room_name[32]; snprintf(room_name, sizeof(room_name), "Room %s", room_hex); if (!ctx->ItemExists(room_name)) { ctx->LogWarning("Room %s not found in selector", room_name); return false; } ctx->ItemDoubleClick(room_name); ctx->Yield(20); // Verify room card opened if (ctx->WindowInfo(room_name).Window == nullptr) { ctx->LogWarning("Room card %s did not open", room_name); return false; } ctx->LogInfo("Successfully opened %s", room_name); return true; } /** * @brief Helper to check if a layer control checkbox exists and get its state * @param ctx ImGui test context * @param room_window Room window name (e.g., "Room 0x00") * @param checkbox_label Checkbox label (e.g., "BG1") * @return true if checkbox exists, false otherwise */ bool CheckLayerControlExists(ImGuiTestContext* ctx, const char* room_window, const char* checkbox_label) { if (ctx->WindowInfo(room_window).Window == nullptr) { return false; } ctx->SetRef(room_window); return ctx->ItemExists(checkbox_label); } } // namespace // ============================================================================= // Test Implementation: Toggle BG1 // ============================================================================= void E2ETest_DungeonLayers_ToggleBG1(ImGuiTestContext* ctx) { ctx->LogInfo("=== E2E Test: Dungeon Layers - Toggle BG1 ==="); ctx->LogInfo("Purpose: Verify BG1 layer visibility toggle updates canvas"); // Setup if (!SetupDungeonEditorWithRoom(ctx, "0x00")) { ctx->LogError("Failed to set up dungeon editor with Room 0x00"); return; } // Access the room card ctx->SetRef("Room 0x00"); // Test 1: Verify BG1 checkbox exists if (!ctx->ItemExists("BG1")) { ctx->LogError("BG1 checkbox not found in room card"); return; } ctx->LogInfo("PASS: BG1 checkbox found in room card"); // Test 2: Toggle BG1 OFF ctx->LogInfo("Toggling BG1 visibility OFF..."); ctx->ItemClick("BG1"); ctx->Yield(10); ctx->LogInfo("BG1 toggled - canvas should hide BG1 layer"); // Visual verification note: In a full visual test, we would capture // a screenshot here and verify BG1 content is not visible // Test 3: Toggle BG1 back ON ctx->LogInfo("Toggling BG1 visibility ON..."); ctx->ItemClick("BG1"); ctx->Yield(10); ctx->LogInfo("BG1 toggled - canvas should show BG1 layer"); // Test 4: Rapid toggle (stress test) ctx->LogInfo("Performing rapid BG1 toggle test..."); for (int i = 0; i < 3; i++) { ctx->ItemClick("BG1"); ctx->Yield(2); } ctx->LogInfo("PASS: Rapid toggle completed without errors"); // Final state: ensure BG1 is visible ctx->ItemClick("BG1"); // Toggle to known state ctx->Yield(5); ctx->LogInfo("=== Toggle BG1 Test COMPLETE ==="); } // ============================================================================= // Test Implementation: Toggle BG2 // ============================================================================= void E2ETest_DungeonLayers_ToggleBG2(ImGuiTestContext* ctx) { ctx->LogInfo("=== E2E Test: Dungeon Layers - Toggle BG2 ==="); ctx->LogInfo("Purpose: Verify BG2 layer visibility toggle updates canvas"); // Setup if (!SetupDungeonEditorWithRoom(ctx, "0x00")) { ctx->LogError("Failed to set up dungeon editor with Room 0x00"); return; } ctx->SetRef("Room 0x00"); // Test 1: Verify BG2 checkbox exists if (!ctx->ItemExists("BG2")) { ctx->LogError("BG2 checkbox not found in room card"); return; } ctx->LogInfo("PASS: BG2 checkbox found in room card"); // Test 2: Toggle BG2 OFF ctx->LogInfo("Toggling BG2 visibility OFF..."); ctx->ItemClick("BG2"); ctx->Yield(10); ctx->LogInfo("BG2 toggled - canvas should hide BG2 layer"); // Test 3: Toggle BG2 back ON ctx->LogInfo("Toggling BG2 visibility ON..."); ctx->ItemClick("BG2"); ctx->Yield(10); ctx->LogInfo("BG2 toggled - canvas should show BG2 layer"); // Test 4: Test BG2 layer type combo (if accessible) if (ctx->ItemExists("##BG2Type")) { ctx->LogInfo("Testing BG2 layer type combo..."); ctx->ItemClick("##BG2Type"); ctx->Yield(3); // Select different blend modes ctx->KeyPress(ImGuiKey_DownArrow); ctx->KeyPress(ImGuiKey_Enter); ctx->Yield(5); ctx->LogInfo("PASS: BG2 type combo accessible"); } ctx->LogInfo("=== Toggle BG2 Test COMPLETE ==="); } // ============================================================================= // Test Implementation: All Layers Off // ============================================================================= void E2ETest_DungeonLayers_AllLayersOff(ImGuiTestContext* ctx) { ctx->LogInfo("=== E2E Test: Dungeon Layers - All Layers Off ==="); ctx->LogInfo("Purpose: Verify canvas appears blank when all layers disabled"); // Setup if (!SetupDungeonEditorWithRoom(ctx, "0x00")) { ctx->LogError("Failed to set up dungeon editor with Room 0x00"); return; } ctx->SetRef("Room 0x00"); // Ensure both checkboxes exist bool has_bg1 = ctx->ItemExists("BG1"); bool has_bg2 = ctx->ItemExists("BG2"); if (!has_bg1 || !has_bg2) { ctx->LogError("Missing layer controls: BG1=%s, BG2=%s", has_bg1 ? "found" : "missing", has_bg2 ? "found" : "missing"); return; } ctx->LogInfo("Both layer controls found"); // Step 1: Turn off BG1 ctx->LogInfo("Disabling BG1..."); ctx->ItemClick("BG1"); ctx->Yield(5); // Step 2: Turn off BG2 ctx->LogInfo("Disabling BG2..."); ctx->ItemClick("BG2"); ctx->Yield(5); // Verification: Canvas should now show minimal content (just grid/border) ctx->LogInfo("Both layers disabled - canvas should show blank room"); ctx->Yield(10); // Visual note: At this point, the canvas should display only the canvas // background/grid, with no room tile graphics visible // Step 3: Re-enable layers ctx->LogInfo("Re-enabling BG1..."); ctx->ItemClick("BG1"); ctx->Yield(3); ctx->LogInfo("Re-enabling BG2..."); ctx->ItemClick("BG2"); ctx->Yield(3); ctx->LogInfo("Layers restored - canvas should show full room"); ctx->LogInfo("=== All Layers Off Test COMPLETE ==="); } // ============================================================================= // Test Implementation: Per-Room Settings // ============================================================================= void E2ETest_DungeonLayers_PerRoomSettings(ImGuiTestContext* ctx) { ctx->LogInfo("=== E2E Test: Dungeon Layers - Per-Room Settings ==="); ctx->LogInfo( "Purpose: Verify each room maintains independent layer visibility"); // Setup first room if (!SetupDungeonEditorWithRoom(ctx, "0x00")) { ctx->LogError("Failed to set up dungeon editor with Room 0x00"); return; } // Open second room ctx->LogInfo("Opening second room (Room 0x01)..."); ctx->SetRef("Room Selector"); if (!ctx->ItemExists("Room 0x01")) { ctx->LogWarning("Room 0x01 not found - skipping multi-room test"); return; } ctx->ItemDoubleClick("Room 0x01"); ctx->Yield(20); // Verify both room cards are open bool room0_open = ctx->WindowInfo("Room 0x00").Window != nullptr; bool room1_open = ctx->WindowInfo("Room 0x01").Window != nullptr; if (!room0_open || !room1_open) { ctx->LogError("Could not open both rooms: Room0=%s, Room1=%s", room0_open ? "open" : "closed", room1_open ? "open" : "closed"); return; } ctx->LogInfo("PASS: Both room cards are open"); // Test: Toggle BG1 in Room 0 only ctx->LogInfo("Toggling BG1 in Room 0x00..."); ctx->SetRef("Room 0x00"); if (ctx->ItemExists("BG1")) { ctx->ItemClick("BG1"); ctx->Yield(5); } // Verify Room 1's BG1 setting is unaffected // (In ImGuiTestEngine, we can't directly read checkbox state, // but we verify the control exists and is accessible) ctx->LogInfo("Verifying Room 0x01 layer controls are independent..."); ctx->SetRef("Room 0x01"); if (ctx->ItemExists("BG1")) { ctx->LogInfo("PASS: Room 0x01 has independent BG1 control"); // Toggle Room 1's BG1 to verify it works independently ctx->ItemClick("BG1"); ctx->Yield(5); ctx->ItemClick("BG1"); // Toggle back ctx->Yield(3); } // Test: Different layer settings between rooms ctx->LogInfo("Setting different layer configs in each room..."); // Room 0: BG1=off, BG2=on ctx->SetRef("Room 0x00"); if (ctx->ItemExists("BG1")) { ctx->ItemClick("BG1"); // Already off, this toggles back on ctx->Yield(2); ctx->ItemClick("BG1"); // Off again ctx->Yield(2); } // Room 1: BG1=on, BG2=off ctx->SetRef("Room 0x01"); if (ctx->ItemExists("BG2")) { ctx->ItemClick("BG2"); // Toggle BG2 off ctx->Yield(5); } ctx->LogInfo("PASS: Rooms configured with different layer settings"); // Cleanup: Restore defaults ctx->LogInfo("Restoring default layer settings..."); ctx->SetRef("Room 0x00"); ctx->ItemClick("BG1"); // Toggle back on ctx->Yield(2); ctx->SetRef("Room 0x01"); ctx->ItemClick("BG2"); // Toggle back on ctx->Yield(2); // Close Room 1 to clean up ctx->WindowClose("Room 0x01"); ctx->Yield(3); ctx->LogInfo("=== Per-Room Settings Test COMPLETE ==="); } // ============================================================================= // Test Implementation: Objects Above Background // ============================================================================= void E2ETest_DungeonLayers_ObjectsAboveBackground(ImGuiTestContext* ctx) { ctx->LogInfo("=== E2E Test: Dungeon Layers - Objects Above Background ==="); ctx->LogInfo("Purpose: Verify rendering order (BG1 -> BG2 -> Objects)"); // Setup if (!SetupDungeonEditorWithRoom(ctx, "0x00")) { ctx->LogError("Failed to set up dungeon editor with Room 0x00"); return; } ctx->SetRef("Room 0x00"); // Enable object editor to see objects ctx->LogInfo("Enabling Object Editor..."); ctx->SetRef("Dungeon Controls"); ctx->ItemClick("Objects"); ctx->Yield(10); // Return to room card ctx->SetRef("Room 0x00"); // Test 1: Verify layers are visible with objects ctx->LogInfo("Checking initial layer state with objects..."); ctx->Yield(5); // Test 2: Toggle BG1 off - objects should remain visible if (ctx->ItemExists("BG1")) { ctx->LogInfo("Toggling BG1 off - objects should remain visible..."); ctx->ItemClick("BG1"); ctx->Yield(10); // Visual check note: Objects (outlined in the canvas) should still // be visible even when BG1 is hidden ctx->ItemClick("BG1"); // Restore ctx->Yield(5); } // Test 3: Toggle BG2 off - objects should remain visible if (ctx->ItemExists("BG2")) { ctx->LogInfo("Toggling BG2 off - objects should remain visible..."); ctx->ItemClick("BG2"); ctx->Yield(10); // Visual check note: Objects should still be visible even when BG2 hidden ctx->ItemClick("BG2"); // Restore ctx->Yield(5); } // Test 4: Both layers off - only object outlines should be visible ctx->LogInfo("Disabling all background layers..."); if (ctx->ItemExists("BG1")) { ctx->ItemClick("BG1"); ctx->Yield(3); } if (ctx->ItemExists("BG2")) { ctx->ItemClick("BG2"); ctx->Yield(3); } ctx->LogInfo("Background layers off - checking object visibility..."); ctx->Yield(10); // Visual note: Object outlines/sprites should still render on canvas // even with both background layers hidden, confirming render order // Test 5: Check object outline toggle (if available via context menu) ctx->LogInfo("Testing object outline visibility controls..."); // Object outlines are controlled via context menu or separate toggles // The canvas should still show object positions // Cleanup: Restore layers ctx->LogInfo("Restoring background layers..."); if (ctx->ItemExists("BG1")) { ctx->ItemClick("BG1"); ctx->Yield(3); } if (ctx->ItemExists("BG2")) { ctx->ItemClick("BG2"); ctx->Yield(3); } ctx->LogInfo("=== Objects Above Background Test COMPLETE ==="); } } // namespace test } // namespace yaze