yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
rom_dependent_test_suite.h
Go to the documentation of this file.
1#ifndef YAZE_APP_TEST_ROM_DEPENDENT_TEST_SUITE_H
2#define YAZE_APP_TEST_ROM_DEPENDENT_TEST_SUITE_H
3
4#include <chrono>
5#include <vector>
6
7#include "absl/strings/str_format.h"
10#include "rom/rom.h"
11#include "zelda3/game_data.h"
14
15namespace yaze {
16namespace test {
17
18// ROM-dependent test suite that works with the currently loaded ROM
20 public:
22 ~RomDependentTestSuite() override = default;
23
24 std::string GetName() const override { return "ROM-Dependent Tests"; }
25 TestCategory GetCategory() const override {
27 }
28
29 absl::Status RunTests(TestResults& results) override {
30 Rom* current_rom = TestManager::Get().GetCurrentRom();
31
32 // Add detailed ROM availability check
33 TestResult rom_check_result;
34 rom_check_result.name = "ROM_Available_Check";
35 rom_check_result.suite_name = GetName();
36 rom_check_result.category = GetCategory();
37 rom_check_result.timestamp = std::chrono::steady_clock::now();
38
39 if (!current_rom) {
40 rom_check_result.status = TestStatus::kSkipped;
41 rom_check_result.error_message = "ROM pointer is null";
42 } else if (!current_rom->is_loaded()) {
43 rom_check_result.status = TestStatus::kSkipped;
44 rom_check_result.error_message =
45 absl::StrFormat("ROM not loaded (ptr: %p, title: '%s', size: %zu)",
46 (void*)current_rom, current_rom->title().c_str(),
47 current_rom->size());
48 } else {
49 rom_check_result.status = TestStatus::kPassed;
50 rom_check_result.error_message = absl::StrFormat(
51 "ROM loaded successfully (title: '%s', size: %.2f MB)",
52 current_rom->title().c_str(), current_rom->size() / 1048576.0f);
53 }
54
55 rom_check_result.duration = std::chrono::milliseconds{0};
56 results.AddResult(rom_check_result);
57
58 // If no ROM is available, skip other tests
59 if (!current_rom || !current_rom->is_loaded()) {
60 return absl::OkStatus();
61 }
62
63 // Run ROM-dependent tests (only if enabled)
64 auto& test_manager = TestManager::Get();
65
66 if (test_manager.IsTestEnabled("ROM_Header_Validation_Test")) {
67 RunRomHeaderValidationTest(results, current_rom);
68 } else {
69 AddSkippedTest(results, "ROM_Header_Validation_Test",
70 "Test disabled by user");
71 }
72
73 if (test_manager.IsTestEnabled("ROM_Data_Access_Test")) {
74 RunRomDataAccessTest(results, current_rom);
75 } else {
76 AddSkippedTest(results, "ROM_Data_Access_Test", "Test disabled by user");
77 }
78
79 if (test_manager.IsTestEnabled("ROM_Graphics_Extraction_Test")) {
80 RunRomGraphicsExtractionTest(results, current_rom);
81 } else {
82 AddSkippedTest(results, "ROM_Graphics_Extraction_Test",
83 "Test disabled by user");
84 }
85
86 if (test_manager.IsTestEnabled("ROM_Overworld_Loading_Test")) {
87 RunRomOverworldLoadingTest(results, current_rom);
88 } else {
89 AddSkippedTest(results, "ROM_Overworld_Loading_Test",
90 "Test disabled by user");
91 }
92
93 if (test_manager.IsTestEnabled("Tile16_Editor_Test")) {
94 RunTile16EditorTest(results, current_rom);
95 } else {
96 AddSkippedTest(results, "Tile16_Editor_Test", "Test disabled by user");
97 }
98
99 if (test_manager.IsTestEnabled("Comprehensive_Save_Test")) {
100 RunComprehensiveSaveTest(results, current_rom);
101 } else {
102 AddSkippedTest(results, "Comprehensive_Save_Test",
103 "Test disabled by user (known to crash)");
104 }
105
107 RunRomSpriteDataTest(results, current_rom);
108 RunRomMusicDataTest(results, current_rom);
109 }
110
111 return absl::OkStatus();
112 }
113
114 void DrawConfiguration() override {
115 Rom* current_rom = TestManager::Get().GetCurrentRom();
116
117 ImGui::Text("%s ROM-Dependent Test Configuration", ICON_MD_STORAGE);
118
119 if (current_rom && current_rom->is_loaded()) {
120 ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "%s Current ROM: %s",
121 ICON_MD_CHECK_CIRCLE, current_rom->title().c_str());
122 ImGui::Text("Size: %zu bytes", current_rom->size());
123 ImGui::Text("File: %s", current_rom->filename().c_str());
124 } else {
125 ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.0f, 1.0f),
126 "%s No ROM currently loaded", ICON_MD_WARNING);
127 ImGui::Text("Load a ROM in the editor to enable ROM-dependent tests");
128 }
129
130 ImGui::Separator();
131 ImGui::Checkbox("Test ROM header validation", &test_header_validation_);
132 ImGui::Checkbox("Test ROM data access", &test_data_access_);
133 ImGui::Checkbox("Test graphics extraction", &test_graphics_extraction_);
134 ImGui::Checkbox("Test overworld loading", &test_overworld_loading_);
135 ImGui::Checkbox("Test advanced features", &test_advanced_features_);
136
138 ImGui::Indent();
139 ImGui::Checkbox("Test sprite data", &test_sprite_data_);
140 ImGui::Checkbox("Test music data", &test_music_data_);
141 ImGui::Unindent();
142 }
143 }
144
145 private:
146 // Helper method to add skipped test results
147 void AddSkippedTest(TestResults& results, const std::string& test_name,
148 const std::string& reason) {
149 TestResult result;
150 result.name = test_name;
151 result.suite_name = GetName();
152 result.category = GetCategory();
154 result.error_message = reason;
155 result.duration = std::chrono::milliseconds{0};
156 result.timestamp = std::chrono::steady_clock::now();
157 results.AddResult(result);
158 }
159
161 auto start_time = std::chrono::steady_clock::now();
162
163 TestResult result;
164 result.name = "ROM_Header_Validation_Test";
165 result.suite_name = GetName();
166 result.category = GetCategory();
167 result.timestamp = start_time;
168
171 result.error_message = "Header validation disabled in configuration";
172 } else {
173 try {
174 std::string title = rom->title();
175 size_t size = rom->size();
176
177 // Basic validation
178 bool valid_title =
179 !title.empty() && title != "ZELDA3" && title.length() <= 21;
180 bool valid_size =
181 size >= 1024 * 1024 && size <= 8 * 1024 * 1024; // 1MB to 8MB
182
183 if (valid_title && valid_size) {
185 result.error_message = absl::StrFormat(
186 "ROM header valid: '%s' (%zu bytes)", title.c_str(), size);
187 } else {
189 result.error_message = absl::StrFormat(
190 "ROM header validation failed: title='%s' size=%zu",
191 title.c_str(), size);
192 }
193 } catch (const std::exception& e) {
195 result.error_message =
196 "Header validation failed: " + std::string(e.what());
197 }
198 }
199
200 auto end_time = std::chrono::steady_clock::now();
201 result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
202 end_time - start_time);
203
204 results.AddResult(result);
205 }
206
207 void RunRomDataAccessTest(TestResults& results, Rom* rom) {
208 auto start_time = std::chrono::steady_clock::now();
209
210 TestResult result;
211 result.name = "ROM_Data_Access_Test";
212 result.suite_name = GetName();
213 result.category = GetCategory();
214 result.timestamp = start_time;
215
216 if (!test_data_access_) {
218 result.error_message = "Data access testing disabled in configuration";
219 } else {
220 try {
221 // Test basic ROM data access patterns
222 size_t bytes_tested = 0;
223 bool access_success = true;
224
225 // Test reading from various ROM regions
226 try {
227 [[maybe_unused]] auto header_byte = rom->ReadByte(0x7FC0);
228 bytes_tested++;
229 [[maybe_unused]] auto code_byte = rom->ReadByte(0x8000);
230 bytes_tested++;
231 [[maybe_unused]] auto data_word = rom->ReadWord(0x8002);
232 bytes_tested++;
233 } catch (...) {
234 access_success = false;
235 }
236
237 if (access_success && bytes_tested >= 3) {
239 result.error_message = absl::StrFormat(
240 "ROM data access verified: %zu operations", bytes_tested);
241 } else {
243 result.error_message = "ROM data access failed";
244 }
245 } catch (const std::exception& e) {
247 result.error_message =
248 "Data access test failed: " + std::string(e.what());
249 }
250 }
251
252 auto end_time = std::chrono::steady_clock::now();
253 result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
254 end_time - start_time);
255
256 results.AddResult(result);
257 }
258
260 auto start_time = std::chrono::steady_clock::now();
261
262 TestResult result;
263 result.name = "ROM_Graphics_Extraction_Test";
264 result.suite_name = GetName();
265 result.category = GetCategory();
266 result.timestamp = start_time;
267
270 result.error_message =
271 "Graphics extraction testing disabled in configuration";
272 } else {
273 try {
274 zelda3::GameData game_data;
275 auto load_status = zelda3::LoadGameData(*rom, game_data);
276 if (load_status.ok()) {
277 auto& sheets = game_data.gfx_bitmaps;
278 size_t loaded_sheets = 0;
279 for (const auto& sheet : sheets) {
280 if (sheet.is_active()) {
281 loaded_sheets++;
282 }
283 }
284
286 result.error_message = absl::StrFormat(
287 "Graphics extraction successful: %zu/%zu sheets loaded",
288 loaded_sheets, sheets.size());
289 } else {
291 result.error_message =
292 "Graphics extraction failed: " +
293 std::string(load_status.message());
294 }
295 } catch (const std::exception& e) {
297 result.error_message =
298 "Graphics extraction test failed: " + std::string(e.what());
299 }
300 }
301
302 auto end_time = std::chrono::steady_clock::now();
303 result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
304 end_time - start_time);
305
306 results.AddResult(result);
307 }
308
310 auto start_time = std::chrono::steady_clock::now();
311
312 TestResult result;
313 result.name = "ROM_Overworld_Loading_Test";
314 result.suite_name = GetName();
315 result.category = GetCategory();
316 result.timestamp = start_time;
317
320 result.error_message =
321 "Overworld loading testing disabled in configuration";
322 } else {
323 try {
324 zelda3::Overworld overworld(rom);
325 auto ow_status = overworld.Load(rom);
326
327 if (ow_status.ok()) {
329 result.error_message =
330 "Overworld loading successful from current ROM";
331 } else {
333 result.error_message =
334 "Overworld loading failed: " + std::string(ow_status.message());
335 }
336 } catch (const std::exception& e) {
338 result.error_message =
339 "Overworld loading test failed: " + std::string(e.what());
340 }
341 }
342
343 auto end_time = std::chrono::steady_clock::now();
344 result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
345 end_time - start_time);
346
347 results.AddResult(result);
348 }
349
350 void RunRomSpriteDataTest(TestResults& results, Rom* rom) {
351 auto start_time = std::chrono::steady_clock::now();
352
353 TestResult result;
354 result.name = "ROM_Sprite_Data_Test";
355 result.suite_name = GetName();
356 result.category = GetCategory();
357 result.timestamp = start_time;
358
359 if (!test_sprite_data_) {
361 result.error_message = "Sprite data testing disabled in configuration";
362 } else {
363 try {
364 // Basic sprite data validation (simplified for now)
365 // In a full implementation, this would test sprite loading
367 result.error_message = "Sprite data testing not yet implemented";
368 } catch (const std::exception& e) {
370 result.error_message =
371 "Sprite data test failed: " + std::string(e.what());
372 }
373 }
374
375 auto end_time = std::chrono::steady_clock::now();
376 result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
377 end_time - start_time);
378
379 results.AddResult(result);
380 }
381
382 void RunRomMusicDataTest(TestResults& results, Rom* rom) {
383 auto start_time = std::chrono::steady_clock::now();
384
385 TestResult result;
386 result.name = "ROM_Music_Data_Test";
387 result.suite_name = GetName();
388 result.category = GetCategory();
389 result.timestamp = start_time;
390
391 if (!test_music_data_) {
393 result.error_message = "Music data testing disabled in configuration";
394 } else {
395 try {
396 // Basic music data validation (simplified for now)
397 // In a full implementation, this would test music loading
399 result.error_message = "Music data testing not yet implemented";
400 } catch (const std::exception& e) {
402 result.error_message =
403 "Music data test failed: " + std::string(e.what());
404 }
405 }
406
407 auto end_time = std::chrono::steady_clock::now();
408 result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
409 end_time - start_time);
410
411 results.AddResult(result);
412 }
413
414 void RunTile16EditorTest(TestResults& results, Rom* rom) {
415 auto start_time = std::chrono::steady_clock::now();
416
417 TestResult result;
418 result.name = "Tile16_Editor_Test";
419 result.suite_name = GetName();
420 result.category = GetCategory();
421 result.timestamp = start_time;
422
423 try {
424 // Load game data for palette access
425 zelda3::GameData game_data;
426 auto load_status = zelda3::LoadGameData(*rom, game_data);
427
428 // Verify ROM and palette data
429 if (load_status.ok() && game_data.palette_groups.overworld_main.size() > 0) {
430 // Test Tile16 editor functionality with real ROM data
431 editor::Tile16Editor tile16_editor(rom, nullptr);
432
433 // Create test bitmaps with realistic data
434 std::vector<uint8_t> test_blockset_data(256 * 8192,
435 1); // Tile16 blockset size
436 std::vector<uint8_t> test_gfx_data(256 * 256, 1); // Area graphics size
437
438 gfx::Bitmap test_blockset_bmp, test_gfx_bmp;
439 test_blockset_bmp.Create(256, 8192, 8, test_blockset_data);
440 test_gfx_bmp.Create(256, 256, 8, test_gfx_data);
441
442 // Set realistic palettes
443 if (game_data.palette_groups.overworld_main.size() > 0) {
444 test_blockset_bmp.SetPalette(game_data.palette_groups.overworld_main[0]);
445 test_gfx_bmp.SetPalette(game_data.palette_groups.overworld_main[0]);
446 }
447
448 std::array<uint8_t, 0x200> tile_types{};
449
450 // Test initialization
451 auto init_status = tile16_editor.Initialize(test_blockset_bmp,
452 test_gfx_bmp, tile_types);
453 if (!init_status.ok()) {
455 result.error_message =
456 "Tile16Editor initialization failed: " + init_status.ToString();
457 } else {
458 // Test setting a tile
459 auto set_tile_status = tile16_editor.SetCurrentTile(0);
460 if (!set_tile_status.ok()) {
462 result.error_message =
463 "SetCurrentTile failed: " + set_tile_status.ToString();
464 } else {
466 result.error_message = absl::StrFormat(
467 "Tile16Editor working correctly (ROM: %s, Palette groups: %zu)",
468 rom->title().c_str(),
470 }
471 }
472 } else {
474 result.error_message = "ROM palette data not available or failed to load game data";
475 }
476 } catch (const std::exception& e) {
478 result.error_message =
479 "Tile16Editor test exception: " + std::string(e.what());
480 }
481
482 auto end_time = std::chrono::steady_clock::now();
483 result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
484 end_time - start_time);
485
486 results.AddResult(result);
487 }
488
490 auto start_time = std::chrono::steady_clock::now();
491
492 TestResult result;
493 result.name = "Comprehensive_Save_Test";
494 result.suite_name = GetName();
495 result.category = GetCategory();
496 result.timestamp = start_time;
497
498 try {
499 // Test comprehensive save functionality using ROM copy
500 auto& test_manager = TestManager::Get();
501
502 auto test_status =
503 test_manager.TestRomWithCopy(rom, [&](Rom* test_rom) -> absl::Status {
504 // Test overworld modifications on the copy
505 zelda3::Overworld overworld(test_rom);
506 auto load_status = overworld.Load(test_rom);
507 if (!load_status.ok()) {
508 return load_status;
509 }
510
511 // Make modifications to the copy
512 auto* test_map = overworld.mutable_overworld_map(0);
513 uint8_t original_gfx = test_map->area_graphics();
514 test_map->set_area_graphics(
515 0x01); // Change to a different graphics set
516
517 // Test save operations
518 auto save_maps_status = overworld.SaveOverworldMaps();
519 auto save_props_status = overworld.SaveMapProperties();
520
521 if (!save_maps_status.ok()) {
522 return save_maps_status;
523 }
524 if (!save_props_status.ok()) {
525 return save_props_status;
526 }
527
528 // Save the test ROM with timestamp
529 Rom::SaveSettings settings;
530 settings.backup = false;
531 settings.save_new = true;
532 settings.filename =
533 test_manager.GenerateTestRomFilename(test_rom->title());
534
535 auto save_file_status = test_rom->SaveToFile(settings);
536 if (!save_file_status.ok()) {
537 return save_file_status;
538 }
539
540 // Offer to open test ROM in new session
541 test_manager.OfferTestSessionCreation(settings.filename);
542
543 return absl::OkStatus();
544 });
545
546 if (test_status.ok()) {
548 result.error_message =
549 "Comprehensive save test completed successfully using ROM copy";
550 } else {
552 result.error_message = "Save test failed: " + test_status.ToString();
553 }
554
555 } catch (const std::exception& e) {
557 result.error_message =
558 "Comprehensive save test exception: " + std::string(e.what());
559 }
560
561 auto end_time = std::chrono::steady_clock::now();
562 result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
563 end_time - start_time);
564
565 results.AddResult(result);
566 }
567
568 // Configuration
570 bool test_data_access_ = true;
576 bool test_sprite_data_ = false;
577 bool test_music_data_ = false;
578};
579
580} // namespace test
581} // namespace yaze
582
583#endif // YAZE_APP_TEST_ROM_DEPENDENT_TEST_SUITE_H
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:24
auto filename() const
Definition rom.h:141
absl::StatusOr< uint16_t > ReadWord(int offset)
Definition rom.cc:228
absl::Status SaveToFile(const SaveSettings &settings)
Definition rom.cc:164
auto size() const
Definition rom.h:134
absl::StatusOr< uint8_t > ReadByte(int offset)
Definition rom.cc:221
bool is_loaded() const
Definition rom.h:128
auto title() const
Definition rom.h:133
Popup window to edit Tile16 data.
absl::Status Initialize(const gfx::Bitmap &tile16_blockset_bmp, const gfx::Bitmap &current_gfx_bmp, std::array< uint8_t, 0x200 > &all_tiles_types)
absl::Status SetCurrentTile(int id)
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:67
void Create(int width, int height, int depth, std::span< uint8_t > data)
Create a bitmap with the given dimensions and data.
Definition bitmap.cc:199
void SetPalette(const SnesPalette &palette)
Set the palette for the bitmap using SNES palette format.
Definition bitmap.cc:382
void RunTile16EditorTest(TestResults &results, Rom *rom)
void RunRomMusicDataTest(TestResults &results, Rom *rom)
void RunRomOverworldLoadingTest(TestResults &results, Rom *rom)
void RunComprehensiveSaveTest(TestResults &results, Rom *rom)
void RunRomGraphicsExtractionTest(TestResults &results, Rom *rom)
void RunRomHeaderValidationTest(TestResults &results, Rom *rom)
~RomDependentTestSuite() override=default
void AddSkippedTest(TestResults &results, const std::string &test_name, const std::string &reason)
void RunRomSpriteDataTest(TestResults &results, Rom *rom)
void RunRomDataAccessTest(TestResults &results, Rom *rom)
absl::Status RunTests(TestResults &results) override
TestCategory GetCategory() const override
Rom * GetCurrentRom() const
static TestManager & Get()
Represents the full Overworld data, light and dark world.
Definition overworld.h:217
absl::Status Load(Rom *rom)
Load all overworld data from ROM.
Definition overworld.cc:36
absl::Status SaveMapProperties()
Save per-area graphics, palettes, and messages.
auto mutable_overworld_map(int i)
Definition overworld.h:479
absl::Status SaveOverworldMaps()
Save compressed map tile data to ROM.
#define ICON_MD_STORAGE
Definition icons.h:1865
#define ICON_MD_WARNING
Definition icons.h:2123
#define ICON_MD_CHECK_CIRCLE
Definition icons.h:400
absl::Status LoadGameData(Rom &rom, GameData &data, const LoadOptions &options)
Loads all Zelda3-specific game data from a generic ROM.
Definition game_data.cc:80
std::string filename
Definition rom.h:29
std::chrono::milliseconds duration
std::string error_message
std::chrono::time_point< std::chrono::steady_clock > timestamp
void AddResult(const TestResult &result)
gfx::PaletteGroupMap palette_groups
Definition game_data.h:89
std::array< gfx::Bitmap, kNumGfxSheets > gfx_bitmaps
Definition game_data.h:84