yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
room_integration_test.cc
Go to the documentation of this file.
1// Integration tests for Room object load/save cycle with real ROM data
2// Phase 1, Task 2.1: Full round-trip verification
3
4#include <gtest/gtest.h>
5#include <gmock/gmock.h>
6
7#include "app/rom.h"
10
11// Helper function for SNES to PC address conversion
12inline int SnesToPc(int addr) {
13 int temp = (addr & 0x7FFF) + ((addr / 2) & 0xFF8000);
14 return (temp + 0x0);
15}
16
17namespace yaze {
18namespace zelda3 {
19namespace test {
20
21class RoomIntegrationTest : public ::testing::Test {
22 protected:
23 void SetUp() override {
24 // Load the ROM file
25 rom_ = std::make_unique<Rom>();
26
27 // Check if ROM file exists
28 const char* rom_path = std::getenv("YAZE_TEST_ROM_PATH");
29 if (!rom_path) {
30 rom_path = "zelda3.sfc";
31 }
32
33 auto status = rom_->LoadFromFile(rom_path);
34 if (!status.ok()) {
35 GTEST_SKIP() << "ROM file not available: " << status.message();
36 }
37
38 // Create backup of ROM data for restoration after tests
39 original_rom_data_ = rom_->vector();
40 }
41
42 void TearDown() override {
43 // Restore original ROM data
44 if (rom_ && !original_rom_data_.empty()) {
45 for (size_t i = 0; i < original_rom_data_.size(); i++) {
46 rom_->WriteByte(i, original_rom_data_[i]);
47 }
48 }
49 }
50
51 std::unique_ptr<Rom> rom_;
52 std::vector<uint8_t> original_rom_data_;
53};
54
55// ============================================================================
56// Test 1: Basic Load/Save Round-Trip
57// ============================================================================
58
59TEST_F(RoomIntegrationTest, BasicLoadSaveRoundTrip) {
60 // Load room 0 (Hyrule Castle Entrance)
61 Room room1(0x00, rom_.get());
62
63 // Get original object count
64 size_t original_count = room1.GetTileObjects().size();
65 ASSERT_GT(original_count, 0) << "Room should have objects";
66
67 // Store original objects
68 auto original_objects = room1.GetTileObjects();
69
70 // Save the room (should write same data back)
71 auto save_status = room1.SaveObjects();
72 ASSERT_TRUE(save_status.ok()) << save_status.message();
73
74 // Load the room again
75 Room room2(0x00, rom_.get());
76
77 // Verify object count matches
78 EXPECT_EQ(room2.GetTileObjects().size(), original_count);
79
80 // Verify each object matches
81 auto reloaded_objects = room2.GetTileObjects();
82 ASSERT_EQ(reloaded_objects.size(), original_objects.size());
83
84 for (size_t i = 0; i < original_objects.size(); i++) {
85 SCOPED_TRACE("Object " + std::to_string(i));
86
87 const auto& orig = original_objects[i];
88 const auto& reload = reloaded_objects[i];
89
90 EXPECT_EQ(reload.id_, orig.id_) << "ID mismatch";
91 EXPECT_EQ(reload.x(), orig.x()) << "X position mismatch";
92 EXPECT_EQ(reload.y(), orig.y()) << "Y position mismatch";
93 EXPECT_EQ(reload.size(), orig.size()) << "Size mismatch";
94 EXPECT_EQ(reload.GetLayerValue(), orig.GetLayerValue()) << "Layer mismatch";
95 }
96}
97
98// ============================================================================
99// Test 2: Multi-Room Verification
100// ============================================================================
101
102TEST_F(RoomIntegrationTest, MultiRoomLoadSaveRoundTrip) {
103 // Test several different rooms to ensure broad coverage
104 std::vector<int> test_rooms = {0x00, 0x01, 0x02, 0x10, 0x20};
105
106 for (int room_id : test_rooms) {
107 SCOPED_TRACE("Room " + std::to_string(room_id));
108
109 // Load room
110 Room room1(room_id, rom_.get());
111 auto original_objects = room1.GetTileObjects();
112
113 if (original_objects.empty()) {
114 continue; // Skip empty rooms
115 }
116
117 // Save objects
118 auto save_status = room1.SaveObjects();
119 ASSERT_TRUE(save_status.ok()) << save_status.message();
120
121 // Reload and verify
122 Room room2(room_id, rom_.get());
123 auto reloaded_objects = room2.GetTileObjects();
124
125 EXPECT_EQ(reloaded_objects.size(), original_objects.size());
126
127 // Verify objects match
128 for (size_t i = 0; i < std::min(original_objects.size(), reloaded_objects.size()); i++) {
129 const auto& orig = original_objects[i];
130 const auto& reload = reloaded_objects[i];
131
132 EXPECT_EQ(reload.id_, orig.id_);
133 EXPECT_EQ(reload.x(), orig.x());
134 EXPECT_EQ(reload.y(), orig.y());
135 EXPECT_EQ(reload.size(), orig.size());
136 EXPECT_EQ(reload.GetLayerValue(), orig.GetLayerValue());
137 }
138 }
139}
140
141// ============================================================================
142// Test 3: Layer Verification
143// ============================================================================
144
145TEST_F(RoomIntegrationTest, LayerPreservation) {
146 // Load a room known to have multiple layers
147 Room room(0x01, rom_.get());
148
149 auto objects = room.GetTileObjects();
150 ASSERT_GT(objects.size(), 0);
151
152 // Count objects per layer
153 int layer0_count = 0, layer1_count = 0, layer2_count = 0;
154 for (const auto& obj : objects) {
155 switch (obj.GetLayerValue()) {
156 case 0: layer0_count++; break;
157 case 1: layer1_count++; break;
158 case 2: layer2_count++; break;
159 }
160 }
161
162 // Save and reload
163 ASSERT_TRUE(room.SaveObjects().ok());
164
165 Room room2(0x01, rom_.get());
166 auto reloaded = room2.GetTileObjects();
167
168 // Verify layer counts match
169 int reload_layer0 = 0, reload_layer1 = 0, reload_layer2 = 0;
170 for (const auto& obj : reloaded) {
171 switch (obj.GetLayerValue()) {
172 case 0: reload_layer0++; break;
173 case 1: reload_layer1++; break;
174 case 2: reload_layer2++; break;
175 }
176 }
177
178 EXPECT_EQ(reload_layer0, layer0_count);
179 EXPECT_EQ(reload_layer1, layer1_count);
180 EXPECT_EQ(reload_layer2, layer2_count);
181}
182
183// ============================================================================
184// Test 4: Object Type Distribution
185// ============================================================================
186
187TEST_F(RoomIntegrationTest, ObjectTypeDistribution) {
188 Room room(0x00, rom_.get());
189
190 auto objects = room.GetTileObjects();
191 ASSERT_GT(objects.size(), 0);
192
193 // Count object types
194 int type1_count = 0; // ID < 0x100
195 int type2_count = 0; // ID 0x100-0x13F
196 int type3_count = 0; // ID >= 0xF00
197
198 for (const auto& obj : objects) {
199 if (obj.id_ >= 0xF00) {
200 type3_count++;
201 } else if (obj.id_ >= 0x100) {
202 type2_count++;
203 } else {
204 type1_count++;
205 }
206 }
207
208 // Save and reload
209 ASSERT_TRUE(room.SaveObjects().ok());
210
211 Room room2(0x00, rom_.get());
212 auto reloaded = room2.GetTileObjects();
213
214 // Verify type distribution matches
215 int reload_type1 = 0, reload_type2 = 0, reload_type3 = 0;
216 for (const auto& obj : reloaded) {
217 if (obj.id_ >= 0xF00) {
218 reload_type3++;
219 } else if (obj.id_ >= 0x100) {
220 reload_type2++;
221 } else {
222 reload_type1++;
223 }
224 }
225
226 EXPECT_EQ(reload_type1, type1_count);
227 EXPECT_EQ(reload_type2, type2_count);
228 EXPECT_EQ(reload_type3, type3_count);
229}
230
231// ============================================================================
232// Test 5: Binary Data Verification
233// ============================================================================
234
235TEST_F(RoomIntegrationTest, BinaryDataExactMatch) {
236 // This test verifies that saving doesn't change ROM data
237 // when no modifications are made
238
239 Room room(0x02, rom_.get());
240
241 // Get the ROM location where objects are stored
242 auto rom_data = rom_->vector();
243 int object_pointer = (rom_data[0x874C + 2] << 16) +
244 (rom_data[0x874C + 1] << 8) +
245 (rom_data[0x874C]);
246 object_pointer = SnesToPc(object_pointer);
247
248 int room_address = object_pointer + (0x02 * 3);
249 int tile_address = (rom_data[room_address + 2] << 16) +
250 (rom_data[room_address + 1] << 8) +
251 rom_data[room_address];
252 int objects_location = SnesToPc(tile_address);
253
254 // Read original bytes (up to 500 bytes should cover most rooms)
255 std::vector<uint8_t> original_bytes;
256 for (int i = 0; i < 500 && objects_location + i < (int)rom_data.size(); i++) {
257 original_bytes.push_back(rom_data[objects_location + i]);
258 // Stop at final terminator
259 if (i > 0 && original_bytes[i] == 0xFF && original_bytes[i-1] == 0xFF) {
260 // Check if this is the final terminator (3rd layer end)
261 bool might_be_final = true;
262 for (int j = i - 10; j < i - 1; j += 2) {
263 if (j >= 0 && original_bytes[j] == 0xFF && original_bytes[j+1] == 0xFF) {
264 // Found another FF FF marker, keep going
265 break;
266 }
267 }
268 if (might_be_final) break;
269 }
270 }
271
272 // Save objects (should write identical data)
273 ASSERT_TRUE(room.SaveObjects().ok());
274
275 // Read bytes after save
276 rom_data = rom_->vector();
277 std::vector<uint8_t> saved_bytes;
278 for (size_t i = 0; i < original_bytes.size() && objects_location + i < rom_data.size(); i++) {
279 saved_bytes.push_back(rom_data[objects_location + i]);
280 }
281
282 // Verify binary match
283 ASSERT_EQ(saved_bytes.size(), original_bytes.size());
284 for (size_t i = 0; i < original_bytes.size(); i++) {
285 EXPECT_EQ(saved_bytes[i], original_bytes[i])
286 << "Byte mismatch at offset " << i;
287 }
288}
289
290// ============================================================================
291// Test 6: Known Room Data Verification
292// ============================================================================
293
295 // Room 0x00 (Hyrule Castle Entrance) - verify known objects exist
296 Room room(0x00, rom_.get());
297
298 auto objects = room.GetTileObjects();
299 ASSERT_GT(objects.size(), 0) << "Room 0x00 should have objects";
300
301 // Verify we can find common object types
302 bool found_type1 = false;
303 bool found_layer0 = false;
304 bool found_layer1 = false;
305
306 for (const auto& obj : objects) {
307 if (obj.id_ < 0x100) found_type1 = true;
308 if (obj.GetLayerValue() == 0) found_layer0 = true;
309 if (obj.GetLayerValue() == 1) found_layer1 = true;
310 }
311
312 EXPECT_TRUE(found_type1) << "Should have Type 1 objects";
313 EXPECT_TRUE(found_layer0) << "Should have Layer 0 objects";
314
315 // Verify coordinates are in valid range (0-63)
316 for (const auto& obj : objects) {
317 EXPECT_GE(obj.x(), 0);
318 EXPECT_LE(obj.x(), 63);
319 EXPECT_GE(obj.y(), 0);
320 EXPECT_LE(obj.y(), 63);
321 }
322}
323
324} // namespace test
325} // namespace zelda3
326} // namespace yaze
327
absl::Status SaveObjects()
Definition room.cc:783
const std::vector< RoomObject > & GetTileObjects() const
Definition room.h:220
TEST_F(RoomIntegrationTest, BasicLoadSaveRoundTrip)
constexpr int tile_address
Definition room.h:58
Main namespace for the application.
uint32_t SnesToPc(uint32_t addr) noexcept
Definition snes.h:8
int SnesToPc(int addr)