#include "core/asar_wrapper.h" #include "test_utils.h" #include #include #include #include namespace yaze { namespace core { namespace { class AsarWrapperTest : public ::testing::Test { protected: void SetUp() override { wrapper_ = std::make_unique(); CreateTestFiles(); } void TearDown() override { CleanupTestFiles(); } void CreateTestFiles() { // Create test directory test_dir_ = std::filesystem::temp_directory_path() / "yaze_asar_test"; std::filesystem::create_directories(test_dir_); // Create a simple test assembly file test_asm_path_ = test_dir_ / "test_patch.asm"; std::ofstream asm_file(test_asm_path_); asm_file << R"( ; Test assembly patch for yaze org $008000 testlabel: LDA #$42 STA $7E0000 RTS anotherlabel: LDA #$FF STA $7E0001 RTL )"; asm_file.close(); // Create invalid assembly file for error testing invalid_asm_path_ = test_dir_ / "invalid_patch.asm"; std::ofstream invalid_file(invalid_asm_path_); invalid_file << R"( ; Invalid assembly that should cause errors org $008000 invalid_instruction_here LDA unknown_operand )"; invalid_file.close(); // Create test ROM data using utility test_rom_ = yaze::test::TestRomManager::CreateMinimalTestRom(1024 * 1024); } void CleanupTestFiles() { try { if (std::filesystem::exists(test_dir_)) { std::filesystem::remove_all(test_dir_); } } catch (const std::exception& e) { // Ignore cleanup errors in tests } } std::unique_ptr wrapper_; std::filesystem::path test_dir_; std::filesystem::path test_asm_path_; std::filesystem::path invalid_asm_path_; std::vector test_rom_; }; TEST_F(AsarWrapperTest, InitializationAndShutdown) { // Test initialization ASSERT_FALSE(wrapper_->IsInitialized()); auto status = wrapper_->Initialize(); EXPECT_TRUE(status.ok()) << status.message(); EXPECT_TRUE(wrapper_->IsInitialized()); // Test version info std::string version = wrapper_->GetVersion(); EXPECT_FALSE(version.empty()); EXPECT_NE(version, "Not initialized"); int api_version = wrapper_->GetApiVersion(); EXPECT_GT(api_version, 0); // Test shutdown wrapper_->Shutdown(); EXPECT_FALSE(wrapper_->IsInitialized()); } TEST_F(AsarWrapperTest, DoubleInitialization) { auto status1 = wrapper_->Initialize(); EXPECT_TRUE(status1.ok()); auto status2 = wrapper_->Initialize(); EXPECT_TRUE(status2.ok()); // Should not fail on double init EXPECT_TRUE(wrapper_->IsInitialized()); } TEST_F(AsarWrapperTest, OperationsWithoutInitialization) { // Operations should fail when not initialized ASSERT_FALSE(wrapper_->IsInitialized()); std::vector rom_copy = test_rom_; auto patch_result = wrapper_->ApplyPatch(test_asm_path_.string(), rom_copy); EXPECT_FALSE(patch_result.ok()); EXPECT_THAT(patch_result.status().message(), testing::HasSubstr("not initialized")); auto symbols_result = wrapper_->ExtractSymbols(test_asm_path_.string()); EXPECT_FALSE(symbols_result.ok()); EXPECT_THAT(symbols_result.status().message(), testing::HasSubstr("not initialized")); } TEST_F(AsarWrapperTest, ValidPatchApplication) { ASSERT_TRUE(wrapper_->Initialize().ok()); std::vector rom_copy = test_rom_; size_t original_size = rom_copy.size(); auto patch_result = wrapper_->ApplyPatch(test_asm_path_.string(), rom_copy); ASSERT_TRUE(patch_result.ok()) << patch_result.status().message(); const auto& result = patch_result.value(); EXPECT_TRUE(result.success) << "Patch failed: " << testing::PrintToString(result.errors); EXPECT_GT(result.rom_size, 0); EXPECT_EQ(rom_copy.size(), result.rom_size); // Check that ROM was actually modified EXPECT_NE(rom_copy, test_rom_); // Should be different after patching } TEST_F(AsarWrapperTest, InvalidPatchHandling) { ASSERT_TRUE(wrapper_->Initialize().ok()); std::vector rom_copy = test_rom_; auto patch_result = wrapper_->ApplyPatch(invalid_asm_path_.string(), rom_copy); EXPECT_FALSE(patch_result.ok()); EXPECT_THAT(patch_result.status().message(), testing::HasSubstr("Patch failed")); } TEST_F(AsarWrapperTest, NonexistentPatchFile) { ASSERT_TRUE(wrapper_->Initialize().ok()); std::vector rom_copy = test_rom_; std::string nonexistent_path = test_dir_.string() + "/nonexistent.asm"; auto patch_result = wrapper_->ApplyPatch(nonexistent_path, rom_copy); EXPECT_FALSE(patch_result.ok()); } TEST_F(AsarWrapperTest, SymbolExtraction) { ASSERT_TRUE(wrapper_->Initialize().ok()); auto symbols_result = wrapper_->ExtractSymbols(test_asm_path_.string()); ASSERT_TRUE(symbols_result.ok()) << symbols_result.status().message(); const auto& symbols = symbols_result.value(); EXPECT_GT(symbols.size(), 0); // Check for expected symbols from our test assembly bool found_testlabel = false; bool found_anotherlabel = false; for (const auto& symbol : symbols) { EXPECT_FALSE(symbol.name.empty()); EXPECT_GT(symbol.address, 0); if (symbol.name == "testlabel") { found_testlabel = true; EXPECT_EQ(symbol.address, 0x008000); // Expected address from org directive } else if (symbol.name == "anotherlabel") { found_anotherlabel = true; } } EXPECT_TRUE(found_testlabel) << "Expected 'testlabel' symbol not found"; EXPECT_TRUE(found_anotherlabel) << "Expected 'anotherlabel' symbol not found"; } TEST_F(AsarWrapperTest, SymbolTableOperations) { ASSERT_TRUE(wrapper_->Initialize().ok()); std::vector rom_copy = test_rom_; auto patch_result = wrapper_->ApplyPatch(test_asm_path_.string(), rom_copy); ASSERT_TRUE(patch_result.ok()); // Test symbol table retrieval auto symbol_table = wrapper_->GetSymbolTable(); EXPECT_GT(symbol_table.size(), 0); // Test symbol lookup by name auto testlabel_symbol = wrapper_->FindSymbol("testlabel"); EXPECT_TRUE(testlabel_symbol.has_value()); if (testlabel_symbol) { EXPECT_EQ(testlabel_symbol->name, "testlabel"); EXPECT_GT(testlabel_symbol->address, 0); } // Test lookup of non-existent symbol auto nonexistent_symbol = wrapper_->FindSymbol("nonexistent_symbol"); EXPECT_FALSE(nonexistent_symbol.has_value()); // Test symbols at address lookup if (testlabel_symbol) { auto symbols_at_addr = wrapper_->GetSymbolsAtAddress(testlabel_symbol->address); EXPECT_GT(symbols_at_addr.size(), 0); bool found = false; for (const auto& symbol : symbols_at_addr) { if (symbol.name == "testlabel") { found = true; break; } } EXPECT_TRUE(found); } } TEST_F(AsarWrapperTest, PatchFromString) { ASSERT_TRUE(wrapper_->Initialize().ok()); std::string patch_content = R"( org $009000 stringpatchlabel: LDA #$55 STA $7E0002 RTS )"; std::vector rom_copy = test_rom_; auto patch_result = wrapper_->ApplyPatchFromString( patch_content, rom_copy, test_dir_.string()); ASSERT_TRUE(patch_result.ok()) << patch_result.status().message(); const auto& result = patch_result.value(); EXPECT_TRUE(result.success); EXPECT_GT(result.symbols.size(), 0); // Check for the symbol we defined bool found_symbol = false; for (const auto& symbol : result.symbols) { if (symbol.name == "stringpatchlabel") { found_symbol = true; EXPECT_EQ(symbol.address, 0x009000); break; } } EXPECT_TRUE(found_symbol); } TEST_F(AsarWrapperTest, AssemblyValidation) { ASSERT_TRUE(wrapper_->Initialize().ok()); // Test valid assembly auto valid_status = wrapper_->ValidateAssembly(test_asm_path_.string()); EXPECT_TRUE(valid_status.ok()) << valid_status.message(); // Test invalid assembly auto invalid_status = wrapper_->ValidateAssembly(invalid_asm_path_.string()); EXPECT_FALSE(invalid_status.ok()); EXPECT_THAT(invalid_status.message(), testing::AnyOf(testing::HasSubstr("validation failed"), testing::HasSubstr("Patch failed"), testing::HasSubstr("Unknown command"), testing::HasSubstr("Label"))); } TEST_F(AsarWrapperTest, ResetFunctionality) { ASSERT_TRUE(wrapper_->Initialize().ok()); // Apply a patch to generate some state std::vector rom_copy = test_rom_; auto patch_result = wrapper_->ApplyPatch(test_asm_path_.string(), rom_copy); ASSERT_TRUE(patch_result.ok()); // Verify we have symbols and potentially warnings/errors auto symbol_table_before = wrapper_->GetSymbolTable(); EXPECT_GT(symbol_table_before.size(), 0); // Reset and verify state is cleared wrapper_->Reset(); auto symbol_table_after = wrapper_->GetSymbolTable(); EXPECT_EQ(symbol_table_after.size(), 0); auto errors = wrapper_->GetLastErrors(); auto warnings = wrapper_->GetLastWarnings(); EXPECT_EQ(errors.size(), 0); EXPECT_EQ(warnings.size(), 0); } TEST_F(AsarWrapperTest, CreatePatchNotImplemented) { ASSERT_TRUE(wrapper_->Initialize().ok()); std::vector original_rom = test_rom_; std::vector modified_rom = test_rom_; modified_rom[100] = 0x42; // Make a small change std::string patch_path = test_dir_.string() + "/generated.asm"; auto status = wrapper_->CreatePatch(original_rom, modified_rom, patch_path); EXPECT_FALSE(status.ok()); EXPECT_THAT(status.message(), testing::HasSubstr("not yet implemented")); } } // namespace } // namespace core } // namespace yaze