Add integration tests for Dungeon Editor and object parsing functionality
- Updated CMakeLists.txt to include new test files for CPU, PPU, SPC700, APU, and dungeon editor integration tests. - Introduced new testing helpers in testing.h for validating StatusOr objects with specific error messages and codes. - Added comprehensive integration tests for the DungeonEditor, covering object parsing, rendering, and room graphics. - Created mock ROM and object data setups to facilitate testing without real ROM files. - Implemented various test cases to ensure the reliability of object parsing and rendering logic in the dungeon editor.
This commit is contained in:
89
test/zelda3/object_parser_structs_test.cc
Normal file
89
test/zelda3/object_parser_structs_test.cc
Normal file
@@ -0,0 +1,89 @@
|
||||
#include "app/zelda3/dungeon/object_parser.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace test {
|
||||
|
||||
class ObjectParserStructsTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {}
|
||||
};
|
||||
|
||||
TEST_F(ObjectParserStructsTest, ObjectRoutineInfoDefaultConstructor) {
|
||||
zelda3::ObjectRoutineInfo info;
|
||||
|
||||
EXPECT_EQ(info.routine_ptr, 0);
|
||||
EXPECT_EQ(info.tile_ptr, 0);
|
||||
EXPECT_EQ(info.tile_count, 0);
|
||||
EXPECT_FALSE(info.is_repeatable);
|
||||
EXPECT_FALSE(info.is_orientation_dependent);
|
||||
}
|
||||
|
||||
TEST_F(ObjectParserStructsTest, ObjectSubtypeInfoDefaultConstructor) {
|
||||
zelda3::ObjectSubtypeInfo info;
|
||||
|
||||
EXPECT_EQ(info.subtype, 0);
|
||||
EXPECT_EQ(info.subtype_ptr, 0);
|
||||
EXPECT_EQ(info.routine_ptr, 0);
|
||||
EXPECT_EQ(info.max_tile_count, 0);
|
||||
}
|
||||
|
||||
TEST_F(ObjectParserStructsTest, ObjectSizeInfoDefaultConstructor) {
|
||||
zelda3::ObjectSizeInfo info;
|
||||
|
||||
EXPECT_EQ(info.width_tiles, 0);
|
||||
EXPECT_EQ(info.height_tiles, 0);
|
||||
EXPECT_TRUE(info.is_horizontal);
|
||||
EXPECT_FALSE(info.is_repeatable);
|
||||
EXPECT_EQ(info.repeat_count, 1);
|
||||
}
|
||||
|
||||
TEST_F(ObjectParserStructsTest, ObjectRoutineInfoAssignment) {
|
||||
zelda3::ObjectRoutineInfo info;
|
||||
|
||||
info.routine_ptr = 0x12345;
|
||||
info.tile_ptr = 0x67890;
|
||||
info.tile_count = 8;
|
||||
info.is_repeatable = true;
|
||||
info.is_orientation_dependent = true;
|
||||
|
||||
EXPECT_EQ(info.routine_ptr, 0x12345);
|
||||
EXPECT_EQ(info.tile_ptr, 0x67890);
|
||||
EXPECT_EQ(info.tile_count, 8);
|
||||
EXPECT_TRUE(info.is_repeatable);
|
||||
EXPECT_TRUE(info.is_orientation_dependent);
|
||||
}
|
||||
|
||||
TEST_F(ObjectParserStructsTest, ObjectSubtypeInfoAssignment) {
|
||||
zelda3::ObjectSubtypeInfo info;
|
||||
|
||||
info.subtype = 2;
|
||||
info.subtype_ptr = 0x83F0;
|
||||
info.routine_ptr = 0x8470;
|
||||
info.max_tile_count = 16;
|
||||
|
||||
EXPECT_EQ(info.subtype, 2);
|
||||
EXPECT_EQ(info.subtype_ptr, 0x83F0);
|
||||
EXPECT_EQ(info.routine_ptr, 0x8470);
|
||||
EXPECT_EQ(info.max_tile_count, 16);
|
||||
}
|
||||
|
||||
TEST_F(ObjectParserStructsTest, ObjectSizeInfoAssignment) {
|
||||
zelda3::ObjectSizeInfo info;
|
||||
|
||||
info.width_tiles = 4;
|
||||
info.height_tiles = 2;
|
||||
info.is_horizontal = false;
|
||||
info.is_repeatable = true;
|
||||
info.repeat_count = 3;
|
||||
|
||||
EXPECT_EQ(info.width_tiles, 4);
|
||||
EXPECT_EQ(info.height_tiles, 2);
|
||||
EXPECT_FALSE(info.is_horizontal);
|
||||
EXPECT_TRUE(info.is_repeatable);
|
||||
EXPECT_EQ(info.repeat_count, 3);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace yaze
|
||||
147
test/zelda3/object_parser_test.cc
Normal file
147
test/zelda3/object_parser_test.cc
Normal file
@@ -0,0 +1,147 @@
|
||||
#include "app/zelda3/dungeon/object_parser.h"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "test/mocks/mock_rom.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace test {
|
||||
|
||||
class ObjectParserTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
mock_rom_ = std::make_unique<MockRom>();
|
||||
SetupMockData();
|
||||
parser_ = std::make_unique<zelda3::ObjectParser>(mock_rom_.get());
|
||||
}
|
||||
|
||||
void SetupMockData() {
|
||||
std::vector<uint8_t> mock_data(0x100000, 0x00);
|
||||
|
||||
// Set up object subtype tables
|
||||
SetupSubtypeTable(mock_data, 0x8000, 0x100); // Subtype 1 table
|
||||
SetupSubtypeTable(mock_data, 0x83F0, 0x80); // Subtype 2 table
|
||||
SetupSubtypeTable(mock_data, 0x84F0, 0x100); // Subtype 3 table
|
||||
|
||||
// Set up tile data
|
||||
SetupTileData(mock_data, 0x1B52, 0x1000);
|
||||
|
||||
static_cast<MockRom*>(mock_rom_.get())->SetTestData(mock_data);
|
||||
}
|
||||
|
||||
void SetupSubtypeTable(std::vector<uint8_t>& data, int base_addr, int count) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
int addr = base_addr + (i * 2);
|
||||
if (addr + 1 < (int)data.size()) {
|
||||
// Point to tile data at 0x1B52 + (i * 8)
|
||||
int tile_offset = (i * 8) & 0xFFFF;
|
||||
data[addr] = tile_offset & 0xFF;
|
||||
data[addr + 1] = (tile_offset >> 8) & 0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetupTileData(std::vector<uint8_t>& data, int base_addr, int size) {
|
||||
for (int i = 0; i < size; i += 8) {
|
||||
int addr = base_addr + i;
|
||||
if (addr + 7 < (int)data.size()) {
|
||||
// Create simple tile data (4 words per tile)
|
||||
for (int j = 0; j < 8; j++) {
|
||||
data[addr + j] = (i + j) & 0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<MockRom> mock_rom_;
|
||||
std::unique_ptr<zelda3::ObjectParser> parser_;
|
||||
};
|
||||
|
||||
TEST_F(ObjectParserTest, ParseSubtype1Object) {
|
||||
auto result = parser_->ParseObject(0x01);
|
||||
ASSERT_TRUE(result.ok());
|
||||
|
||||
const auto& tiles = result.value();
|
||||
EXPECT_EQ(tiles.size(), 8);
|
||||
|
||||
// Verify tile data was parsed correctly
|
||||
for (const auto& tile : tiles) {
|
||||
EXPECT_NE(tile.tile0_.id_, 0);
|
||||
EXPECT_NE(tile.tile1_.id_, 0);
|
||||
EXPECT_NE(tile.tile2_.id_, 0);
|
||||
EXPECT_NE(tile.tile3_.id_, 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ObjectParserTest, ParseSubtype2Object) {
|
||||
auto result = parser_->ParseObject(0x101);
|
||||
ASSERT_TRUE(result.ok());
|
||||
|
||||
const auto& tiles = result.value();
|
||||
EXPECT_EQ(tiles.size(), 8);
|
||||
}
|
||||
|
||||
TEST_F(ObjectParserTest, ParseSubtype3Object) {
|
||||
auto result = parser_->ParseObject(0x201);
|
||||
ASSERT_TRUE(result.ok());
|
||||
|
||||
const auto& tiles = result.value();
|
||||
EXPECT_EQ(tiles.size(), 8);
|
||||
}
|
||||
|
||||
TEST_F(ObjectParserTest, GetObjectSubtype) {
|
||||
auto result1 = parser_->GetObjectSubtype(0x01);
|
||||
ASSERT_TRUE(result1.ok());
|
||||
EXPECT_EQ(result1->subtype, 1);
|
||||
|
||||
auto result2 = parser_->GetObjectSubtype(0x101);
|
||||
ASSERT_TRUE(result2.ok());
|
||||
EXPECT_EQ(result2->subtype, 2);
|
||||
|
||||
auto result3 = parser_->GetObjectSubtype(0x201);
|
||||
ASSERT_TRUE(result3.ok());
|
||||
EXPECT_EQ(result3->subtype, 3);
|
||||
}
|
||||
|
||||
TEST_F(ObjectParserTest, ParseObjectSize) {
|
||||
auto result = parser_->ParseObjectSize(0x01, 0x12);
|
||||
ASSERT_TRUE(result.ok());
|
||||
|
||||
const auto& size_info = result.value();
|
||||
EXPECT_EQ(size_info.width_tiles, 4); // (1 + 1) * 2
|
||||
EXPECT_EQ(size_info.height_tiles, 6); // (2 + 1) * 2
|
||||
EXPECT_TRUE(size_info.is_horizontal);
|
||||
EXPECT_TRUE(size_info.is_repeatable);
|
||||
EXPECT_EQ(size_info.repeat_count, 0x12);
|
||||
}
|
||||
|
||||
TEST_F(ObjectParserTest, ParseObjectRoutine) {
|
||||
auto result = parser_->ParseObjectRoutine(0x01);
|
||||
ASSERT_TRUE(result.ok());
|
||||
|
||||
const auto& routine_info = result.value();
|
||||
EXPECT_NE(routine_info.routine_ptr, 0);
|
||||
EXPECT_NE(routine_info.tile_ptr, 0);
|
||||
EXPECT_EQ(routine_info.tile_count, 8);
|
||||
EXPECT_TRUE(routine_info.is_repeatable);
|
||||
EXPECT_TRUE(routine_info.is_orientation_dependent);
|
||||
}
|
||||
|
||||
TEST_F(ObjectParserTest, InvalidObjectId) {
|
||||
auto result = parser_->ParseObject(-1);
|
||||
EXPECT_FALSE(result.ok());
|
||||
EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
|
||||
}
|
||||
|
||||
TEST_F(ObjectParserTest, NullRom) {
|
||||
zelda3::ObjectParser null_parser(nullptr);
|
||||
auto result = null_parser.ParseObject(0x01);
|
||||
EXPECT_FALSE(result.ok());
|
||||
EXPECT_EQ(result.status().code(), absl::StatusCode::kInvalidArgument);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace yaze
|
||||
366
test/zelda3/test_dungeon_objects.cc
Normal file
366
test/zelda3/test_dungeon_objects.cc
Normal file
@@ -0,0 +1,366 @@
|
||||
#include "test_dungeon_objects.h"
|
||||
#include "test/mocks/mock_rom.h"
|
||||
#include "app/zelda3/dungeon/object_parser.h"
|
||||
#include "app/zelda3/dungeon/object_renderer.h"
|
||||
#include "app/zelda3/dungeon/room_object.h"
|
||||
#include "app/zelda3/dungeon/room_layout.h"
|
||||
#include "app/gfx/snes_color.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "test/testing.h"
|
||||
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace test {
|
||||
|
||||
void TestDungeonObjects::SetUp() {
|
||||
test_rom_ = std::make_unique<MockRom>();
|
||||
ASSERT_TRUE(CreateTestRom().ok());
|
||||
ASSERT_TRUE(SetupObjectData().ok());
|
||||
}
|
||||
|
||||
void TestDungeonObjects::TearDown() {
|
||||
test_rom_.reset();
|
||||
}
|
||||
|
||||
absl::Status TestDungeonObjects::CreateTestRom() {
|
||||
// Create basic ROM data
|
||||
std::vector<uint8_t> rom_data(kTestRomSize, 0x00);
|
||||
|
||||
// Set up ROM header
|
||||
std::string title = "ZELDA3 TEST";
|
||||
std::memcpy(&rom_data[0x7FC0], title.c_str(), std::min(title.length(), size_t(21)));
|
||||
rom_data[0x7FD7] = 0x21; // 2MB ROM
|
||||
|
||||
// Set up object tables
|
||||
auto subtype1_table = CreateObjectSubtypeTable(0x8000, 0x100);
|
||||
auto subtype2_table = CreateObjectSubtypeTable(0x83F0, 0x80);
|
||||
auto subtype3_table = CreateObjectSubtypeTable(0x84F0, 0x100);
|
||||
|
||||
// Copy tables to ROM data
|
||||
std::copy(subtype1_table.begin(), subtype1_table.end(), rom_data.begin() + 0x8000);
|
||||
std::copy(subtype2_table.begin(), subtype2_table.end(), rom_data.begin() + 0x83F0);
|
||||
std::copy(subtype3_table.begin(), subtype3_table.end(), rom_data.begin() + 0x84F0);
|
||||
|
||||
// Set up tile data
|
||||
auto tile_data = CreateTileData(0x1B52, 0x400);
|
||||
std::copy(tile_data.begin(), tile_data.end(), rom_data.begin() + 0x1B52);
|
||||
|
||||
return test_rom_->SetTestData(rom_data);
|
||||
}
|
||||
|
||||
absl::Status TestDungeonObjects::SetupObjectData() {
|
||||
// Set up test object data
|
||||
std::vector<uint8_t> object_data = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
|
||||
test_rom_->SetObjectData(kTestObjectId, object_data);
|
||||
|
||||
// Set up test room data
|
||||
auto room_header = CreateRoomHeader(kTestRoomId);
|
||||
test_rom_->SetRoomData(kTestRoomId, room_header);
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
std::vector<uint8_t> TestDungeonObjects::CreateObjectSubtypeTable(int base_addr, int count) {
|
||||
std::vector<uint8_t> table(count * 2, 0x00);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
int addr = i * 2;
|
||||
// Point to tile data at 0x1B52 + (i * 8)
|
||||
int tile_offset = (i * 8) & 0xFFFF;
|
||||
table[addr] = tile_offset & 0xFF;
|
||||
table[addr + 1] = (tile_offset >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> TestDungeonObjects::CreateTileData(int base_addr, int tile_count) {
|
||||
std::vector<uint8_t> data(tile_count * 8, 0x00);
|
||||
|
||||
for (int i = 0; i < tile_count; i++) {
|
||||
int addr = i * 8;
|
||||
// Create simple tile data
|
||||
for (int j = 0; j < 8; j++) {
|
||||
data[addr + j] = (i + j) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> TestDungeonObjects::CreateRoomHeader(int room_id) {
|
||||
std::vector<uint8_t> header(32, 0x00);
|
||||
|
||||
// Basic room properties
|
||||
header[0] = 0x00; // Background type, collision, light
|
||||
header[1] = 0x00; // Palette
|
||||
header[2] = 0x01; // Blockset
|
||||
header[3] = 0x01; // Spriteset
|
||||
header[4] = 0x00; // Effect
|
||||
header[5] = 0x00; // Tag1
|
||||
header[6] = 0x00; // Tag2
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
// Test cases
|
||||
TEST_F(TestDungeonObjects, ObjectParserBasicTest) {
|
||||
zelda3::ObjectParser parser(test_rom_.get());
|
||||
|
||||
auto result = parser.ParseObject(kTestObjectId);
|
||||
ASSERT_TRUE(result.ok());
|
||||
EXPECT_FALSE(result->empty());
|
||||
}
|
||||
|
||||
TEST_F(TestDungeonObjects, ObjectRendererBasicTest) {
|
||||
zelda3::ObjectRenderer renderer(test_rom_.get());
|
||||
|
||||
// Create test object
|
||||
auto room_object = zelda3::RoomObject(kTestObjectId, 0, 0, 0x12, 0);
|
||||
room_object.set_rom(test_rom_.get());
|
||||
room_object.EnsureTilesLoaded();
|
||||
|
||||
// Create test palette
|
||||
gfx::SnesPalette palette;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
palette.AddColor(gfx::SnesColor(i * 16, i * 16, i * 16));
|
||||
}
|
||||
|
||||
auto result = renderer.RenderObject(room_object, palette);
|
||||
ASSERT_TRUE(result.ok());
|
||||
EXPECT_GT(result->width(), 0);
|
||||
EXPECT_GT(result->height(), 0);
|
||||
}
|
||||
|
||||
TEST_F(TestDungeonObjects, RoomObjectTileLoadingTest) {
|
||||
auto room_object = zelda3::RoomObject(kTestObjectId, 5, 5, 0x12, 0);
|
||||
room_object.set_rom(test_rom_.get());
|
||||
|
||||
// Test tile loading
|
||||
room_object.EnsureTilesLoaded();
|
||||
EXPECT_FALSE(room_object.tiles().empty());
|
||||
}
|
||||
|
||||
TEST_F(TestDungeonObjects, MockRomDataTest) {
|
||||
auto* mock_rom = static_cast<MockRom*>(test_rom_.get());
|
||||
|
||||
EXPECT_TRUE(mock_rom->HasObjectData(kTestObjectId));
|
||||
EXPECT_TRUE(mock_rom->HasRoomData(kTestRoomId));
|
||||
EXPECT_TRUE(mock_rom->IsValid());
|
||||
}
|
||||
|
||||
TEST_F(TestDungeonObjects, RoomObjectTileAccessTest) {
|
||||
auto room_object = zelda3::RoomObject(kTestObjectId, 5, 5, 0x12, 0);
|
||||
room_object.set_rom(test_rom_.get());
|
||||
room_object.EnsureTilesLoaded();
|
||||
|
||||
// Test new tile access methods
|
||||
auto tiles_result = room_object.GetTiles();
|
||||
EXPECT_TRUE(tiles_result.ok());
|
||||
if (tiles_result.ok()) {
|
||||
EXPECT_FALSE(tiles_result->empty());
|
||||
}
|
||||
|
||||
// Test individual tile access
|
||||
auto tile_result = room_object.GetTile(0);
|
||||
EXPECT_TRUE(tile_result.ok());
|
||||
|
||||
if (tile_result.ok()) {
|
||||
const auto* tile = tile_result.value();
|
||||
EXPECT_NE(tile, nullptr);
|
||||
}
|
||||
|
||||
// Test tile count
|
||||
EXPECT_GT(room_object.GetTileCount(), 0);
|
||||
|
||||
// Test out of range access
|
||||
auto bad_tile_result = room_object.GetTile(999);
|
||||
EXPECT_FALSE(bad_tile_result.ok());
|
||||
}
|
||||
|
||||
TEST_F(TestDungeonObjects, ObjectRendererGraphicsSheetTest) {
|
||||
zelda3::ObjectRenderer renderer(test_rom_.get());
|
||||
|
||||
// Create test object
|
||||
auto room_object = zelda3::RoomObject(kTestObjectId, 0, 0, 0x12, 0);
|
||||
room_object.set_rom(test_rom_.get());
|
||||
room_object.EnsureTilesLoaded();
|
||||
|
||||
// Create test palette
|
||||
gfx::SnesPalette palette;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
palette.AddColor(gfx::SnesColor(i * 16, i * 16, i * 16));
|
||||
}
|
||||
|
||||
// Test rendering with graphics sheet lookup
|
||||
auto result = renderer.RenderObject(room_object, palette);
|
||||
ASSERT_TRUE(result.ok());
|
||||
|
||||
auto bitmap = std::move(result.value());
|
||||
EXPECT_TRUE(bitmap.is_active());
|
||||
EXPECT_NE(bitmap.surface(), nullptr);
|
||||
EXPECT_GT(bitmap.width(), 0);
|
||||
EXPECT_GT(bitmap.height(), 0);
|
||||
}
|
||||
|
||||
TEST_F(TestDungeonObjects, BitmapCopySemanticsTest) {
|
||||
// Test bitmap copying works correctly
|
||||
std::vector<uint8_t> data(32 * 32, 0x42);
|
||||
gfx::Bitmap original(32, 32, 8, data);
|
||||
|
||||
// Test copy constructor
|
||||
gfx::Bitmap copy = original;
|
||||
EXPECT_EQ(copy.width(), original.width());
|
||||
EXPECT_EQ(copy.height(), original.height());
|
||||
EXPECT_TRUE(copy.is_active());
|
||||
EXPECT_NE(copy.surface(), nullptr);
|
||||
|
||||
// Test copy assignment
|
||||
gfx::Bitmap assigned;
|
||||
assigned = original;
|
||||
EXPECT_EQ(assigned.width(), original.width());
|
||||
EXPECT_EQ(assigned.height(), original.height());
|
||||
EXPECT_TRUE(assigned.is_active());
|
||||
EXPECT_NE(assigned.surface(), nullptr);
|
||||
}
|
||||
|
||||
TEST_F(TestDungeonObjects, BitmapMoveSemanticsTest) {
|
||||
// Test bitmap moving works correctly
|
||||
std::vector<uint8_t> data(32 * 32, 0x42);
|
||||
gfx::Bitmap original(32, 32, 8, data);
|
||||
|
||||
// Test move constructor
|
||||
gfx::Bitmap moved = std::move(original);
|
||||
EXPECT_EQ(moved.width(), 32);
|
||||
EXPECT_EQ(moved.height(), 32);
|
||||
EXPECT_TRUE(moved.is_active());
|
||||
EXPECT_NE(moved.surface(), nullptr);
|
||||
|
||||
// Original should be in a valid but empty state
|
||||
EXPECT_EQ(original.width(), 0);
|
||||
EXPECT_EQ(original.height(), 0);
|
||||
EXPECT_FALSE(original.is_active());
|
||||
EXPECT_EQ(original.surface(), nullptr);
|
||||
}
|
||||
|
||||
TEST_F(TestDungeonObjects, PaletteHandlingTest) {
|
||||
// Test palette handling and hash calculation
|
||||
gfx::SnesPalette palette;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
palette.AddColor(gfx::SnesColor(i * 16, i * 16, i * 16));
|
||||
}
|
||||
|
||||
EXPECT_EQ(palette.size(), 16);
|
||||
|
||||
// Test palette hash calculation (used in caching)
|
||||
uint64_t hash1 = 0;
|
||||
for (size_t i = 0; i < palette.size(); ++i) {
|
||||
hash1 ^= std::hash<uint16_t>{}(palette[i].snes()) + 0x9e3779b9 + (hash1 << 6) + (hash1 >> 2);
|
||||
}
|
||||
|
||||
// Same palette should produce same hash
|
||||
uint64_t hash2 = 0;
|
||||
for (size_t i = 0; i < palette.size(); ++i) {
|
||||
hash2 ^= std::hash<uint16_t>{}(palette[i].snes()) + 0x9e3779b9 + (hash2 << 6) + (hash2 >> 2);
|
||||
}
|
||||
|
||||
EXPECT_EQ(hash1, hash2);
|
||||
EXPECT_NE(hash1, 0); // Hash should not be zero
|
||||
}
|
||||
|
||||
TEST_F(TestDungeonObjects, ObjectSizeCalculationTest) {
|
||||
zelda3::ObjectParser parser(test_rom_.get());
|
||||
|
||||
// Test object size parsing
|
||||
auto size_result = parser.ParseObjectSize(0x01, 0x12);
|
||||
EXPECT_TRUE(size_result.ok());
|
||||
|
||||
if (size_result.ok()) {
|
||||
const auto& size_info = size_result.value();
|
||||
EXPECT_GT(size_info.width_tiles, 0);
|
||||
EXPECT_GT(size_info.height_tiles, 0);
|
||||
EXPECT_TRUE(size_info.is_repeatable);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestDungeonObjects, ObjectSubtypeDeterminationTest) {
|
||||
zelda3::ObjectParser parser(test_rom_.get());
|
||||
|
||||
// Test subtype determination
|
||||
EXPECT_EQ(parser.DetermineSubtype(0x01), 1);
|
||||
EXPECT_EQ(parser.DetermineSubtype(0x100), 2);
|
||||
EXPECT_EQ(parser.DetermineSubtype(0x200), 3);
|
||||
|
||||
// Test object subtype info
|
||||
auto subtype_result = parser.GetObjectSubtype(0x01);
|
||||
EXPECT_TRUE(subtype_result.ok());
|
||||
|
||||
if (subtype_result.ok()) {
|
||||
EXPECT_EQ(subtype_result->subtype, 1);
|
||||
EXPECT_GT(subtype_result->max_tile_count, 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestDungeonObjects, RoomLayoutObjectCreationTest) {
|
||||
zelda3::RoomLayoutObject obj(0x01, 5, 10, zelda3::RoomLayoutObject::Type::kWall, 0);
|
||||
|
||||
EXPECT_EQ(obj.id(), 0x01);
|
||||
EXPECT_EQ(obj.x(), 5);
|
||||
EXPECT_EQ(obj.y(), 10);
|
||||
EXPECT_EQ(obj.type(), zelda3::RoomLayoutObject::Type::kWall);
|
||||
EXPECT_EQ(obj.layer(), 0);
|
||||
|
||||
// Test type name
|
||||
EXPECT_EQ(obj.GetTypeName(), "Wall");
|
||||
|
||||
// Test tile creation
|
||||
auto tile_result = obj.GetTile();
|
||||
EXPECT_TRUE(tile_result.ok());
|
||||
}
|
||||
|
||||
TEST_F(TestDungeonObjects, RoomLayoutLoadingTest) {
|
||||
zelda3::RoomLayout layout(test_rom_.get());
|
||||
|
||||
// Test loading layout for room 0
|
||||
auto status = layout.LoadLayout(0);
|
||||
// This might fail due to missing layout data, which is expected
|
||||
// We're testing that the method doesn't crash
|
||||
|
||||
// Test getting objects by type
|
||||
auto walls = layout.GetObjectsByType(zelda3::RoomLayoutObject::Type::kWall);
|
||||
auto floors = layout.GetObjectsByType(zelda3::RoomLayoutObject::Type::kFloor);
|
||||
|
||||
// Test dimensions
|
||||
auto [width, height] = layout.GetDimensions();
|
||||
EXPECT_GT(width, 0);
|
||||
EXPECT_GT(height, 0);
|
||||
|
||||
// Test object access
|
||||
auto obj_result = layout.GetObjectAt(0, 0, 0);
|
||||
// This might fail if no object exists at that position, which is expected
|
||||
}
|
||||
|
||||
TEST_F(TestDungeonObjects, RoomLayoutCollisionTest) {
|
||||
zelda3::RoomLayout layout(test_rom_.get());
|
||||
|
||||
// Test collision detection methods
|
||||
EXPECT_FALSE(layout.HasWall(0, 0, 0)); // Should be false for empty layout
|
||||
EXPECT_FALSE(layout.HasFloor(0, 0, 0)); // Should be false for empty layout
|
||||
|
||||
// Test with a simple layout
|
||||
std::vector<uint8_t> layout_data = {
|
||||
0x01, 0x01, 0x00, 0x00, // Wall, Wall, Empty, Empty
|
||||
0x21, 0x21, 0x21, 0x21, // Floor, Floor, Floor, Floor
|
||||
0x00, 0x00, 0x00, 0x00, // Empty, Empty, Empty, Empty
|
||||
};
|
||||
|
||||
// This would require the layout to be properly set up
|
||||
// For now, we just test that the methods don't crash
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace yaze
|
||||
46
test/zelda3/test_dungeon_objects.h
Normal file
46
test/zelda3/test_dungeon_objects.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#ifndef YAZE_TEST_TEST_DUNGEON_OBJECTS_H
|
||||
#define YAZE_TEST_TEST_DUNGEON_OBJECTS_H
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "app/rom.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "test/mocks/mock_rom.h"
|
||||
#include "test/testing.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace test {
|
||||
|
||||
/**
|
||||
* @brief Simplified test framework for dungeon object rendering
|
||||
*
|
||||
* This provides a clean, focused testing environment for dungeon object
|
||||
* functionality without the complexity of full integration tests.
|
||||
*/
|
||||
class TestDungeonObjects : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override;
|
||||
void TearDown() override;
|
||||
|
||||
// Test helpers
|
||||
absl::Status CreateTestRom();
|
||||
absl::Status SetupObjectData();
|
||||
|
||||
// Mock data generators
|
||||
std::vector<uint8_t> CreateObjectSubtypeTable(int base_addr, int count);
|
||||
std::vector<uint8_t> CreateTileData(int base_addr, int tile_count);
|
||||
std::vector<uint8_t> CreateRoomHeader(int room_id);
|
||||
|
||||
std::unique_ptr<MockRom> test_rom_;
|
||||
|
||||
// Test constants
|
||||
static constexpr int kTestObjectId = 0x01;
|
||||
static constexpr int kTestRoomId = 0x00;
|
||||
static constexpr size_t kTestRomSize = 0x100000; // 1MB test ROM
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_TEST_TEST_DUNGEON_OBJECTS_H
|
||||
Reference in New Issue
Block a user