#define SDL_MAIN_HANDLED // Must define before any ImGui includes #ifndef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS #endif #include #include #include #include #include #include #include "absl/debugging/failure_signal_handler.h" #include "absl/debugging/symbolize.h" #include "app/controller.h" #include "app/gfx/backend/sdl2_renderer.h" #include "app/gfx/resource/arena.h" #include "app/platform/window.h" #include "e2e/canvas_selection_test.h" #include "e2e/dungeon_e2e_tests.h" #include "e2e/framework_smoke_test.h" #include "imgui/backends/imgui_impl_sdl2.h" #include "imgui/backends/imgui_impl_sdlrenderer2.h" #include "imgui/imgui.h" #include "imgui_test_engine/imgui_te_context.h" #include "imgui_test_engine/imgui_te_engine.h" #include "imgui_test_engine/imgui_te_ui.h" // #include "test_editor.h" // Not used in main namespace yaze { namespace test { class ArenaQueueCleaner : public ::testing::EmptyTestEventListener { public: void OnTestEnd(const ::testing::TestInfo&) override { gfx::Arena::Get().ClearTextureQueue(); } }; // Test execution modes for AI agents and developers enum class TestMode { kAll, // Run all tests (default) kUnit, // Run only unit tests kIntegration, // Run only integration tests kE2E, // Run only end-to-end tests kRomDependent, // Run ROM-dependent tests only kZSCustomOverworld, // Run ZSCustomOverworld specific tests kCore, // Run core functionality tests kGraphics, // Run graphics-related tests kEditor, // Run editor tests kDeprecated, // Run deprecated tests (for cleanup) kSpecific // Run specific test pattern }; struct TestConfig { TestMode mode = TestMode::kAll; std::string test_pattern; std::string rom_path; std::string rom_vanilla; std::string rom_us; std::string rom_jp; std::string rom_eu; std::string rom_expanded; bool verbose = false; bool skip_rom_tests = false; bool enable_ui_tests = false; bool show_gui = false; ImGuiTestRunSpeed test_speed = ImGuiTestRunSpeed_Fast; }; // Parse command line arguments for better AI agent testing support TestConfig ParseArguments(int argc, char* argv[]) { TestConfig config; std::cout << "Available options:\n" << " --ui : Enable UI tests\n" << " --show-gui : Show GUI during tests\n" << " --fast : Run tests at max speed (default)\n" << " --normal : Run tests at watchable speed\n" << " --cinematic : Run tests in slow-motion with pauses\n" << " --rom= : Legacy ROM path (vanilla)\n" << " --rom-vanilla=\n" << " --rom-us=\n" << " --rom-jp=\n" << " --rom-eu=\n" << " --rom-expanded=\n" << " --pattern= : Run tests matching pattern\n" << std::endl; for (int i = 1; i < argc; i++) { std::string arg = argv[i]; if (arg == "--help" || arg == "-h") { std::cout << "YAZE Test Runner - Enhanced for AI Agent Testing\n\n"; std::cout << "Usage: yaze_test [options] [test_pattern]\n\n"; std::cout << "Test Modes:\n"; std::cout << " --unit Run unit tests only\n"; std::cout << " --integration Run integration tests only\n"; std::cout << " --e2e Run end-to-end tests only\n"; std::cout << " --rom-dependent Run ROM-dependent tests only\n"; std::cout << " --zscustomoverworld Run ZSCustomOverworld tests only\n"; std::cout << " --core Run core functionality tests\n"; std::cout << " --graphics Run graphics tests\n"; std::cout << " --editor Run editor tests\n"; std::cout << " --deprecated Run deprecated tests\n\n"; std::cout << "Options:\n"; std::cout << " --rom-path PATH Specify ROM path for testing\n"; std::cout << " --rom-vanilla PATH Specify vanilla ROM path\n"; std::cout << " --rom-us PATH Specify US ROM path\n"; std::cout << " --rom-jp PATH Specify JP ROM path\n"; std::cout << " --rom-eu PATH Specify EU ROM path\n"; std::cout << " --rom-expanded PATH Specify expanded ROM path (ZSCustom/OOS)\n"; std::cout << " --skip-rom-tests Skip tests requiring ROM files\n"; std::cout << " --enable-ui-tests Enable UI tests (requires display)\n"; std::cout << " --verbose Enable verbose output\n"; std::cout << " --help Show this help message\n\n"; std::cout << "Examples:\n"; std::cout << " yaze_test --unit --verbose\n"; std::cout << " yaze_test --e2e --rom-vanilla my_rom.sfc\n"; std::cout << " yaze_test --zscustomoverworld --verbose\n"; std::cout << " yaze_test RomTest.*\n"; exit(0); } else if (arg == "--unit") { config.mode = TestMode::kUnit; } else if (arg == "--integration") { config.mode = TestMode::kIntegration; } else if (arg == "--e2e") { config.mode = TestMode::kE2E; } else if (arg == "--rom-dependent") { config.mode = TestMode::kRomDependent; } else if (arg == "--zscustomoverworld") { config.mode = TestMode::kZSCustomOverworld; } else if (arg == "--core") { config.mode = TestMode::kCore; } else if (arg == "--graphics") { config.mode = TestMode::kGraphics; } else if (arg == "--editor") { config.mode = TestMode::kEditor; } else if (arg == "--deprecated") { config.mode = TestMode::kDeprecated; } else if (arg == "--rom-path" || arg == "--rom") { if (i + 1 < argc) { config.rom_path = argv[++i]; } } else if (arg.rfind("--rom-path=", 0) == 0) { config.rom_path = arg.substr(std::string("--rom-path=").size()); } else if (arg.rfind("--rom=", 0) == 0) { config.rom_path = arg.substr(std::string("--rom=").size()); } else if (arg == "--rom-vanilla") { if (i + 1 < argc) { config.rom_vanilla = argv[++i]; } } else if (arg.rfind("--rom-vanilla=", 0) == 0) { config.rom_vanilla = arg.substr(std::string("--rom-vanilla=").size()); } else if (arg == "--rom-us") { if (i + 1 < argc) { config.rom_us = argv[++i]; } } else if (arg.rfind("--rom-us=", 0) == 0) { config.rom_us = arg.substr(std::string("--rom-us=").size()); } else if (arg == "--rom-jp") { if (i + 1 < argc) { config.rom_jp = argv[++i]; } } else if (arg.rfind("--rom-jp=", 0) == 0) { config.rom_jp = arg.substr(std::string("--rom-jp=").size()); } else if (arg == "--rom-eu") { if (i + 1 < argc) { config.rom_eu = argv[++i]; } } else if (arg.rfind("--rom-eu=", 0) == 0) { config.rom_eu = arg.substr(std::string("--rom-eu=").size()); } else if (arg == "--rom-expanded" || arg == "--rom-oos") { if (i + 1 < argc) { config.rom_expanded = argv[++i]; } } else if (arg.rfind("--rom-expanded=", 0) == 0) { config.rom_expanded = arg.substr(std::string("--rom-expanded=").size()); } else if (arg.rfind("--rom-oos=", 0) == 0) { config.rom_expanded = arg.substr(std::string("--rom-oos=").size()); } else if (arg == "--skip-rom-tests") { config.skip_rom_tests = true; } else if (arg == "--enable-ui-tests") { config.enable_ui_tests = true; } else if (arg == "--verbose") { config.verbose = true; } else if (arg == "--show-gui") { config.show_gui = true; } else if (arg == "--fast") { config.test_speed = ImGuiTestRunSpeed_Fast; } else if (arg == "--normal") { config.test_speed = ImGuiTestRunSpeed_Normal; } else if (arg == "--cinematic") { config.test_speed = ImGuiTestRunSpeed_Cinematic; } else if (arg == "--ui") { config.enable_ui_tests = true; } else if (arg.find("--") != 0) { // Test pattern (not a flag) config.mode = TestMode::kSpecific; config.test_pattern = arg; } } return config; } // Set up test environment based on configuration void SetupTestEnvironment(const TestConfig& config) { // Set environment variables for tests using SDL's cross-platform function if (!config.rom_path.empty()) { SDL_setenv("YAZE_TEST_ROM_PATH", config.rom_path.c_str(), 1); if (config.rom_vanilla.empty()) { SDL_setenv("YAZE_TEST_ROM_VANILLA", config.rom_path.c_str(), 1); } } if (!config.rom_vanilla.empty()) { SDL_setenv("YAZE_TEST_ROM_VANILLA", config.rom_vanilla.c_str(), 1); if (config.rom_path.empty()) { SDL_setenv("YAZE_TEST_ROM_PATH", config.rom_vanilla.c_str(), 1); } } if (!config.rom_us.empty()) { SDL_setenv("YAZE_TEST_ROM_US", config.rom_us.c_str(), 1); SDL_setenv("YAZE_TEST_ROM_US_PATH", config.rom_us.c_str(), 1); } if (!config.rom_jp.empty()) { SDL_setenv("YAZE_TEST_ROM_JP", config.rom_jp.c_str(), 1); SDL_setenv("YAZE_TEST_ROM_JP_PATH", config.rom_jp.c_str(), 1); } if (!config.rom_eu.empty()) { SDL_setenv("YAZE_TEST_ROM_EU", config.rom_eu.c_str(), 1); SDL_setenv("YAZE_TEST_ROM_EU_PATH", config.rom_eu.c_str(), 1); } if (!config.rom_expanded.empty()) { SDL_setenv("YAZE_TEST_ROM_EXPANDED", config.rom_expanded.c_str(), 1); SDL_setenv("YAZE_TEST_ROM_OOS", config.rom_expanded.c_str(), 1); SDL_setenv("YAZE_TEST_ROM_EXPANDED_PATH", config.rom_expanded.c_str(), 1); } if (config.skip_rom_tests) { SDL_setenv("YAZE_SKIP_ROM_TESTS", "1", 1); } if (config.enable_ui_tests) { SDL_setenv("YAZE_ENABLE_UI_TESTS", "1", 1); } if (config.verbose) { SDL_setenv("YAZE_VERBOSE_TESTS", "1", 1); } } // Configure Google Test filters based on test mode void ConfigureTestFilters(const TestConfig& config) { std::vector filters; switch (config.mode) { case TestMode::kUnit: filters.push_back("UnitTest.*"); break; case TestMode::kIntegration: filters.push_back("IntegrationTest.*"); break; case TestMode::kE2E: filters.push_back("E2ETest.*"); break; case TestMode::kRomDependent: filters.push_back("*RomDependent*"); break; case TestMode::kZSCustomOverworld: filters.push_back("*ZSCustomOverworld*"); break; case TestMode::kCore: filters.push_back("*Core*"); filters.push_back("*Asar*"); filters.push_back("*Rom*"); break; case TestMode::kGraphics: filters.push_back("*Graphics*"); filters.push_back("*Gfx*"); filters.push_back("*Palette*"); filters.push_back("*Tile*"); break; case TestMode::kEditor: filters.push_back("*Editor*"); break; case TestMode::kDeprecated: filters.push_back("*Deprecated*"); break; case TestMode::kSpecific: if (!config.test_pattern.empty()) { filters.push_back(config.test_pattern); } break; case TestMode::kAll: default: // No filters - run all tests break; } if (!filters.empty()) { std::string filter_string; for (size_t i = 0; i < filters.size(); i++) { if (i > 0) filter_string += ":"; filter_string += filters[i]; } ::testing::GTEST_FLAG(filter) = filter_string; if (config.verbose) { std::cout << "Test filter: " << filter_string << std::endl; } } } } // namespace test } // namespace yaze int main(int argc, char* argv[]) { absl::InitializeSymbolizer(argv[0]); // Configure failure signal handler to be less aggressive for testing absl::FailureSignalHandlerOptions options; options.symbolize_stacktrace = true; options.use_alternate_stack = false; options.alarm_on_failure_secs = false; options.call_previous_handler = true; options.writerfn = nullptr; absl::InstallFailureSignalHandler(options); // Initialize SDL to prevent crashes in graphics components if (SDL_Init(SDL_INIT_VIDEO) != 0) { SDL_Log("Failed to initialize SDL: %s", SDL_GetError()); // Continue anyway for tests that don't need graphics } // Parse command line arguments auto config = yaze::test::ParseArguments(argc, argv); // Set up test environment yaze::test::SetupTestEnvironment(config); // Configure test filters yaze::test::ConfigureTestFilters(config); // Initialize Google Test ::testing::InitGoogleTest(&argc, argv); auto& listeners = ::testing::UnitTest::GetInstance()->listeners(); listeners.Append(new yaze::test::ArenaQueueCleaner()); if (config.enable_ui_tests) { #ifdef YAZE_GUI_TEST_TARGET // Use the Controller to create window and initialize everything // This ensures the yaze UI is properly set up for testing yaze::Controller controller; // Initialize the controller - this creates window, renderer, ImGui context auto init_status = controller.OnEntry(); if (!init_status.ok()) { std::cerr << "Failed to initialize controller: " << init_status.message() << std::endl; return 1; } // Get ImGui IO for additional test-specific configuration ImGuiIO& io = ImGui::GetIO(); // Get SDL renderer for test engine setup SDL_Renderer* sdl_renderer = static_cast(controller.renderer()->GetBackendRenderer()); // Setup test engine ImGuiTestEngine* engine = ImGuiTestEngine_CreateContext(); ImGuiTestEngineIO& test_io = ImGuiTestEngine_GetIO(engine); test_io.ConfigRunSpeed = config.test_speed; // Use configured speed test_io.ConfigVerboseLevel = ImGuiTestVerboseLevel_Info; test_io.ConfigVerboseLevelOnError = ImGuiTestVerboseLevel_Debug; // Log test speed mode const char* speed_name = "Fast"; if (config.test_speed == ImGuiTestRunSpeed_Normal) speed_name = "Normal"; else if (config.test_speed == ImGuiTestRunSpeed_Cinematic) speed_name = "Cinematic"; std::cout << "Running tests in " << speed_name << " mode" << std::endl; // Register E2E tests only for GUI test targets (they have the source files) // Framework smoke tests ImGuiTest* smoke_test = IM_REGISTER_TEST(engine, "E2ETest", "FrameworkSmokeTest"); smoke_test->TestFunc = E2ETest_FrameworkSmokeTest; ImGuiTest* canvas_test = IM_REGISTER_TEST(engine, "E2ETest", "CanvasSelectionTest"); canvas_test->TestFunc = E2ETest_CanvasSelectionTest; canvas_test->UserData = &controller; // Register all dungeon E2E tests via unified registration function // This includes: smoke tests, visual verification, object drawing, // canvas interaction, and layer rendering tests (18 total) yaze::test::e2e::RegisterDungeonE2ETests(engine, &controller); // Queue all registered tests to run automatically ImGuiTestEngine_QueueTests(engine, ImGuiTestGroup_Tests, nullptr, 0); // Main loop - runs the full yaze UI with test engine overlay while (controller.IsActive()) { // Process input events via controller controller.OnInput(); // Load/update the yaze UI (draws menus, editors, etc.) // This is where the actual application UI gets rendered auto load_status = controller.OnLoad(); if (!load_status.ok()) { std::cerr << "OnLoad error: " << load_status.message() << std::endl; break; } // Show the test engine windows as overlay (if requested) if (config.show_gui) { ImGuiTestEngine_ShowTestEngineWindows(engine, &config.show_gui); } // Render everything controller.DoRender(); // Run test engine post-swap processing ImGuiTestEngine_PostSwap(engine); // Check if all tests have completed (auto-exit when done) if (ImGuiTestEngine_IsTestQueueEmpty(engine)) { // All tests finished, exit break; } } // Get test result ImGuiTestEngineResultSummary summary; ImGuiTestEngine_GetResultSummary(engine, &summary); int result = (summary.CountSuccess == summary.CountTested) ? 0 : 1; std::cout << "Test Results: " << summary.CountSuccess << "/" << summary.CountTested << " passed" << std::endl; // Cleanup ImGuiTestEngine_DestroyContext(engine); controller.OnExit(); return result; #else std::cerr << "UI tests are not supported in this build configuration." << std::endl; return 1; #endif } else { // Run tests int result = RUN_ALL_TESTS(); // Cleanup SDL SDL_Quit(); return result; } }