- Introduced session renaming functionality, allowing users to customize session names for better organization. - Implemented duplicate session detection to prevent loading the same ROM in multiple sessions. - Enhanced workspace preset management with improved error handling and validation during loading and saving. - Updated UI elements to support lazy loading of workspace presets, ensuring they are available when needed. - Refactored session display logic to provide clearer naming and improved user experience in session management.
1277 lines
45 KiB
C++
1277 lines
45 KiB
C++
#include "app/test/test_manager.h"
|
|
|
|
#include "absl/strings/str_format.h"
|
|
#include "absl/strings/str_cat.h"
|
|
#include "app/core/features.h"
|
|
#include "app/core/platform/file_dialog.h"
|
|
#include "app/gfx/arena.h"
|
|
#include "app/gui/icons.h"
|
|
#include "imgui/imgui.h"
|
|
#include "util/log.h"
|
|
|
|
// Forward declaration to avoid circular dependency
|
|
namespace yaze {
|
|
namespace editor {
|
|
class EditorManager;
|
|
}
|
|
}
|
|
|
|
#ifdef YAZE_ENABLE_IMGUI_TEST_ENGINE
|
|
#include "imgui_test_engine/imgui_te_engine.h"
|
|
#endif
|
|
|
|
namespace yaze {
|
|
namespace test {
|
|
|
|
// Utility function implementations
|
|
const char* TestStatusToString(TestStatus status) {
|
|
switch (status) {
|
|
case TestStatus::kNotRun: return "Not Run";
|
|
case TestStatus::kRunning: return "Running";
|
|
case TestStatus::kPassed: return "Passed";
|
|
case TestStatus::kFailed: return "Failed";
|
|
case TestStatus::kSkipped: return "Skipped";
|
|
}
|
|
return "Unknown";
|
|
}
|
|
|
|
const char* TestCategoryToString(TestCategory category) {
|
|
switch (category) {
|
|
case TestCategory::kUnit: return "Unit";
|
|
case TestCategory::kIntegration: return "Integration";
|
|
case TestCategory::kUI: return "UI";
|
|
case TestCategory::kPerformance: return "Performance";
|
|
case TestCategory::kMemory: return "Memory";
|
|
}
|
|
return "Unknown";
|
|
}
|
|
|
|
ImVec4 GetTestStatusColor(TestStatus status) {
|
|
switch (status) {
|
|
case TestStatus::kNotRun: return ImVec4(0.6f, 0.6f, 0.6f, 1.0f); // Gray
|
|
case TestStatus::kRunning: return ImVec4(1.0f, 1.0f, 0.0f, 1.0f); // Yellow
|
|
case TestStatus::kPassed: return ImVec4(0.0f, 1.0f, 0.0f, 1.0f); // Green
|
|
case TestStatus::kFailed: return ImVec4(1.0f, 0.0f, 0.0f, 1.0f); // Red
|
|
case TestStatus::kSkipped: return ImVec4(1.0f, 0.5f, 0.0f, 1.0f); // Orange
|
|
}
|
|
return ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
|
|
}
|
|
|
|
// TestManager implementation
|
|
TestManager& TestManager::Get() {
|
|
static TestManager instance;
|
|
return instance;
|
|
}
|
|
|
|
TestManager::TestManager() {
|
|
// Initialize UI test engine
|
|
InitializeUITesting();
|
|
}
|
|
|
|
TestManager::~TestManager() {
|
|
ShutdownUITesting();
|
|
}
|
|
|
|
#ifdef YAZE_ENABLE_IMGUI_TEST_ENGINE
|
|
void TestManager::InitializeUITesting() {
|
|
if (!ui_test_engine_) {
|
|
ui_test_engine_ = ImGuiTestEngine_CreateContext();
|
|
if (ui_test_engine_) {
|
|
ImGuiTestEngineIO& test_io = ImGuiTestEngine_GetIO(ui_test_engine_);
|
|
test_io.ConfigVerboseLevel = ImGuiTestVerboseLevel_Info;
|
|
test_io.ConfigVerboseLevelOnError = ImGuiTestVerboseLevel_Debug;
|
|
test_io.ConfigRunSpeed = ImGuiTestRunSpeed_Fast;
|
|
|
|
// Start the test engine
|
|
ImGuiTestEngine_Start(ui_test_engine_, ImGui::GetCurrentContext());
|
|
}
|
|
}
|
|
}
|
|
|
|
void TestManager::StopUITesting() {
|
|
if (ui_test_engine_ && ImGui::GetCurrentContext() != nullptr) {
|
|
ImGuiTestEngine_Stop(ui_test_engine_);
|
|
}
|
|
}
|
|
|
|
void TestManager::DestroyUITestingContext() {
|
|
if (ui_test_engine_) {
|
|
ImGuiTestEngine_DestroyContext(ui_test_engine_);
|
|
ui_test_engine_ = nullptr;
|
|
}
|
|
}
|
|
|
|
void TestManager::ShutdownUITesting() {
|
|
// Complete shutdown - calls both phases
|
|
StopUITesting();
|
|
DestroyUITestingContext();
|
|
}
|
|
#endif
|
|
|
|
absl::Status TestManager::RunAllTests() {
|
|
if (is_running_) {
|
|
return absl::FailedPreconditionError("Tests are already running");
|
|
}
|
|
|
|
is_running_ = true;
|
|
progress_ = 0.0f;
|
|
last_results_.Clear();
|
|
|
|
// Execute all test suites
|
|
for (auto& suite : test_suites_) {
|
|
if (suite->IsEnabled()) {
|
|
current_test_name_ = suite->GetName();
|
|
auto status = ExecuteTestSuite(suite.get());
|
|
if (!status.ok()) {
|
|
is_running_ = false;
|
|
return status;
|
|
}
|
|
UpdateProgress();
|
|
}
|
|
}
|
|
|
|
is_running_ = false;
|
|
current_test_name_.clear();
|
|
progress_ = 1.0f;
|
|
|
|
return absl::OkStatus();
|
|
}
|
|
|
|
absl::Status TestManager::RunTestsByCategory(TestCategory category) {
|
|
if (is_running_) {
|
|
return absl::FailedPreconditionError("Tests are already running");
|
|
}
|
|
|
|
is_running_ = true;
|
|
progress_ = 0.0f;
|
|
last_results_.Clear();
|
|
|
|
// Filter and execute test suites by category
|
|
std::vector<TestSuite*> filtered_suites;
|
|
for (auto& suite : test_suites_) {
|
|
if (suite->IsEnabled() && suite->GetCategory() == category) {
|
|
filtered_suites.push_back(suite.get());
|
|
}
|
|
}
|
|
|
|
for (auto* suite : filtered_suites) {
|
|
current_test_name_ = suite->GetName();
|
|
auto status = ExecuteTestSuite(suite);
|
|
if (!status.ok()) {
|
|
is_running_ = false;
|
|
return status;
|
|
}
|
|
UpdateProgress();
|
|
}
|
|
|
|
is_running_ = false;
|
|
current_test_name_.clear();
|
|
progress_ = 1.0f;
|
|
|
|
return absl::OkStatus();
|
|
}
|
|
|
|
absl::Status TestManager::RunTestSuite(const std::string& suite_name) {
|
|
if (is_running_) {
|
|
return absl::FailedPreconditionError("Tests are already running");
|
|
}
|
|
|
|
auto it = suite_lookup_.find(suite_name);
|
|
if (it == suite_lookup_.end()) {
|
|
return absl::NotFoundError("Test suite not found: " + suite_name);
|
|
}
|
|
|
|
is_running_ = true;
|
|
progress_ = 0.0f;
|
|
last_results_.Clear();
|
|
current_test_name_ = suite_name;
|
|
|
|
auto status = ExecuteTestSuite(it->second);
|
|
|
|
is_running_ = false;
|
|
current_test_name_.clear();
|
|
progress_ = 1.0f;
|
|
|
|
return status;
|
|
}
|
|
|
|
void TestManager::RegisterTestSuite(std::unique_ptr<TestSuite> suite) {
|
|
if (suite) {
|
|
std::string name = suite->GetName();
|
|
suite_lookup_[name] = suite.get();
|
|
test_suites_.push_back(std::move(suite));
|
|
}
|
|
}
|
|
|
|
std::vector<std::string> TestManager::GetTestSuiteNames() const {
|
|
std::vector<std::string> names;
|
|
names.reserve(test_suites_.size());
|
|
for (const auto& suite : test_suites_) {
|
|
names.push_back(suite->GetName());
|
|
}
|
|
return names;
|
|
}
|
|
|
|
TestSuite* TestManager::GetTestSuite(const std::string& name) {
|
|
auto it = suite_lookup_.find(name);
|
|
return it != suite_lookup_.end() ? it->second : nullptr;
|
|
}
|
|
|
|
void TestManager::UpdateResourceStats() {
|
|
CollectResourceStats();
|
|
TrimResourceHistory();
|
|
}
|
|
|
|
absl::Status TestManager::ExecuteTestSuite(TestSuite* suite) {
|
|
if (!suite) {
|
|
return absl::InvalidArgumentError("Test suite is null");
|
|
}
|
|
|
|
// Collect resource stats before test
|
|
CollectResourceStats();
|
|
|
|
// Execute the test suite
|
|
auto status = suite->RunTests(last_results_);
|
|
|
|
// Collect resource stats after test
|
|
CollectResourceStats();
|
|
|
|
return status;
|
|
}
|
|
|
|
void TestManager::UpdateProgress() {
|
|
if (test_suites_.empty()) {
|
|
progress_ = 1.0f;
|
|
return;
|
|
}
|
|
|
|
size_t completed = 0;
|
|
for (const auto& suite : test_suites_) {
|
|
if (suite->IsEnabled()) {
|
|
completed++;
|
|
}
|
|
}
|
|
|
|
progress_ = static_cast<float>(completed) / test_suites_.size();
|
|
}
|
|
|
|
void TestManager::CollectResourceStats() {
|
|
ResourceStats stats;
|
|
stats.timestamp = std::chrono::steady_clock::now();
|
|
|
|
// Get Arena statistics
|
|
auto& arena = gfx::Arena::Get();
|
|
stats.texture_count = arena.GetTextureCount();
|
|
stats.surface_count = arena.GetSurfaceCount();
|
|
|
|
// Get frame rate from ImGui
|
|
stats.frame_rate = ImGui::GetIO().Framerate;
|
|
|
|
// Estimate memory usage (simplified)
|
|
stats.memory_usage_mb = (stats.texture_count + stats.surface_count) / 1024; // Rough estimate
|
|
|
|
resource_history_.push_back(stats);
|
|
}
|
|
|
|
void TestManager::TrimResourceHistory() {
|
|
if (resource_history_.size() > kMaxResourceHistorySize) {
|
|
resource_history_.erase(
|
|
resource_history_.begin(),
|
|
resource_history_.begin() + (resource_history_.size() - kMaxResourceHistorySize));
|
|
}
|
|
}
|
|
|
|
void TestManager::DrawTestDashboard(bool* show_dashboard) {
|
|
bool* dashboard_flag = show_dashboard ? show_dashboard : &show_dashboard_;
|
|
|
|
// Set a larger default window size
|
|
ImGui::SetNextWindowSize(ImVec2(900, 700), ImGuiCond_FirstUseEver);
|
|
|
|
if (!ImGui::Begin("Test Dashboard", dashboard_flag, ImGuiWindowFlags_MenuBar)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
// ROM status indicator with detailed information
|
|
bool has_rom = current_rom_ && current_rom_->is_loaded();
|
|
|
|
// Add real-time ROM status checking
|
|
static int frame_counter = 0;
|
|
frame_counter++;
|
|
if (frame_counter % 60 == 0) { // Check every 60 frames
|
|
// Log ROM status periodically for debugging
|
|
util::logf("TestManager ROM status check - Frame %d: ROM %p, loaded: %s",
|
|
frame_counter, (void*)current_rom_, has_rom ? "true" : "false");
|
|
}
|
|
|
|
if (ImGui::BeginTable("ROM_Status_Table", 2, ImGuiTableFlags_BordersInner)) {
|
|
ImGui::TableSetupColumn("Property", ImGuiTableColumnFlags_WidthFixed, 120);
|
|
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch);
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("ROM Status:");
|
|
ImGui::TableNextColumn();
|
|
if (has_rom) {
|
|
ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f),
|
|
"%s Loaded", ICON_MD_CHECK_CIRCLE);
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("ROM Title:");
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%s", current_rom_->title().c_str());
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("File Name:");
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%s", current_rom_->filename().c_str());
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("Size:");
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%.2f MB (%zu bytes)", current_rom_->size() / 1048576.0f, current_rom_->size());
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("Modified:");
|
|
ImGui::TableNextColumn();
|
|
if (current_rom_->dirty()) {
|
|
ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.0f, 1.0f), "%s Yes", ICON_MD_EDIT);
|
|
} else {
|
|
ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "%s No", ICON_MD_CHECK);
|
|
}
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("ROM Pointer:");
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%p", (void*)current_rom_);
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("Actions:");
|
|
ImGui::TableNextColumn();
|
|
if (ImGui::Button("Refresh ROM Reference")) {
|
|
RefreshCurrentRom();
|
|
}
|
|
|
|
} else {
|
|
ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.0f, 1.0f),
|
|
"%s Not Loaded", ICON_MD_WARNING);
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("ROM Pointer:");
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%p", (void*)current_rom_);
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("Status:");
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("ROM-dependent tests will be skipped");
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("Actions:");
|
|
ImGui::TableNextColumn();
|
|
if (ImGui::Button("Refresh ROM Reference")) {
|
|
RefreshCurrentRom();
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Debug ROM State")) {
|
|
util::logf("=== ROM DEBUG INFO ===");
|
|
util::logf("current_rom_ pointer: %p", (void*)current_rom_);
|
|
if (current_rom_) {
|
|
util::logf("ROM title: '%s'", current_rom_->title().c_str());
|
|
util::logf("ROM size: %zu", current_rom_->size());
|
|
util::logf("ROM is_loaded(): %s", current_rom_->is_loaded() ? "true" : "false");
|
|
util::logf("ROM data pointer: %p", (void*)current_rom_->data());
|
|
}
|
|
util::logf("======================");
|
|
}
|
|
}
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::Separator();
|
|
|
|
// Menu bar
|
|
if (ImGui::BeginMenuBar()) {
|
|
if (ImGui::BeginMenu("Run")) {
|
|
if (ImGui::MenuItem("All Tests", "Ctrl+T", false, !is_running_)) {
|
|
[[maybe_unused]] auto status = RunAllTests();
|
|
}
|
|
ImGui::Separator();
|
|
if (ImGui::MenuItem("Unit Tests", nullptr, false, !is_running_)) {
|
|
[[maybe_unused]] auto status = RunTestsByCategory(TestCategory::kUnit);
|
|
}
|
|
if (ImGui::MenuItem("Integration Tests", nullptr, false, !is_running_)) {
|
|
[[maybe_unused]] auto status = RunTestsByCategory(TestCategory::kIntegration);
|
|
}
|
|
if (ImGui::MenuItem("UI Tests", nullptr, false, !is_running_)) {
|
|
[[maybe_unused]] auto status = RunTestsByCategory(TestCategory::kUI);
|
|
}
|
|
if (ImGui::MenuItem("Performance Tests", nullptr, false, !is_running_)) {
|
|
[[maybe_unused]] auto status = RunTestsByCategory(TestCategory::kPerformance);
|
|
}
|
|
if (ImGui::MenuItem("Memory Tests", nullptr, false, !is_running_)) {
|
|
[[maybe_unused]] auto status = RunTestsByCategory(TestCategory::kMemory);
|
|
}
|
|
ImGui::EndMenu();
|
|
}
|
|
|
|
if (ImGui::BeginMenu("View")) {
|
|
ImGui::MenuItem("Resource Monitor", nullptr, &show_resource_monitor_);
|
|
ImGui::MenuItem("Google Tests", nullptr, &show_google_tests_);
|
|
ImGui::MenuItem("ROM Test Results", nullptr, &show_rom_test_results_);
|
|
ImGui::Separator();
|
|
if (ImGui::MenuItem("Export Results", nullptr, false, last_results_.total_tests > 0)) {
|
|
// TODO: Implement result export
|
|
}
|
|
ImGui::EndMenu();
|
|
}
|
|
|
|
if (ImGui::BeginMenu("ROM")) {
|
|
if (ImGui::MenuItem("Test Current ROM", nullptr, false, current_rom_ && current_rom_->is_loaded())) {
|
|
[[maybe_unused]] auto status = RunTestsByCategory(TestCategory::kIntegration);
|
|
}
|
|
if (ImGui::MenuItem("Load ROM for Testing...")) {
|
|
show_rom_file_dialog_ = true;
|
|
}
|
|
ImGui::Separator();
|
|
if (ImGui::MenuItem("Refresh ROM Reference")) {
|
|
RefreshCurrentRom();
|
|
}
|
|
ImGui::EndMenu();
|
|
}
|
|
|
|
if (ImGui::BeginMenu("Configure")) {
|
|
if (ImGui::MenuItem("Test Configuration")) {
|
|
show_test_configuration_ = true;
|
|
}
|
|
ImGui::Separator();
|
|
bool nfd_mode = core::FeatureFlags::get().kUseNativeFileDialog;
|
|
if (ImGui::MenuItem("Use NFD File Dialog", nullptr, &nfd_mode)) {
|
|
core::FeatureFlags::get().kUseNativeFileDialog = nfd_mode;
|
|
util::logf("Global file dialog mode changed to: %s", nfd_mode ? "NFD" : "Bespoke");
|
|
}
|
|
ImGui::EndMenu();
|
|
}
|
|
|
|
ImGui::EndMenuBar();
|
|
}
|
|
|
|
// Show test configuration status
|
|
int enabled_count = 0;
|
|
int total_count = 0;
|
|
static const std::vector<std::string> all_test_names = {
|
|
"ROM_Header_Validation_Test", "ROM_Data_Access_Test", "ROM_Graphics_Extraction_Test",
|
|
"ROM_Overworld_Loading_Test", "Tile16_Editor_Test", "Comprehensive_Save_Test",
|
|
"ROM_Sprite_Data_Test", "ROM_Music_Data_Test"
|
|
};
|
|
|
|
for (const auto& test_name : all_test_names) {
|
|
total_count++;
|
|
if (IsTestEnabled(test_name)) {
|
|
enabled_count++;
|
|
}
|
|
}
|
|
|
|
ImGui::Text("%s Test Status: %d/%d enabled", ICON_MD_CHECKLIST, enabled_count, total_count);
|
|
if (enabled_count < total_count) {
|
|
ImGui::SameLine();
|
|
ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.0f, 1.0f),
|
|
"(Some tests disabled - check Configuration)");
|
|
}
|
|
|
|
// Enhanced test execution status
|
|
if (is_running_) {
|
|
ImGui::PushStyleColor(ImGuiCol_Text, GetTestStatusColor(TestStatus::kRunning));
|
|
ImGui::Text("%s Running: %s", ICON_MD_PLAY_CIRCLE_FILLED, current_test_name_.c_str());
|
|
ImGui::PopStyleColor();
|
|
ImGui::ProgressBar(progress_, ImVec2(-1, 0),
|
|
absl::StrFormat("%.0f%%", progress_ * 100.0f).c_str());
|
|
} else {
|
|
// Enhanced control buttons
|
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.7f, 0.2f, 1.0f));
|
|
if (ImGui::Button(absl::StrCat(ICON_MD_PLAY_ARROW, " Run All Tests").c_str(), ImVec2(140, 0))) {
|
|
[[maybe_unused]] auto status = RunAllTests();
|
|
}
|
|
ImGui::PopStyleColor();
|
|
|
|
ImGui::SameLine();
|
|
if (ImGui::Button(absl::StrCat(ICON_MD_SPEED, " Quick Test").c_str(), ImVec2(100, 0))) {
|
|
[[maybe_unused]] auto status = RunTestsByCategory(TestCategory::kMemory);
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
bool has_rom = current_rom_ && current_rom_->is_loaded();
|
|
if (has_rom) {
|
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.5f, 0.8f, 1.0f));
|
|
}
|
|
if (ImGui::Button(absl::StrCat(ICON_MD_STORAGE, " ROM Tests").c_str(), ImVec2(100, 0))) {
|
|
if (has_rom) {
|
|
[[maybe_unused]] auto status = RunTestsByCategory(TestCategory::kIntegration);
|
|
}
|
|
}
|
|
if (has_rom) {
|
|
ImGui::PopStyleColor();
|
|
}
|
|
if (ImGui::IsItemHovered()) {
|
|
if (has_rom) {
|
|
ImGui::SetTooltip("Run tests on current ROM: %s", current_rom_->title().c_str());
|
|
} else {
|
|
ImGui::SetTooltip("Load a ROM to enable ROM-dependent tests");
|
|
}
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
if (ImGui::Button(absl::StrCat(ICON_MD_CLEAR, " Clear").c_str(), ImVec2(80, 0))) {
|
|
ClearResults();
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
if (ImGui::Button(absl::StrCat(ICON_MD_SETTINGS, " Config").c_str(), ImVec2(80, 0))) {
|
|
show_test_configuration_ = true;
|
|
}
|
|
}
|
|
|
|
ImGui::Separator();
|
|
|
|
// Enhanced test results summary with better visuals
|
|
if (last_results_.total_tests > 0) {
|
|
// Test summary header
|
|
ImGui::Text("%s Test Results Summary", ICON_MD_ASSESSMENT);
|
|
|
|
// Progress bar showing pass rate
|
|
float pass_rate = last_results_.GetPassRate();
|
|
ImVec4 progress_color = pass_rate >= 0.9f ? ImVec4(0.0f, 1.0f, 0.0f, 1.0f) :
|
|
pass_rate >= 0.7f ? ImVec4(1.0f, 1.0f, 0.0f, 1.0f) :
|
|
ImVec4(1.0f, 0.0f, 0.0f, 1.0f);
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_PlotHistogram, progress_color);
|
|
ImGui::ProgressBar(pass_rate, ImVec2(-1, 0),
|
|
absl::StrFormat("Pass Rate: %.1f%%", pass_rate * 100.0f).c_str());
|
|
ImGui::PopStyleColor();
|
|
|
|
// Test counts with icons
|
|
ImGui::Text("%s Total: %zu", ICON_MD_ANALYTICS, last_results_.total_tests);
|
|
ImGui::SameLine();
|
|
ImGui::TextColored(GetTestStatusColor(TestStatus::kPassed),
|
|
"%s %zu", ICON_MD_CHECK_CIRCLE, last_results_.passed_tests);
|
|
ImGui::SameLine();
|
|
ImGui::TextColored(GetTestStatusColor(TestStatus::kFailed),
|
|
"%s %zu", ICON_MD_ERROR, last_results_.failed_tests);
|
|
ImGui::SameLine();
|
|
ImGui::TextColored(GetTestStatusColor(TestStatus::kSkipped),
|
|
"%s %zu", ICON_MD_SKIP_NEXT, last_results_.skipped_tests);
|
|
|
|
ImGui::Text("%s Duration: %lld ms", ICON_MD_TIMER, last_results_.total_duration.count());
|
|
|
|
// Test suite breakdown
|
|
if (ImGui::CollapsingHeader("Test Suite Breakdown")) {
|
|
std::unordered_map<std::string, std::pair<size_t, size_t>> suite_stats; // passed, total
|
|
for (const auto& result : last_results_.individual_results) {
|
|
suite_stats[result.suite_name].second++; // total
|
|
if (result.status == TestStatus::kPassed) {
|
|
suite_stats[result.suite_name].first++; // passed
|
|
}
|
|
}
|
|
|
|
for (const auto& [suite_name, stats] : suite_stats) {
|
|
float suite_pass_rate = stats.second > 0 ?
|
|
static_cast<float>(stats.first) / stats.second : 0.0f;
|
|
ImGui::Text("%s: %zu/%zu (%.0f%%)",
|
|
suite_name.c_str(), stats.first, stats.second,
|
|
suite_pass_rate * 100.0f);
|
|
}
|
|
}
|
|
}
|
|
|
|
ImGui::Separator();
|
|
|
|
// Enhanced test filter with category selection
|
|
ImGui::Text("%s Filter & View Options", ICON_MD_FILTER_LIST);
|
|
|
|
// Category filter
|
|
const char* categories[] = {"All", "Unit", "Integration", "UI", "Performance", "Memory"};
|
|
static int selected_category = 0;
|
|
if (ImGui::Combo("Category", &selected_category, categories, IM_ARRAYSIZE(categories))) {
|
|
switch (selected_category) {
|
|
case 0: category_filter_ = TestCategory::kUnit; break; // All - use Unit as default
|
|
case 1: category_filter_ = TestCategory::kUnit; break;
|
|
case 2: category_filter_ = TestCategory::kIntegration; break;
|
|
case 3: category_filter_ = TestCategory::kUI; break;
|
|
case 4: category_filter_ = TestCategory::kPerformance; break;
|
|
case 5: category_filter_ = TestCategory::kMemory; break;
|
|
}
|
|
}
|
|
|
|
// Text filter
|
|
static char filter_buffer[256] = "";
|
|
ImGui::SetNextItemWidth(-80);
|
|
if (ImGui::InputTextWithHint("##filter", "Search tests...", filter_buffer, sizeof(filter_buffer))) {
|
|
test_filter_ = std::string(filter_buffer);
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Clear")) {
|
|
filter_buffer[0] = '\0';
|
|
test_filter_.clear();
|
|
}
|
|
|
|
ImGui::Separator();
|
|
|
|
// Enhanced test results list with better formatting
|
|
if (ImGui::BeginChild("TestResults", ImVec2(0, 0), true)) {
|
|
if (last_results_.individual_results.empty()) {
|
|
ImGui::TextColored(ImVec4(0.6f, 0.6f, 0.6f, 1.0f),
|
|
"No test results to display. Run some tests to see results here.");
|
|
} else {
|
|
for (const auto& result : last_results_.individual_results) {
|
|
// Apply filters
|
|
bool category_match = (selected_category == 0) || (result.category == category_filter_);
|
|
bool text_match = test_filter_.empty() ||
|
|
result.name.find(test_filter_) != std::string::npos ||
|
|
result.suite_name.find(test_filter_) != std::string::npos;
|
|
|
|
if (!category_match || !text_match) {
|
|
continue;
|
|
}
|
|
|
|
ImGui::PushID(&result);
|
|
|
|
// Status icon and test name
|
|
const char* status_icon = ICON_MD_HELP;
|
|
switch (result.status) {
|
|
case TestStatus::kPassed: status_icon = ICON_MD_CHECK_CIRCLE; break;
|
|
case TestStatus::kFailed: status_icon = ICON_MD_ERROR; break;
|
|
case TestStatus::kSkipped: status_icon = ICON_MD_SKIP_NEXT; break;
|
|
case TestStatus::kRunning: status_icon = ICON_MD_PLAY_CIRCLE_FILLED; break;
|
|
default: break;
|
|
}
|
|
|
|
ImGui::TextColored(GetTestStatusColor(result.status),
|
|
"%s %s::%s",
|
|
status_icon,
|
|
result.suite_name.c_str(),
|
|
result.name.c_str());
|
|
|
|
// Show duration and timestamp on same line if space allows
|
|
if (ImGui::GetContentRegionAvail().x > 200) {
|
|
ImGui::SameLine();
|
|
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f),
|
|
"(%lld ms)", result.duration.count());
|
|
}
|
|
|
|
// Show detailed information for failed tests
|
|
if (result.status == TestStatus::kFailed && !result.error_message.empty()) {
|
|
ImGui::Indent();
|
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.8f, 0.8f, 1.0f));
|
|
ImGui::TextWrapped("%s %s", ICON_MD_ERROR_OUTLINE, result.error_message.c_str());
|
|
ImGui::PopStyleColor();
|
|
ImGui::Unindent();
|
|
}
|
|
|
|
// Show additional info for passed tests if they have messages
|
|
if (result.status == TestStatus::kPassed && !result.error_message.empty()) {
|
|
ImGui::Indent();
|
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.8f, 1.0f, 0.8f, 1.0f));
|
|
ImGui::TextWrapped("%s %s", ICON_MD_INFO, result.error_message.c_str());
|
|
ImGui::PopStyleColor();
|
|
ImGui::Unindent();
|
|
}
|
|
|
|
ImGui::PopID();
|
|
}
|
|
}
|
|
}
|
|
ImGui::EndChild();
|
|
|
|
ImGui::End();
|
|
|
|
// Resource monitor window
|
|
if (show_resource_monitor_) {
|
|
ImGui::Begin(absl::StrCat(ICON_MD_MONITOR, " Resource Monitor").c_str(), &show_resource_monitor_);
|
|
|
|
if (!resource_history_.empty()) {
|
|
const auto& latest = resource_history_.back();
|
|
ImGui::Text("%s Textures: %zu", ICON_MD_TEXTURE, latest.texture_count);
|
|
ImGui::Text("%s Surfaces: %zu", ICON_MD_LAYERS, latest.surface_count);
|
|
ImGui::Text("%s Memory: %zu MB", ICON_MD_MEMORY, latest.memory_usage_mb);
|
|
ImGui::Text("%s FPS: %.1f", ICON_MD_SPEED, latest.frame_rate);
|
|
|
|
// Simple plot of resource usage over time
|
|
if (resource_history_.size() > 1) {
|
|
std::vector<float> texture_counts;
|
|
std::vector<float> surface_counts;
|
|
texture_counts.reserve(resource_history_.size());
|
|
surface_counts.reserve(resource_history_.size());
|
|
|
|
for (const auto& stats : resource_history_) {
|
|
texture_counts.push_back(static_cast<float>(stats.texture_count));
|
|
surface_counts.push_back(static_cast<float>(stats.surface_count));
|
|
}
|
|
|
|
ImGui::PlotLines("Textures", texture_counts.data(),
|
|
static_cast<int>(texture_counts.size()), 0, nullptr,
|
|
0.0f, FLT_MAX, ImVec2(0, 80));
|
|
ImGui::PlotLines("Surfaces", surface_counts.data(),
|
|
static_cast<int>(surface_counts.size()), 0, nullptr,
|
|
0.0f, FLT_MAX, ImVec2(0, 80));
|
|
}
|
|
}
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
// Google Tests window
|
|
if (show_google_tests_) {
|
|
ImGui::SetNextWindowSize(ImVec2(600, 400), ImGuiCond_FirstUseEver);
|
|
if (ImGui::Begin("Google Tests", &show_google_tests_)) {
|
|
ImGui::Text("%s Google Test Integration", ICON_MD_SCIENCE);
|
|
ImGui::Separator();
|
|
|
|
#ifdef YAZE_ENABLE_GTEST
|
|
ImGui::Text("Google Test framework is available");
|
|
|
|
if (ImGui::Button("Run All Google Tests")) {
|
|
// Run Google tests - this would integrate with gtest
|
|
util::logf("Running Google Tests...");
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Run Specific Test Suite")) {
|
|
// Show test suite selector
|
|
}
|
|
|
|
ImGui::Separator();
|
|
ImGui::Text("Available Test Suites:");
|
|
ImGui::BulletText("Unit Tests");
|
|
ImGui::BulletText("Integration Tests");
|
|
ImGui::BulletText("Performance Tests");
|
|
#else
|
|
ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.0f, 1.0f),
|
|
"%s Google Test framework not available", ICON_MD_WARNING);
|
|
ImGui::Text("Enable YAZE_ENABLE_GTEST to use Google Test integration");
|
|
#endif
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
// ROM Test Results window
|
|
if (show_rom_test_results_) {
|
|
ImGui::SetNextWindowSize(ImVec2(700, 500), ImGuiCond_FirstUseEver);
|
|
if (ImGui::Begin("ROM Test Results", &show_rom_test_results_)) {
|
|
ImGui::Text("%s ROM Analysis Results", ICON_MD_ANALYTICS);
|
|
|
|
if (current_rom_ && current_rom_->is_loaded()) {
|
|
ImGui::Text("Testing ROM: %s", current_rom_->title().c_str());
|
|
ImGui::Separator();
|
|
|
|
// Show ROM-specific test results
|
|
if (ImGui::CollapsingHeader("ROM Data Integrity", ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
ImGui::Text("ROM Size: %.2f MB", current_rom_->size() / 1048576.0f);
|
|
ImGui::Text("Modified: %s", current_rom_->dirty() ? "Yes" : "No");
|
|
|
|
if (ImGui::Button("Run Data Integrity Check")) {
|
|
[[maybe_unused]] auto status = TestRomDataIntegrity(current_rom_);
|
|
[[maybe_unused]] auto suite_status = RunTestsByCategory(TestCategory::kIntegration);
|
|
}
|
|
}
|
|
|
|
if (ImGui::CollapsingHeader("Save/Load Testing")) {
|
|
ImGui::Text("Test ROM save and load operations");
|
|
|
|
if (ImGui::Button("Test Save Operations")) {
|
|
[[maybe_unused]] auto status = TestRomSaveLoad(current_rom_);
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Test Load Operations")) {
|
|
[[maybe_unused]] auto status = TestRomSaveLoad(current_rom_);
|
|
}
|
|
}
|
|
|
|
if (ImGui::CollapsingHeader("Editor Integration")) {
|
|
ImGui::Text("Test editor components with current ROM");
|
|
|
|
if (ImGui::Button("Test Overworld Editor")) {
|
|
// Test overworld editor with current ROM
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Test Tile16 Editor")) {
|
|
// Test tile16 editor with current ROM
|
|
}
|
|
}
|
|
|
|
} else {
|
|
ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.0f, 1.0f),
|
|
"%s No ROM loaded for analysis", ICON_MD_WARNING);
|
|
}
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
// ROM File Dialog
|
|
if (show_rom_file_dialog_) {
|
|
ImGui::SetNextWindowSize(ImVec2(400, 200), ImGuiCond_Appearing);
|
|
if (ImGui::Begin("Load ROM for Testing", &show_rom_file_dialog_, ImGuiWindowFlags_NoResize)) {
|
|
ImGui::Text("%s Load ROM for Testing", ICON_MD_FOLDER_OPEN);
|
|
ImGui::Separator();
|
|
|
|
ImGui::Text("Select a ROM file to run tests on:");
|
|
|
|
if (ImGui::Button("Browse ROM File...", ImVec2(-1, 0))) {
|
|
// TODO: Implement file dialog to load ROM specifically for testing
|
|
// This would be separate from the main editor ROM
|
|
show_rom_file_dialog_ = false;
|
|
}
|
|
|
|
ImGui::Separator();
|
|
if (ImGui::Button("Cancel", ImVec2(-1, 0))) {
|
|
show_rom_file_dialog_ = false;
|
|
}
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
// Test Configuration Window
|
|
if (show_test_configuration_) {
|
|
ImGui::SetNextWindowSize(ImVec2(600, 500), ImGuiCond_FirstUseEver);
|
|
if (ImGui::Begin("Test Configuration", &show_test_configuration_)) {
|
|
ImGui::Text("%s Test Configuration", ICON_MD_SETTINGS);
|
|
ImGui::Separator();
|
|
|
|
// File Dialog Configuration
|
|
if (ImGui::CollapsingHeader("File Dialog Settings", ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
ImGui::Text("File Dialog Implementation:");
|
|
|
|
bool nfd_mode = core::FeatureFlags::get().kUseNativeFileDialog;
|
|
if (ImGui::RadioButton("NFD (Native File Dialog)", nfd_mode)) {
|
|
core::FeatureFlags::get().kUseNativeFileDialog = true;
|
|
util::logf("Global file dialog mode set to: NFD");
|
|
}
|
|
if (ImGui::IsItemHovered()) {
|
|
ImGui::SetTooltip("Use NFD library for native OS file dialogs (global setting)");
|
|
}
|
|
|
|
if (ImGui::RadioButton("Bespoke Implementation", !nfd_mode)) {
|
|
core::FeatureFlags::get().kUseNativeFileDialog = false;
|
|
util::logf("Global file dialog mode set to: Bespoke");
|
|
}
|
|
if (ImGui::IsItemHovered()) {
|
|
ImGui::SetTooltip("Use custom file dialog implementation (global setting)");
|
|
}
|
|
|
|
ImGui::Separator();
|
|
ImGui::Text("Current Mode: %s", core::FeatureFlags::get().kUseNativeFileDialog ? "NFD" : "Bespoke");
|
|
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Note: This setting affects ALL file dialogs in the application");
|
|
|
|
if (ImGui::Button("Test Current File Dialog")) {
|
|
// Test the current file dialog implementation
|
|
util::logf("Testing global file dialog mode: %s",
|
|
core::FeatureFlags::get().kUseNativeFileDialog ? "NFD" : "Bespoke");
|
|
|
|
// Actually test the file dialog
|
|
auto result = core::FileDialogWrapper::ShowOpenFileDialog();
|
|
if (!result.empty()) {
|
|
util::logf("File dialog test successful: %s", result.c_str());
|
|
} else {
|
|
util::logf("File dialog test: No file selected or dialog canceled");
|
|
}
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Test NFD Directly")) {
|
|
auto result = core::FileDialogWrapper::ShowOpenFileDialogNFD();
|
|
if (!result.empty()) {
|
|
util::logf("NFD test successful: %s", result.c_str());
|
|
} else {
|
|
util::logf("NFD test: No file selected, canceled, or error occurred");
|
|
}
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Test Bespoke Directly")) {
|
|
auto result = core::FileDialogWrapper::ShowOpenFileDialogBespoke();
|
|
if (!result.empty()) {
|
|
util::logf("Bespoke test successful: %s", result.c_str());
|
|
} else {
|
|
util::logf("Bespoke test: No file selected or not implemented");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test Selection Configuration
|
|
if (ImGui::CollapsingHeader("Test Selection", ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
ImGui::Text("Enable/Disable Individual Tests:");
|
|
ImGui::Separator();
|
|
|
|
// List of known tests with their risk levels
|
|
static const std::vector<std::pair<std::string, std::string>> known_tests = {
|
|
{"ROM_Header_Validation_Test", "Safe - Read-only ROM header validation"},
|
|
{"ROM_Data_Access_Test", "Safe - Basic ROM data access testing"},
|
|
{"ROM_Graphics_Extraction_Test", "Safe - Graphics data extraction testing"},
|
|
{"ROM_Overworld_Loading_Test", "Safe - Overworld data loading testing"},
|
|
{"Tile16_Editor_Test", "Moderate - Tile16 editor initialization"},
|
|
{"Comprehensive_Save_Test", "DANGEROUS - Known to crash, uses ROM copies"},
|
|
{"ROM_Sprite_Data_Test", "Safe - Sprite data validation"},
|
|
{"ROM_Music_Data_Test", "Safe - Music data validation"}
|
|
};
|
|
|
|
// Initialize problematic tests as disabled by default
|
|
static bool initialized_defaults = false;
|
|
if (!initialized_defaults) {
|
|
DisableTest("Comprehensive_Save_Test"); // Disable crash-prone test by default
|
|
initialized_defaults = true;
|
|
}
|
|
|
|
if (ImGui::BeginTable("TestSelection", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
|
ImGui::TableSetupColumn("Test Name", ImGuiTableColumnFlags_WidthFixed, 200);
|
|
ImGui::TableSetupColumn("Risk Level", ImGuiTableColumnFlags_WidthStretch);
|
|
ImGui::TableSetupColumn("Status", ImGuiTableColumnFlags_WidthFixed, 80);
|
|
ImGui::TableSetupColumn("Action", ImGuiTableColumnFlags_WidthFixed, 100);
|
|
ImGui::TableHeadersRow();
|
|
|
|
for (const auto& [test_name, description] : known_tests) {
|
|
bool enabled = IsTestEnabled(test_name);
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%s", test_name.c_str());
|
|
|
|
ImGui::TableNextColumn();
|
|
// Color-code the risk level
|
|
if (description.find("DANGEROUS") != std::string::npos) {
|
|
ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "%s", description.c_str());
|
|
} else if (description.find("Moderate") != std::string::npos) {
|
|
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.0f, 1.0f), "%s", description.c_str());
|
|
} else {
|
|
ImGui::TextColored(ImVec4(0.0f, 0.8f, 0.0f, 1.0f), "%s", description.c_str());
|
|
}
|
|
|
|
ImGui::TableNextColumn();
|
|
if (enabled) {
|
|
ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "%s ON", ICON_MD_CHECK);
|
|
} else {
|
|
ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.0f, 1.0f), "%s OFF", ICON_MD_BLOCK);
|
|
}
|
|
|
|
ImGui::TableNextColumn();
|
|
ImGui::PushID(test_name.c_str());
|
|
if (enabled) {
|
|
if (ImGui::Button("Disable")) {
|
|
DisableTest(test_name);
|
|
util::logf("Disabled test: %s", test_name.c_str());
|
|
}
|
|
} else {
|
|
if (ImGui::Button("Enable")) {
|
|
EnableTest(test_name);
|
|
util::logf("Enabled test: %s", test_name.c_str());
|
|
}
|
|
}
|
|
ImGui::PopID();
|
|
}
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
ImGui::Separator();
|
|
ImGui::Text("Quick Actions:");
|
|
|
|
if (ImGui::Button("Enable Safe Tests Only")) {
|
|
for (const auto& [test_name, description] : known_tests) {
|
|
if (description.find("Safe") != std::string::npos) {
|
|
EnableTest(test_name);
|
|
} else {
|
|
DisableTest(test_name);
|
|
}
|
|
}
|
|
util::logf("Enabled only safe tests");
|
|
}
|
|
ImGui::SameLine();
|
|
|
|
if (ImGui::Button("Enable All Tests")) {
|
|
for (const auto& [test_name, description] : known_tests) {
|
|
EnableTest(test_name);
|
|
}
|
|
util::logf("Enabled all tests (including dangerous ones)");
|
|
}
|
|
ImGui::SameLine();
|
|
|
|
if (ImGui::Button("Disable All Tests")) {
|
|
for (const auto& [test_name, description] : known_tests) {
|
|
DisableTest(test_name);
|
|
}
|
|
util::logf("Disabled all tests");
|
|
}
|
|
|
|
ImGui::Separator();
|
|
ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.0f, 1.0f),
|
|
"⚠️ Recommendation: Use 'Enable Safe Tests Only' to avoid crashes");
|
|
}
|
|
|
|
// Platform-specific settings
|
|
if (ImGui::CollapsingHeader("Platform Settings")) {
|
|
ImGui::Text("macOS Tahoe Compatibility:");
|
|
ImGui::BulletText("NFD may have issues on macOS Sequoia+");
|
|
ImGui::BulletText("Bespoke dialog provides fallback option");
|
|
ImGui::BulletText("Global setting affects File → Open, Project dialogs, etc.");
|
|
|
|
ImGui::Separator();
|
|
ImGui::Text("Test Both Implementations:");
|
|
|
|
if (ImGui::Button("Quick Test NFD")) {
|
|
auto result = core::FileDialogWrapper::ShowOpenFileDialogNFD();
|
|
util::logf("NFD test result: %s", result.empty() ? "Failed/Canceled" : result.c_str());
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Quick Test Bespoke")) {
|
|
auto result = core::FileDialogWrapper::ShowOpenFileDialogBespoke();
|
|
util::logf("Bespoke test result: %s", result.empty() ? "Failed/Not Implemented" : result.c_str());
|
|
}
|
|
|
|
ImGui::Separator();
|
|
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f),
|
|
"Note: These tests don't change the global setting");
|
|
}
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
// Test Session Creation Dialog
|
|
if (show_test_session_dialog_) {
|
|
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
|
ImGui::SetNextWindowSize(ImVec2(500, 300), ImGuiCond_Appearing);
|
|
|
|
if (ImGui::Begin("Test ROM Session", &show_test_session_dialog_, ImGuiWindowFlags_NoResize)) {
|
|
ImGui::Text("%s Test ROM Created Successfully", ICON_MD_CHECK_CIRCLE);
|
|
ImGui::Separator();
|
|
|
|
ImGui::Text("A test ROM has been created with your modifications:");
|
|
ImGui::Text("File: %s", test_rom_path_for_session_.c_str());
|
|
|
|
// Extract just the filename for display
|
|
std::string display_filename = test_rom_path_for_session_;
|
|
auto last_slash = display_filename.find_last_of("/\\");
|
|
if (last_slash != std::string::npos) {
|
|
display_filename = display_filename.substr(last_slash + 1);
|
|
}
|
|
|
|
ImGui::Separator();
|
|
ImGui::Text("Would you like to open this test ROM in a new session?");
|
|
|
|
if (ImGui::Button(absl::StrFormat("%s Open in New Session", ICON_MD_TAB).c_str(), ImVec2(200, 0))) {
|
|
// TODO: This would need access to EditorManager to create a new session
|
|
// For now, just show a message
|
|
util::logf("User requested to open test ROM in new session: %s", test_rom_path_for_session_.c_str());
|
|
show_test_session_dialog_ = false;
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
if (ImGui::Button(absl::StrFormat("%s Keep Current Session", ICON_MD_CLOSE).c_str(), ImVec2(200, 0))) {
|
|
show_test_session_dialog_ = false;
|
|
}
|
|
|
|
ImGui::Separator();
|
|
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f),
|
|
"Note: Test ROM contains your modifications and can be");
|
|
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f),
|
|
"opened later using File → Open");
|
|
}
|
|
ImGui::End();
|
|
}
|
|
}
|
|
|
|
void TestManager::RefreshCurrentRom() {
|
|
util::logf("=== TestManager ROM Refresh ===");
|
|
|
|
// Log current TestManager ROM state for debugging
|
|
if (current_rom_) {
|
|
util::logf("TestManager ROM pointer: %p", (void*)current_rom_);
|
|
util::logf("ROM is_loaded(): %s", current_rom_->is_loaded() ? "true" : "false");
|
|
if (current_rom_->is_loaded()) {
|
|
util::logf("ROM title: '%s'", current_rom_->title().c_str());
|
|
util::logf("ROM size: %.2f MB", current_rom_->size() / 1048576.0f);
|
|
util::logf("ROM dirty: %s", current_rom_->dirty() ? "true" : "false");
|
|
}
|
|
} else {
|
|
util::logf("TestManager ROM pointer is null");
|
|
util::logf("Note: ROM should be set by EditorManager when ROM is loaded");
|
|
}
|
|
util::logf("===============================");
|
|
}
|
|
|
|
absl::Status TestManager::CreateTestRomCopy(Rom* source_rom, std::unique_ptr<Rom>& test_rom) {
|
|
if (!source_rom || !source_rom->is_loaded()) {
|
|
return absl::FailedPreconditionError("Source ROM not loaded");
|
|
}
|
|
|
|
util::logf("Creating test ROM copy from: %s", source_rom->title().c_str());
|
|
|
|
// Create a new ROM instance
|
|
test_rom = std::make_unique<Rom>();
|
|
|
|
// Copy the ROM data
|
|
auto rom_data = source_rom->vector();
|
|
auto load_status = test_rom->LoadFromData(rom_data, true);
|
|
if (!load_status.ok()) {
|
|
return load_status;
|
|
}
|
|
|
|
util::logf("Test ROM copy created successfully (size: %.2f MB)",
|
|
test_rom->size() / 1048576.0f);
|
|
return absl::OkStatus();
|
|
}
|
|
|
|
std::string TestManager::GenerateTestRomFilename(const std::string& base_name) {
|
|
// Generate filename with timestamp
|
|
auto now = std::chrono::system_clock::now();
|
|
auto time_t = std::chrono::system_clock::to_time_t(now);
|
|
auto local_time = *std::localtime(&time_t);
|
|
|
|
std::string timestamp = absl::StrFormat("%04d%02d%02d_%02d%02d%02d",
|
|
local_time.tm_year + 1900,
|
|
local_time.tm_mon + 1,
|
|
local_time.tm_mday,
|
|
local_time.tm_hour,
|
|
local_time.tm_min,
|
|
local_time.tm_sec);
|
|
|
|
std::string base_filename = base_name;
|
|
// Remove any path and extension
|
|
auto last_slash = base_filename.find_last_of("/\\");
|
|
if (last_slash != std::string::npos) {
|
|
base_filename = base_filename.substr(last_slash + 1);
|
|
}
|
|
auto last_dot = base_filename.find_last_of('.');
|
|
if (last_dot != std::string::npos) {
|
|
base_filename = base_filename.substr(0, last_dot);
|
|
}
|
|
|
|
return absl::StrFormat("%s_test_%s.sfc", base_filename.c_str(), timestamp.c_str());
|
|
}
|
|
|
|
void TestManager::OfferTestSessionCreation(const std::string& test_rom_path) {
|
|
// Store the test ROM path for the dialog
|
|
test_rom_path_for_session_ = test_rom_path;
|
|
show_test_session_dialog_ = true;
|
|
}
|
|
|
|
absl::Status TestManager::TestRomWithCopy(Rom* source_rom, std::function<absl::Status(Rom*)> test_function) {
|
|
if (!source_rom || !source_rom->is_loaded()) {
|
|
return absl::FailedPreconditionError("Source ROM not loaded");
|
|
}
|
|
|
|
// Create a copy of the ROM for testing
|
|
std::unique_ptr<Rom> test_rom;
|
|
RETURN_IF_ERROR(CreateTestRomCopy(source_rom, test_rom));
|
|
|
|
util::logf("Executing test function on ROM copy");
|
|
|
|
// Run the test function on the copy
|
|
auto test_result = test_function(test_rom.get());
|
|
|
|
util::logf("Test function completed with status: %s", test_result.ToString().c_str());
|
|
|
|
return test_result;
|
|
}
|
|
|
|
absl::Status TestManager::LoadRomForTesting(const std::string& filename) {
|
|
// This would load a ROM specifically for testing purposes
|
|
// For now, just log the request
|
|
util::logf("Request to load ROM for testing: %s", filename.c_str());
|
|
return absl::UnimplementedError("ROM loading for testing not yet implemented");
|
|
}
|
|
|
|
void TestManager::ShowRomComparisonResults(const Rom& before, const Rom& after) {
|
|
if (ImGui::Begin("ROM Comparison Results")) {
|
|
ImGui::Text("%s ROM Before/After Comparison", ICON_MD_COMPARE);
|
|
ImGui::Separator();
|
|
|
|
if (ImGui::BeginTable("RomComparison", 3, ImGuiTableFlags_Borders)) {
|
|
ImGui::TableSetupColumn("Property");
|
|
ImGui::TableSetupColumn("Before");
|
|
ImGui::TableSetupColumn("After");
|
|
ImGui::TableHeadersRow();
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn(); ImGui::Text("Size");
|
|
ImGui::TableNextColumn(); ImGui::Text("%.2f MB", before.size() / 1048576.0f);
|
|
ImGui::TableNextColumn(); ImGui::Text("%.2f MB", after.size() / 1048576.0f);
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn(); ImGui::Text("Modified");
|
|
ImGui::TableNextColumn(); ImGui::Text("%s", before.dirty() ? "Yes" : "No");
|
|
ImGui::TableNextColumn(); ImGui::Text("%s", after.dirty() ? "Yes" : "No");
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
absl::Status TestManager::TestRomSaveLoad(Rom* rom) {
|
|
if (!rom || !rom->is_loaded()) {
|
|
return absl::FailedPreconditionError("No ROM loaded for testing");
|
|
}
|
|
|
|
// Use TestRomWithCopy to avoid affecting the original ROM
|
|
return TestRomWithCopy(rom, [this](Rom* test_rom) -> absl::Status {
|
|
util::logf("Testing ROM save/load operations on copy: %s", test_rom->title().c_str());
|
|
|
|
// Perform test modifications on the copy
|
|
// Test save operations
|
|
Rom::SaveSettings settings;
|
|
settings.backup = false;
|
|
settings.save_new = true;
|
|
settings.filename = GenerateTestRomFilename(test_rom->title());
|
|
|
|
auto save_status = test_rom->SaveToFile(settings);
|
|
if (!save_status.ok()) {
|
|
return save_status;
|
|
}
|
|
|
|
util::logf("Test ROM saved successfully to: %s", settings.filename.c_str());
|
|
|
|
// Offer to open test ROM in new session
|
|
OfferTestSessionCreation(settings.filename);
|
|
|
|
return absl::OkStatus();
|
|
});
|
|
}
|
|
|
|
absl::Status TestManager::TestRomDataIntegrity(Rom* rom) {
|
|
if (!rom || !rom->is_loaded()) {
|
|
return absl::FailedPreconditionError("No ROM loaded for testing");
|
|
}
|
|
|
|
// Use TestRomWithCopy for integrity testing (read-only but uses copy for safety)
|
|
return TestRomWithCopy(rom, [](Rom* test_rom) -> absl::Status {
|
|
util::logf("Testing ROM data integrity on copy: %s", test_rom->title().c_str());
|
|
|
|
// Perform data integrity checks on the copy
|
|
// This validates ROM structure, checksums, etc. without affecting original
|
|
|
|
// Basic ROM structure validation
|
|
if (test_rom->size() < 0x100000) { // 1MB minimum for ALTTP
|
|
return absl::FailedPreconditionError("ROM file too small for A Link to the Past");
|
|
}
|
|
|
|
// Check ROM header
|
|
auto header_status = test_rom->ReadByteVector(0x7FC0, 32);
|
|
if (!header_status.ok()) {
|
|
return header_status.status();
|
|
}
|
|
|
|
util::logf("ROM integrity check passed for: %s", test_rom->title().c_str());
|
|
return absl::OkStatus();
|
|
});
|
|
}
|
|
|
|
} // namespace test
|
|
} // namespace yaze
|