332 lines
9.8 KiB
C++
332 lines
9.8 KiB
C++
/**
|
|
* @file dungeon_visual_verification_test.cc
|
|
* @brief AI-powered visual verification tests for dungeon object rendering
|
|
*
|
|
* This test integrates ImGuiTestEngine with Gemini Vision API to perform
|
|
* automated visual verification of dungeon rendering. The workflow:
|
|
* 1. Use ImGuiTestEngine to navigate to specific dungeon rooms
|
|
* 2. Capture screenshots of rendered content
|
|
* 3. Send screenshots to Gemini Vision for analysis
|
|
* 4. Verify AI response matches expected rendering criteria
|
|
*
|
|
* Requires:
|
|
* - GEMINI_API_KEY environment variable
|
|
* - ROM file for testing
|
|
* - GUI test mode (--ui flag)
|
|
*/
|
|
|
|
#define IMGUI_DEFINE_MATH_OPERATORS
|
|
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
|
|
#include "app/controller.h"
|
|
#include "app/platform/window.h"
|
|
#include "rom/rom.h"
|
|
#include "gtest/gtest.h"
|
|
#include "imgui.h"
|
|
#include "imgui_test_engine/imgui_te_context.h"
|
|
#include "imgui_test_engine/imgui_te_engine.h"
|
|
#include "test_utils.h"
|
|
|
|
#ifdef YAZE_AI_RUNTIME_AVAILABLE
|
|
#include "cli/service/ai/gemini_ai_service.h"
|
|
#endif
|
|
|
|
namespace yaze {
|
|
namespace test {
|
|
|
|
// =============================================================================
|
|
// Visual Verification Test Functions (registered with ImGuiTestEngine)
|
|
// =============================================================================
|
|
|
|
/**
|
|
* @brief Basic room rendering verification test
|
|
* Navigates to room 0 and verifies it renders correctly
|
|
*/
|
|
void E2ETest_VisualVerification_BasicRoomRendering(ImGuiTestContext* ctx) {
|
|
ctx->LogInfo("=== Visual Verification: Basic Room Rendering ===");
|
|
|
|
// Load ROM
|
|
ctx->LogInfo("Loading ROM...");
|
|
gui::LoadRomInTest(ctx, yaze::test::TestRomManager::GetTestRomPath());
|
|
|
|
// Open Dungeon Editor
|
|
ctx->LogInfo("Opening Dungeon Editor...");
|
|
gui::OpenEditorInTest(ctx, "Dungeon");
|
|
ctx->Yield(10);
|
|
|
|
// Wait for the dungeon controls to appear
|
|
ctx->LogInfo("Waiting for Dungeon Controls...");
|
|
ctx->Yield(30);
|
|
|
|
// Enable room selector
|
|
if (ctx->WindowInfo("Dungeon Controls").Window != nullptr) {
|
|
ctx->SetRef("Dungeon Controls");
|
|
ctx->ItemClick("Rooms");
|
|
ctx->Yield(5);
|
|
}
|
|
|
|
// Navigate to room 0
|
|
ctx->LogInfo("Navigating to Room 0...");
|
|
if (ctx->WindowInfo("Room Selector").Window != nullptr) {
|
|
ctx->SetRef("Room Selector");
|
|
if (ctx->ItemExists("Room 0x00")) {
|
|
ctx->ItemDoubleClick("Room 0x00");
|
|
ctx->Yield(30);
|
|
ctx->LogInfo("Room 0 opened successfully");
|
|
}
|
|
}
|
|
|
|
// Verify room card exists and has content
|
|
if (ctx->WindowInfo("Room 0x00").Window != nullptr) {
|
|
ctx->LogInfo("Room 0x00 card is visible");
|
|
ctx->SetRef("Room 0x00");
|
|
|
|
// Check for canvas
|
|
if (ctx->ItemExists("##RoomCanvas")) {
|
|
ctx->LogInfo("Room canvas found - rendering appears successful");
|
|
} else {
|
|
ctx->LogWarning("Room canvas not found - check rendering");
|
|
}
|
|
}
|
|
|
|
ctx->LogInfo("=== Basic Room Rendering Test Complete ===");
|
|
}
|
|
|
|
/**
|
|
* @brief Layer visibility verification test
|
|
* Tests that toggling layer visibility changes the rendered output
|
|
*/
|
|
void E2ETest_VisualVerification_LayerVisibility(ImGuiTestContext* ctx) {
|
|
ctx->LogInfo("=== Visual Verification: Layer Visibility ===");
|
|
|
|
// Load ROM and open editor
|
|
gui::LoadRomInTest(ctx, yaze::test::TestRomManager::GetTestRomPath());
|
|
gui::OpenEditorInTest(ctx, "Dungeon");
|
|
ctx->Yield(20);
|
|
|
|
// Enable controls
|
|
if (ctx->WindowInfo("Dungeon Controls").Window != nullptr) {
|
|
ctx->SetRef("Dungeon Controls");
|
|
ctx->ItemClick("Rooms");
|
|
ctx->Yield(5);
|
|
}
|
|
|
|
// Open a room
|
|
if (ctx->WindowInfo("Room Selector").Window != nullptr) {
|
|
ctx->SetRef("Room Selector");
|
|
if (ctx->ItemExists("Room 0x00")) {
|
|
ctx->ItemDoubleClick("Room 0x00");
|
|
ctx->Yield(20);
|
|
}
|
|
}
|
|
|
|
// Test layer visibility controls
|
|
if (ctx->WindowInfo("Room 0x00").Window != nullptr) {
|
|
ctx->SetRef("Room 0x00");
|
|
|
|
// Toggle BG1 visibility
|
|
if (ctx->ItemExists("Show BG1")) {
|
|
ctx->LogInfo("Testing BG1 layer toggle...");
|
|
ctx->ItemClick("Show BG1");
|
|
ctx->Yield(10);
|
|
ctx->ItemClick("Show BG1");
|
|
ctx->Yield(5);
|
|
ctx->LogInfo("BG1 layer toggle successful");
|
|
}
|
|
|
|
// Toggle BG2 visibility
|
|
if (ctx->ItemExists("Show BG2")) {
|
|
ctx->LogInfo("Testing BG2 layer toggle...");
|
|
ctx->ItemClick("Show BG2");
|
|
ctx->Yield(10);
|
|
ctx->ItemClick("Show BG2");
|
|
ctx->Yield(5);
|
|
ctx->LogInfo("BG2 layer toggle successful");
|
|
}
|
|
}
|
|
|
|
ctx->LogInfo("=== Layer Visibility Test Complete ===");
|
|
}
|
|
|
|
/**
|
|
* @brief Object editor panel verification test
|
|
* Verifies the object editor panel opens and displays correctly
|
|
*/
|
|
void E2ETest_VisualVerification_ObjectEditor(ImGuiTestContext* ctx) {
|
|
ctx->LogInfo("=== Visual Verification: Object Editor ===");
|
|
|
|
gui::LoadRomInTest(ctx, yaze::test::TestRomManager::GetTestRomPath());
|
|
gui::OpenEditorInTest(ctx, "Dungeon");
|
|
ctx->Yield(20);
|
|
|
|
// Open object editor
|
|
if (ctx->WindowInfo("Dungeon Controls").Window != nullptr) {
|
|
ctx->SetRef("Dungeon Controls");
|
|
ctx->ItemClick("Objects");
|
|
ctx->Yield(10);
|
|
ctx->LogInfo("Object Editor toggled");
|
|
}
|
|
|
|
// Verify object editor panel
|
|
if (ctx->WindowInfo("Object Editor").Window != nullptr) {
|
|
ctx->LogInfo("Object Editor panel is visible");
|
|
ctx->SetRef("Object Editor");
|
|
|
|
// Check for object list or selector
|
|
if (ctx->ItemExists("##ObjectList")) {
|
|
ctx->LogInfo("Object list found");
|
|
}
|
|
}
|
|
|
|
ctx->LogInfo("=== Object Editor Test Complete ===");
|
|
}
|
|
|
|
/**
|
|
* @brief Multi-room navigation verification test
|
|
* Tests navigating between multiple rooms
|
|
*/
|
|
void E2ETest_VisualVerification_MultiRoomNavigation(ImGuiTestContext* ctx) {
|
|
ctx->LogInfo("=== Visual Verification: Multi-Room Navigation ===");
|
|
|
|
gui::LoadRomInTest(ctx, yaze::test::TestRomManager::GetTestRomPath());
|
|
gui::OpenEditorInTest(ctx, "Dungeon");
|
|
ctx->Yield(20);
|
|
|
|
// Enable room selector
|
|
if (ctx->WindowInfo("Dungeon Controls").Window != nullptr) {
|
|
ctx->SetRef("Dungeon Controls");
|
|
ctx->ItemClick("Rooms");
|
|
ctx->Yield(5);
|
|
}
|
|
|
|
// Test multiple rooms
|
|
std::vector<std::string> test_rooms = {"Room 0x00", "Room 0x01", "Room 0x02"};
|
|
|
|
for (const auto& room_name : test_rooms) {
|
|
ctx->LogInfo("Opening %s...", room_name.c_str());
|
|
|
|
if (ctx->WindowInfo("Room Selector").Window != nullptr) {
|
|
ctx->SetRef("Room Selector");
|
|
if (ctx->ItemExists(room_name.c_str())) {
|
|
ctx->ItemDoubleClick(room_name.c_str());
|
|
ctx->Yield(20);
|
|
ctx->LogInfo("%s opened", room_name.c_str());
|
|
} else {
|
|
ctx->LogWarning("%s not found in selector", room_name.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
ctx->LogInfo("=== Multi-Room Navigation Test Complete ===");
|
|
}
|
|
|
|
// =============================================================================
|
|
// GTest Integration - Unit Tests for verification infrastructure
|
|
// =============================================================================
|
|
|
|
/**
|
|
* @class DungeonVisualVerificationTest
|
|
* @brief GTest fixture for visual verification infrastructure tests
|
|
*/
|
|
class DungeonVisualVerificationTest : public ::testing::Test {
|
|
protected:
|
|
void SetUp() override {
|
|
// Check for Gemini API key
|
|
const char* api_key = std::getenv("GEMINI_API_KEY");
|
|
if (!api_key || std::string(api_key).empty()) {
|
|
skip_ai_tests_ = true;
|
|
} else {
|
|
api_key_ = api_key;
|
|
}
|
|
|
|
// Create test output directory
|
|
test_dir_ =
|
|
std::filesystem::temp_directory_path() / "yaze_visual_verification";
|
|
std::filesystem::create_directories(test_dir_);
|
|
}
|
|
|
|
void TearDown() override {
|
|
// Keep test artifacts for debugging - cleanup manually if needed
|
|
}
|
|
|
|
std::filesystem::path test_dir_;
|
|
std::string api_key_;
|
|
bool skip_ai_tests_ = false;
|
|
};
|
|
|
|
TEST_F(DungeonVisualVerificationTest, TestDirectoryCreated) {
|
|
ASSERT_TRUE(std::filesystem::exists(test_dir_));
|
|
}
|
|
|
|
TEST_F(DungeonVisualVerificationTest, ApiKeyCheck) {
|
|
if (skip_ai_tests_) {
|
|
GTEST_SKIP() << "GEMINI_API_KEY not set - skipping AI tests";
|
|
}
|
|
EXPECT_FALSE(api_key_.empty());
|
|
}
|
|
|
|
#ifdef YAZE_AI_RUNTIME_AVAILABLE
|
|
TEST_F(DungeonVisualVerificationTest, GeminiServiceAvailable) {
|
|
if (skip_ai_tests_) {
|
|
GTEST_SKIP() << "GEMINI_API_KEY not set";
|
|
}
|
|
|
|
cli::GeminiConfig config;
|
|
config.api_key = api_key_;
|
|
config.model = "gemini-2.5-flash";
|
|
|
|
cli::GeminiAIService service(config);
|
|
auto status = service.CheckAvailability();
|
|
EXPECT_TRUE(status.ok()) << status.message();
|
|
}
|
|
|
|
TEST_F(DungeonVisualVerificationTest, ImageAnalysisBasic) {
|
|
if (skip_ai_tests_) {
|
|
GTEST_SKIP() << "GEMINI_API_KEY not set";
|
|
}
|
|
|
|
// Create a simple test image
|
|
auto image_path = test_dir_ / "test_image.png";
|
|
|
|
// Minimal PNG (8x8 pixels)
|
|
const unsigned char png_data[] = {
|
|
// PNG signature
|
|
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
|
|
// IHDR chunk
|
|
0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x08,
|
|
0x00, 0x00, 0x00, 0x08, 0x08, 0x02, 0x00, 0x00, 0x00, 0x4B, 0x6D, 0x29,
|
|
0xDE,
|
|
// IDAT chunk
|
|
0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41, 0x54, 0x08, 0x99, 0x63, 0xF8,
|
|
0xCF, 0xC0, 0x00, 0x00, 0x03, 0x01, 0x01, 0x00, 0x18, 0xDD, 0x8D, 0xB4,
|
|
// IEND chunk
|
|
0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82};
|
|
|
|
std::ofstream file(image_path, std::ios::binary);
|
|
file.write(reinterpret_cast<const char*>(png_data), sizeof(png_data));
|
|
file.close();
|
|
|
|
ASSERT_TRUE(std::filesystem::exists(image_path));
|
|
|
|
cli::GeminiConfig config;
|
|
config.api_key = api_key_;
|
|
config.model = "gemini-2.5-flash";
|
|
config.verbose = false;
|
|
|
|
cli::GeminiAIService service(config);
|
|
|
|
auto response = service.GenerateMultimodalResponse(
|
|
image_path.string(), "What do you see in this image? Keep response brief.");
|
|
|
|
ASSERT_TRUE(response.ok()) << response.status().message();
|
|
EXPECT_FALSE(response->text_response.empty());
|
|
|
|
std::cout << "AI Response: " << response->text_response << std::endl;
|
|
}
|
|
#endif // YAZE_AI_RUNTIME_AVAILABLE
|
|
|
|
} // namespace test
|
|
} // namespace yaze
|