324 lines
9.4 KiB
C++
324 lines
9.4 KiB
C++
#include "core/asar_wrapper.h"
|
|
#include "test_utils.h"
|
|
|
|
#include <gtest/gtest.h>
|
|
#include <gmock/gmock.h>
|
|
#include <fstream>
|
|
#include <filesystem>
|
|
|
|
namespace yaze {
|
|
namespace core {
|
|
namespace {
|
|
|
|
class AsarWrapperTest : public ::testing::Test {
|
|
protected:
|
|
void SetUp() override {
|
|
wrapper_ = std::make_unique<AsarWrapper>();
|
|
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<AsarWrapper> wrapper_;
|
|
std::filesystem::path test_dir_;
|
|
std::filesystem::path test_asm_path_;
|
|
std::filesystem::path invalid_asm_path_;
|
|
std::vector<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> original_rom = test_rom_;
|
|
std::vector<uint8_t> 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
|