Refactor test structure and enhance object encoding tests

- Updated CMakeLists.txt to correct file paths for unit tests.
- Modified DungeonObjectRenderingE2ETests to inherit from BoundRomTest for better ROM management.
- Enhanced DungeonEditorIntegrationTest with improved mock ROM handling and added graphics data setup.
- Introduced a new MockRom class with methods for setting mock data and initializing memory layout.
- Added comprehensive unit tests for RoomObject encoding and decoding, covering all object types and edge cases.
- Refactored DungeonObjectRenderingTests to utilize BoundRomTest, ensuring consistent ROM loading and setup.
- Improved assertions in rendering tests for better clarity and reliability.
This commit is contained in:
scawful
2025-10-04 13:37:52 -04:00
parent 6990e565b8
commit 20a406892c
12 changed files with 1261 additions and 469 deletions

View File

@@ -4,12 +4,15 @@
#include <vector>
#include "absl/strings/str_format.h"
#include "app/snes.h"
#include "app/zelda3/dungeon/room.h"
#include "app/zelda3/dungeon/room_object.h"
namespace yaze {
namespace test {
using namespace yaze::zelda3;
void DungeonEditorIntegrationTest::SetUp() {
ASSERT_TRUE(CreateMockRom().ok());
ASSERT_TRUE(LoadTestRoomData().ok());
@@ -51,9 +54,7 @@ absl::Status DungeonEditorIntegrationTest::CreateMockRom() {
mock_data[0x874D] = 0x00; // Object pointer mid
mock_data[0x874E] = 0x00; // Object pointer high
static_cast<MockRom*>(mock_rom_.get())->SetMockData(mock_data);
return absl::OkStatus();
return mock_rom_->LoadAndOwnData(mock_data);
}
absl::Status DungeonEditorIntegrationTest::LoadTestRoomData() {
@@ -62,8 +63,10 @@ absl::Status DungeonEditorIntegrationTest::LoadTestRoomData() {
auto object_data = GenerateMockObjectData();
auto graphics_data = GenerateMockGraphicsData();
static_cast<MockRom*>(mock_rom_.get())->SetMockRoomData(kTestRoomId, room_header);
static_cast<MockRom*>(mock_rom_.get())->SetMockObjectData(kTestObjectId, object_data);
auto mock_rom = static_cast<MockRom*>(mock_rom_.get());
mock_rom->SetMockRoomData(kTestRoomId, room_header);
mock_rom->SetMockObjectData(kTestObjectId, object_data);
mock_rom->SetMockGraphicsData(graphics_data);
return absl::OkStatus();
}
@@ -187,16 +190,174 @@ std::vector<uint8_t> DungeonEditorIntegrationTest::GenerateMockGraphicsData() {
return data;
}
void MockRom::SetMockData(const std::vector<uint8_t>& data) {
mock_data_ = data;
absl::Status MockRom::SetMockData(const std::vector<uint8_t>& data) {
backing_buffer_.assign(data.begin(), data.end());
Expand(static_cast<int>(backing_buffer_.size()));
if (!backing_buffer_.empty()) {
std::memcpy(mutable_data(), backing_buffer_.data(), backing_buffer_.size());
}
ClearDirty();
InitializeMemoryLayout();
return absl::OkStatus();
}
absl::Status MockRom::LoadAndOwnData(const std::vector<uint8_t>& data) {
backing_buffer_.assign(data.begin(), data.end());
Expand(static_cast<int>(backing_buffer_.size()));
if (!backing_buffer_.empty()) {
std::memcpy(mutable_data(), backing_buffer_.data(), backing_buffer_.size());
}
ClearDirty();
// Minimal metadata setup via public API
set_filename("mock_rom.sfc");
auto& palette_groups = *mutable_palette_group();
palette_groups.clear();
if (palette_groups.dungeon_main.size() == 0) {
gfx::SnesPalette default_palette;
default_palette.Resize(16);
palette_groups.dungeon_main.AddPalette(default_palette);
}
// Ensure graphics buffer is sized
auto* gfx_buffer = mutable_graphics_buffer();
gfx_buffer->assign(backing_buffer_.begin(), backing_buffer_.end());
InitializeMemoryLayout();
return absl::OkStatus();
}
void MockRom::SetMockRoomData(int room_id, const std::vector<uint8_t>& data) {
mock_room_data_[room_id] = data;
if (room_header_table_pc_ == 0 || room_header_data_base_pc_ == 0) {
return;
}
uint32_t header_offset = room_header_data_base_pc_ + kRoomHeaderStride * static_cast<uint32_t>(room_id);
EnsureBufferCapacity(header_offset + static_cast<uint32_t>(data.size()));
std::memcpy(backing_buffer_.data() + header_offset, data.data(), data.size());
std::memcpy(mutable_data() + header_offset, data.data(), data.size());
uint32_t snes_offset = PcToSnes(header_offset);
uint32_t pointer_entry = room_header_table_pc_ + static_cast<uint32_t>(room_id) * 2;
EnsureBufferCapacity(pointer_entry + 2);
backing_buffer_[pointer_entry] = static_cast<uint8_t>(snes_offset & 0xFF);
backing_buffer_[pointer_entry + 1] = static_cast<uint8_t>((snes_offset >> 8) & 0xFF);
mutable_data()[pointer_entry] = backing_buffer_[pointer_entry];
mutable_data()[pointer_entry + 1] = backing_buffer_[pointer_entry + 1];
}
void MockRom::SetMockObjectData(int object_id, const std::vector<uint8_t>& data) {
mock_object_data_[object_id] = data;
if (room_object_table_pc_ == 0 || room_object_data_base_pc_ == 0) {
return;
}
uint32_t object_offset = room_object_data_base_pc_ + kRoomObjectStride * static_cast<uint32_t>(object_id);
EnsureBufferCapacity(object_offset + static_cast<uint32_t>(data.size()));
std::memcpy(backing_buffer_.data() + object_offset, data.data(), data.size());
std::memcpy(mutable_data() + object_offset, data.data(), data.size());
uint32_t snes_offset = PcToSnes(object_offset);
uint32_t entry = room_object_table_pc_ + static_cast<uint32_t>(object_id) * 3;
EnsureBufferCapacity(entry + 3);
backing_buffer_[entry] = static_cast<uint8_t>(snes_offset & 0xFF);
backing_buffer_[entry + 1] = static_cast<uint8_t>((snes_offset >> 8) & 0xFF);
backing_buffer_[entry + 2] = static_cast<uint8_t>((snes_offset >> 16) & 0xFF);
mutable_data()[entry] = backing_buffer_[entry];
mutable_data()[entry + 1] = backing_buffer_[entry + 1];
mutable_data()[entry + 2] = backing_buffer_[entry + 2];
}
void MockRom::SetMockGraphicsData(const std::vector<uint8_t>& data) {
mock_graphics_data_ = data;
if (auto* gfx_buffer = mutable_graphics_buffer(); gfx_buffer != nullptr) {
gfx_buffer->assign(data.begin(), data.end());
}
}
void MockRom::EnsureBufferCapacity(uint32_t size) {
if (size <= backing_buffer_.size()) {
return;
}
auto old_size = backing_buffer_.size();
backing_buffer_.resize(size, 0);
Expand(static_cast<int>(size));
std::memcpy(mutable_data(), backing_buffer_.data(), old_size);
}
void MockRom::InitializeMemoryLayout() {
if (backing_buffer_.empty()) {
return;
}
room_header_table_pc_ = SnesToPc(0x040000);
room_header_data_base_pc_ = SnesToPc(0x040000 + 0x1000);
room_object_table_pc_ = SnesToPc(0x050000);
room_object_data_base_pc_ = SnesToPc(0x050000 + 0x2000);
EnsureBufferCapacity(room_header_table_pc_ + 2);
EnsureBufferCapacity(room_object_table_pc_ + 3);
uint32_t header_table_snes = PcToSnes(room_header_table_pc_);
EnsureBufferCapacity(kRoomHeaderPointer + 3);
backing_buffer_[kRoomHeaderPointer] = static_cast<uint8_t>(header_table_snes & 0xFF);
backing_buffer_[kRoomHeaderPointer + 1] = static_cast<uint8_t>((header_table_snes >> 8) & 0xFF);
backing_buffer_[kRoomHeaderPointer + 2] = static_cast<uint8_t>((header_table_snes >> 16) & 0xFF);
mutable_data()[kRoomHeaderPointer] = backing_buffer_[kRoomHeaderPointer];
mutable_data()[kRoomHeaderPointer + 1] = backing_buffer_[kRoomHeaderPointer + 1];
mutable_data()[kRoomHeaderPointer + 2] = backing_buffer_[kRoomHeaderPointer + 2];
EnsureBufferCapacity(kRoomHeaderPointerBank + 1);
backing_buffer_[kRoomHeaderPointerBank] = static_cast<uint8_t>((header_table_snes >> 16) & 0xFF);
mutable_data()[kRoomHeaderPointerBank] = backing_buffer_[kRoomHeaderPointerBank];
uint32_t object_table_snes = PcToSnes(room_object_table_pc_);
EnsureBufferCapacity(room_object_pointer + 3);
backing_buffer_[room_object_pointer] = static_cast<uint8_t>(object_table_snes & 0xFF);
backing_buffer_[room_object_pointer + 1] = static_cast<uint8_t>((object_table_snes >> 8) & 0xFF);
backing_buffer_[room_object_pointer + 2] = static_cast<uint8_t>((object_table_snes >> 16) & 0xFF);
mutable_data()[room_object_pointer] = backing_buffer_[room_object_pointer];
mutable_data()[room_object_pointer + 1] = backing_buffer_[room_object_pointer + 1];
mutable_data()[room_object_pointer + 2] = backing_buffer_[room_object_pointer + 2];
for (const auto& [room_id, bytes] : mock_room_data_) {
uint32_t offset = room_header_data_base_pc_ + kRoomHeaderStride * static_cast<uint32_t>(room_id);
EnsureBufferCapacity(offset + static_cast<uint32_t>(bytes.size()));
std::memcpy(backing_buffer_.data() + offset, bytes.data(), bytes.size());
std::memcpy(mutable_data() + offset, bytes.data(), bytes.size());
uint32_t snes = PcToSnes(offset);
uint32_t entry = room_header_table_pc_ + static_cast<uint32_t>(room_id) * 2;
EnsureBufferCapacity(entry + 2);
backing_buffer_[entry] = static_cast<uint8_t>(snes & 0xFF);
backing_buffer_[entry + 1] = static_cast<uint8_t>((snes >> 8) & 0xFF);
mutable_data()[entry] = backing_buffer_[entry];
mutable_data()[entry + 1] = backing_buffer_[entry + 1];
}
for (const auto& [object_id, bytes] : mock_object_data_) {
uint32_t offset = room_object_data_base_pc_ + kRoomObjectStride * static_cast<uint32_t>(object_id);
EnsureBufferCapacity(offset + static_cast<uint32_t>(bytes.size()));
std::memcpy(backing_buffer_.data() + offset, bytes.data(), bytes.size());
std::memcpy(mutable_data() + offset, bytes.data(), bytes.size());
uint32_t snes = PcToSnes(offset);
uint32_t entry = room_object_table_pc_ + static_cast<uint32_t>(object_id) * 3;
EnsureBufferCapacity(entry + 3);
backing_buffer_[entry] = static_cast<uint8_t>(snes & 0xFF);
backing_buffer_[entry + 1] = static_cast<uint8_t>((snes >> 8) & 0xFF);
backing_buffer_[entry + 2] = static_cast<uint8_t>((snes >> 16) & 0xFF);
mutable_data()[entry] = backing_buffer_[entry];
mutable_data()[entry + 1] = backing_buffer_[entry + 1];
mutable_data()[entry + 2] = backing_buffer_[entry + 2];
}
}
bool MockRom::ValidateRoomData(int room_id) const {

View File

@@ -1,6 +1,8 @@
#ifndef YAZE_TEST_INTEGRATION_DUNGEON_EDITOR_TEST_H
#define YAZE_TEST_INTEGRATION_DUNGEON_EDITOR_TEST_H
#include <cstdint>
#include <map>
#include <memory>
#include <string>
@@ -12,6 +14,8 @@
namespace yaze {
namespace test {
class MockRom;
/**
* @brief Integration test framework for dungeon editor components
*
@@ -38,7 +42,7 @@ class DungeonEditorIntegrationTest : public ::testing::Test {
std::vector<uint8_t> GenerateMockObjectData();
std::vector<uint8_t> GenerateMockGraphicsData();
std::unique_ptr<Rom> mock_rom_;
std::unique_ptr<MockRom> mock_rom_;
std::unique_ptr<editor::DungeonEditor> dungeon_editor_;
// Test constants
@@ -55,18 +59,32 @@ class MockRom : public Rom {
MockRom() = default;
// Test data injection
void SetMockData(const std::vector<uint8_t>& data);
absl::Status SetMockData(const std::vector<uint8_t>& data);
absl::Status LoadAndOwnData(const std::vector<uint8_t>& data);
void SetMockRoomData(int room_id, const std::vector<uint8_t>& data);
void SetMockObjectData(int object_id, const std::vector<uint8_t>& data);
void SetMockGraphicsData(const std::vector<uint8_t>& data);
// Validation helpers
bool ValidateRoomData(int room_id) const;
bool ValidateObjectData(int object_id) const;
private:
std::vector<uint8_t> mock_data_;
void EnsureBufferCapacity(uint32_t size);
void InitializeMemoryLayout();
std::vector<uint8_t> backing_buffer_;
std::map<int, std::vector<uint8_t>> mock_room_data_;
std::map<int, std::vector<uint8_t>> mock_object_data_;
std::vector<uint8_t> mock_graphics_data_;
uint32_t room_header_table_pc_ = 0;
uint32_t room_header_data_base_pc_ = 0;
uint32_t room_object_table_pc_ = 0;
uint32_t room_object_data_base_pc_ = 0;
static constexpr uint32_t kRoomHeaderStride = 0x40;
static constexpr uint32_t kRoomObjectStride = 0x100;
};
} // namespace test