Add E2E and ZSCustomOverworld test suites for comprehensive testing
- Introduced new E2E test suite for comprehensive ROM testing, validating the complete ROM editing workflow. - Added ZSCustomOverworld test suite to validate version upgrades and data integrity. - Updated `EditorManager` to register the new test suites. - Enhanced CMake configuration to include the new test files. - Updated README to reflect the new testing capabilities and best practices for AI agent testing.
This commit is contained in:
@@ -30,6 +30,8 @@
|
||||
#include "app/test/test_manager.h"
|
||||
#include "app/test/integrated_test_suite.h"
|
||||
#include "app/test/rom_dependent_test_suite.h"
|
||||
#include "app/test/e2e_test_suite.h"
|
||||
#include "app/test/zscustomoverworld_test_suite.h"
|
||||
#ifdef YAZE_ENABLE_GTEST
|
||||
#include "app/test/unit_test_suite.h"
|
||||
#endif
|
||||
@@ -160,6 +162,10 @@ void EditorManager::InitializeTestSuites() {
|
||||
test_manager.RegisterTestSuite(std::make_unique<test::UITestSuite>());
|
||||
test_manager.RegisterTestSuite(std::make_unique<test::RomDependentTestSuite>());
|
||||
|
||||
// Register new E2E and ZSCustomOverworld test suites
|
||||
test_manager.RegisterTestSuite(std::make_unique<test::E2ETestSuite>());
|
||||
test_manager.RegisterTestSuite(std::make_unique<test::ZSCustomOverworldTestSuite>());
|
||||
|
||||
// Register Google Test suite if available
|
||||
#ifdef YAZE_ENABLE_GTEST
|
||||
test_manager.RegisterTestSuite(std::make_unique<test::UnitTestSuite>());
|
||||
|
||||
420
src/app/test/e2e_test_suite.h
Normal file
420
src/app/test/e2e_test_suite.h
Normal file
@@ -0,0 +1,420 @@
|
||||
#ifndef YAZE_APP_TEST_E2E_TEST_SUITE_H
|
||||
#define YAZE_APP_TEST_E2E_TEST_SUITE_H
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/test/test_manager.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/transaction.h"
|
||||
#include "app/gui/icons.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace test {
|
||||
|
||||
/**
|
||||
* @brief End-to-End test suite for comprehensive ROM testing
|
||||
*
|
||||
* This test suite provides comprehensive E2E testing capabilities including:
|
||||
* - ROM loading/saving validation
|
||||
* - Data integrity testing
|
||||
* - Transaction system testing
|
||||
* - Large-scale editing validation
|
||||
*/
|
||||
class E2ETestSuite : public TestSuite {
|
||||
public:
|
||||
E2ETestSuite() = default;
|
||||
~E2ETestSuite() override = default;
|
||||
|
||||
std::string GetName() const override { return "End-to-End ROM Tests"; }
|
||||
TestCategory GetCategory() const override { return TestCategory::kIntegration; }
|
||||
|
||||
absl::Status RunTests(TestResults& results) override {
|
||||
Rom* current_rom = TestManager::Get().GetCurrentRom();
|
||||
|
||||
// Check ROM availability
|
||||
if (!current_rom || !current_rom->is_loaded()) {
|
||||
AddSkippedTest(results, "ROM_Availability_Check", "No ROM loaded");
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
// Run E2E tests
|
||||
if (test_rom_load_save_) {
|
||||
RunRomLoadSaveTest(results, current_rom);
|
||||
}
|
||||
|
||||
if (test_data_integrity_) {
|
||||
RunDataIntegrityTest(results, current_rom);
|
||||
}
|
||||
|
||||
if (test_transaction_system_) {
|
||||
RunTransactionSystemTest(results, current_rom);
|
||||
}
|
||||
|
||||
if (test_large_scale_editing_) {
|
||||
RunLargeScaleEditingTest(results, current_rom);
|
||||
}
|
||||
|
||||
if (test_corruption_detection_) {
|
||||
RunCorruptionDetectionTest(results, current_rom);
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void DrawConfiguration() override {
|
||||
Rom* current_rom = TestManager::Get().GetCurrentRom();
|
||||
|
||||
ImGui::Text("%s E2E Test Configuration", ICON_MD_VERIFIED_USER);
|
||||
|
||||
if (current_rom && current_rom->is_loaded()) {
|
||||
ImGui::TextColored(ImVec4(0.0F, 1.0F, 0.0F, 1.0F),
|
||||
"%s Current ROM: %s", ICON_MD_CHECK_CIRCLE, current_rom->title().c_str());
|
||||
ImGui::Text("Size: %.2F MB", current_rom->size() / 1048576.0F);
|
||||
} else {
|
||||
ImGui::TextColored(ImVec4(1.0F, 0.5F, 0.0F, 1.0F),
|
||||
"%s No ROM currently loaded", ICON_MD_WARNING);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Checkbox("Test ROM load/save", &test_rom_load_save_);
|
||||
ImGui::Checkbox("Test data integrity", &test_data_integrity_);
|
||||
ImGui::Checkbox("Test transaction system", &test_transaction_system_);
|
||||
ImGui::Checkbox("Test large-scale editing", &test_large_scale_editing_);
|
||||
ImGui::Checkbox("Test corruption detection", &test_corruption_detection_);
|
||||
|
||||
if (test_large_scale_editing_) {
|
||||
ImGui::Indent();
|
||||
ImGui::InputInt("Number of edits", &num_edits_);
|
||||
if (num_edits_ < 1) num_edits_ = 1;
|
||||
if (num_edits_ > 100) num_edits_ = 100;
|
||||
ImGui::Unindent();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void AddSkippedTest(TestResults& results, const std::string& test_name, const std::string& reason) {
|
||||
TestResult result;
|
||||
result.name = test_name;
|
||||
result.suite_name = GetName();
|
||||
result.category = GetCategory();
|
||||
result.status = TestStatus::kSkipped;
|
||||
result.error_message = reason;
|
||||
result.duration = std::chrono::milliseconds{0};
|
||||
result.timestamp = std::chrono::steady_clock::now();
|
||||
results.AddResult(result);
|
||||
}
|
||||
|
||||
void RunRomLoadSaveTest(TestResults& results, Rom* rom) {
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
|
||||
TestResult result;
|
||||
result.name = "ROM_Load_Save_Test";
|
||||
result.suite_name = GetName();
|
||||
result.category = GetCategory();
|
||||
result.timestamp = start_time;
|
||||
|
||||
try {
|
||||
auto& test_manager = TestManager::Get();
|
||||
|
||||
auto test_status = test_manager.TestRomWithCopy(rom, [&](Rom* test_rom) -> absl::Status {
|
||||
// Test basic ROM operations
|
||||
if (test_rom->size() != rom->size()) {
|
||||
return absl::InternalError("ROM copy size mismatch");
|
||||
}
|
||||
|
||||
// Test save and reload
|
||||
std::string test_filename = test_manager.GenerateTestRomFilename("e2e_test");
|
||||
auto save_status = test_rom->SaveToFile(Rom::SaveSettings{.filename = test_filename});
|
||||
if (!save_status.ok()) {
|
||||
return save_status;
|
||||
}
|
||||
|
||||
// Clean up test file
|
||||
std::filesystem::remove(test_filename);
|
||||
|
||||
return absl::OkStatus();
|
||||
});
|
||||
|
||||
if (test_status.ok()) {
|
||||
result.status = TestStatus::kPassed;
|
||||
result.error_message = "ROM load/save test completed successfully";
|
||||
} else {
|
||||
result.status = TestStatus::kFailed;
|
||||
result.error_message = "ROM load/save test failed: " + test_status.ToString();
|
||||
}
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
result.status = TestStatus::kFailed;
|
||||
result.error_message = "ROM load/save test exception: " + std::string(e.what());
|
||||
}
|
||||
|
||||
auto end_time = std::chrono::steady_clock::now();
|
||||
result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
end_time - start_time);
|
||||
|
||||
results.AddResult(result);
|
||||
}
|
||||
|
||||
void RunDataIntegrityTest(TestResults& results, Rom* rom) {
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
|
||||
TestResult result;
|
||||
result.name = "Data_Integrity_Test";
|
||||
result.suite_name = GetName();
|
||||
result.category = GetCategory();
|
||||
result.timestamp = start_time;
|
||||
|
||||
try {
|
||||
auto& test_manager = TestManager::Get();
|
||||
|
||||
auto test_status = test_manager.TestRomWithCopy(rom, [&](Rom* test_rom) -> absl::Status {
|
||||
// Test data integrity by comparing key areas
|
||||
std::vector<uint32_t> test_addresses = {0x7FC0, 0x8000, 0x10000, 0x20000};
|
||||
|
||||
for (uint32_t addr : test_addresses) {
|
||||
auto original_byte = rom->ReadByte(addr);
|
||||
auto copy_byte = test_rom->ReadByte(addr);
|
||||
|
||||
if (!original_byte.ok() || !copy_byte.ok()) {
|
||||
return absl::InternalError("Failed to read ROM data for comparison");
|
||||
}
|
||||
|
||||
if (*original_byte != *copy_byte) {
|
||||
return absl::InternalError(absl::StrFormat(
|
||||
"Data integrity check failed at address 0x%X: original=0x%02X, copy=0x%02X",
|
||||
addr, *original_byte, *copy_byte));
|
||||
}
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
});
|
||||
|
||||
if (test_status.ok()) {
|
||||
result.status = TestStatus::kPassed;
|
||||
result.error_message = "Data integrity test passed - all checked addresses match";
|
||||
} else {
|
||||
result.status = TestStatus::kFailed;
|
||||
result.error_message = "Data integrity test failed: " + test_status.ToString();
|
||||
}
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
result.status = TestStatus::kFailed;
|
||||
result.error_message = "Data integrity test exception: " + std::string(e.what());
|
||||
}
|
||||
|
||||
auto end_time = std::chrono::steady_clock::now();
|
||||
result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
end_time - start_time);
|
||||
|
||||
results.AddResult(result);
|
||||
}
|
||||
|
||||
void RunTransactionSystemTest(TestResults& results, Rom* rom) {
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
|
||||
TestResult result;
|
||||
result.name = "Transaction_System_Test";
|
||||
result.suite_name = GetName();
|
||||
result.category = GetCategory();
|
||||
result.timestamp = start_time;
|
||||
|
||||
try {
|
||||
auto& test_manager = TestManager::Get();
|
||||
|
||||
auto test_status = test_manager.TestRomWithCopy(rom, [&](Rom* test_rom) -> absl::Status {
|
||||
// Test transaction system
|
||||
Transaction transaction(*test_rom);
|
||||
|
||||
// Store original values
|
||||
auto original_byte1 = test_rom->ReadByte(0x1000);
|
||||
auto original_byte2 = test_rom->ReadByte(0x2000);
|
||||
auto original_word = test_rom->ReadWord(0x3000);
|
||||
|
||||
if (!original_byte1.ok() || !original_byte2.ok() || !original_word.ok()) {
|
||||
return absl::InternalError("Failed to read original ROM data");
|
||||
}
|
||||
|
||||
// Make changes in transaction
|
||||
transaction.WriteByte(0x1000, 0xAA)
|
||||
.WriteByte(0x2000, 0xBB)
|
||||
.WriteWord(0x3000, 0xCCDD);
|
||||
|
||||
// Commit transaction
|
||||
RETURN_IF_ERROR(transaction.Commit());
|
||||
|
||||
// Verify changes
|
||||
auto new_byte1 = test_rom->ReadByte(0x1000);
|
||||
auto new_byte2 = test_rom->ReadByte(0x2000);
|
||||
auto new_word = test_rom->ReadWord(0x3000);
|
||||
|
||||
if (!new_byte1.ok() || !new_byte2.ok() || !new_word.ok()) {
|
||||
return absl::InternalError("Failed to read modified ROM data");
|
||||
}
|
||||
|
||||
if (*new_byte1 != 0xAA || *new_byte2 != 0xBB || *new_word != 0xCCDD) {
|
||||
return absl::InternalError("Transaction changes not applied correctly");
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
});
|
||||
|
||||
if (test_status.ok()) {
|
||||
result.status = TestStatus::kPassed;
|
||||
result.error_message = "Transaction system test completed successfully";
|
||||
} else {
|
||||
result.status = TestStatus::kFailed;
|
||||
result.error_message = "Transaction system test failed: " + test_status.ToString();
|
||||
}
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
result.status = TestStatus::kFailed;
|
||||
result.error_message = "Transaction system test exception: " + std::string(e.what());
|
||||
}
|
||||
|
||||
auto end_time = std::chrono::steady_clock::now();
|
||||
result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
end_time - start_time);
|
||||
|
||||
results.AddResult(result);
|
||||
}
|
||||
|
||||
void RunLargeScaleEditingTest(TestResults& results, Rom* rom) {
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
|
||||
TestResult result;
|
||||
result.name = "Large_Scale_Editing_Test";
|
||||
result.suite_name = GetName();
|
||||
result.category = GetCategory();
|
||||
result.timestamp = start_time;
|
||||
|
||||
try {
|
||||
auto& test_manager = TestManager::Get();
|
||||
|
||||
auto test_status = test_manager.TestRomWithCopy(rom, [&](Rom* test_rom) -> absl::Status {
|
||||
// Test large-scale editing
|
||||
for (int i = 0; i < num_edits_; i++) {
|
||||
uint32_t addr = 0x1000 + (i * 4);
|
||||
uint8_t value = i % 256;
|
||||
|
||||
RETURN_IF_ERROR(test_rom->WriteByte(addr, value));
|
||||
}
|
||||
|
||||
// Verify all changes
|
||||
for (int i = 0; i < num_edits_; i++) {
|
||||
uint32_t addr = 0x1000 + (i * 4);
|
||||
uint8_t expected_value = i % 256;
|
||||
|
||||
auto actual_value = test_rom->ReadByte(addr);
|
||||
if (!actual_value.ok()) {
|
||||
return absl::InternalError(absl::StrFormat("Failed to read address 0x%X", addr));
|
||||
}
|
||||
|
||||
if (*actual_value != expected_value) {
|
||||
return absl::InternalError(absl::StrFormat(
|
||||
"Value mismatch at 0x%X: expected=0x%02X, actual=0x%02X",
|
||||
addr, expected_value, *actual_value));
|
||||
}
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
});
|
||||
|
||||
if (test_status.ok()) {
|
||||
result.status = TestStatus::kPassed;
|
||||
result.error_message = absl::StrFormat("Large-scale editing test passed: %d edits", num_edits_);
|
||||
} else {
|
||||
result.status = TestStatus::kFailed;
|
||||
result.error_message = "Large-scale editing test failed: " + test_status.ToString();
|
||||
}
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
result.status = TestStatus::kFailed;
|
||||
result.error_message = "Large-scale editing test exception: " + std::string(e.what());
|
||||
}
|
||||
|
||||
auto end_time = std::chrono::steady_clock::now();
|
||||
result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
end_time - start_time);
|
||||
|
||||
results.AddResult(result);
|
||||
}
|
||||
|
||||
void RunCorruptionDetectionTest(TestResults& results, Rom* rom) {
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
|
||||
TestResult result;
|
||||
result.name = "Corruption_Detection_Test";
|
||||
result.suite_name = GetName();
|
||||
result.category = GetCategory();
|
||||
result.timestamp = start_time;
|
||||
|
||||
try {
|
||||
auto& test_manager = TestManager::Get();
|
||||
|
||||
auto test_status = test_manager.TestRomWithCopy(rom, [&](Rom* test_rom) -> absl::Status {
|
||||
// Intentionally corrupt some data
|
||||
RETURN_IF_ERROR(test_rom->WriteByte(0x1000, 0xFF));
|
||||
RETURN_IF_ERROR(test_rom->WriteByte(0x2000, 0xAA));
|
||||
|
||||
// Verify corruption is detected
|
||||
auto corrupted_byte1 = test_rom->ReadByte(0x1000);
|
||||
auto corrupted_byte2 = test_rom->ReadByte(0x2000);
|
||||
|
||||
if (!corrupted_byte1.ok() || !corrupted_byte2.ok()) {
|
||||
return absl::InternalError("Failed to read corrupted data");
|
||||
}
|
||||
|
||||
if (*corrupted_byte1 != 0xFF || *corrupted_byte2 != 0xAA) {
|
||||
return absl::InternalError("Corruption not applied correctly");
|
||||
}
|
||||
|
||||
// Verify original data is different
|
||||
auto original_byte1 = rom->ReadByte(0x1000);
|
||||
auto original_byte2 = rom->ReadByte(0x2000);
|
||||
|
||||
if (!original_byte1.ok() || !original_byte2.ok()) {
|
||||
return absl::InternalError("Failed to read original data for comparison");
|
||||
}
|
||||
|
||||
if (*corrupted_byte1 == *original_byte1 || *corrupted_byte2 == *original_byte2) {
|
||||
return absl::InternalError("Corruption detection test failed - data not actually corrupted");
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
});
|
||||
|
||||
if (test_status.ok()) {
|
||||
result.status = TestStatus::kPassed;
|
||||
result.error_message = "Corruption detection test passed - corruption successfully applied and detected";
|
||||
} else {
|
||||
result.status = TestStatus::kFailed;
|
||||
result.error_message = "Corruption detection test failed: " + test_status.ToString();
|
||||
}
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
result.status = TestStatus::kFailed;
|
||||
result.error_message = "Corruption detection test exception: " + std::string(e.what());
|
||||
}
|
||||
|
||||
auto end_time = std::chrono::steady_clock::now();
|
||||
result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
end_time - start_time);
|
||||
|
||||
results.AddResult(result);
|
||||
}
|
||||
|
||||
// Configuration
|
||||
bool test_rom_load_save_ = true;
|
||||
bool test_data_integrity_ = true;
|
||||
bool test_transaction_system_ = true;
|
||||
bool test_large_scale_editing_ = true;
|
||||
bool test_corruption_detection_ = true;
|
||||
int num_edits_ = 10;
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_TEST_E2E_TEST_SUITE_H
|
||||
@@ -4,6 +4,10 @@ set(YAZE_TEST_CORE_SOURCES
|
||||
app/test/test_manager.cc
|
||||
app/test/test_manager.h
|
||||
app/test/unit_test_suite.h
|
||||
app/test/integrated_test_suite.h
|
||||
app/test/rom_dependent_test_suite.h
|
||||
app/test/e2e_test_suite.h
|
||||
app/test/zscustomoverworld_test_suite.h
|
||||
)
|
||||
|
||||
# Add test sources to the main app target if testing is enabled
|
||||
|
||||
591
src/app/test/zscustomoverworld_test_suite.h
Normal file
591
src/app/test/zscustomoverworld_test_suite.h
Normal file
@@ -0,0 +1,591 @@
|
||||
#ifndef YAZE_APP_TEST_ZSCUSTOMOVERWORLD_TEST_SUITE_H
|
||||
#define YAZE_APP_TEST_ZSCUSTOMOVERWORLD_TEST_SUITE_H
|
||||
|
||||
#include <chrono>
|
||||
#include <map>
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/test/test_manager.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/gui/icons.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace test {
|
||||
|
||||
/**
|
||||
* @brief ZSCustomOverworld upgrade testing suite
|
||||
*
|
||||
* This test suite validates ZSCustomOverworld version upgrades:
|
||||
* - Vanilla -> v2 -> v3 upgrade path testing
|
||||
* - Address validation for each version
|
||||
* - Feature enablement/disablement testing
|
||||
* - Data integrity validation during upgrades
|
||||
* - Save compatibility between versions
|
||||
*/
|
||||
class ZSCustomOverworldTestSuite : public TestSuite {
|
||||
public:
|
||||
ZSCustomOverworldTestSuite() = default;
|
||||
~ZSCustomOverworldTestSuite() override = default;
|
||||
|
||||
std::string GetName() const override { return "ZSCustomOverworld Upgrade Tests"; }
|
||||
TestCategory GetCategory() const override { return TestCategory::kIntegration; }
|
||||
|
||||
absl::Status RunTests(TestResults& results) override {
|
||||
Rom* current_rom = TestManager::Get().GetCurrentRom();
|
||||
|
||||
// Check ROM availability
|
||||
if (!current_rom || !current_rom->is_loaded()) {
|
||||
AddSkippedTest(results, "ROM_Availability_Check", "No ROM loaded");
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
// Initialize version data
|
||||
InitializeVersionData();
|
||||
|
||||
// Run ZSCustomOverworld tests
|
||||
if (test_vanilla_baseline_) {
|
||||
RunVanillaBaselineTest(results, current_rom);
|
||||
}
|
||||
|
||||
if (test_v2_upgrade_) {
|
||||
RunV2UpgradeTest(results, current_rom);
|
||||
}
|
||||
|
||||
if (test_v3_upgrade_) {
|
||||
RunV3UpgradeTest(results, current_rom);
|
||||
}
|
||||
|
||||
if (test_address_validation_) {
|
||||
RunAddressValidationTest(results, current_rom);
|
||||
}
|
||||
|
||||
if (test_feature_toggle_) {
|
||||
RunFeatureToggleTest(results, current_rom);
|
||||
}
|
||||
|
||||
if (test_data_integrity_) {
|
||||
RunDataIntegrityTest(results, current_rom);
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void DrawConfiguration() override {
|
||||
Rom* current_rom = TestManager::Get().GetCurrentRom();
|
||||
|
||||
ImGui::Text("%s ZSCustomOverworld Test Configuration", ICON_MD_UPGRADE);
|
||||
|
||||
if (current_rom && current_rom->is_loaded()) {
|
||||
ImGui::TextColored(ImVec4(0.0F, 1.0F, 0.0F, 1.0F),
|
||||
"%s Current ROM: %s", ICON_MD_CHECK_CIRCLE, current_rom->title().c_str());
|
||||
|
||||
// Check current version
|
||||
auto version_byte = current_rom->ReadByte(0x140145);
|
||||
if (version_byte.ok()) {
|
||||
std::string version_name = "Unknown";
|
||||
if (*version_byte == 0xFF) version_name = "Vanilla";
|
||||
else if (*version_byte == 0x02) version_name = "v2";
|
||||
else if (*version_byte == 0x03) version_name = "v3";
|
||||
|
||||
ImGui::Text("Current ZSCustomOverworld version: %s (0x%02X)",
|
||||
version_name.c_str(), *version_byte);
|
||||
}
|
||||
} else {
|
||||
ImGui::TextColored(ImVec4(1.0F, 0.5F, 0.0F, 1.0F),
|
||||
"%s No ROM currently loaded", ICON_MD_WARNING);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Checkbox("Test vanilla baseline", &test_vanilla_baseline_);
|
||||
ImGui::Checkbox("Test v2 upgrade", &test_v2_upgrade_);
|
||||
ImGui::Checkbox("Test v3 upgrade", &test_v3_upgrade_);
|
||||
ImGui::Checkbox("Test address validation", &test_address_validation_);
|
||||
ImGui::Checkbox("Test feature toggle", &test_feature_toggle_);
|
||||
ImGui::Checkbox("Test data integrity", &test_data_integrity_);
|
||||
|
||||
if (ImGui::CollapsingHeader("Version Settings")) {
|
||||
ImGui::Text("Version-specific addresses and features:");
|
||||
ImGui::Text("Vanilla: 0x140145 = 0xFF");
|
||||
ImGui::Text("v2: 0x140145 = 0x02, main palettes enabled");
|
||||
ImGui::Text("v3: 0x140145 = 0x03, all features enabled");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void InitializeVersionData() {
|
||||
// Vanilla ROM addresses and values
|
||||
vanilla_data_ = {
|
||||
{"version_flag", {0x140145, 0xFF}}, // OverworldCustomASMHasBeenApplied
|
||||
{"message_ids", {0x3F51D, 0x00}}, // Message ID table start
|
||||
{"area_graphics", {0x7C9C, 0x00}}, // Area graphics table
|
||||
{"area_palettes", {0x7D1C, 0x00}}, // Area palettes table
|
||||
};
|
||||
|
||||
// v2 ROM addresses and values
|
||||
v2_data_ = {
|
||||
{"version_flag", {0x140145, 0x02}}, // v2 version
|
||||
{"message_ids", {0x1417F8, 0x00}}, // Expanded message ID table
|
||||
{"area_graphics", {0x7C9C, 0x00}}, // Same as vanilla
|
||||
{"area_palettes", {0x7D1C, 0x00}}, // Same as vanilla
|
||||
{"main_palettes", {0x140160, 0x00}}, // New v2 feature
|
||||
};
|
||||
|
||||
// v3 ROM addresses and values
|
||||
v3_data_ = {
|
||||
{"version_flag", {0x140145, 0x03}}, // v3 version
|
||||
{"message_ids", {0x1417F8, 0x00}}, // Same as v2
|
||||
{"area_graphics", {0x7C9C, 0x00}}, // Same as vanilla
|
||||
{"area_palettes", {0x7D1C, 0x00}}, // Same as vanilla
|
||||
{"main_palettes", {0x140160, 0x00}}, // Same as v2
|
||||
{"bg_colors", {0x140000, 0x00}}, // New v3 feature
|
||||
{"subscreen_overlays", {0x140340, 0x00}}, // New v3 feature
|
||||
{"animated_gfx", {0x1402A0, 0x00}}, // New v3 feature
|
||||
{"custom_tiles", {0x140480, 0x00}}, // New v3 feature
|
||||
};
|
||||
}
|
||||
|
||||
void AddSkippedTest(TestResults& results, const std::string& test_name, const std::string& reason) {
|
||||
TestResult result;
|
||||
result.name = test_name;
|
||||
result.suite_name = GetName();
|
||||
result.category = GetCategory();
|
||||
result.status = TestStatus::kSkipped;
|
||||
result.error_message = reason;
|
||||
result.duration = std::chrono::milliseconds{0};
|
||||
result.timestamp = std::chrono::steady_clock::now();
|
||||
results.AddResult(result);
|
||||
}
|
||||
|
||||
absl::Status ApplyVersionPatch(Rom& rom, const std::string& version) {
|
||||
const auto* data = &vanilla_data_;
|
||||
if (version == "v2") {
|
||||
data = &v2_data_;
|
||||
} else if (version == "v3") {
|
||||
data = &v3_data_;
|
||||
}
|
||||
|
||||
// Apply version-specific data
|
||||
for (const auto& [key, value] : *data) {
|
||||
RETURN_IF_ERROR(rom.WriteByte(value.first, value.second));
|
||||
}
|
||||
|
||||
// Apply version-specific features
|
||||
if (version == "v2") {
|
||||
// Enable v2 features
|
||||
RETURN_IF_ERROR(rom.WriteByte(0x140146, 0x01)); // Enable main palettes
|
||||
} else if (version == "v3") {
|
||||
// Enable v3 features
|
||||
RETURN_IF_ERROR(rom.WriteByte(0x140146, 0x01)); // Enable main palettes
|
||||
RETURN_IF_ERROR(rom.WriteByte(0x140147, 0x01)); // Enable area-specific BG
|
||||
RETURN_IF_ERROR(rom.WriteByte(0x140148, 0x01)); // Enable subscreen overlay
|
||||
RETURN_IF_ERROR(rom.WriteByte(0x140149, 0x01)); // Enable animated GFX
|
||||
RETURN_IF_ERROR(rom.WriteByte(0x14014A, 0x01)); // Enable custom tile GFX groups
|
||||
RETURN_IF_ERROR(rom.WriteByte(0x14014B, 0x01)); // Enable mosaic
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
bool ValidateVersionAddresses(Rom& rom, const std::string& version) {
|
||||
const auto* data = &vanilla_data_;
|
||||
if (version == "v2") {
|
||||
data = &v2_data_;
|
||||
} else if (version == "v3") {
|
||||
data = &v3_data_;
|
||||
}
|
||||
|
||||
for (const auto& [key, value] : *data) {
|
||||
auto byte_value = rom.ReadByte(value.first);
|
||||
if (!byte_value.ok() || *byte_value != value.second) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RunVanillaBaselineTest(TestResults& results, Rom* rom) {
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
|
||||
TestResult result;
|
||||
result.name = "Vanilla_Baseline_Test";
|
||||
result.suite_name = GetName();
|
||||
result.category = GetCategory();
|
||||
result.timestamp = start_time;
|
||||
|
||||
try {
|
||||
auto& test_manager = TestManager::Get();
|
||||
|
||||
auto test_status = test_manager.TestRomWithCopy(rom, [&](Rom* test_rom) -> absl::Status {
|
||||
// Validate vanilla addresses
|
||||
if (!ValidateVersionAddresses(*test_rom, "vanilla")) {
|
||||
return absl::InternalError("Vanilla address validation failed");
|
||||
}
|
||||
|
||||
// Verify version flag
|
||||
auto version_byte = test_rom->ReadByte(0x140145);
|
||||
if (!version_byte.ok()) {
|
||||
return absl::InternalError("Failed to read version flag");
|
||||
}
|
||||
|
||||
if (*version_byte != 0xFF) {
|
||||
return absl::InternalError(absl::StrFormat(
|
||||
"Expected vanilla version flag (0xFF), got 0x%02X", *version_byte));
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
});
|
||||
|
||||
if (test_status.ok()) {
|
||||
result.status = TestStatus::kPassed;
|
||||
result.error_message = "Vanilla baseline test passed - ROM is in vanilla state";
|
||||
} else {
|
||||
result.status = TestStatus::kFailed;
|
||||
result.error_message = "Vanilla baseline test failed: " + test_status.ToString();
|
||||
}
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
result.status = TestStatus::kFailed;
|
||||
result.error_message = "Vanilla baseline test exception: " + std::string(e.what());
|
||||
}
|
||||
|
||||
auto end_time = std::chrono::steady_clock::now();
|
||||
result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
end_time - start_time);
|
||||
|
||||
results.AddResult(result);
|
||||
}
|
||||
|
||||
void RunV2UpgradeTest(TestResults& results, Rom* rom) {
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
|
||||
TestResult result;
|
||||
result.name = "V2_Upgrade_Test";
|
||||
result.suite_name = GetName();
|
||||
result.category = GetCategory();
|
||||
result.timestamp = start_time;
|
||||
|
||||
try {
|
||||
auto& test_manager = TestManager::Get();
|
||||
|
||||
auto test_status = test_manager.TestRomWithCopy(rom, [&](Rom* test_rom) -> absl::Status {
|
||||
// Apply v2 patch
|
||||
RETURN_IF_ERROR(ApplyVersionPatch(*test_rom, "v2"));
|
||||
|
||||
// Validate v2 addresses
|
||||
if (!ValidateVersionAddresses(*test_rom, "v2")) {
|
||||
return absl::InternalError("v2 address validation failed");
|
||||
}
|
||||
|
||||
// Verify version flag
|
||||
auto version_byte = test_rom->ReadByte(0x140145);
|
||||
if (!version_byte.ok()) {
|
||||
return absl::InternalError("Failed to read version flag");
|
||||
}
|
||||
|
||||
if (*version_byte != 0x02) {
|
||||
return absl::InternalError(absl::StrFormat(
|
||||
"Expected v2 version flag (0x02), got 0x%02X", *version_byte));
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
});
|
||||
|
||||
if (test_status.ok()) {
|
||||
result.status = TestStatus::kPassed;
|
||||
result.error_message = "v2 upgrade test passed - ROM successfully upgraded to v2";
|
||||
} else {
|
||||
result.status = TestStatus::kFailed;
|
||||
result.error_message = "v2 upgrade test failed: " + test_status.ToString();
|
||||
}
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
result.status = TestStatus::kFailed;
|
||||
result.error_message = "v2 upgrade test exception: " + std::string(e.what());
|
||||
}
|
||||
|
||||
auto end_time = std::chrono::steady_clock::now();
|
||||
result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
end_time - start_time);
|
||||
|
||||
results.AddResult(result);
|
||||
}
|
||||
|
||||
void RunV3UpgradeTest(TestResults& results, Rom* rom) {
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
|
||||
TestResult result;
|
||||
result.name = "V3_Upgrade_Test";
|
||||
result.suite_name = GetName();
|
||||
result.category = GetCategory();
|
||||
result.timestamp = start_time;
|
||||
|
||||
try {
|
||||
auto& test_manager = TestManager::Get();
|
||||
|
||||
auto test_status = test_manager.TestRomWithCopy(rom, [&](Rom* test_rom) -> absl::Status {
|
||||
// Apply v3 patch
|
||||
RETURN_IF_ERROR(ApplyVersionPatch(*test_rom, "v3"));
|
||||
|
||||
// Validate v3 addresses
|
||||
if (!ValidateVersionAddresses(*test_rom, "v3")) {
|
||||
return absl::InternalError("v3 address validation failed");
|
||||
}
|
||||
|
||||
// Verify version flag
|
||||
auto version_byte = test_rom->ReadByte(0x140145);
|
||||
if (!version_byte.ok()) {
|
||||
return absl::InternalError("Failed to read version flag");
|
||||
}
|
||||
|
||||
if (*version_byte != 0x03) {
|
||||
return absl::InternalError(absl::StrFormat(
|
||||
"Expected v3 version flag (0x03), got 0x%02X", *version_byte));
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
});
|
||||
|
||||
if (test_status.ok()) {
|
||||
result.status = TestStatus::kPassed;
|
||||
result.error_message = "v3 upgrade test passed - ROM successfully upgraded to v3";
|
||||
} else {
|
||||
result.status = TestStatus::kFailed;
|
||||
result.error_message = "v3 upgrade test failed: " + test_status.ToString();
|
||||
}
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
result.status = TestStatus::kFailed;
|
||||
result.error_message = "v3 upgrade test exception: " + std::string(e.what());
|
||||
}
|
||||
|
||||
auto end_time = std::chrono::steady_clock::now();
|
||||
result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
end_time - start_time);
|
||||
|
||||
results.AddResult(result);
|
||||
}
|
||||
|
||||
void RunAddressValidationTest(TestResults& results, Rom* rom) {
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
|
||||
TestResult result;
|
||||
result.name = "Address_Validation_Test";
|
||||
result.suite_name = GetName();
|
||||
result.category = GetCategory();
|
||||
result.timestamp = start_time;
|
||||
|
||||
try {
|
||||
auto& test_manager = TestManager::Get();
|
||||
|
||||
auto test_status = test_manager.TestRomWithCopy(rom, [&](Rom* test_rom) -> absl::Status {
|
||||
// Test vanilla addresses
|
||||
if (!ValidateVersionAddresses(*test_rom, "vanilla")) {
|
||||
return absl::InternalError("Vanilla address validation failed");
|
||||
}
|
||||
|
||||
// Test v2 addresses
|
||||
RETURN_IF_ERROR(ApplyVersionPatch(*test_rom, "v2"));
|
||||
if (!ValidateVersionAddresses(*test_rom, "v2")) {
|
||||
return absl::InternalError("v2 address validation failed");
|
||||
}
|
||||
|
||||
// Test v3 addresses
|
||||
RETURN_IF_ERROR(ApplyVersionPatch(*test_rom, "v3"));
|
||||
if (!ValidateVersionAddresses(*test_rom, "v3")) {
|
||||
return absl::InternalError("v3 address validation failed");
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
});
|
||||
|
||||
if (test_status.ok()) {
|
||||
result.status = TestStatus::kPassed;
|
||||
result.error_message = "Address validation test passed - all version addresses valid";
|
||||
} else {
|
||||
result.status = TestStatus::kFailed;
|
||||
result.error_message = "Address validation test failed: " + test_status.ToString();
|
||||
}
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
result.status = TestStatus::kFailed;
|
||||
result.error_message = "Address validation test exception: " + std::string(e.what());
|
||||
}
|
||||
|
||||
auto end_time = std::chrono::steady_clock::now();
|
||||
result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
end_time - start_time);
|
||||
|
||||
results.AddResult(result);
|
||||
}
|
||||
|
||||
void RunFeatureToggleTest(TestResults& results, Rom* rom) {
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
|
||||
TestResult result;
|
||||
result.name = "Feature_Toggle_Test";
|
||||
result.suite_name = GetName();
|
||||
result.category = GetCategory();
|
||||
result.timestamp = start_time;
|
||||
|
||||
try {
|
||||
auto& test_manager = TestManager::Get();
|
||||
|
||||
auto test_status = test_manager.TestRomWithCopy(rom, [&](Rom* test_rom) -> absl::Status {
|
||||
// Apply v3 patch
|
||||
RETURN_IF_ERROR(ApplyVersionPatch(*test_rom, "v3"));
|
||||
|
||||
// Test feature flags
|
||||
auto main_palettes = test_rom->ReadByte(0x140146);
|
||||
auto area_bg = test_rom->ReadByte(0x140147);
|
||||
auto subscreen_overlay = test_rom->ReadByte(0x140148);
|
||||
auto animated_gfx = test_rom->ReadByte(0x140149);
|
||||
auto custom_tiles = test_rom->ReadByte(0x14014A);
|
||||
auto mosaic = test_rom->ReadByte(0x14014B);
|
||||
|
||||
if (!main_palettes.ok() || !area_bg.ok() || !subscreen_overlay.ok() ||
|
||||
!animated_gfx.ok() || !custom_tiles.ok() || !mosaic.ok()) {
|
||||
return absl::InternalError("Failed to read feature flags");
|
||||
}
|
||||
|
||||
if (*main_palettes != 0x01 || *area_bg != 0x01 || *subscreen_overlay != 0x01 ||
|
||||
*animated_gfx != 0x01 || *custom_tiles != 0x01 || *mosaic != 0x01) {
|
||||
return absl::InternalError("Feature flags not properly enabled");
|
||||
}
|
||||
|
||||
// Disable some features
|
||||
RETURN_IF_ERROR(test_rom->WriteByte(0x140147, 0x00)); // Disable area-specific BG
|
||||
RETURN_IF_ERROR(test_rom->WriteByte(0x140149, 0x00)); // Disable animated GFX
|
||||
|
||||
// Verify features are disabled
|
||||
auto disabled_area_bg = test_rom->ReadByte(0x140147);
|
||||
auto disabled_animated_gfx = test_rom->ReadByte(0x140149);
|
||||
|
||||
if (!disabled_area_bg.ok() || !disabled_animated_gfx.ok()) {
|
||||
return absl::InternalError("Failed to read disabled feature flags");
|
||||
}
|
||||
|
||||
if (*disabled_area_bg != 0x00 || *disabled_animated_gfx != 0x00) {
|
||||
return absl::InternalError("Feature flags not properly disabled");
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
});
|
||||
|
||||
if (test_status.ok()) {
|
||||
result.status = TestStatus::kPassed;
|
||||
result.error_message = "Feature toggle test passed - features can be enabled/disabled";
|
||||
} else {
|
||||
result.status = TestStatus::kFailed;
|
||||
result.error_message = "Feature toggle test failed: " + test_status.ToString();
|
||||
}
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
result.status = TestStatus::kFailed;
|
||||
result.error_message = "Feature toggle test exception: " + std::string(e.what());
|
||||
}
|
||||
|
||||
auto end_time = std::chrono::steady_clock::now();
|
||||
result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
end_time - start_time);
|
||||
|
||||
results.AddResult(result);
|
||||
}
|
||||
|
||||
void RunDataIntegrityTest(TestResults& results, Rom* rom) {
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
|
||||
TestResult result;
|
||||
result.name = "Data_Integrity_Test";
|
||||
result.suite_name = GetName();
|
||||
result.category = GetCategory();
|
||||
result.timestamp = start_time;
|
||||
|
||||
try {
|
||||
auto& test_manager = TestManager::Get();
|
||||
|
||||
auto test_status = test_manager.TestRomWithCopy(rom, [&](Rom* test_rom) -> absl::Status {
|
||||
// Store some original data
|
||||
auto original_graphics = test_rom->ReadByte(0x7C9C);
|
||||
auto original_palette = test_rom->ReadByte(0x7D1C);
|
||||
auto original_sprite_set = test_rom->ReadByte(0x7A41);
|
||||
|
||||
if (!original_graphics.ok() || !original_palette.ok() || !original_sprite_set.ok()) {
|
||||
return absl::InternalError("Failed to read original data");
|
||||
}
|
||||
|
||||
// Upgrade to v3
|
||||
RETURN_IF_ERROR(ApplyVersionPatch(*test_rom, "v3"));
|
||||
|
||||
// Verify original data is preserved
|
||||
auto preserved_graphics = test_rom->ReadByte(0x7C9C);
|
||||
auto preserved_palette = test_rom->ReadByte(0x7D1C);
|
||||
auto preserved_sprite_set = test_rom->ReadByte(0x7A41);
|
||||
|
||||
if (!preserved_graphics.ok() || !preserved_palette.ok() || !preserved_sprite_set.ok()) {
|
||||
return absl::InternalError("Failed to read preserved data");
|
||||
}
|
||||
|
||||
if (*preserved_graphics != *original_graphics ||
|
||||
*preserved_palette != *original_palette ||
|
||||
*preserved_sprite_set != *original_sprite_set) {
|
||||
return absl::InternalError("Original data not preserved during upgrade");
|
||||
}
|
||||
|
||||
// Verify new v3 data is initialized
|
||||
auto bg_colors = test_rom->ReadByte(0x140000);
|
||||
auto subscreen_overlays = test_rom->ReadByte(0x140340);
|
||||
auto animated_gfx = test_rom->ReadByte(0x1402A0);
|
||||
auto custom_tiles = test_rom->ReadByte(0x140480);
|
||||
|
||||
if (!bg_colors.ok() || !subscreen_overlays.ok() ||
|
||||
!animated_gfx.ok() || !custom_tiles.ok()) {
|
||||
return absl::InternalError("Failed to read new v3 data");
|
||||
}
|
||||
|
||||
if (*bg_colors != 0x00 || *subscreen_overlays != 0x00 ||
|
||||
*animated_gfx != 0x00 || *custom_tiles != 0x00) {
|
||||
return absl::InternalError("New v3 data not properly initialized");
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
});
|
||||
|
||||
if (test_status.ok()) {
|
||||
result.status = TestStatus::kPassed;
|
||||
result.error_message = "Data integrity test passed - original data preserved, new data initialized";
|
||||
} else {
|
||||
result.status = TestStatus::kFailed;
|
||||
result.error_message = "Data integrity test failed: " + test_status.ToString();
|
||||
}
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
result.status = TestStatus::kFailed;
|
||||
result.error_message = "Data integrity test exception: " + std::string(e.what());
|
||||
}
|
||||
|
||||
auto end_time = std::chrono::steady_clock::now();
|
||||
result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
end_time - start_time);
|
||||
|
||||
results.AddResult(result);
|
||||
}
|
||||
|
||||
// Configuration
|
||||
bool test_vanilla_baseline_ = true;
|
||||
bool test_v2_upgrade_ = true;
|
||||
bool test_v3_upgrade_ = true;
|
||||
bool test_address_validation_ = true;
|
||||
bool test_feature_toggle_ = true;
|
||||
bool test_data_integrity_ = true;
|
||||
|
||||
// Version data
|
||||
std::map<std::string, std::pair<uint32_t, uint8_t>> vanilla_data_;
|
||||
std::map<std::string, std::pair<uint32_t, uint8_t>> v2_data_;
|
||||
std::map<std::string, std::pair<uint32_t, uint8_t>> v3_data_;
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_TEST_ZSCUSTOMOVERWORLD_TEST_SUITE_H
|
||||
Reference in New Issue
Block a user