backend-infra-engineer: Release v0.3.3 snapshot
This commit is contained in:
@@ -1,13 +1,14 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <chrono>
|
||||
|
||||
#include "app/rom.h"
|
||||
#include "zelda3/dungeon/room.h"
|
||||
#include "zelda3/dungeon/dungeon_editor_system.h"
|
||||
#include "zelda3/dungeon/dungeon_object_editor.h"
|
||||
#include "zelda3/dungeon/room.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace zelda3 {
|
||||
@@ -19,18 +20,18 @@ class DungeonEditorSystemIntegrationTest : public ::testing::Test {
|
||||
#if defined(__linux__)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
|
||||
|
||||
// Use the real ROM from build directory
|
||||
rom_path_ = "build/bin/zelda3.sfc";
|
||||
|
||||
|
||||
// Load ROM
|
||||
rom_ = std::make_unique<Rom>();
|
||||
ASSERT_TRUE(rom_->LoadFromFile(rom_path_).ok());
|
||||
|
||||
|
||||
// Initialize dungeon editor system
|
||||
dungeon_editor_system_ = std::make_unique<DungeonEditorSystem>(rom_.get());
|
||||
ASSERT_TRUE(dungeon_editor_system_->Initialize().ok());
|
||||
|
||||
|
||||
// Load test room data
|
||||
ASSERT_TRUE(LoadTestRoomData().ok());
|
||||
}
|
||||
@@ -43,22 +44,23 @@ class DungeonEditorSystemIntegrationTest : public ::testing::Test {
|
||||
absl::Status LoadTestRoomData() {
|
||||
// Load representative rooms for testing
|
||||
test_rooms_ = {0x0000, 0x0001, 0x0002, 0x0010, 0x0012, 0x0020};
|
||||
|
||||
|
||||
for (int room_id : test_rooms_) {
|
||||
auto room_result = dungeon_editor_system_->GetRoom(room_id);
|
||||
if (room_result.ok()) {
|
||||
rooms_[room_id] = room_result.value();
|
||||
std::cout << "Loaded room 0x" << std::hex << room_id << std::dec << std::endl;
|
||||
std::cout << "Loaded room 0x" << std::hex << room_id << std::dec
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
std::string rom_path_;
|
||||
std::unique_ptr<Rom> rom_;
|
||||
std::unique_ptr<DungeonEditorSystem> dungeon_editor_system_;
|
||||
|
||||
|
||||
std::vector<int> test_rooms_;
|
||||
std::map<int, Room> rooms_;
|
||||
};
|
||||
@@ -74,19 +76,21 @@ TEST_F(DungeonEditorSystemIntegrationTest, BasicInitialization) {
|
||||
TEST_F(DungeonEditorSystemIntegrationTest, RoomLoadingAndManagement) {
|
||||
// Test loading a specific room
|
||||
auto room_result = dungeon_editor_system_->GetRoom(0x0000);
|
||||
ASSERT_TRUE(room_result.ok()) << "Failed to load room 0x0000: " << room_result.status().message();
|
||||
|
||||
ASSERT_TRUE(room_result.ok())
|
||||
<< "Failed to load room 0x0000: " << room_result.status().message();
|
||||
|
||||
const auto& room = room_result.value();
|
||||
// Note: room_id_ is private, so we can't directly access it in tests
|
||||
|
||||
|
||||
// Test setting current room
|
||||
ASSERT_TRUE(dungeon_editor_system_->SetCurrentRoom(0x0000).ok());
|
||||
EXPECT_EQ(dungeon_editor_system_->GetCurrentRoom(), 0x0000);
|
||||
|
||||
|
||||
// Test loading another room
|
||||
auto room2_result = dungeon_editor_system_->GetRoom(0x0001);
|
||||
ASSERT_TRUE(room2_result.ok()) << "Failed to load room 0x0001: " << room2_result.status().message();
|
||||
|
||||
ASSERT_TRUE(room2_result.ok())
|
||||
<< "Failed to load room 0x0001: " << room2_result.status().message();
|
||||
|
||||
const auto& room2 = room2_result.value();
|
||||
// Note: room_id_ is private, so we can't directly access it in tests
|
||||
}
|
||||
@@ -96,22 +100,22 @@ TEST_F(DungeonEditorSystemIntegrationTest, ObjectEditorIntegration) {
|
||||
// Get object editor from system
|
||||
auto object_editor = dungeon_editor_system_->GetObjectEditor();
|
||||
ASSERT_NE(object_editor, nullptr);
|
||||
|
||||
|
||||
// Set current room
|
||||
ASSERT_TRUE(dungeon_editor_system_->SetCurrentRoom(0x0000).ok());
|
||||
|
||||
|
||||
// Test object insertion
|
||||
ASSERT_TRUE(object_editor->InsertObject(5, 5, 0x10, 0x12, 0).ok());
|
||||
ASSERT_TRUE(object_editor->InsertObject(10, 10, 0x20, 0x22, 1).ok());
|
||||
|
||||
|
||||
// Verify objects were added
|
||||
EXPECT_EQ(object_editor->GetObjectCount(), 2);
|
||||
|
||||
|
||||
// Test object selection
|
||||
ASSERT_TRUE(object_editor->SelectObject(5 * 16, 5 * 16).ok());
|
||||
auto selection = object_editor->GetSelection();
|
||||
EXPECT_EQ(selection.selected_objects.size(), 1);
|
||||
|
||||
|
||||
// Test object deletion
|
||||
ASSERT_TRUE(object_editor->DeleteSelectedObjects().ok());
|
||||
EXPECT_EQ(object_editor->GetObjectCount(), 1);
|
||||
@@ -121,7 +125,7 @@ TEST_F(DungeonEditorSystemIntegrationTest, ObjectEditorIntegration) {
|
||||
TEST_F(DungeonEditorSystemIntegrationTest, SpriteManagement) {
|
||||
// Set current room
|
||||
ASSERT_TRUE(dungeon_editor_system_->SetCurrentRoom(0x0000).ok());
|
||||
|
||||
|
||||
// Create sprite data
|
||||
DungeonEditorSystem::SpriteData sprite_data;
|
||||
sprite_data.sprite_id = 1;
|
||||
@@ -131,31 +135,32 @@ TEST_F(DungeonEditorSystemIntegrationTest, SpriteManagement) {
|
||||
sprite_data.y = 100;
|
||||
sprite_data.layer = 0;
|
||||
sprite_data.is_active = true;
|
||||
|
||||
|
||||
// Add sprite
|
||||
ASSERT_TRUE(dungeon_editor_system_->AddSprite(sprite_data).ok());
|
||||
|
||||
|
||||
// Get sprites for room
|
||||
auto sprites_result = dungeon_editor_system_->GetSpritesByRoom(0x0000);
|
||||
ASSERT_TRUE(sprites_result.ok()) << "Failed to get sprites: " << sprites_result.status().message();
|
||||
|
||||
ASSERT_TRUE(sprites_result.ok())
|
||||
<< "Failed to get sprites: " << sprites_result.status().message();
|
||||
|
||||
const auto& sprites = sprites_result.value();
|
||||
EXPECT_EQ(sprites.size(), 1);
|
||||
EXPECT_EQ(sprites[0].sprite_id, 1);
|
||||
EXPECT_EQ(sprites[0].name, "Test Sprite");
|
||||
|
||||
|
||||
// Update sprite
|
||||
sprite_data.x = 150;
|
||||
ASSERT_TRUE(dungeon_editor_system_->UpdateSprite(1, sprite_data).ok());
|
||||
|
||||
|
||||
// Get updated sprite
|
||||
auto sprite_result = dungeon_editor_system_->GetSprite(1);
|
||||
ASSERT_TRUE(sprite_result.ok());
|
||||
EXPECT_EQ(sprite_result.value().x, 150);
|
||||
|
||||
|
||||
// Remove sprite
|
||||
ASSERT_TRUE(dungeon_editor_system_->RemoveSprite(1).ok());
|
||||
|
||||
|
||||
// Verify sprite was removed
|
||||
auto sprites_after = dungeon_editor_system_->GetSpritesByRoom(0x0000);
|
||||
ASSERT_TRUE(sprites_after.ok());
|
||||
@@ -166,7 +171,7 @@ TEST_F(DungeonEditorSystemIntegrationTest, SpriteManagement) {
|
||||
TEST_F(DungeonEditorSystemIntegrationTest, ItemManagement) {
|
||||
// Set current room
|
||||
ASSERT_TRUE(dungeon_editor_system_->SetCurrentRoom(0x0000).ok());
|
||||
|
||||
|
||||
// Create item data
|
||||
DungeonEditorSystem::ItemData item_data;
|
||||
item_data.item_id = 1;
|
||||
@@ -176,31 +181,32 @@ TEST_F(DungeonEditorSystemIntegrationTest, ItemManagement) {
|
||||
item_data.y = 200;
|
||||
item_data.room_id = 0x0000;
|
||||
item_data.is_hidden = false;
|
||||
|
||||
|
||||
// Add item
|
||||
ASSERT_TRUE(dungeon_editor_system_->AddItem(item_data).ok());
|
||||
|
||||
|
||||
// Get items for room
|
||||
auto items_result = dungeon_editor_system_->GetItemsByRoom(0x0000);
|
||||
ASSERT_TRUE(items_result.ok()) << "Failed to get items: " << items_result.status().message();
|
||||
|
||||
ASSERT_TRUE(items_result.ok())
|
||||
<< "Failed to get items: " << items_result.status().message();
|
||||
|
||||
const auto& items = items_result.value();
|
||||
EXPECT_EQ(items.size(), 1);
|
||||
EXPECT_EQ(items[0].item_id, 1);
|
||||
EXPECT_EQ(items[0].name, "Small Key");
|
||||
|
||||
|
||||
// Update item
|
||||
item_data.is_hidden = true;
|
||||
ASSERT_TRUE(dungeon_editor_system_->UpdateItem(1, item_data).ok());
|
||||
|
||||
|
||||
// Get updated item
|
||||
auto item_result = dungeon_editor_system_->GetItem(1);
|
||||
ASSERT_TRUE(item_result.ok());
|
||||
EXPECT_TRUE(item_result.value().is_hidden);
|
||||
|
||||
|
||||
// Remove item
|
||||
ASSERT_TRUE(dungeon_editor_system_->RemoveItem(1).ok());
|
||||
|
||||
|
||||
// Verify item was removed
|
||||
auto items_after = dungeon_editor_system_->GetItemsByRoom(0x0000);
|
||||
ASSERT_TRUE(items_after.ok());
|
||||
@@ -221,32 +227,35 @@ TEST_F(DungeonEditorSystemIntegrationTest, EntranceManagement) {
|
||||
entrance_data.target_x = 200;
|
||||
entrance_data.target_y = 200;
|
||||
entrance_data.is_bidirectional = true;
|
||||
|
||||
|
||||
// Add entrance
|
||||
ASSERT_TRUE(dungeon_editor_system_->AddEntrance(entrance_data).ok());
|
||||
|
||||
|
||||
// Get entrances for room
|
||||
auto entrances_result = dungeon_editor_system_->GetEntrancesByRoom(0x0000);
|
||||
ASSERT_TRUE(entrances_result.ok()) << "Failed to get entrances: " << entrances_result.status().message();
|
||||
|
||||
ASSERT_TRUE(entrances_result.ok())
|
||||
<< "Failed to get entrances: " << entrances_result.status().message();
|
||||
|
||||
const auto& entrances = entrances_result.value();
|
||||
EXPECT_EQ(entrances.size(), 1);
|
||||
EXPECT_EQ(entrances[0].name, "Test Entrance");
|
||||
|
||||
|
||||
// Store the entrance ID for later removal
|
||||
int entrance_id = entrances[0].entrance_id;
|
||||
|
||||
|
||||
// Test room connection
|
||||
ASSERT_TRUE(dungeon_editor_system_->ConnectRooms(0x0000, 0x0001, 150, 150, 250, 250).ok());
|
||||
|
||||
ASSERT_TRUE(
|
||||
dungeon_editor_system_->ConnectRooms(0x0000, 0x0001, 150, 150, 250, 250)
|
||||
.ok());
|
||||
|
||||
// Get updated entrances
|
||||
auto entrances_after = dungeon_editor_system_->GetEntrancesByRoom(0x0000);
|
||||
ASSERT_TRUE(entrances_after.ok());
|
||||
EXPECT_GE(entrances_after.value().size(), 1);
|
||||
|
||||
|
||||
// Remove entrance using the correct ID
|
||||
ASSERT_TRUE(dungeon_editor_system_->RemoveEntrance(entrance_id).ok());
|
||||
|
||||
|
||||
// Verify entrance was removed
|
||||
auto entrances_final = dungeon_editor_system_->GetEntrancesByRoom(0x0000);
|
||||
ASSERT_TRUE(entrances_final.ok());
|
||||
@@ -262,47 +271,48 @@ TEST_F(DungeonEditorSystemIntegrationTest, DoorManagement) {
|
||||
door_data.room_id = 0x0000;
|
||||
door_data.x = 100;
|
||||
door_data.y = 100;
|
||||
door_data.direction = 0; // up
|
||||
door_data.direction = 0; // up
|
||||
door_data.target_room_id = 0x0001;
|
||||
door_data.target_x = 200;
|
||||
door_data.target_y = 200;
|
||||
door_data.requires_key = false;
|
||||
door_data.key_type = 0;
|
||||
door_data.is_locked = false;
|
||||
|
||||
|
||||
// Add door
|
||||
ASSERT_TRUE(dungeon_editor_system_->AddDoor(door_data).ok());
|
||||
|
||||
|
||||
// Get doors for room
|
||||
auto doors_result = dungeon_editor_system_->GetDoorsByRoom(0x0000);
|
||||
ASSERT_TRUE(doors_result.ok()) << "Failed to get doors: " << doors_result.status().message();
|
||||
|
||||
ASSERT_TRUE(doors_result.ok())
|
||||
<< "Failed to get doors: " << doors_result.status().message();
|
||||
|
||||
const auto& doors = doors_result.value();
|
||||
EXPECT_EQ(doors.size(), 1);
|
||||
EXPECT_EQ(doors[0].door_id, 1);
|
||||
EXPECT_EQ(doors[0].name, "Test Door");
|
||||
|
||||
|
||||
// Update door
|
||||
door_data.is_locked = true;
|
||||
ASSERT_TRUE(dungeon_editor_system_->UpdateDoor(1, door_data).ok());
|
||||
|
||||
|
||||
// Get updated door
|
||||
auto door_result = dungeon_editor_system_->GetDoor(1);
|
||||
ASSERT_TRUE(door_result.ok());
|
||||
EXPECT_TRUE(door_result.value().is_locked);
|
||||
|
||||
|
||||
// Set door key requirement
|
||||
ASSERT_TRUE(dungeon_editor_system_->SetDoorKeyRequirement(1, true, 1).ok());
|
||||
|
||||
|
||||
// Get door with key requirement
|
||||
auto door_with_key = dungeon_editor_system_->GetDoor(1);
|
||||
ASSERT_TRUE(door_with_key.ok());
|
||||
EXPECT_TRUE(door_with_key.value().requires_key);
|
||||
EXPECT_EQ(door_with_key.value().key_type, 1);
|
||||
|
||||
|
||||
// Remove door
|
||||
ASSERT_TRUE(dungeon_editor_system_->RemoveDoor(1).ok());
|
||||
|
||||
|
||||
// Verify door was removed
|
||||
auto doors_after = dungeon_editor_system_->GetDoorsByRoom(0x0000);
|
||||
ASSERT_TRUE(doors_after.ok());
|
||||
@@ -321,39 +331,40 @@ TEST_F(DungeonEditorSystemIntegrationTest, ChestManagement) {
|
||||
chest_data.item_id = 10;
|
||||
chest_data.item_quantity = 1;
|
||||
chest_data.is_opened = false;
|
||||
|
||||
|
||||
// Add chest
|
||||
ASSERT_TRUE(dungeon_editor_system_->AddChest(chest_data).ok());
|
||||
|
||||
|
||||
// Get chests for room
|
||||
auto chests_result = dungeon_editor_system_->GetChestsByRoom(0x0000);
|
||||
ASSERT_TRUE(chests_result.ok()) << "Failed to get chests: " << chests_result.status().message();
|
||||
|
||||
ASSERT_TRUE(chests_result.ok())
|
||||
<< "Failed to get chests: " << chests_result.status().message();
|
||||
|
||||
const auto& chests = chests_result.value();
|
||||
EXPECT_EQ(chests.size(), 1);
|
||||
EXPECT_EQ(chests[0].chest_id, 1);
|
||||
EXPECT_EQ(chests[0].item_id, 10);
|
||||
|
||||
|
||||
// Update chest item
|
||||
ASSERT_TRUE(dungeon_editor_system_->SetChestItem(1, 20, 5).ok());
|
||||
|
||||
|
||||
// Get updated chest
|
||||
auto chest_result = dungeon_editor_system_->GetChest(1);
|
||||
ASSERT_TRUE(chest_result.ok());
|
||||
EXPECT_EQ(chest_result.value().item_id, 20);
|
||||
EXPECT_EQ(chest_result.value().item_quantity, 5);
|
||||
|
||||
|
||||
// Set chest as opened
|
||||
ASSERT_TRUE(dungeon_editor_system_->SetChestOpened(1, true).ok());
|
||||
|
||||
|
||||
// Get opened chest
|
||||
auto opened_chest = dungeon_editor_system_->GetChest(1);
|
||||
ASSERT_TRUE(opened_chest.ok());
|
||||
EXPECT_TRUE(opened_chest.value().is_opened);
|
||||
|
||||
|
||||
// Remove chest
|
||||
ASSERT_TRUE(dungeon_editor_system_->RemoveChest(1).ok());
|
||||
|
||||
|
||||
// Verify chest was removed
|
||||
auto chests_after = dungeon_editor_system_->GetChestsByRoom(0x0000);
|
||||
ASSERT_TRUE(chests_after.ok());
|
||||
@@ -374,25 +385,29 @@ TEST_F(DungeonEditorSystemIntegrationTest, RoomPropertiesManagement) {
|
||||
properties.is_shop_room = false;
|
||||
properties.music_id = 1;
|
||||
properties.ambient_sound_id = 0;
|
||||
|
||||
|
||||
// Set room properties
|
||||
ASSERT_TRUE(dungeon_editor_system_->SetRoomProperties(0x0000, properties).ok());
|
||||
|
||||
ASSERT_TRUE(
|
||||
dungeon_editor_system_->SetRoomProperties(0x0000, properties).ok());
|
||||
|
||||
// Get room properties
|
||||
auto properties_result = dungeon_editor_system_->GetRoomProperties(0x0000);
|
||||
ASSERT_TRUE(properties_result.ok()) << "Failed to get room properties: " << properties_result.status().message();
|
||||
|
||||
ASSERT_TRUE(properties_result.ok()) << "Failed to get room properties: "
|
||||
<< properties_result.status().message();
|
||||
|
||||
const auto& retrieved_properties = properties_result.value();
|
||||
EXPECT_EQ(retrieved_properties.room_id, 0x0000);
|
||||
EXPECT_EQ(retrieved_properties.name, "Test Room");
|
||||
EXPECT_EQ(retrieved_properties.description, "A test room for integration testing");
|
||||
EXPECT_EQ(retrieved_properties.description,
|
||||
"A test room for integration testing");
|
||||
EXPECT_EQ(retrieved_properties.dungeon_id, 1);
|
||||
|
||||
|
||||
// Update properties
|
||||
properties.name = "Updated Test Room";
|
||||
properties.is_boss_room = true;
|
||||
ASSERT_TRUE(dungeon_editor_system_->SetRoomProperties(0x0000, properties).ok());
|
||||
|
||||
ASSERT_TRUE(
|
||||
dungeon_editor_system_->SetRoomProperties(0x0000, properties).ok());
|
||||
|
||||
// Verify update
|
||||
auto updated_properties = dungeon_editor_system_->GetRoomProperties(0x0000);
|
||||
ASSERT_TRUE(updated_properties.ok());
|
||||
@@ -415,14 +430,15 @@ TEST_F(DungeonEditorSystemIntegrationTest, DungeonSettingsManagement) {
|
||||
settings.has_map = true;
|
||||
settings.has_compass = true;
|
||||
settings.has_big_key = true;
|
||||
|
||||
|
||||
// Set dungeon settings
|
||||
ASSERT_TRUE(dungeon_editor_system_->SetDungeonSettings(settings).ok());
|
||||
|
||||
|
||||
// Get dungeon settings
|
||||
auto settings_result = dungeon_editor_system_->GetDungeonSettings();
|
||||
ASSERT_TRUE(settings_result.ok()) << "Failed to get dungeon settings: " << settings_result.status().message();
|
||||
|
||||
ASSERT_TRUE(settings_result.ok()) << "Failed to get dungeon settings: "
|
||||
<< settings_result.status().message();
|
||||
|
||||
const auto& retrieved_settings = settings_result.value();
|
||||
EXPECT_EQ(retrieved_settings.dungeon_id, 1);
|
||||
EXPECT_EQ(retrieved_settings.name, "Test Dungeon");
|
||||
@@ -438,31 +454,31 @@ TEST_F(DungeonEditorSystemIntegrationTest, DungeonSettingsManagement) {
|
||||
TEST_F(DungeonEditorSystemIntegrationTest, UndoRedoFunctionality) {
|
||||
// Set current room
|
||||
ASSERT_TRUE(dungeon_editor_system_->SetCurrentRoom(0x0000).ok());
|
||||
|
||||
|
||||
// Get object editor
|
||||
auto object_editor = dungeon_editor_system_->GetObjectEditor();
|
||||
ASSERT_NE(object_editor, nullptr);
|
||||
|
||||
|
||||
// Add some objects
|
||||
ASSERT_TRUE(object_editor->InsertObject(5, 5, 0x10, 0x12, 0).ok());
|
||||
ASSERT_TRUE(object_editor->InsertObject(10, 10, 0x20, 0x22, 1).ok());
|
||||
|
||||
|
||||
// Verify objects were added
|
||||
EXPECT_EQ(object_editor->GetObjectCount(), 2);
|
||||
|
||||
|
||||
// Test undo
|
||||
ASSERT_TRUE(dungeon_editor_system_->Undo().ok());
|
||||
EXPECT_EQ(object_editor->GetObjectCount(), 1);
|
||||
|
||||
|
||||
// Test redo
|
||||
ASSERT_TRUE(dungeon_editor_system_->Redo().ok());
|
||||
EXPECT_EQ(object_editor->GetObjectCount(), 2);
|
||||
|
||||
|
||||
// Test multiple undos
|
||||
ASSERT_TRUE(dungeon_editor_system_->Undo().ok());
|
||||
ASSERT_TRUE(dungeon_editor_system_->Undo().ok());
|
||||
EXPECT_EQ(object_editor->GetObjectCount(), 0);
|
||||
|
||||
|
||||
// Test multiple redos
|
||||
ASSERT_TRUE(dungeon_editor_system_->Redo().ok());
|
||||
ASSERT_TRUE(dungeon_editor_system_->Redo().ok());
|
||||
@@ -473,37 +489,39 @@ TEST_F(DungeonEditorSystemIntegrationTest, UndoRedoFunctionality) {
|
||||
TEST_F(DungeonEditorSystemIntegrationTest, ValidationFunctionality) {
|
||||
// Set current room
|
||||
ASSERT_TRUE(dungeon_editor_system_->SetCurrentRoom(0x0000).ok());
|
||||
|
||||
|
||||
// Validate room
|
||||
auto room_validation = dungeon_editor_system_->ValidateRoom(0x0000);
|
||||
ASSERT_TRUE(room_validation.ok()) << "Room validation failed: " << room_validation.message();
|
||||
|
||||
ASSERT_TRUE(room_validation.ok())
|
||||
<< "Room validation failed: " << room_validation.message();
|
||||
|
||||
// Validate dungeon
|
||||
auto dungeon_validation = dungeon_editor_system_->ValidateDungeon();
|
||||
ASSERT_TRUE(dungeon_validation.ok()) << "Dungeon validation failed: " << dungeon_validation.message();
|
||||
ASSERT_TRUE(dungeon_validation.ok())
|
||||
<< "Dungeon validation failed: " << dungeon_validation.message();
|
||||
}
|
||||
|
||||
// Test save/load functionality
|
||||
TEST_F(DungeonEditorSystemIntegrationTest, SaveLoadFunctionality) {
|
||||
// Set current room and add some objects
|
||||
ASSERT_TRUE(dungeon_editor_system_->SetCurrentRoom(0x0000).ok());
|
||||
|
||||
|
||||
auto object_editor = dungeon_editor_system_->GetObjectEditor();
|
||||
ASSERT_NE(object_editor, nullptr);
|
||||
|
||||
|
||||
ASSERT_TRUE(object_editor->InsertObject(5, 5, 0x10, 0x12, 0).ok());
|
||||
ASSERT_TRUE(object_editor->InsertObject(10, 10, 0x20, 0x22, 1).ok());
|
||||
|
||||
|
||||
// Save room
|
||||
ASSERT_TRUE(dungeon_editor_system_->SaveRoom(0x0000).ok());
|
||||
|
||||
|
||||
// Reload room
|
||||
ASSERT_TRUE(dungeon_editor_system_->ReloadRoom(0x0000).ok());
|
||||
|
||||
|
||||
// Verify objects are still there
|
||||
auto reloaded_objects = object_editor->GetObjects();
|
||||
EXPECT_EQ(reloaded_objects.size(), 2);
|
||||
|
||||
|
||||
// Save entire dungeon
|
||||
ASSERT_TRUE(dungeon_editor_system_->SaveDungeon().ok());
|
||||
}
|
||||
@@ -511,7 +529,7 @@ TEST_F(DungeonEditorSystemIntegrationTest, SaveLoadFunctionality) {
|
||||
// Test performance with multiple operations
|
||||
TEST_F(DungeonEditorSystemIntegrationTest, PerformanceTest) {
|
||||
auto start_time = std::chrono::high_resolution_clock::now();
|
||||
|
||||
|
||||
// Perform many operations
|
||||
for (int i = 0; i < 100; i++) {
|
||||
// Add sprite
|
||||
@@ -521,9 +539,9 @@ TEST_F(DungeonEditorSystemIntegrationTest, PerformanceTest) {
|
||||
sprite_data.x = i * 10;
|
||||
sprite_data.y = i * 10;
|
||||
sprite_data.layer = 0;
|
||||
|
||||
|
||||
ASSERT_TRUE(dungeon_editor_system_->AddSprite(sprite_data).ok());
|
||||
|
||||
|
||||
// Add item
|
||||
DungeonEditorSystem::ItemData item_data;
|
||||
item_data.item_id = i;
|
||||
@@ -531,17 +549,20 @@ TEST_F(DungeonEditorSystemIntegrationTest, PerformanceTest) {
|
||||
item_data.x = i * 15;
|
||||
item_data.y = i * 15;
|
||||
item_data.room_id = 0x0000;
|
||||
|
||||
|
||||
ASSERT_TRUE(dungeon_editor_system_->AddItem(item_data).ok());
|
||||
}
|
||||
|
||||
|
||||
auto end_time = std::chrono::high_resolution_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
|
||||
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
end_time - start_time);
|
||||
|
||||
// Should complete in reasonable time (less than 5 seconds for 200 operations)
|
||||
EXPECT_LT(duration.count(), 5000) << "Performance test too slow: " << duration.count() << "ms";
|
||||
|
||||
std::cout << "Performance test: 200 operations took " << duration.count() << "ms" << std::endl;
|
||||
EXPECT_LT(duration.count(), 5000)
|
||||
<< "Performance test too slow: " << duration.count() << "ms";
|
||||
|
||||
std::cout << "Performance test: 200 operations took " << duration.count()
|
||||
<< "ms" << std::endl;
|
||||
}
|
||||
|
||||
// Test error handling
|
||||
@@ -549,26 +570,26 @@ TEST_F(DungeonEditorSystemIntegrationTest, ErrorHandling) {
|
||||
// Test with invalid room ID
|
||||
auto invalid_room = dungeon_editor_system_->GetRoom(-1);
|
||||
EXPECT_FALSE(invalid_room.ok());
|
||||
|
||||
|
||||
auto invalid_room_large = dungeon_editor_system_->GetRoom(10000);
|
||||
EXPECT_FALSE(invalid_room_large.ok());
|
||||
|
||||
|
||||
// Test with invalid sprite ID
|
||||
auto invalid_sprite = dungeon_editor_system_->GetSprite(-1);
|
||||
EXPECT_FALSE(invalid_sprite.ok());
|
||||
|
||||
|
||||
// Test with invalid item ID
|
||||
auto invalid_item = dungeon_editor_system_->GetItem(-1);
|
||||
EXPECT_FALSE(invalid_item.ok());
|
||||
|
||||
|
||||
// Test with invalid entrance ID
|
||||
auto invalid_entrance = dungeon_editor_system_->GetEntrance(-1);
|
||||
EXPECT_FALSE(invalid_entrance.ok());
|
||||
|
||||
|
||||
// Test with invalid door ID
|
||||
auto invalid_door = dungeon_editor_system_->GetDoor(-1);
|
||||
EXPECT_FALSE(invalid_door.ok());
|
||||
|
||||
|
||||
// Test with invalid chest ID
|
||||
auto invalid_chest = dungeon_editor_system_->GetChest(-1);
|
||||
EXPECT_FALSE(invalid_chest.ok());
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
// Integration tests for dungeon object rendering using ObjectDrawer
|
||||
// Updated for DungeonEditorV2 architecture - uses ObjectDrawer (production system)
|
||||
// instead of the obsolete ObjectRenderer
|
||||
// Updated for DungeonEditorV2 architecture - uses ObjectDrawer (production
|
||||
// system) instead of the obsolete ObjectRenderer
|
||||
|
||||
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
#endif
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "app/gfx/render/background_buffer.h"
|
||||
#include "app/gfx/types/snes_palette.h"
|
||||
#include "app/rom.h"
|
||||
#include "test_utils.h"
|
||||
#include "testing.h"
|
||||
#include "zelda3/dungeon/object_drawer.h"
|
||||
#include "zelda3/dungeon/room.h"
|
||||
#include "zelda3/dungeon/room_object.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
|
||||
#include "app/rom.h"
|
||||
#include "app/gfx/types/snes_palette.h"
|
||||
#include "app/gfx/render/background_buffer.h"
|
||||
#include "testing.h"
|
||||
#include "test_utils.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace test {
|
||||
|
||||
/**
|
||||
* @brief Tests for ObjectDrawer with realistic dungeon scenarios
|
||||
*
|
||||
*
|
||||
* These tests validate that ObjectDrawer correctly renders dungeon objects
|
||||
* to BackgroundBuffers using pattern-based drawing routines.
|
||||
*/
|
||||
@@ -56,18 +56,19 @@ class DungeonObjectRenderingTests : public TestRomManager::BoundRomTest {
|
||||
gfx::PaletteGroup CreateTestPaletteGroup() {
|
||||
gfx::PaletteGroup group;
|
||||
gfx::SnesPalette palette;
|
||||
|
||||
|
||||
// Create standard dungeon palette
|
||||
for (int i = 0; i < 16; i++) {
|
||||
int intensity = i * 16;
|
||||
palette.AddColor(gfx::SnesColor(intensity, intensity, intensity));
|
||||
}
|
||||
|
||||
|
||||
group.AddPalette(palette);
|
||||
return group;
|
||||
}
|
||||
|
||||
zelda3::RoomObject CreateTestObject(int id, int x, int y, int size = 0x12, int layer = 0) {
|
||||
|
||||
zelda3::RoomObject CreateTestObject(int id, int x, int y, int size = 0x12,
|
||||
int layer = 0) {
|
||||
zelda3::RoomObject obj(id, x, y, size, layer);
|
||||
obj.set_rom(rom());
|
||||
obj.EnsureTilesLoaded();
|
||||
@@ -83,15 +84,15 @@ class DungeonObjectRenderingTests : public TestRomManager::BoundRomTest {
|
||||
// Test basic object drawing
|
||||
TEST_F(DungeonObjectRenderingTests, BasicObjectDrawing) {
|
||||
std::vector<zelda3::RoomObject> objects;
|
||||
objects.push_back(CreateTestObject(0x10, 5, 5, 0x12, 0)); // Wall
|
||||
objects.push_back(CreateTestObject(0x10, 5, 5, 0x12, 0)); // Wall
|
||||
objects.push_back(CreateTestObject(0x20, 10, 10, 0x22, 0)); // Floor
|
||||
|
||||
|
||||
bg1_->ClearBuffer();
|
||||
bg2_->ClearBuffer();
|
||||
|
||||
|
||||
auto status = drawer_->DrawObjectList(objects, *bg1_, *bg2_, palette_group_);
|
||||
ASSERT_TRUE(status.ok()) << "Drawing failed: " << status.message();
|
||||
|
||||
|
||||
// Verify buffers have content
|
||||
auto& bg1_bitmap = bg1_->bitmap();
|
||||
EXPECT_TRUE(bg1_bitmap.is_active());
|
||||
@@ -101,28 +102,54 @@ TEST_F(DungeonObjectRenderingTests, BasicObjectDrawing) {
|
||||
// Test objects on different layers
|
||||
TEST_F(DungeonObjectRenderingTests, MultiLayerRendering) {
|
||||
std::vector<zelda3::RoomObject> objects;
|
||||
objects.push_back(CreateTestObject(0x10, 5, 5, 0x12, 0)); // BG1
|
||||
objects.push_back(CreateTestObject(0x20, 10, 10, 0x22, 1)); // BG2
|
||||
objects.push_back(CreateTestObject(0x30, 15, 15, 0x12, 2)); // BG3
|
||||
|
||||
objects.push_back(CreateTestObject(0x10, 5, 5, 0x12, 0)); // BG1
|
||||
objects.push_back(CreateTestObject(0x20, 10, 10, 0x22, 1)); // BG2
|
||||
objects.push_back(CreateTestObject(0x30, 15, 15, 0x12, 2)); // BG3
|
||||
|
||||
bg1_->ClearBuffer();
|
||||
bg2_->ClearBuffer();
|
||||
|
||||
|
||||
auto status = drawer_->DrawObjectList(objects, *bg1_, *bg2_, palette_group_);
|
||||
ASSERT_TRUE(status.ok());
|
||||
|
||||
|
||||
// Both buffers should be active
|
||||
EXPECT_TRUE(bg1_->bitmap().is_active());
|
||||
EXPECT_TRUE(bg2_->bitmap().is_active());
|
||||
}
|
||||
|
||||
// Test that a compact buffer (preview-sized) receives pixels
|
||||
TEST_F(DungeonObjectRenderingTests, PreviewBufferRendersContent) {
|
||||
std::vector<zelda3::RoomObject> objects;
|
||||
objects.push_back(CreateTestObject(0x10, 1, 1, 0x12, 0)); // Small wall
|
||||
|
||||
gfx::BackgroundBuffer preview_bg(64, 64);
|
||||
gfx::BackgroundBuffer preview_bg2(64, 64);
|
||||
preview_bg.ClearBuffer();
|
||||
preview_bg2.ClearBuffer();
|
||||
|
||||
auto status =
|
||||
drawer_->DrawObjectList(objects, preview_bg, preview_bg2, palette_group_);
|
||||
ASSERT_TRUE(status.ok());
|
||||
|
||||
auto& bitmap = preview_bg.bitmap();
|
||||
EXPECT_TRUE(bitmap.is_active());
|
||||
const auto& data = bitmap.data();
|
||||
size_t non_zero = 0;
|
||||
for (size_t i = 0; i < data.size(); i += 16) {
|
||||
if (data[i] != 0) {
|
||||
non_zero++;
|
||||
}
|
||||
}
|
||||
EXPECT_GT(non_zero, 0u);
|
||||
}
|
||||
|
||||
// Test empty object list
|
||||
TEST_F(DungeonObjectRenderingTests, EmptyObjectList) {
|
||||
std::vector<zelda3::RoomObject> objects; // Empty
|
||||
|
||||
|
||||
bg1_->ClearBuffer();
|
||||
bg2_->ClearBuffer();
|
||||
|
||||
|
||||
auto status = drawer_->DrawObjectList(objects, *bg1_, *bg2_, palette_group_);
|
||||
// Should succeed (drawing nothing is valid)
|
||||
EXPECT_TRUE(status.ok());
|
||||
@@ -131,40 +158,42 @@ TEST_F(DungeonObjectRenderingTests, EmptyObjectList) {
|
||||
// Test large object set
|
||||
TEST_F(DungeonObjectRenderingTests, LargeObjectSet) {
|
||||
std::vector<zelda3::RoomObject> objects;
|
||||
|
||||
|
||||
// Create 100 test objects
|
||||
for (int i = 0; i < 100; i++) {
|
||||
int x = (i % 10) * 5;
|
||||
int y = (i / 10) * 5;
|
||||
objects.push_back(CreateTestObject(0x10 + (i % 20), x, y, 0x12, i % 2));
|
||||
}
|
||||
|
||||
|
||||
bg1_->ClearBuffer();
|
||||
bg2_->ClearBuffer();
|
||||
|
||||
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
auto status = drawer_->DrawObjectList(objects, *bg1_, *bg2_, palette_group_);
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
|
||||
|
||||
ASSERT_TRUE(status.ok());
|
||||
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
|
||||
|
||||
auto duration =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
|
||||
// Should complete in reasonable time
|
||||
EXPECT_LT(duration.count(), 1000) << "Rendered 100 objects in " << duration.count() << "ms";
|
||||
EXPECT_LT(duration.count(), 1000)
|
||||
<< "Rendered 100 objects in " << duration.count() << "ms";
|
||||
}
|
||||
|
||||
// Test boundary conditions
|
||||
TEST_F(DungeonObjectRenderingTests, BoundaryObjects) {
|
||||
std::vector<zelda3::RoomObject> objects;
|
||||
|
||||
|
||||
// Objects at boundaries
|
||||
objects.push_back(CreateTestObject(0x10, 0, 0, 0x12, 0)); // Origin
|
||||
objects.push_back(CreateTestObject(0x10, 63, 63, 0x12, 0)); // Max valid
|
||||
objects.push_back(CreateTestObject(0x10, 32, 32, 0x12, 0)); // Center
|
||||
|
||||
objects.push_back(CreateTestObject(0x10, 0, 0, 0x12, 0)); // Origin
|
||||
objects.push_back(CreateTestObject(0x10, 63, 63, 0x12, 0)); // Max valid
|
||||
objects.push_back(CreateTestObject(0x10, 32, 32, 0x12, 0)); // Center
|
||||
|
||||
bg1_->ClearBuffer();
|
||||
bg2_->ClearBuffer();
|
||||
|
||||
|
||||
auto status = drawer_->DrawObjectList(objects, *bg1_, *bg2_, palette_group_);
|
||||
EXPECT_TRUE(status.ok());
|
||||
}
|
||||
@@ -173,24 +202,25 @@ TEST_F(DungeonObjectRenderingTests, BoundaryObjects) {
|
||||
TEST_F(DungeonObjectRenderingTests, VariousObjectTypes) {
|
||||
// Test common object types
|
||||
std::vector<int> object_types = {
|
||||
0x00, 0x01, 0x02, 0x03, // Floor/wall objects
|
||||
0x09, 0x0A, // Diagonal objects
|
||||
0x10, 0x11, 0x12, // Standard objects
|
||||
0x20, 0x21, // Decorative objects
|
||||
0x34, // Solid block
|
||||
0x00, 0x01, 0x02, 0x03, // Floor/wall objects
|
||||
0x09, 0x0A, // Diagonal objects
|
||||
0x10, 0x11, 0x12, // Standard objects
|
||||
0x20, 0x21, // Decorative objects
|
||||
0x34, // Solid block
|
||||
};
|
||||
|
||||
|
||||
for (int obj_type : object_types) {
|
||||
std::vector<zelda3::RoomObject> objects;
|
||||
objects.push_back(CreateTestObject(obj_type, 10, 10, 0x12, 0));
|
||||
|
||||
|
||||
bg1_->ClearBuffer();
|
||||
bg2_->ClearBuffer();
|
||||
|
||||
auto status = drawer_->DrawObjectList(objects, *bg1_, *bg2_, palette_group_);
|
||||
|
||||
auto status =
|
||||
drawer_->DrawObjectList(objects, *bg1_, *bg2_, palette_group_);
|
||||
// Some object types might not be valid, that's okay
|
||||
if (!status.ok()) {
|
||||
std::cout << "Object type 0x" << std::hex << obj_type << std::dec
|
||||
std::cout << "Object type 0x" << std::hex << obj_type << std::dec
|
||||
<< " not renderable: " << status.message() << std::endl;
|
||||
}
|
||||
}
|
||||
@@ -202,15 +232,15 @@ TEST_F(DungeonObjectRenderingTests, ErrorHandling) {
|
||||
zelda3::ObjectDrawer null_drawer(nullptr);
|
||||
std::vector<zelda3::RoomObject> objects;
|
||||
objects.push_back(CreateTestObject(0x10, 5, 5));
|
||||
|
||||
|
||||
bg1_->ClearBuffer();
|
||||
bg2_->ClearBuffer();
|
||||
|
||||
auto status = null_drawer.DrawObjectList(objects, *bg1_, *bg2_, palette_group_);
|
||||
|
||||
auto status =
|
||||
null_drawer.DrawObjectList(objects, *bg1_, *bg2_, palette_group_);
|
||||
EXPECT_FALSE(status.ok());
|
||||
EXPECT_EQ(status.code(), absl::StatusCode::kFailedPrecondition);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace yaze
|
||||
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
// Integration tests for dungeon object rendering using ObjectDrawer
|
||||
// Updated for DungeonEditorV2 architecture - uses ObjectDrawer (production system)
|
||||
// instead of the obsolete ObjectRenderer
|
||||
// Updated for DungeonEditorV2 architecture - uses ObjectDrawer (production
|
||||
// system) instead of the obsolete ObjectRenderer
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "app/gfx/background_buffer.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/rom.h"
|
||||
#include "test_utils.h"
|
||||
#include "testing.h"
|
||||
#include "zelda3/dungeon/object_drawer.h"
|
||||
#include "zelda3/dungeon/room.h"
|
||||
#include "zelda3/dungeon/room_object.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
|
||||
#include "app/rom.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/background_buffer.h"
|
||||
#include "testing.h"
|
||||
#include "test_utils.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace test {
|
||||
|
||||
/**
|
||||
* @brief Tests for ObjectDrawer with realistic dungeon scenarios
|
||||
*
|
||||
*
|
||||
* These tests validate that ObjectDrawer correctly renders dungeon objects
|
||||
* to BackgroundBuffers using pattern-based drawing routines.
|
||||
*/
|
||||
@@ -52,18 +52,19 @@ class DungeonObjectRenderingTests : public TestRomManager::BoundRomTest {
|
||||
gfx::PaletteGroup CreateTestPaletteGroup() {
|
||||
gfx::PaletteGroup group;
|
||||
gfx::SnesPalette palette;
|
||||
|
||||
|
||||
// Create standard dungeon palette
|
||||
for (int i = 0; i < 16; i++) {
|
||||
int intensity = i * 16;
|
||||
palette.AddColor(gfx::SnesColor(intensity, intensity, intensity));
|
||||
}
|
||||
|
||||
|
||||
group.AddPalette(palette);
|
||||
return group;
|
||||
}
|
||||
|
||||
zelda3::RoomObject CreateTestObject(int id, int x, int y, int size = 0x12, int layer = 0) {
|
||||
|
||||
zelda3::RoomObject CreateTestObject(int id, int x, int y, int size = 0x12,
|
||||
int layer = 0) {
|
||||
zelda3::RoomObject obj(id, x, y, size, layer);
|
||||
obj.set_rom(rom());
|
||||
obj.EnsureTilesLoaded();
|
||||
@@ -79,15 +80,15 @@ class DungeonObjectRenderingTests : public TestRomManager::BoundRomTest {
|
||||
// Test basic object drawing
|
||||
TEST_F(DungeonObjectRenderingTests, BasicObjectDrawing) {
|
||||
std::vector<zelda3::RoomObject> objects;
|
||||
objects.push_back(CreateTestObject(0x10, 5, 5, 0x12, 0)); // Wall
|
||||
objects.push_back(CreateTestObject(0x10, 5, 5, 0x12, 0)); // Wall
|
||||
objects.push_back(CreateTestObject(0x20, 10, 10, 0x22, 0)); // Floor
|
||||
|
||||
|
||||
bg1_->ClearBuffer();
|
||||
bg2_->ClearBuffer();
|
||||
|
||||
|
||||
auto status = drawer_->DrawObjectList(objects, *bg1_, *bg2_, palette_group_);
|
||||
ASSERT_TRUE(status.ok()) << "Drawing failed: " << status.message();
|
||||
|
||||
|
||||
// Verify buffers have content
|
||||
auto& bg1_bitmap = bg1_->bitmap();
|
||||
EXPECT_TRUE(bg1_bitmap.is_active());
|
||||
@@ -97,16 +98,16 @@ TEST_F(DungeonObjectRenderingTests, BasicObjectDrawing) {
|
||||
// Test objects on different layers
|
||||
TEST_F(DungeonObjectRenderingTests, MultiLayerRendering) {
|
||||
std::vector<zelda3::RoomObject> objects;
|
||||
objects.push_back(CreateTestObject(0x10, 5, 5, 0x12, 0)); // BG1
|
||||
objects.push_back(CreateTestObject(0x20, 10, 10, 0x22, 1)); // BG2
|
||||
objects.push_back(CreateTestObject(0x30, 15, 15, 0x12, 2)); // BG3
|
||||
|
||||
objects.push_back(CreateTestObject(0x10, 5, 5, 0x12, 0)); // BG1
|
||||
objects.push_back(CreateTestObject(0x20, 10, 10, 0x22, 1)); // BG2
|
||||
objects.push_back(CreateTestObject(0x30, 15, 15, 0x12, 2)); // BG3
|
||||
|
||||
bg1_->ClearBuffer();
|
||||
bg2_->ClearBuffer();
|
||||
|
||||
|
||||
auto status = drawer_->DrawObjectList(objects, *bg1_, *bg2_, palette_group_);
|
||||
ASSERT_TRUE(status.ok());
|
||||
|
||||
|
||||
// Both buffers should be active
|
||||
EXPECT_TRUE(bg1_->bitmap().is_active());
|
||||
EXPECT_TRUE(bg2_->bitmap().is_active());
|
||||
@@ -115,10 +116,10 @@ TEST_F(DungeonObjectRenderingTests, MultiLayerRendering) {
|
||||
// Test empty object list
|
||||
TEST_F(DungeonObjectRenderingTests, EmptyObjectList) {
|
||||
std::vector<zelda3::RoomObject> objects; // Empty
|
||||
|
||||
|
||||
bg1_->ClearBuffer();
|
||||
bg2_->ClearBuffer();
|
||||
|
||||
|
||||
auto status = drawer_->DrawObjectList(objects, *bg1_, *bg2_, palette_group_);
|
||||
// Should succeed (drawing nothing is valid)
|
||||
EXPECT_TRUE(status.ok());
|
||||
@@ -127,40 +128,42 @@ TEST_F(DungeonObjectRenderingTests, EmptyObjectList) {
|
||||
// Test large object set
|
||||
TEST_F(DungeonObjectRenderingTests, LargeObjectSet) {
|
||||
std::vector<zelda3::RoomObject> objects;
|
||||
|
||||
|
||||
// Create 100 test objects
|
||||
for (int i = 0; i < 100; i++) {
|
||||
int x = (i % 10) * 5;
|
||||
int y = (i / 10) * 5;
|
||||
objects.push_back(CreateTestObject(0x10 + (i % 20), x, y, 0x12, i % 2));
|
||||
}
|
||||
|
||||
|
||||
bg1_->ClearBuffer();
|
||||
bg2_->ClearBuffer();
|
||||
|
||||
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
auto status = drawer_->DrawObjectList(objects, *bg1_, *bg2_, palette_group_);
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
|
||||
|
||||
ASSERT_TRUE(status.ok());
|
||||
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
|
||||
|
||||
auto duration =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
|
||||
// Should complete in reasonable time
|
||||
EXPECT_LT(duration.count(), 1000) << "Rendered 100 objects in " << duration.count() << "ms";
|
||||
EXPECT_LT(duration.count(), 1000)
|
||||
<< "Rendered 100 objects in " << duration.count() << "ms";
|
||||
}
|
||||
|
||||
// Test boundary conditions
|
||||
TEST_F(DungeonObjectRenderingTests, BoundaryObjects) {
|
||||
std::vector<zelda3::RoomObject> objects;
|
||||
|
||||
|
||||
// Objects at boundaries
|
||||
objects.push_back(CreateTestObject(0x10, 0, 0, 0x12, 0)); // Origin
|
||||
objects.push_back(CreateTestObject(0x10, 63, 63, 0x12, 0)); // Max valid
|
||||
objects.push_back(CreateTestObject(0x10, 32, 32, 0x12, 0)); // Center
|
||||
|
||||
objects.push_back(CreateTestObject(0x10, 0, 0, 0x12, 0)); // Origin
|
||||
objects.push_back(CreateTestObject(0x10, 63, 63, 0x12, 0)); // Max valid
|
||||
objects.push_back(CreateTestObject(0x10, 32, 32, 0x12, 0)); // Center
|
||||
|
||||
bg1_->ClearBuffer();
|
||||
bg2_->ClearBuffer();
|
||||
|
||||
|
||||
auto status = drawer_->DrawObjectList(objects, *bg1_, *bg2_, palette_group_);
|
||||
EXPECT_TRUE(status.ok());
|
||||
}
|
||||
@@ -169,24 +172,25 @@ TEST_F(DungeonObjectRenderingTests, BoundaryObjects) {
|
||||
TEST_F(DungeonObjectRenderingTests, VariousObjectTypes) {
|
||||
// Test common object types
|
||||
std::vector<int> object_types = {
|
||||
0x00, 0x01, 0x02, 0x03, // Floor/wall objects
|
||||
0x09, 0x0A, // Diagonal objects
|
||||
0x10, 0x11, 0x12, // Standard objects
|
||||
0x20, 0x21, // Decorative objects
|
||||
0x34, // Solid block
|
||||
0x00, 0x01, 0x02, 0x03, // Floor/wall objects
|
||||
0x09, 0x0A, // Diagonal objects
|
||||
0x10, 0x11, 0x12, // Standard objects
|
||||
0x20, 0x21, // Decorative objects
|
||||
0x34, // Solid block
|
||||
};
|
||||
|
||||
|
||||
for (int obj_type : object_types) {
|
||||
std::vector<zelda3::RoomObject> objects;
|
||||
objects.push_back(CreateTestObject(obj_type, 10, 10, 0x12, 0));
|
||||
|
||||
|
||||
bg1_->ClearBuffer();
|
||||
bg2_->ClearBuffer();
|
||||
|
||||
auto status = drawer_->DrawObjectList(objects, *bg1_, *bg2_, palette_group_);
|
||||
|
||||
auto status =
|
||||
drawer_->DrawObjectList(objects, *bg1_, *bg2_, palette_group_);
|
||||
// Some object types might not be valid, that's okay
|
||||
if (!status.ok()) {
|
||||
std::cout << "Object type 0x" << std::hex << obj_type << std::dec
|
||||
std::cout << "Object type 0x" << std::hex << obj_type << std::dec
|
||||
<< " not renderable: " << status.message() << std::endl;
|
||||
}
|
||||
}
|
||||
@@ -198,15 +202,15 @@ TEST_F(DungeonObjectRenderingTests, ErrorHandling) {
|
||||
zelda3::ObjectDrawer null_drawer(nullptr);
|
||||
std::vector<zelda3::RoomObject> objects;
|
||||
objects.push_back(CreateTestObject(0x10, 5, 5));
|
||||
|
||||
|
||||
bg1_->ClearBuffer();
|
||||
bg2_->ClearBuffer();
|
||||
|
||||
auto status = null_drawer.DrawObjectList(objects, *bg1_, *bg2_, palette_group_);
|
||||
|
||||
auto status =
|
||||
null_drawer.DrawObjectList(objects, *bg1_, *bg2_, palette_group_);
|
||||
EXPECT_FALSE(status.ok());
|
||||
EXPECT_EQ(status.code(), absl::StatusCode::kFailedPrecondition);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace yaze
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/gfx/background_buffer.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/rom.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "zelda3/dungeon/object_drawer.h"
|
||||
#include "zelda3/dungeon/object_parser.h"
|
||||
#include "zelda3/dungeon/room.h"
|
||||
@@ -18,70 +17,68 @@ class DungeonRenderingIntegrationTest : public ::testing::Test {
|
||||
// Create a mock ROM for testing
|
||||
rom_ = std::make_unique<Rom>();
|
||||
// Initialize with minimal ROM data for testing
|
||||
std::vector<uint8_t> mock_rom_data(1024 * 1024, 0); // 1MB mock ROM
|
||||
std::vector<uint8_t> mock_rom_data(1024 * 1024, 0); // 1MB mock ROM
|
||||
rom_->LoadFromData(mock_rom_data);
|
||||
|
||||
|
||||
// Create test rooms
|
||||
room_0x00_ = CreateTestRoom(0x00); // Link's House
|
||||
room_0x01_ = CreateTestRoom(0x01); // Another test room
|
||||
room_0x00_ = CreateTestRoom(0x00); // Link's House
|
||||
room_0x01_ = CreateTestRoom(0x01); // Another test room
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
rom_.reset();
|
||||
}
|
||||
void TearDown() override { rom_.reset(); }
|
||||
|
||||
std::unique_ptr<Rom> rom_;
|
||||
|
||||
|
||||
// Create a test room with various objects
|
||||
Room CreateTestRoom(int room_id) {
|
||||
Room room(room_id, rom_.get());
|
||||
|
||||
|
||||
// Add some test objects to the room
|
||||
std::vector<RoomObject> objects;
|
||||
|
||||
|
||||
// Add floor objects (object 0x00)
|
||||
objects.emplace_back(0x00, 5, 5, 3, 0); // Horizontal floor
|
||||
objects.emplace_back(0x00, 10, 10, 5, 0); // Another floor section
|
||||
|
||||
objects.emplace_back(0x00, 5, 5, 3, 0); // Horizontal floor
|
||||
objects.emplace_back(0x00, 10, 10, 5, 0); // Another floor section
|
||||
|
||||
// Add wall objects (object 0x01)
|
||||
objects.emplace_back(0x01, 15, 15, 2, 0); // Vertical wall
|
||||
objects.emplace_back(0x01, 20, 20, 4, 1); // Horizontal wall on BG2
|
||||
|
||||
objects.emplace_back(0x01, 15, 15, 2, 0); // Vertical wall
|
||||
objects.emplace_back(0x01, 20, 20, 4, 1); // Horizontal wall on BG2
|
||||
|
||||
// Add diagonal stairs (object 0x09)
|
||||
objects.emplace_back(0x09, 25, 25, 6, 0); // Diagonal stairs
|
||||
|
||||
objects.emplace_back(0x09, 25, 25, 6, 0); // Diagonal stairs
|
||||
|
||||
// Add solid blocks (object 0x34)
|
||||
objects.emplace_back(0x34, 30, 30, 1, 0); // Solid block
|
||||
objects.emplace_back(0x34, 35, 35, 2, 1); // Another solid block on BG2
|
||||
|
||||
objects.emplace_back(0x34, 30, 30, 1, 0); // Solid block
|
||||
objects.emplace_back(0x34, 35, 35, 2, 1); // Another solid block on BG2
|
||||
|
||||
// Set ROM for all objects
|
||||
for (auto& obj : objects) {
|
||||
obj.set_rom(rom_.get());
|
||||
}
|
||||
|
||||
|
||||
// Add objects to room (this would normally be done by LoadObjects)
|
||||
for (const auto& obj : objects) {
|
||||
room.AddObject(obj);
|
||||
}
|
||||
|
||||
|
||||
return room;
|
||||
}
|
||||
|
||||
|
||||
// Create a test palette
|
||||
gfx::SnesPalette CreateTestPalette() {
|
||||
gfx::SnesPalette palette;
|
||||
// Add some test colors
|
||||
palette.AddColor(gfx::SnesColor(0, 0, 0)); // Transparent
|
||||
palette.AddColor(gfx::SnesColor(255, 0, 0)); // Red
|
||||
palette.AddColor(gfx::SnesColor(0, 255, 0)); // Green
|
||||
palette.AddColor(gfx::SnesColor(0, 0, 255)); // Blue
|
||||
palette.AddColor(gfx::SnesColor(255, 255, 0)); // Yellow
|
||||
palette.AddColor(gfx::SnesColor(255, 0, 255)); // Magenta
|
||||
palette.AddColor(gfx::SnesColor(0, 255, 255)); // Cyan
|
||||
palette.AddColor(gfx::SnesColor(255, 255, 255)); // White
|
||||
palette.AddColor(gfx::SnesColor(0, 0, 0)); // Transparent
|
||||
palette.AddColor(gfx::SnesColor(255, 0, 0)); // Red
|
||||
palette.AddColor(gfx::SnesColor(0, 255, 0)); // Green
|
||||
palette.AddColor(gfx::SnesColor(0, 0, 255)); // Blue
|
||||
palette.AddColor(gfx::SnesColor(255, 255, 0)); // Yellow
|
||||
palette.AddColor(gfx::SnesColor(255, 0, 255)); // Magenta
|
||||
palette.AddColor(gfx::SnesColor(0, 255, 255)); // Cyan
|
||||
palette.AddColor(gfx::SnesColor(255, 255, 255)); // White
|
||||
return palette;
|
||||
}
|
||||
|
||||
|
||||
gfx::PaletteGroup CreateTestPaletteGroup() {
|
||||
gfx::PaletteGroup group;
|
||||
group.AddPalette(CreateTestPalette());
|
||||
@@ -96,19 +93,18 @@ class DungeonRenderingIntegrationTest : public ::testing::Test {
|
||||
// Test full room rendering with ObjectDrawer
|
||||
TEST_F(DungeonRenderingIntegrationTest, FullRoomRenderingWorks) {
|
||||
Room test_room = CreateTestRoom(0x00);
|
||||
|
||||
|
||||
// Test that room has objects
|
||||
EXPECT_GT(test_room.GetTileObjects().size(), 0);
|
||||
|
||||
|
||||
// Test ObjectDrawer can render the room
|
||||
ObjectDrawer drawer(rom_.get());
|
||||
auto palette_group = CreateTestPaletteGroup();
|
||||
|
||||
auto status = drawer.DrawObjectList(test_room.GetTileObjects(),
|
||||
test_room.bg1_buffer(),
|
||||
test_room.bg2_buffer(),
|
||||
palette_group);
|
||||
|
||||
|
||||
auto status =
|
||||
drawer.DrawObjectList(test_room.GetTileObjects(), test_room.bg1_buffer(),
|
||||
test_room.bg2_buffer(), palette_group);
|
||||
|
||||
EXPECT_TRUE(status.ok() || status.code() == absl::StatusCode::kOk);
|
||||
}
|
||||
|
||||
@@ -116,21 +112,20 @@ TEST_F(DungeonRenderingIntegrationTest, FullRoomRenderingWorks) {
|
||||
TEST_F(DungeonRenderingIntegrationTest, RoomRenderingWithDifferentPalettes) {
|
||||
Room test_room = CreateTestRoom(0x00);
|
||||
ObjectDrawer drawer(rom_.get());
|
||||
|
||||
|
||||
// Test with different palette configurations
|
||||
std::vector<gfx::PaletteGroup> palette_groups;
|
||||
|
||||
|
||||
// Create multiple palette groups
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
palette_groups.push_back(CreateTestPaletteGroup());
|
||||
}
|
||||
|
||||
|
||||
for (const auto& palette_group : palette_groups) {
|
||||
auto status = drawer.DrawObjectList(test_room.GetTileObjects(),
|
||||
test_room.bg1_buffer(),
|
||||
test_room.bg2_buffer(),
|
||||
palette_group);
|
||||
|
||||
auto status = drawer.DrawObjectList(test_room.GetTileObjects(),
|
||||
test_room.bg1_buffer(),
|
||||
test_room.bg2_buffer(), palette_group);
|
||||
|
||||
EXPECT_TRUE(status.ok() || status.code() == absl::StatusCode::kOk);
|
||||
}
|
||||
}
|
||||
@@ -140,11 +135,11 @@ TEST_F(DungeonRenderingIntegrationTest, RoomRenderingWithMultipleLayers) {
|
||||
Room test_room = CreateTestRoom(0x00);
|
||||
ObjectDrawer drawer(rom_.get());
|
||||
auto palette_group = CreateTestPaletteGroup();
|
||||
|
||||
|
||||
// Separate objects by layer
|
||||
std::vector<RoomObject> bg1_objects;
|
||||
std::vector<RoomObject> bg2_objects;
|
||||
|
||||
|
||||
for (const auto& obj : test_room.GetTileObjects()) {
|
||||
if (obj.GetLayerValue() == 0) {
|
||||
bg1_objects.push_back(obj);
|
||||
@@ -152,22 +147,18 @@ TEST_F(DungeonRenderingIntegrationTest, RoomRenderingWithMultipleLayers) {
|
||||
bg2_objects.push_back(obj);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Render BG1 objects
|
||||
if (!bg1_objects.empty()) {
|
||||
auto status = drawer.DrawObjectList(bg1_objects,
|
||||
test_room.bg1_buffer(),
|
||||
test_room.bg2_buffer(),
|
||||
palette_group);
|
||||
auto status = drawer.DrawObjectList(bg1_objects, test_room.bg1_buffer(),
|
||||
test_room.bg2_buffer(), palette_group);
|
||||
EXPECT_TRUE(status.ok() || status.code() == absl::StatusCode::kOk);
|
||||
}
|
||||
|
||||
|
||||
// Render BG2 objects
|
||||
if (!bg2_objects.empty()) {
|
||||
auto status = drawer.DrawObjectList(bg2_objects,
|
||||
test_room.bg1_buffer(),
|
||||
test_room.bg2_buffer(),
|
||||
palette_group);
|
||||
auto status = drawer.DrawObjectList(bg2_objects, test_room.bg1_buffer(),
|
||||
test_room.bg2_buffer(), palette_group);
|
||||
EXPECT_TRUE(status.ok() || status.code() == absl::StatusCode::kOk);
|
||||
}
|
||||
}
|
||||
@@ -177,20 +168,18 @@ TEST_F(DungeonRenderingIntegrationTest, RoomRenderingWithVariousObjectSizes) {
|
||||
Room test_room = CreateTestRoom(0x00);
|
||||
ObjectDrawer drawer(rom_.get());
|
||||
auto palette_group = CreateTestPaletteGroup();
|
||||
|
||||
|
||||
// Group objects by size
|
||||
std::map<int, std::vector<RoomObject>> objects_by_size;
|
||||
|
||||
|
||||
for (const auto& obj : test_room.GetTileObjects()) {
|
||||
objects_by_size[obj.size_].push_back(obj);
|
||||
}
|
||||
|
||||
|
||||
// Render objects of each size
|
||||
for (const auto& [size, objects] : objects_by_size) {
|
||||
auto status = drawer.DrawObjectList(objects,
|
||||
test_room.bg1_buffer(),
|
||||
test_room.bg2_buffer(),
|
||||
palette_group);
|
||||
auto status = drawer.DrawObjectList(objects, test_room.bg1_buffer(),
|
||||
test_room.bg2_buffer(), palette_group);
|
||||
EXPECT_TRUE(status.ok() || status.code() == absl::StatusCode::kOk);
|
||||
}
|
||||
}
|
||||
@@ -199,41 +188,41 @@ TEST_F(DungeonRenderingIntegrationTest, RoomRenderingWithVariousObjectSizes) {
|
||||
TEST_F(DungeonRenderingIntegrationTest, RoomRenderingPerformance) {
|
||||
// Create a room with many objects
|
||||
Room large_room(0x00, rom_.get());
|
||||
|
||||
|
||||
// Add many test objects
|
||||
for (int i = 0; i < 200; ++i) {
|
||||
int id = i % 65; // Cycle through object IDs 0-64
|
||||
int x = (i * 2) % 60; // Spread across buffer
|
||||
int id = i % 65; // Cycle through object IDs 0-64
|
||||
int x = (i * 2) % 60; // Spread across buffer
|
||||
int y = (i * 3) % 60;
|
||||
int size = (i % 8) + 1; // Size 1-8
|
||||
int layer = i % 2; // Alternate layers
|
||||
|
||||
int size = (i % 8) + 1; // Size 1-8
|
||||
int layer = i % 2; // Alternate layers
|
||||
|
||||
RoomObject obj(id, x, y, size, layer);
|
||||
obj.set_rom(rom_.get());
|
||||
large_room.AddObject(obj);
|
||||
}
|
||||
|
||||
|
||||
ObjectDrawer drawer(rom_.get());
|
||||
auto palette_group = CreateTestPaletteGroup();
|
||||
|
||||
|
||||
// Time the rendering operation
|
||||
auto start_time = std::chrono::high_resolution_clock::now();
|
||||
|
||||
auto status = drawer.DrawObjectList(large_room.GetTileObjects(),
|
||||
large_room.bg1_buffer(),
|
||||
large_room.bg2_buffer(),
|
||||
palette_group);
|
||||
|
||||
|
||||
auto status = drawer.DrawObjectList(large_room.GetTileObjects(),
|
||||
large_room.bg1_buffer(),
|
||||
large_room.bg2_buffer(), palette_group);
|
||||
|
||||
auto end_time = std::chrono::high_resolution_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
end_time - start_time);
|
||||
|
||||
|
||||
EXPECT_TRUE(status.ok() || status.code() == absl::StatusCode::kOk);
|
||||
|
||||
|
||||
// Should complete in reasonable time (less than 2 seconds for 200 objects)
|
||||
EXPECT_LT(duration.count(), 2000);
|
||||
|
||||
std::cout << "Rendered room with 200 objects in " << duration.count() << "ms" << std::endl;
|
||||
|
||||
std::cout << "Rendered room with 200 objects in " << duration.count() << "ms"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
// Test room rendering with edge case coordinates
|
||||
@@ -241,26 +230,24 @@ TEST_F(DungeonRenderingIntegrationTest, RoomRenderingWithEdgeCaseCoordinates) {
|
||||
Room test_room = CreateTestRoom(0x00);
|
||||
ObjectDrawer drawer(rom_.get());
|
||||
auto palette_group = CreateTestPaletteGroup();
|
||||
|
||||
|
||||
// Add objects at edge coordinates
|
||||
std::vector<RoomObject> edge_objects;
|
||||
|
||||
edge_objects.emplace_back(0x34, 0, 0, 1, 0); // Origin
|
||||
edge_objects.emplace_back(0x34, 63, 63, 1, 0); // Near buffer edge
|
||||
edge_objects.emplace_back(0x34, 32, 32, 1, 0); // Center
|
||||
edge_objects.emplace_back(0x34, 1, 1, 1, 0); // Near origin
|
||||
edge_objects.emplace_back(0x34, 62, 62, 1, 0); // Near edge
|
||||
|
||||
|
||||
edge_objects.emplace_back(0x34, 0, 0, 1, 0); // Origin
|
||||
edge_objects.emplace_back(0x34, 63, 63, 1, 0); // Near buffer edge
|
||||
edge_objects.emplace_back(0x34, 32, 32, 1, 0); // Center
|
||||
edge_objects.emplace_back(0x34, 1, 1, 1, 0); // Near origin
|
||||
edge_objects.emplace_back(0x34, 62, 62, 1, 0); // Near edge
|
||||
|
||||
// Set ROM for all objects
|
||||
for (auto& obj : edge_objects) {
|
||||
obj.set_rom(rom_.get());
|
||||
}
|
||||
|
||||
auto status = drawer.DrawObjectList(edge_objects,
|
||||
test_room.bg1_buffer(),
|
||||
test_room.bg2_buffer(),
|
||||
palette_group);
|
||||
|
||||
|
||||
auto status = drawer.DrawObjectList(edge_objects, test_room.bg1_buffer(),
|
||||
test_room.bg2_buffer(), palette_group);
|
||||
|
||||
EXPECT_TRUE(status.ok() || status.code() == absl::StatusCode::kOk);
|
||||
}
|
||||
|
||||
@@ -269,56 +256,53 @@ TEST_F(DungeonRenderingIntegrationTest, RoomRenderingWithMixedObjectTypes) {
|
||||
Room test_room = CreateTestRoom(0x00);
|
||||
ObjectDrawer drawer(rom_.get());
|
||||
auto palette_group = CreateTestPaletteGroup();
|
||||
|
||||
|
||||
// Add various object types
|
||||
std::vector<RoomObject> mixed_objects;
|
||||
|
||||
|
||||
// Floor objects
|
||||
mixed_objects.emplace_back(0x00, 5, 5, 3, 0);
|
||||
mixed_objects.emplace_back(0x01, 10, 10, 2, 0);
|
||||
|
||||
|
||||
// Wall objects
|
||||
mixed_objects.emplace_back(0x02, 15, 15, 4, 0);
|
||||
mixed_objects.emplace_back(0x03, 20, 20, 1, 1);
|
||||
|
||||
|
||||
// Diagonal objects
|
||||
mixed_objects.emplace_back(0x09, 25, 25, 5, 0);
|
||||
mixed_objects.emplace_back(0x0A, 30, 30, 3, 0);
|
||||
|
||||
|
||||
// Solid objects
|
||||
mixed_objects.emplace_back(0x34, 35, 35, 1, 0);
|
||||
mixed_objects.emplace_back(0x33, 40, 40, 2, 1);
|
||||
|
||||
|
||||
// Decorative objects
|
||||
mixed_objects.emplace_back(0x36, 45, 45, 3, 0);
|
||||
mixed_objects.emplace_back(0x38, 50, 50, 1, 0);
|
||||
|
||||
|
||||
// Set ROM for all objects
|
||||
for (auto& obj : mixed_objects) {
|
||||
obj.set_rom(rom_.get());
|
||||
}
|
||||
|
||||
auto status = drawer.DrawObjectList(mixed_objects,
|
||||
test_room.bg1_buffer(),
|
||||
test_room.bg2_buffer(),
|
||||
palette_group);
|
||||
|
||||
|
||||
auto status = drawer.DrawObjectList(mixed_objects, test_room.bg1_buffer(),
|
||||
test_room.bg2_buffer(), palette_group);
|
||||
|
||||
EXPECT_TRUE(status.ok() || status.code() == absl::StatusCode::kOk);
|
||||
}
|
||||
|
||||
// Test room rendering error handling
|
||||
TEST_F(DungeonRenderingIntegrationTest, RoomRenderingErrorHandling) {
|
||||
Room test_room = CreateTestRoom(0x00);
|
||||
|
||||
|
||||
// Test with null ROM
|
||||
ObjectDrawer null_drawer(nullptr);
|
||||
auto palette_group = CreateTestPaletteGroup();
|
||||
|
||||
auto status = null_drawer.DrawObjectList(test_room.GetTileObjects(),
|
||||
test_room.bg1_buffer(),
|
||||
test_room.bg2_buffer(),
|
||||
palette_group);
|
||||
|
||||
|
||||
auto status = null_drawer.DrawObjectList(
|
||||
test_room.GetTileObjects(), test_room.bg1_buffer(),
|
||||
test_room.bg2_buffer(), palette_group);
|
||||
|
||||
EXPECT_FALSE(status.ok());
|
||||
EXPECT_EQ(status.code(), absl::StatusCode::kFailedPrecondition);
|
||||
}
|
||||
@@ -328,26 +312,25 @@ TEST_F(DungeonRenderingIntegrationTest, RoomRenderingWithInvalidObjectData) {
|
||||
Room test_room = CreateTestRoom(0x00);
|
||||
ObjectDrawer drawer(rom_.get());
|
||||
auto palette_group = CreateTestPaletteGroup();
|
||||
|
||||
|
||||
// Create objects with invalid data
|
||||
std::vector<RoomObject> invalid_objects;
|
||||
|
||||
|
||||
invalid_objects.emplace_back(0x999, 5, 5, 1, 0); // Invalid object ID
|
||||
invalid_objects.emplace_back(0x00, -1, -1, 1, 0); // Negative coordinates
|
||||
invalid_objects.emplace_back(0x00, 100, 100, 1, 0); // Out of bounds coordinates
|
||||
invalid_objects.emplace_back(0x00, 100, 100, 1,
|
||||
0); // Out of bounds coordinates
|
||||
invalid_objects.emplace_back(0x00, 5, 5, 255, 0); // Maximum size
|
||||
|
||||
|
||||
// Set ROM for all objects
|
||||
for (auto& obj : invalid_objects) {
|
||||
obj.set_rom(rom_.get());
|
||||
}
|
||||
|
||||
|
||||
// Should handle gracefully
|
||||
auto status = drawer.DrawObjectList(invalid_objects,
|
||||
test_room.bg1_buffer(),
|
||||
test_room.bg2_buffer(),
|
||||
palette_group);
|
||||
|
||||
auto status = drawer.DrawObjectList(invalid_objects, test_room.bg1_buffer(),
|
||||
test_room.bg2_buffer(), palette_group);
|
||||
|
||||
// Should succeed or fail gracefully
|
||||
EXPECT_TRUE(status.ok() || status.code() == absl::StatusCode::kOk);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include "app/editor/message/message_data.h"
|
||||
@@ -211,23 +212,24 @@ TEST_F(MessageRomTest, BuildDictionaryEntries_CorrectSize) {
|
||||
TEST_F(MessageRomTest, ParseMessageData_CommandWithArgument_NoExtraCharacters) {
|
||||
// This test specifically checks for the bug where command arguments
|
||||
// were being incorrectly parsed as characters (e.g., capital 'A' after [W])
|
||||
// The bug was caused by using a range-based for loop while also tracking position
|
||||
|
||||
// The bug was caused by using a range-based for loop while also tracking
|
||||
// position
|
||||
|
||||
// Message: [W:01]ABC
|
||||
// Bytes: 0x6B (W command), 0x01 (argument), 0x00 (A), 0x01 (B), 0x02 (C)
|
||||
std::vector<uint8_t> data = {0x6B, 0x01, 0x00, 0x01, 0x02};
|
||||
|
||||
|
||||
editor::MessageData message;
|
||||
message.ID = 0;
|
||||
message.Address = 0;
|
||||
message.Data = data;
|
||||
|
||||
|
||||
std::vector<editor::MessageData> message_data_vector = {message};
|
||||
auto parsed = editor::ParseMessageData(message_data_vector, dictionary_);
|
||||
|
||||
|
||||
// Should be "[W:01]ABC" NOT "[W:01]BABC" or "[W:01]AABC"
|
||||
EXPECT_EQ(parsed[0], "[W:01]ABC");
|
||||
|
||||
|
||||
// The 'B' should not appear twice or be skipped
|
||||
EXPECT_EQ(parsed[0].find("BABC"), std::string::npos);
|
||||
EXPECT_EQ(parsed[0].find("AABC"), std::string::npos);
|
||||
@@ -237,20 +239,20 @@ TEST_F(MessageRomTest, ParseMessageData_MultipleCommandsWithArguments) {
|
||||
// Test multiple commands with arguments in sequence
|
||||
// [W:01][C:02]AB
|
||||
std::vector<uint8_t> data = {
|
||||
0x6B, 0x01, // [W:01] - Window border command with arg
|
||||
0x77, 0x02, // [C:02] - Color command with arg
|
||||
0x00, 0x01 // AB - Regular characters
|
||||
0x6B, 0x01, // [W:01] - Window border command with arg
|
||||
0x77, 0x02, // [C:02] - Color command with arg
|
||||
0x00, 0x01 // AB - Regular characters
|
||||
};
|
||||
|
||||
|
||||
editor::MessageData message;
|
||||
message.ID = 0;
|
||||
message.Data = data;
|
||||
|
||||
|
||||
std::vector<editor::MessageData> message_data_vector = {message};
|
||||
auto parsed = editor::ParseMessageData(message_data_vector, dictionary_);
|
||||
|
||||
|
||||
EXPECT_EQ(parsed[0], "[W:01][C:02]AB");
|
||||
|
||||
|
||||
// Make sure argument bytes (0x01, 0x02) weren't parsed as characters
|
||||
EXPECT_EQ(parsed[0].find("BAB"), std::string::npos);
|
||||
EXPECT_EQ(parsed[0].find("CAB"), std::string::npos);
|
||||
@@ -260,17 +262,17 @@ TEST_F(MessageRomTest, ParseMessageData_CommandWithoutArgument) {
|
||||
// Test command without argument followed by text
|
||||
// [K]ABC - Wait for key command (no arg) followed by ABC
|
||||
std::vector<uint8_t> data = {
|
||||
0x7E, // [K] - Wait for key (no argument)
|
||||
0x00, 0x01, 0x02 // ABC
|
||||
0x7E, // [K] - Wait for key (no argument)
|
||||
0x00, 0x01, 0x02 // ABC
|
||||
};
|
||||
|
||||
|
||||
editor::MessageData message;
|
||||
message.ID = 0;
|
||||
message.Data = data;
|
||||
|
||||
|
||||
std::vector<editor::MessageData> message_data_vector = {message};
|
||||
auto parsed = editor::ParseMessageData(message_data_vector, dictionary_);
|
||||
|
||||
|
||||
EXPECT_EQ(parsed[0], "[K]ABC");
|
||||
}
|
||||
|
||||
@@ -278,21 +280,21 @@ TEST_F(MessageRomTest, ParseMessageData_MixedCommands) {
|
||||
// Test mix of commands with and without arguments
|
||||
// [W:01]A[K]B[C:02]C
|
||||
std::vector<uint8_t> data = {
|
||||
0x6B, 0x01, // [W:01] - with arg
|
||||
0x00, // A
|
||||
0x7E, // [K] - no arg
|
||||
0x01, // B
|
||||
0x77, 0x02, // [C:02] - with arg
|
||||
0x02 // C
|
||||
0x6B, 0x01, // [W:01] - with arg
|
||||
0x00, // A
|
||||
0x7E, // [K] - no arg
|
||||
0x01, // B
|
||||
0x77, 0x02, // [C:02] - with arg
|
||||
0x02 // C
|
||||
};
|
||||
|
||||
|
||||
editor::MessageData message;
|
||||
message.ID = 0;
|
||||
message.Data = data;
|
||||
|
||||
|
||||
std::vector<editor::MessageData> message_data_vector = {message};
|
||||
auto parsed = editor::ParseMessageData(message_data_vector, dictionary_);
|
||||
|
||||
|
||||
EXPECT_EQ(parsed[0], "[W:01]A[K]B[C:02]C");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "app/rom.h"
|
||||
#include "testing.h"
|
||||
#include "zelda3/overworld/overworld.h"
|
||||
#include "zelda3/overworld/overworld_map.h"
|
||||
#include "testing.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace zelda3 {
|
||||
|
||||
/**
|
||||
* @brief Comprehensive overworld integration test that validates YAZE C++
|
||||
* implementation against ZScream C# logic and existing test infrastructure
|
||||
*
|
||||
* @brief Comprehensive overworld integration test that validates YAZE C++
|
||||
* implementation against ZScream C# logic and existing test
|
||||
* infrastructure
|
||||
*
|
||||
* This test suite:
|
||||
* 1. Validates overworld loading logic matches ZScream behavior
|
||||
* 2. Tests integration with ZSCustomOverworld versions (vanilla, v2, v3)
|
||||
@@ -28,15 +30,15 @@ class OverworldIntegrationTest : public ::testing::Test {
|
||||
#if defined(__linux__)
|
||||
GTEST_SKIP();
|
||||
#endif
|
||||
|
||||
|
||||
// Check if we should use real ROM or mock data
|
||||
const char* rom_path_env = getenv("YAZE_TEST_ROM_PATH");
|
||||
const char* skip_rom_tests = getenv("YAZE_SKIP_ROM_TESTS");
|
||||
|
||||
|
||||
if (skip_rom_tests) {
|
||||
GTEST_SKIP() << "ROM tests disabled";
|
||||
}
|
||||
|
||||
|
||||
if (rom_path_env && std::filesystem::exists(rom_path_env)) {
|
||||
// Use real ROM for testing
|
||||
rom_ = std::make_unique<Rom>();
|
||||
@@ -47,7 +49,7 @@ class OverworldIntegrationTest : public ::testing::Test {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Fall back to mock data
|
||||
use_real_rom_ = false;
|
||||
rom_ = std::make_unique<Rom>();
|
||||
@@ -63,36 +65,37 @@ class OverworldIntegrationTest : public ::testing::Test {
|
||||
|
||||
void SetupMockRomData() {
|
||||
mock_rom_data_.resize(0x200000, 0x00);
|
||||
|
||||
|
||||
// Basic ROM structure
|
||||
mock_rom_data_[0x140145] = 0xFF; // Vanilla ASM
|
||||
|
||||
mock_rom_data_[0x140145] = 0xFF; // Vanilla ASM
|
||||
|
||||
// Tile16 expansion flag
|
||||
mock_rom_data_[0x017D28] = 0x0F; // Vanilla
|
||||
|
||||
// Tile32 expansion flag
|
||||
mock_rom_data_[0x01772E] = 0x04; // Vanilla
|
||||
|
||||
mock_rom_data_[0x017D28] = 0x0F; // Vanilla
|
||||
|
||||
// Tile32 expansion flag
|
||||
mock_rom_data_[0x01772E] = 0x04; // Vanilla
|
||||
|
||||
// Basic map data
|
||||
for (int i = 0; i < 160; i++) {
|
||||
mock_rom_data_[0x012844 + i] = 0x00; // Small areas
|
||||
mock_rom_data_[0x012844 + i] = 0x00; // Small areas
|
||||
}
|
||||
|
||||
// Setup entrance data (matches ZScream Constants.OWEntranceMap/Pos/EntranceId)
|
||||
|
||||
// Setup entrance data (matches ZScream
|
||||
// Constants.OWEntranceMap/Pos/EntranceId)
|
||||
for (int i = 0; i < 129; i++) {
|
||||
mock_rom_data_[0x0DB96F + (i * 2)] = i & 0xFF; // Map ID
|
||||
mock_rom_data_[0x0DB96F + (i * 2)] = i & 0xFF; // Map ID
|
||||
mock_rom_data_[0x0DB96F + (i * 2) + 1] = (i >> 8) & 0xFF;
|
||||
mock_rom_data_[0x0DBA71 + (i * 2)] = (i * 16) & 0xFF; // Map Position
|
||||
mock_rom_data_[0x0DBA71 + (i * 2)] = (i * 16) & 0xFF; // Map Position
|
||||
mock_rom_data_[0x0DBA71 + (i * 2) + 1] = ((i * 16) >> 8) & 0xFF;
|
||||
mock_rom_data_[0x0DBB73 + i] = i & 0xFF; // Entrance ID
|
||||
mock_rom_data_[0x0DBB73 + i] = i & 0xFF; // Entrance ID
|
||||
}
|
||||
|
||||
|
||||
// Setup exit data (matches ZScream Constants.OWExit*)
|
||||
for (int i = 0; i < 0x4F; i++) {
|
||||
mock_rom_data_[0x015D8A + (i * 2)] = i & 0xFF; // Room ID
|
||||
mock_rom_data_[0x015D8A + (i * 2)] = i & 0xFF; // Room ID
|
||||
mock_rom_data_[0x015D8A + (i * 2) + 1] = (i >> 8) & 0xFF;
|
||||
mock_rom_data_[0x015E28 + i] = i & 0xFF; // Map ID
|
||||
mock_rom_data_[0x015E77 + (i * 2)] = i & 0xFF; // VRAM
|
||||
mock_rom_data_[0x015E28 + i] = i & 0xFF; // Map ID
|
||||
mock_rom_data_[0x015E77 + (i * 2)] = i & 0xFF; // VRAM
|
||||
mock_rom_data_[0x015E77 + (i * 2) + 1] = (i >> 8) & 0xFF;
|
||||
// Add other exit fields...
|
||||
}
|
||||
@@ -108,30 +111,30 @@ class OverworldIntegrationTest : public ::testing::Test {
|
||||
TEST_F(OverworldIntegrationTest, Tile32ExpansionDetection) {
|
||||
mock_rom_data_[0x01772E] = 0x04;
|
||||
mock_rom_data_[0x140145] = 0xFF;
|
||||
|
||||
|
||||
auto status = overworld_->Load(rom_.get());
|
||||
ASSERT_TRUE(status.ok());
|
||||
|
||||
|
||||
// Test expanded detection
|
||||
mock_rom_data_[0x01772E] = 0x05;
|
||||
overworld_ = std::make_unique<Overworld>(rom_.get());
|
||||
|
||||
|
||||
status = overworld_->Load(rom_.get());
|
||||
ASSERT_TRUE(status.ok());
|
||||
}
|
||||
|
||||
// Test Tile16 expansion detection
|
||||
// Test Tile16 expansion detection
|
||||
TEST_F(OverworldIntegrationTest, Tile16ExpansionDetection) {
|
||||
mock_rom_data_[0x017D28] = 0x0F;
|
||||
mock_rom_data_[0x140145] = 0xFF;
|
||||
|
||||
|
||||
auto status = overworld_->Load(rom_.get());
|
||||
ASSERT_TRUE(status.ok());
|
||||
|
||||
|
||||
// Test expanded detection
|
||||
mock_rom_data_[0x017D28] = 0x10;
|
||||
overworld_ = std::make_unique<Overworld>(rom_.get());
|
||||
|
||||
|
||||
status = overworld_->Load(rom_.get());
|
||||
ASSERT_TRUE(status.ok());
|
||||
}
|
||||
@@ -140,29 +143,30 @@ TEST_F(OverworldIntegrationTest, Tile16ExpansionDetection) {
|
||||
TEST_F(OverworldIntegrationTest, EntranceCoordinateCalculation) {
|
||||
auto status = overworld_->Load(rom_.get());
|
||||
ASSERT_TRUE(status.ok());
|
||||
|
||||
|
||||
const auto& entrances = overworld_->entrances();
|
||||
EXPECT_EQ(entrances.size(), 129);
|
||||
|
||||
|
||||
// Verify coordinate calculation matches ZScream logic:
|
||||
// int p = mapPos >> 1;
|
||||
// int x = p % 64;
|
||||
// int y = p >> 6;
|
||||
// int real_x = (x * 16) + (((mapId % 64) - (((mapId % 64) / 8) * 8)) * 512);
|
||||
// int real_y = (y * 16) + (((mapId % 64) / 8) * 512);
|
||||
|
||||
|
||||
for (int i = 0; i < std::min(10, static_cast<int>(entrances.size())); i++) {
|
||||
const auto& entrance = entrances[i];
|
||||
|
||||
uint16_t map_pos = i * 16; // Our test data
|
||||
uint16_t map_id = i; // Our test data
|
||||
|
||||
|
||||
uint16_t map_pos = i * 16; // Our test data
|
||||
uint16_t map_id = i; // Our test data
|
||||
|
||||
int position = map_pos >> 1;
|
||||
int x_coord = position % 64;
|
||||
int y_coord = position >> 6;
|
||||
int expected_x = (x_coord * 16) + (((map_id % 64) - (((map_id % 64) / 8) * 8)) * 512);
|
||||
int expected_x =
|
||||
(x_coord * 16) + (((map_id % 64) - (((map_id % 64) / 8) * 8)) * 512);
|
||||
int expected_y = (y_coord * 16) + (((map_id % 64) / 8) * 512);
|
||||
|
||||
|
||||
EXPECT_EQ(entrance.x_, expected_x);
|
||||
EXPECT_EQ(entrance.y_, expected_y);
|
||||
EXPECT_EQ(entrance.entrance_id_, i);
|
||||
@@ -174,10 +178,10 @@ TEST_F(OverworldIntegrationTest, EntranceCoordinateCalculation) {
|
||||
TEST_F(OverworldIntegrationTest, ExitDataLoading) {
|
||||
auto status = overworld_->Load(rom_.get());
|
||||
ASSERT_TRUE(status.ok());
|
||||
|
||||
|
||||
const auto& exits = overworld_->exits();
|
||||
EXPECT_EQ(exits->size(), 0x4F);
|
||||
|
||||
|
||||
// Verify exit data matches our test data
|
||||
for (int i = 0; i < std::min(5, static_cast<int>(exits->size())); i++) {
|
||||
const auto& exit = exits->at(i);
|
||||
@@ -192,19 +196,19 @@ TEST_F(OverworldIntegrationTest, ASMVersionItemLoading) {
|
||||
// Test vanilla ASM (should limit to 0x80 maps)
|
||||
mock_rom_data_[0x140145] = 0xFF;
|
||||
overworld_ = std::make_unique<Overworld>(rom_.get());
|
||||
|
||||
|
||||
auto status = overworld_->Load(rom_.get());
|
||||
ASSERT_TRUE(status.ok());
|
||||
|
||||
|
||||
const auto& items = overworld_->all_items();
|
||||
|
||||
|
||||
// Test v3+ ASM (should support all 0xA0 maps)
|
||||
mock_rom_data_[0x140145] = 0x03;
|
||||
overworld_ = std::make_unique<Overworld>(rom_.get());
|
||||
|
||||
|
||||
status = overworld_->Load(rom_.get());
|
||||
ASSERT_TRUE(status.ok());
|
||||
|
||||
|
||||
const auto& items_v3 = overworld_->all_items();
|
||||
// v3 should have more comprehensive support
|
||||
EXPECT_GE(items_v3.size(), items.size());
|
||||
@@ -214,10 +218,10 @@ TEST_F(OverworldIntegrationTest, ASMVersionItemLoading) {
|
||||
TEST_F(OverworldIntegrationTest, MapSizeAssignment) {
|
||||
auto status = overworld_->Load(rom_.get());
|
||||
ASSERT_TRUE(status.ok());
|
||||
|
||||
|
||||
const auto& maps = overworld_->overworld_maps();
|
||||
EXPECT_EQ(maps.size(), 160);
|
||||
|
||||
|
||||
// Verify all maps are initialized
|
||||
for (const auto& map : maps) {
|
||||
EXPECT_GE(map.area_size(), AreaSizeEnum::SmallArea);
|
||||
@@ -230,16 +234,16 @@ TEST_F(OverworldIntegrationTest, ZSCustomOverworldVersionIntegration) {
|
||||
if (!use_real_rom_) {
|
||||
GTEST_SKIP() << "Real ROM required for ZSCustomOverworld version testing";
|
||||
}
|
||||
|
||||
|
||||
auto status = overworld_->Load(rom_.get());
|
||||
ASSERT_TRUE(status.ok());
|
||||
|
||||
|
||||
// Check ASM version detection
|
||||
auto version_byte = rom_->ReadByte(0x140145);
|
||||
ASSERT_TRUE(version_byte.ok());
|
||||
|
||||
|
||||
uint8_t asm_version = *version_byte;
|
||||
|
||||
|
||||
if (asm_version == 0xFF) {
|
||||
// Vanilla ROM
|
||||
EXPECT_FALSE(overworld_->expanded_tile16());
|
||||
@@ -250,50 +254,53 @@ TEST_F(OverworldIntegrationTest, ZSCustomOverworldVersionIntegration) {
|
||||
EXPECT_TRUE(overworld_->expanded_tile16());
|
||||
EXPECT_TRUE(overworld_->expanded_tile32());
|
||||
}
|
||||
|
||||
|
||||
// Verify version-specific features are properly detected
|
||||
if (asm_version >= 0x03) {
|
||||
// v3 features should be available
|
||||
const auto& maps = overworld_->overworld_maps();
|
||||
EXPECT_EQ(maps.size(), 160); // All 160 maps supported in v3
|
||||
EXPECT_EQ(maps.size(), 160); // All 160 maps supported in v3
|
||||
}
|
||||
}
|
||||
|
||||
// Test compatibility with RomDependentTestSuite infrastructure
|
||||
TEST_F(OverworldIntegrationTest, RomDependentTestSuiteCompatibility) {
|
||||
if (!use_real_rom_) {
|
||||
GTEST_SKIP() << "Real ROM required for RomDependentTestSuite compatibility testing";
|
||||
GTEST_SKIP()
|
||||
<< "Real ROM required for RomDependentTestSuite compatibility testing";
|
||||
}
|
||||
|
||||
// Test that our overworld loading works with the same patterns as RomDependentTestSuite
|
||||
|
||||
// Test that our overworld loading works with the same patterns as
|
||||
// RomDependentTestSuite
|
||||
auto status = overworld_->Load(rom_.get());
|
||||
ASSERT_TRUE(status.ok());
|
||||
|
||||
|
||||
// Verify ROM-dependent features work correctly
|
||||
EXPECT_TRUE(overworld_->is_loaded());
|
||||
|
||||
|
||||
const auto& maps = overworld_->overworld_maps();
|
||||
EXPECT_EQ(maps.size(), 160);
|
||||
|
||||
|
||||
// Test that we can access the same data structures as RomDependentTestSuite
|
||||
for (int i = 0; i < std::min(10, static_cast<int>(maps.size())); i++) {
|
||||
const auto& map = maps[i];
|
||||
|
||||
|
||||
// Verify map properties are accessible
|
||||
EXPECT_GE(map.area_graphics(), 0);
|
||||
EXPECT_GE(map.main_palette(), 0);
|
||||
EXPECT_GE(map.area_size(), AreaSizeEnum::SmallArea);
|
||||
EXPECT_LE(map.area_size(), AreaSizeEnum::TallArea);
|
||||
}
|
||||
|
||||
// Test that sprite data is accessible (matches RomDependentTestSuite expectations)
|
||||
|
||||
// Test that sprite data is accessible (matches RomDependentTestSuite
|
||||
// expectations)
|
||||
const auto& sprites = overworld_->sprites(0);
|
||||
EXPECT_EQ(sprites.size(), 3); // Three game states
|
||||
|
||||
EXPECT_EQ(sprites.size(), 3); // Three game states
|
||||
|
||||
// Test that item data is accessible
|
||||
const auto& items = overworld_->all_items();
|
||||
EXPECT_GE(items.size(), 0);
|
||||
|
||||
|
||||
// Test that entrance/exit data is accessible
|
||||
const auto& entrances = overworld_->entrances();
|
||||
const auto& exits = overworld_->exits();
|
||||
@@ -305,17 +312,17 @@ TEST_F(OverworldIntegrationTest, RomDependentTestSuiteCompatibility) {
|
||||
TEST_F(OverworldIntegrationTest, ComprehensiveDataIntegrity) {
|
||||
auto status = overworld_->Load(rom_.get());
|
||||
ASSERT_TRUE(status.ok());
|
||||
|
||||
|
||||
// Verify all major data structures are properly loaded
|
||||
EXPECT_GT(overworld_->tiles16().size(), 0);
|
||||
EXPECT_GT(overworld_->tiles32_unique().size(), 0);
|
||||
|
||||
|
||||
// Verify map organization matches ZScream expectations
|
||||
const auto& map_tiles = overworld_->map_tiles();
|
||||
EXPECT_EQ(map_tiles.light_world.size(), 512);
|
||||
EXPECT_EQ(map_tiles.dark_world.size(), 512);
|
||||
EXPECT_EQ(map_tiles.special_world.size(), 512);
|
||||
|
||||
|
||||
// Verify each world has proper 512x512 tile data
|
||||
for (const auto& row : map_tiles.light_world) {
|
||||
EXPECT_EQ(row.size(), 512);
|
||||
@@ -326,16 +333,16 @@ TEST_F(OverworldIntegrationTest, ComprehensiveDataIntegrity) {
|
||||
for (const auto& row : map_tiles.special_world) {
|
||||
EXPECT_EQ(row.size(), 512);
|
||||
}
|
||||
|
||||
|
||||
// Verify overworld maps are properly initialized
|
||||
const auto& maps = overworld_->overworld_maps();
|
||||
EXPECT_EQ(maps.size(), 160);
|
||||
|
||||
|
||||
for (const auto& map : maps) {
|
||||
// TODO: Find a way to compare
|
||||
// EXPECT_TRUE(map.bitmap_data() != nullptr);
|
||||
}
|
||||
|
||||
|
||||
// Verify tile types are loaded
|
||||
const auto& tile_types = overworld_->all_tiles_types();
|
||||
EXPECT_EQ(tile_types.size(), 0x200);
|
||||
@@ -345,62 +352,64 @@ TEST_F(OverworldIntegrationTest, ComprehensiveDataIntegrity) {
|
||||
TEST_F(OverworldIntegrationTest, ZScreamCoordinateCompatibility) {
|
||||
auto status = overworld_->Load(rom_.get());
|
||||
ASSERT_TRUE(status.ok());
|
||||
|
||||
|
||||
const auto& entrances = overworld_->entrances();
|
||||
EXPECT_EQ(entrances.size(), 129);
|
||||
|
||||
|
||||
// Test coordinate calculation matches ZScream logic exactly
|
||||
for (int i = 0; i < std::min(10, static_cast<int>(entrances.size())); i++) {
|
||||
const auto& entrance = entrances[i];
|
||||
|
||||
|
||||
// ZScream coordinate calculation:
|
||||
// int p = mapPos >> 1;
|
||||
// int x = p % 64;
|
||||
// int y = p >> 6;
|
||||
// int real_x = (x * 16) + (((mapId % 64) - (((mapId % 64) / 8) * 8)) * 512);
|
||||
// int real_y = (y * 16) + (((mapId % 64) / 8) * 512);
|
||||
|
||||
// int real_x = (x * 16) + (((mapId % 64) - (((mapId % 64) / 8) * 8)) *
|
||||
// 512); int real_y = (y * 16) + (((mapId % 64) / 8) * 512);
|
||||
|
||||
uint16_t map_pos = entrance.map_pos_;
|
||||
uint16_t map_id = entrance.map_id_;
|
||||
|
||||
|
||||
int position = map_pos >> 1;
|
||||
int x_coord = position % 64;
|
||||
int y_coord = position >> 6;
|
||||
int expected_x = (x_coord * 16) + (((map_id % 64) - (((map_id % 64) / 8) * 8)) * 512);
|
||||
int expected_x =
|
||||
(x_coord * 16) + (((map_id % 64) - (((map_id % 64) / 8) * 8)) * 512);
|
||||
int expected_y = (y_coord * 16) + (((map_id % 64) / 8) * 512);
|
||||
|
||||
|
||||
EXPECT_EQ(entrance.x_, expected_x);
|
||||
EXPECT_EQ(entrance.y_, expected_y);
|
||||
}
|
||||
|
||||
|
||||
// Test hole coordinate calculation with 0x400 offset
|
||||
const auto& holes = overworld_->holes();
|
||||
EXPECT_EQ(holes.size(), 0x13);
|
||||
|
||||
|
||||
for (int i = 0; i < std::min(5, static_cast<int>(holes.size())); i++) {
|
||||
const auto& hole = holes[i];
|
||||
|
||||
|
||||
// ZScream hole coordinate calculation:
|
||||
// int p = (mapPos + 0x400) >> 1;
|
||||
// int x = p % 64;
|
||||
// int y = p >> 6;
|
||||
// int real_x = (x * 16) + (((mapId % 64) - (((mapId % 64) / 8) * 8)) * 512);
|
||||
// int real_y = (y * 16) + (((mapId % 64) / 8) * 512);
|
||||
|
||||
// int real_x = (x * 16) + (((mapId % 64) - (((mapId % 64) / 8) * 8)) *
|
||||
// 512); int real_y = (y * 16) + (((mapId % 64) / 8) * 512);
|
||||
|
||||
uint16_t map_pos = hole.map_pos_;
|
||||
uint16_t map_id = hole.map_id_;
|
||||
|
||||
|
||||
int position = map_pos >> 1;
|
||||
int x_coord = position % 64;
|
||||
int y_coord = position >> 6;
|
||||
int expected_x = (x_coord * 16) + (((map_id % 64) - (((map_id % 64) / 8) * 8)) * 512);
|
||||
int expected_x =
|
||||
(x_coord * 16) + (((map_id % 64) - (((map_id % 64) / 8) * 8)) * 512);
|
||||
int expected_y = (y_coord * 16) + (((map_id % 64) / 8) * 512);
|
||||
|
||||
|
||||
EXPECT_EQ(hole.x_, expected_x);
|
||||
EXPECT_EQ(hole.y_, expected_y);
|
||||
EXPECT_TRUE(hole.is_hole_);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace zelda3
|
||||
} // namespace yaze
|
||||
} // namespace zelda3
|
||||
} // namespace yaze
|
||||
@@ -1,8 +1,8 @@
|
||||
// Integration tests for Room object load/save cycle with real ROM data
|
||||
// Phase 1, Task 2.1: Full round-trip verification
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "app/rom.h"
|
||||
#include "zelda3/dungeon/room.h"
|
||||
@@ -23,22 +23,22 @@ class RoomIntegrationTest : public ::testing::Test {
|
||||
void SetUp() override {
|
||||
// Load the ROM file
|
||||
rom_ = std::make_unique<Rom>();
|
||||
|
||||
|
||||
// Check if ROM file exists
|
||||
const char* rom_path = std::getenv("YAZE_TEST_ROM_PATH");
|
||||
if (!rom_path) {
|
||||
rom_path = "zelda3.sfc";
|
||||
}
|
||||
|
||||
|
||||
auto status = rom_->LoadFromFile(rom_path);
|
||||
if (!status.ok()) {
|
||||
GTEST_SKIP() << "ROM file not available: " << status.message();
|
||||
}
|
||||
|
||||
|
||||
// Create backup of ROM data for restoration after tests
|
||||
original_rom_data_ = rom_->vector();
|
||||
}
|
||||
|
||||
|
||||
void TearDown() override {
|
||||
// Restore original ROM data
|
||||
if (rom_ && !original_rom_data_.empty()) {
|
||||
@@ -47,7 +47,7 @@ class RoomIntegrationTest : public ::testing::Test {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<Rom> rom_;
|
||||
std::vector<uint8_t> original_rom_data_;
|
||||
};
|
||||
@@ -59,34 +59,34 @@ class RoomIntegrationTest : public ::testing::Test {
|
||||
TEST_F(RoomIntegrationTest, BasicLoadSaveRoundTrip) {
|
||||
// Load room 0 (Hyrule Castle Entrance)
|
||||
Room room1(0x00, rom_.get());
|
||||
|
||||
|
||||
// Get original object count
|
||||
size_t original_count = room1.GetTileObjects().size();
|
||||
ASSERT_GT(original_count, 0) << "Room should have objects";
|
||||
|
||||
|
||||
// Store original objects
|
||||
auto original_objects = room1.GetTileObjects();
|
||||
|
||||
|
||||
// Save the room (should write same data back)
|
||||
auto save_status = room1.SaveObjects();
|
||||
ASSERT_TRUE(save_status.ok()) << save_status.message();
|
||||
|
||||
|
||||
// Load the room again
|
||||
Room room2(0x00, rom_.get());
|
||||
|
||||
|
||||
// Verify object count matches
|
||||
EXPECT_EQ(room2.GetTileObjects().size(), original_count);
|
||||
|
||||
|
||||
// Verify each object matches
|
||||
auto reloaded_objects = room2.GetTileObjects();
|
||||
ASSERT_EQ(reloaded_objects.size(), original_objects.size());
|
||||
|
||||
|
||||
for (size_t i = 0; i < original_objects.size(); i++) {
|
||||
SCOPED_TRACE("Object " + std::to_string(i));
|
||||
|
||||
|
||||
const auto& orig = original_objects[i];
|
||||
const auto& reload = reloaded_objects[i];
|
||||
|
||||
|
||||
EXPECT_EQ(reload.id_, orig.id_) << "ID mismatch";
|
||||
EXPECT_EQ(reload.x(), orig.x()) << "X position mismatch";
|
||||
EXPECT_EQ(reload.y(), orig.y()) << "Y position mismatch";
|
||||
@@ -102,33 +102,34 @@ TEST_F(RoomIntegrationTest, BasicLoadSaveRoundTrip) {
|
||||
TEST_F(RoomIntegrationTest, MultiRoomLoadSaveRoundTrip) {
|
||||
// Test several different rooms to ensure broad coverage
|
||||
std::vector<int> test_rooms = {0x00, 0x01, 0x02, 0x10, 0x20};
|
||||
|
||||
|
||||
for (int room_id : test_rooms) {
|
||||
SCOPED_TRACE("Room " + std::to_string(room_id));
|
||||
|
||||
|
||||
// Load room
|
||||
Room room1(room_id, rom_.get());
|
||||
auto original_objects = room1.GetTileObjects();
|
||||
|
||||
|
||||
if (original_objects.empty()) {
|
||||
continue; // Skip empty rooms
|
||||
continue; // Skip empty rooms
|
||||
}
|
||||
|
||||
|
||||
// Save objects
|
||||
auto save_status = room1.SaveObjects();
|
||||
ASSERT_TRUE(save_status.ok()) << save_status.message();
|
||||
|
||||
|
||||
// Reload and verify
|
||||
Room room2(room_id, rom_.get());
|
||||
auto reloaded_objects = room2.GetTileObjects();
|
||||
|
||||
|
||||
EXPECT_EQ(reloaded_objects.size(), original_objects.size());
|
||||
|
||||
|
||||
// Verify objects match
|
||||
for (size_t i = 0; i < std::min(original_objects.size(), reloaded_objects.size()); i++) {
|
||||
for (size_t i = 0;
|
||||
i < std::min(original_objects.size(), reloaded_objects.size()); i++) {
|
||||
const auto& orig = original_objects[i];
|
||||
const auto& reload = reloaded_objects[i];
|
||||
|
||||
|
||||
EXPECT_EQ(reload.id_, orig.id_);
|
||||
EXPECT_EQ(reload.x(), orig.x());
|
||||
EXPECT_EQ(reload.y(), orig.y());
|
||||
@@ -145,36 +146,48 @@ TEST_F(RoomIntegrationTest, MultiRoomLoadSaveRoundTrip) {
|
||||
TEST_F(RoomIntegrationTest, LayerPreservation) {
|
||||
// Load a room known to have multiple layers
|
||||
Room room(0x01, rom_.get());
|
||||
|
||||
|
||||
auto objects = room.GetTileObjects();
|
||||
ASSERT_GT(objects.size(), 0);
|
||||
|
||||
|
||||
// Count objects per layer
|
||||
int layer0_count = 0, layer1_count = 0, layer2_count = 0;
|
||||
for (const auto& obj : objects) {
|
||||
switch (obj.GetLayerValue()) {
|
||||
case 0: layer0_count++; break;
|
||||
case 1: layer1_count++; break;
|
||||
case 2: layer2_count++; break;
|
||||
case 0:
|
||||
layer0_count++;
|
||||
break;
|
||||
case 1:
|
||||
layer1_count++;
|
||||
break;
|
||||
case 2:
|
||||
layer2_count++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Save and reload
|
||||
ASSERT_TRUE(room.SaveObjects().ok());
|
||||
|
||||
|
||||
Room room2(0x01, rom_.get());
|
||||
auto reloaded = room2.GetTileObjects();
|
||||
|
||||
|
||||
// Verify layer counts match
|
||||
int reload_layer0 = 0, reload_layer1 = 0, reload_layer2 = 0;
|
||||
for (const auto& obj : reloaded) {
|
||||
switch (obj.GetLayerValue()) {
|
||||
case 0: reload_layer0++; break;
|
||||
case 1: reload_layer1++; break;
|
||||
case 2: reload_layer2++; break;
|
||||
case 0:
|
||||
reload_layer0++;
|
||||
break;
|
||||
case 1:
|
||||
reload_layer1++;
|
||||
break;
|
||||
case 2:
|
||||
reload_layer2++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
EXPECT_EQ(reload_layer0, layer0_count);
|
||||
EXPECT_EQ(reload_layer1, layer1_count);
|
||||
EXPECT_EQ(reload_layer2, layer2_count);
|
||||
@@ -186,15 +199,15 @@ TEST_F(RoomIntegrationTest, LayerPreservation) {
|
||||
|
||||
TEST_F(RoomIntegrationTest, ObjectTypeDistribution) {
|
||||
Room room(0x00, rom_.get());
|
||||
|
||||
|
||||
auto objects = room.GetTileObjects();
|
||||
ASSERT_GT(objects.size(), 0);
|
||||
|
||||
|
||||
// Count object types
|
||||
int type1_count = 0; // ID < 0x100
|
||||
int type2_count = 0; // ID 0x100-0x13F
|
||||
int type3_count = 0; // ID >= 0xF00
|
||||
|
||||
|
||||
for (const auto& obj : objects) {
|
||||
if (obj.id_ >= 0xF00) {
|
||||
type3_count++;
|
||||
@@ -204,13 +217,13 @@ TEST_F(RoomIntegrationTest, ObjectTypeDistribution) {
|
||||
type1_count++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Save and reload
|
||||
ASSERT_TRUE(room.SaveObjects().ok());
|
||||
|
||||
|
||||
Room room2(0x00, rom_.get());
|
||||
auto reloaded = room2.GetTileObjects();
|
||||
|
||||
|
||||
// Verify type distribution matches
|
||||
int reload_type1 = 0, reload_type2 = 0, reload_type3 = 0;
|
||||
for (const auto& obj : reloaded) {
|
||||
@@ -222,7 +235,7 @@ TEST_F(RoomIntegrationTest, ObjectTypeDistribution) {
|
||||
reload_type1++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
EXPECT_EQ(reload_type1, type1_count);
|
||||
EXPECT_EQ(reload_type2, type2_count);
|
||||
EXPECT_EQ(reload_type3, type3_count);
|
||||
@@ -235,54 +248,56 @@ TEST_F(RoomIntegrationTest, ObjectTypeDistribution) {
|
||||
TEST_F(RoomIntegrationTest, BinaryDataExactMatch) {
|
||||
// This test verifies that saving doesn't change ROM data
|
||||
// when no modifications are made
|
||||
|
||||
|
||||
Room room(0x02, rom_.get());
|
||||
|
||||
|
||||
// Get the ROM location where objects are stored
|
||||
auto rom_data = rom_->vector();
|
||||
int object_pointer = (rom_data[0x874C + 2] << 16) +
|
||||
(rom_data[0x874C + 1] << 8) +
|
||||
(rom_data[0x874C]);
|
||||
(rom_data[0x874C + 1] << 8) + (rom_data[0x874C]);
|
||||
object_pointer = SnesToPc(object_pointer);
|
||||
|
||||
|
||||
int room_address = object_pointer + (0x02 * 3);
|
||||
int tile_address = (rom_data[room_address + 2] << 16) +
|
||||
(rom_data[room_address + 1] << 8) +
|
||||
rom_data[room_address];
|
||||
(rom_data[room_address + 1] << 8) + rom_data[room_address];
|
||||
int objects_location = SnesToPc(tile_address);
|
||||
|
||||
|
||||
// Read original bytes (up to 500 bytes should cover most rooms)
|
||||
std::vector<uint8_t> original_bytes;
|
||||
for (int i = 0; i < 500 && objects_location + i < (int)rom_data.size(); i++) {
|
||||
original_bytes.push_back(rom_data[objects_location + i]);
|
||||
// Stop at final terminator
|
||||
if (i > 0 && original_bytes[i] == 0xFF && original_bytes[i-1] == 0xFF) {
|
||||
if (i > 0 && original_bytes[i] == 0xFF && original_bytes[i - 1] == 0xFF) {
|
||||
// Check if this is the final terminator (3rd layer end)
|
||||
bool might_be_final = true;
|
||||
for (int j = i - 10; j < i - 1; j += 2) {
|
||||
if (j >= 0 && original_bytes[j] == 0xFF && original_bytes[j+1] == 0xFF) {
|
||||
if (j >= 0 && original_bytes[j] == 0xFF &&
|
||||
original_bytes[j + 1] == 0xFF) {
|
||||
// Found another FF FF marker, keep going
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (might_be_final) break;
|
||||
if (might_be_final)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Save objects (should write identical data)
|
||||
ASSERT_TRUE(room.SaveObjects().ok());
|
||||
|
||||
|
||||
// Read bytes after save
|
||||
rom_data = rom_->vector();
|
||||
std::vector<uint8_t> saved_bytes;
|
||||
for (size_t i = 0; i < original_bytes.size() && objects_location + i < rom_data.size(); i++) {
|
||||
for (size_t i = 0;
|
||||
i < original_bytes.size() && objects_location + i < rom_data.size();
|
||||
i++) {
|
||||
saved_bytes.push_back(rom_data[objects_location + i]);
|
||||
}
|
||||
|
||||
|
||||
// Verify binary match
|
||||
ASSERT_EQ(saved_bytes.size(), original_bytes.size());
|
||||
for (size_t i = 0; i < original_bytes.size(); i++) {
|
||||
EXPECT_EQ(saved_bytes[i], original_bytes[i])
|
||||
EXPECT_EQ(saved_bytes[i], original_bytes[i])
|
||||
<< "Byte mismatch at offset " << i;
|
||||
}
|
||||
}
|
||||
@@ -294,24 +309,27 @@ TEST_F(RoomIntegrationTest, BinaryDataExactMatch) {
|
||||
TEST_F(RoomIntegrationTest, KnownRoomData) {
|
||||
// Room 0x00 (Hyrule Castle Entrance) - verify known objects exist
|
||||
Room room(0x00, rom_.get());
|
||||
|
||||
|
||||
auto objects = room.GetTileObjects();
|
||||
ASSERT_GT(objects.size(), 0) << "Room 0x00 should have objects";
|
||||
|
||||
|
||||
// Verify we can find common object types
|
||||
bool found_type1 = false;
|
||||
bool found_layer0 = false;
|
||||
bool found_layer1 = false;
|
||||
|
||||
|
||||
for (const auto& obj : objects) {
|
||||
if (obj.id_ < 0x100) found_type1 = true;
|
||||
if (obj.GetLayerValue() == 0) found_layer0 = true;
|
||||
if (obj.GetLayerValue() == 1) found_layer1 = true;
|
||||
if (obj.id_ < 0x100)
|
||||
found_type1 = true;
|
||||
if (obj.GetLayerValue() == 0)
|
||||
found_layer0 = true;
|
||||
if (obj.GetLayerValue() == 1)
|
||||
found_layer1 = true;
|
||||
}
|
||||
|
||||
|
||||
EXPECT_TRUE(found_type1) << "Should have Type 1 objects";
|
||||
EXPECT_TRUE(found_layer0) << "Should have Layer 0 objects";
|
||||
|
||||
|
||||
// Verify coordinates are in valid range (0-63)
|
||||
for (const auto& obj : objects) {
|
||||
EXPECT_GE(obj.x(), 0);
|
||||
@@ -324,4 +342,3 @@ TEST_F(RoomIntegrationTest, KnownRoomData) {
|
||||
} // namespace test
|
||||
} // namespace zelda3
|
||||
} // namespace yaze
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include "app/rom.h"
|
||||
#include "zelda3/overworld/overworld.h"
|
||||
@@ -12,23 +13,25 @@ namespace yaze {
|
||||
namespace zelda3 {
|
||||
|
||||
class SpritePositionTest : public ::testing::Test {
|
||||
protected:
|
||||
protected:
|
||||
void SetUp() override {
|
||||
// Try to load a vanilla ROM for testing
|
||||
rom_ = std::make_unique<Rom>();
|
||||
std::string rom_path = "bin/zelda3.sfc";
|
||||
|
||||
|
||||
// Check if ROM exists in build directory
|
||||
std::ifstream rom_file(rom_path);
|
||||
if (rom_file.good()) {
|
||||
ASSERT_TRUE(rom_->LoadFromFile(rom_path).ok()) << "Failed to load ROM from " << rom_path;
|
||||
ASSERT_TRUE(rom_->LoadFromFile(rom_path).ok())
|
||||
<< "Failed to load ROM from " << rom_path;
|
||||
} else {
|
||||
// Skip test if ROM not found
|
||||
GTEST_SKIP() << "ROM file not found at " << rom_path;
|
||||
}
|
||||
|
||||
|
||||
overworld_ = std::make_unique<Overworld>(rom_.get());
|
||||
ASSERT_TRUE(overworld_->Load(rom_.get()).ok()) << "Failed to load overworld";
|
||||
ASSERT_TRUE(overworld_->Load(rom_.get()).ok())
|
||||
<< "Failed to load overworld";
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
@@ -47,24 +50,28 @@ TEST_F(SpritePositionTest, SpriteCoordinateSystem) {
|
||||
const auto& sprites = overworld_->sprites(game_state);
|
||||
std::cout << "\n=== Game State " << game_state << " ===" << std::endl;
|
||||
std::cout << "Total sprites: " << sprites.size() << std::endl;
|
||||
|
||||
|
||||
int sprite_count = 0;
|
||||
for (const auto& sprite : sprites) {
|
||||
if (!sprite.deleted() && sprite_count < 10) { // Show first 10 sprites
|
||||
std::cout << "Sprite " << std::hex << std::setw(2) << std::setfill('0')
|
||||
<< static_cast<int>(sprite.id()) << " (" << const_cast<Sprite&>(sprite).name() << ")" << std::endl;
|
||||
std::cout << " Map ID: 0x" << std::hex << std::setw(2) << std::setfill('0')
|
||||
<< sprite.map_id() << std::endl;
|
||||
std::cout << " X: " << std::dec << sprite.x() << " (0x" << std::hex << sprite.x() << ")" << std::endl;
|
||||
std::cout << " Y: " << std::dec << sprite.y() << " (0x" << std::hex << sprite.y() << ")" << std::endl;
|
||||
if (!sprite.deleted() && sprite_count < 10) { // Show first 10 sprites
|
||||
std::cout << "Sprite " << std::hex << std::setw(2) << std::setfill('0')
|
||||
<< static_cast<int>(sprite.id()) << " ("
|
||||
<< const_cast<Sprite&>(sprite).name() << ")" << std::endl;
|
||||
std::cout << " Map ID: 0x" << std::hex << std::setw(2)
|
||||
<< std::setfill('0') << sprite.map_id() << std::endl;
|
||||
std::cout << " X: " << std::dec << sprite.x() << " (0x" << std::hex
|
||||
<< sprite.x() << ")" << std::endl;
|
||||
std::cout << " Y: " << std::dec << sprite.y() << " (0x" << std::hex
|
||||
<< sprite.y() << ")" << std::endl;
|
||||
std::cout << " map_x: " << std::dec << sprite.map_x() << std::endl;
|
||||
std::cout << " map_y: " << std::dec << sprite.map_y() << std::endl;
|
||||
|
||||
|
||||
// Calculate expected world ranges
|
||||
int world_start = game_state * 0x40;
|
||||
int world_end = world_start + 0x40;
|
||||
std::cout << " World range: 0x" << std::hex << world_start << " - 0x" << world_end << std::endl;
|
||||
|
||||
std::cout << " World range: 0x" << std::hex << world_start << " - 0x"
|
||||
<< world_end << std::endl;
|
||||
|
||||
sprite_count++;
|
||||
}
|
||||
}
|
||||
@@ -76,30 +83,32 @@ TEST_F(SpritePositionTest, SpriteFilteringLogic) {
|
||||
// Test the filtering logic used in DrawOverworldSprites
|
||||
for (int current_world = 0; current_world < 3; current_world++) {
|
||||
const auto& sprites = overworld_->sprites(current_world);
|
||||
|
||||
std::cout << "\n=== Testing World " << current_world << " Filtering ===" << std::endl;
|
||||
|
||||
|
||||
std::cout << "\n=== Testing World " << current_world
|
||||
<< " Filtering ===" << std::endl;
|
||||
|
||||
int visible_sprites = 0;
|
||||
int total_sprites = 0;
|
||||
|
||||
|
||||
for (const auto& sprite : sprites) {
|
||||
if (!sprite.deleted()) {
|
||||
total_sprites++;
|
||||
|
||||
|
||||
// This is the filtering logic from DrawOverworldSprites
|
||||
bool should_show = (sprite.map_id() < 0x40 + (current_world * 0x40) &&
|
||||
sprite.map_id() >= (current_world * 0x40));
|
||||
|
||||
sprite.map_id() >= (current_world * 0x40));
|
||||
|
||||
if (should_show) {
|
||||
visible_sprites++;
|
||||
std::cout << " Visible: Sprite 0x" << std::hex << static_cast<int>(sprite.id())
|
||||
<< " on map 0x" << sprite.map_id() << " at ("
|
||||
<< std::dec << sprite.x() << ", " << sprite.y() << ")" << std::endl;
|
||||
std::cout << " Visible: Sprite 0x" << std::hex
|
||||
<< static_cast<int>(sprite.id()) << " on map 0x"
|
||||
<< sprite.map_id() << " at (" << std::dec << sprite.x()
|
||||
<< ", " << sprite.y() << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "World " << current_world << ": " << visible_sprites << "/"
|
||||
|
||||
std::cout << "World " << current_world << ": " << visible_sprites << "/"
|
||||
<< total_sprites << " sprites visible" << std::endl;
|
||||
}
|
||||
}
|
||||
@@ -109,45 +118,53 @@ TEST_F(SpritePositionTest, MapCoordinateCalculations) {
|
||||
// Test how map coordinates should be calculated
|
||||
for (int current_world = 0; current_world < 3; current_world++) {
|
||||
const auto& sprites = overworld_->sprites(current_world);
|
||||
|
||||
std::cout << "\n=== World " << current_world << " Coordinate Analysis ===" << std::endl;
|
||||
|
||||
|
||||
std::cout << "\n=== World " << current_world
|
||||
<< " Coordinate Analysis ===" << std::endl;
|
||||
|
||||
for (const auto& sprite : sprites) {
|
||||
if (!sprite.deleted() &&
|
||||
if (!sprite.deleted() &&
|
||||
sprite.map_id() < 0x40 + (current_world * 0x40) &&
|
||||
sprite.map_id() >= (current_world * 0x40)) {
|
||||
|
||||
// Calculate map position
|
||||
int sprite_map_id = sprite.map_id();
|
||||
int local_map_index = sprite_map_id - (current_world * 0x40);
|
||||
int map_col = local_map_index % 8;
|
||||
int map_row = local_map_index / 8;
|
||||
|
||||
int map_canvas_x = map_col * 512; // kOverworldMapSize
|
||||
|
||||
int map_canvas_x = map_col * 512; // kOverworldMapSize
|
||||
int map_canvas_y = map_row * 512;
|
||||
|
||||
std::cout << "Sprite 0x" << std::hex << static_cast<int>(sprite.id())
|
||||
|
||||
std::cout << "Sprite 0x" << std::hex << static_cast<int>(sprite.id())
|
||||
<< " on map 0x" << sprite_map_id << std::endl;
|
||||
std::cout << " Local map index: " << std::dec << local_map_index << std::endl;
|
||||
std::cout << " Map position: (" << map_col << ", " << map_row << ")" << std::endl;
|
||||
std::cout << " Map canvas pos: (" << map_canvas_x << ", " << map_canvas_y << ")" << std::endl;
|
||||
std::cout << " Sprite global pos: (" << sprite.x() << ", " << sprite.y() << ")" << std::endl;
|
||||
std::cout << " Sprite local pos: (" << sprite.map_x() << ", " << sprite.map_y() << ")" << std::endl;
|
||||
|
||||
std::cout << " Local map index: " << std::dec << local_map_index
|
||||
<< std::endl;
|
||||
std::cout << " Map position: (" << map_col << ", " << map_row << ")"
|
||||
<< std::endl;
|
||||
std::cout << " Map canvas pos: (" << map_canvas_x << ", "
|
||||
<< map_canvas_y << ")" << std::endl;
|
||||
std::cout << " Sprite global pos: (" << sprite.x() << ", "
|
||||
<< sprite.y() << ")" << std::endl;
|
||||
std::cout << " Sprite local pos: (" << sprite.map_x() << ", "
|
||||
<< sprite.map_y() << ")" << std::endl;
|
||||
|
||||
// Verify the calculation
|
||||
int expected_global_x = map_canvas_x + sprite.map_x();
|
||||
int expected_global_y = map_canvas_y + sprite.map_y();
|
||||
|
||||
std::cout << " Expected global: (" << expected_global_x << ", " << expected_global_y << ")" << std::endl;
|
||||
std::cout << " Actual global: (" << sprite.x() << ", " << sprite.y() << ")" << std::endl;
|
||||
|
||||
if (expected_global_x == sprite.x() && expected_global_y == sprite.y()) {
|
||||
|
||||
std::cout << " Expected global: (" << expected_global_x << ", "
|
||||
<< expected_global_y << ")" << std::endl;
|
||||
std::cout << " Actual global: (" << sprite.x() << ", " << sprite.y()
|
||||
<< ")" << std::endl;
|
||||
|
||||
if (expected_global_x == sprite.x() &&
|
||||
expected_global_y == sprite.y()) {
|
||||
std::cout << " ✓ Coordinates match!" << std::endl;
|
||||
} else {
|
||||
std::cout << " ✗ Coordinate mismatch!" << std::endl;
|
||||
}
|
||||
|
||||
break; // Only test first sprite for brevity
|
||||
|
||||
break; // Only test first sprite for brevity
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user