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"
11#include "rom/rom.h"
12#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 = "Graphics extraction failed: " +
292 std::string(load_status.message());
293 }
294 } catch (const std::exception& e) {
296 result.error_message =
297 "Graphics extraction test failed: " + std::string(e.what());
298 }
299 }
300
301 auto end_time = std::chrono::steady_clock::now();
302 result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
303 end_time - start_time);
304
305 results.AddResult(result);
306 }
307
309 auto start_time = std::chrono::steady_clock::now();
310
311 TestResult result;
312 result.name = "ROM_Overworld_Loading_Test";
313 result.suite_name = GetName();
314 result.category = GetCategory();
315 result.timestamp = start_time;
316
319 result.error_message =
320 "Overworld loading testing disabled in configuration";
321 } else {
322 try {
323 zelda3::Overworld overworld(rom);
324 auto ow_status = overworld.Load(rom);
325
326 if (ow_status.ok()) {
328 result.error_message =
329 "Overworld loading successful from current ROM";
330 } else {
332 result.error_message =
333 "Overworld loading failed: " + std::string(ow_status.message());
334 }
335 } catch (const std::exception& e) {
337 result.error_message =
338 "Overworld loading test failed: " + std::string(e.what());
339 }
340 }
341
342 auto end_time = std::chrono::steady_clock::now();
343 result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
344 end_time - start_time);
345
346 results.AddResult(result);
347 }
348
349 void RunRomSpriteDataTest(TestResults& results, Rom* rom) {
350 auto start_time = std::chrono::steady_clock::now();
351
352 TestResult result;
353 result.name = "ROM_Sprite_Data_Test";
354 result.suite_name = GetName();
355 result.category = GetCategory();
356 result.timestamp = start_time;
357
358 if (!test_sprite_data_) {
360 result.error_message = "Sprite data testing disabled in configuration";
361 } else {
362 try {
363 // Basic sprite data validation (simplified for now)
364 // In a full implementation, this would test sprite loading
366 result.error_message = "Sprite data testing not yet implemented";
367 } catch (const std::exception& e) {
369 result.error_message =
370 "Sprite data test failed: " + std::string(e.what());
371 }
372 }
373
374 auto end_time = std::chrono::steady_clock::now();
375 result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
376 end_time - start_time);
377
378 results.AddResult(result);
379 }
380
381 void RunRomMusicDataTest(TestResults& results, Rom* rom) {
382 auto start_time = std::chrono::steady_clock::now();
383
384 TestResult result;
385 result.name = "ROM_Music_Data_Test";
386 result.suite_name = GetName();
387 result.category = GetCategory();
388 result.timestamp = start_time;
389
390 if (!test_music_data_) {
392 result.error_message = "Music data testing disabled in configuration";
393 } else {
394 try {
395 // Basic music data validation (simplified for now)
396 // In a full implementation, this would test music loading
398 result.error_message = "Music data testing not yet implemented";
399 } catch (const std::exception& e) {
401 result.error_message =
402 "Music data test failed: " + std::string(e.what());
403 }
404 }
405
406 auto end_time = std::chrono::steady_clock::now();
407 result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
408 end_time - start_time);
409
410 results.AddResult(result);
411 }
412
413 void RunTile16EditorTest(TestResults& results, Rom* rom) {
414 auto start_time = std::chrono::steady_clock::now();
415
416 TestResult result;
417 result.name = "Tile16_Editor_Test";
418 result.suite_name = GetName();
419 result.category = GetCategory();
420 result.timestamp = start_time;
421
422 try {
423 // Load game data for palette access
424 zelda3::GameData game_data;
425 auto load_status = zelda3::LoadGameData(*rom, game_data);
426
427 // Verify ROM and palette data
428 if (load_status.ok() &&
429 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(
445 game_data.palette_groups.overworld_main[0]);
446 test_gfx_bmp.SetPalette(game_data.palette_groups.overworld_main[0]);
447 }
448
449 std::array<uint8_t, 0x200> tile_types{};
450
451 // Test initialization
452 auto init_status = tile16_editor.Initialize(test_blockset_bmp,
453 test_gfx_bmp, tile_types);
454 if (!init_status.ok()) {
456 result.error_message =
457 "Tile16Editor initialization failed: " + init_status.ToString();
458 } else {
459 // Test setting a tile
460 auto set_tile_status = tile16_editor.SetCurrentTile(0);
461 if (!set_tile_status.ok()) {
463 result.error_message =
464 "SetCurrentTile failed: " + set_tile_status.ToString();
465 } else {
467 result.error_message = absl::StrFormat(
468 "Tile16Editor working correctly (ROM: %s, Palette groups: %zu)",
469 rom->title().c_str(),
471 }
472 }
473 } else {
475 result.error_message =
476 "ROM palette data not available or failed to load game data";
477 }
478 } catch (const std::exception& e) {
480 result.error_message =
481 "Tile16Editor test exception: " + std::string(e.what());
482 }
483
484 auto end_time = std::chrono::steady_clock::now();
485 result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
486 end_time - start_time);
487
488 results.AddResult(result);
489 }
490
492 auto start_time = std::chrono::steady_clock::now();
493
494 TestResult result;
495 result.name = "Comprehensive_Save_Test";
496 result.suite_name = GetName();
497 result.category = GetCategory();
498 result.timestamp = start_time;
499
500 try {
501 // Test comprehensive save functionality using ROM copy
502 auto& test_manager = TestManager::Get();
503
504 auto test_status =
505 test_manager.TestRomWithCopy(rom, [&](Rom* test_rom) -> absl::Status {
506 // Test overworld modifications on the copy
507 zelda3::Overworld overworld(test_rom);
508 auto load_status = overworld.Load(test_rom);
509 if (!load_status.ok()) {
510 return load_status;
511 }
512
513 // Make modifications to the copy
514 auto* test_map = overworld.mutable_overworld_map(0);
515 uint8_t original_gfx = test_map->area_graphics();
516 test_map->set_area_graphics(
517 0x01); // Change to a different graphics set
518
519 // Test save operations
520 auto save_maps_status = overworld.SaveOverworldMaps();
521 auto save_props_status = overworld.SaveMapProperties();
522
523 if (!save_maps_status.ok()) {
524 return save_maps_status;
525 }
526 if (!save_props_status.ok()) {
527 return save_props_status;
528 }
529
530 // Save the test ROM with timestamp
531 Rom::SaveSettings settings;
532 settings.backup = false;
533 settings.save_new = true;
534 settings.filename =
535 test_manager.GenerateTestRomFilename(test_rom->title());
536
537 auto save_file_status = test_rom->SaveToFile(settings);
538 if (!save_file_status.ok()) {
539 return save_file_status;
540 }
541
542 // Offer to open test ROM in new session
543 test_manager.OfferTestSessionCreation(settings.filename);
544
545 return absl::OkStatus();
546 });
547
548 if (test_status.ok()) {
550 result.error_message =
551 "Comprehensive save test completed successfully using ROM copy";
552 } else {
554 result.error_message = "Save test failed: " + test_status.ToString();
555 }
556
557 } catch (const std::exception& e) {
559 result.error_message =
560 "Comprehensive save test exception: " + std::string(e.what());
561 }
562
563 auto end_time = std::chrono::steady_clock::now();
564 result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
565 end_time - start_time);
566
567 results.AddResult(result);
568 }
569
570 // Configuration
572 bool test_data_access_ = true;
578 bool test_sprite_data_ = false;
579 bool test_music_data_ = false;
580};
581
582} // namespace test
583} // namespace yaze
584
585#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:28
auto filename() const
Definition rom.h:157
absl::StatusOr< uint8_t > ReadByte(int offset) const
Definition rom.cc:518
absl::Status SaveToFile(const SaveSettings &settings)
Definition rom.cc:371
absl::StatusOr< uint16_t > ReadWord(int offset) const
Definition rom.cc:526
auto size() const
Definition rom.h:150
bool is_loaded() const
Definition rom.h:144
auto title() const
Definition rom.h:149
Popup window to edit Tile16 data.
absl::Status SetCurrentTile(int id)
absl::Status Initialize(gfx::Bitmap &tile16_blockset_bmp, gfx::Bitmap &current_gfx_bmp, std::array< uint8_t, 0x200 > &all_tiles_types)
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:201
void SetPalette(const SnesPalette &palette)
Set the palette for the bitmap using SNES palette format.
Definition bitmap.cc:384
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:389
absl::Status Load(Rom *rom)
Load all overworld data from ROM.
Definition overworld.cc:131
absl::Status SaveMapProperties()
Save per-area graphics, palettes, and messages.
auto mutable_overworld_map(int i)
Definition overworld.h:668
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:123
std::string filename
Definition rom.h:33
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:92
std::array< gfx::Bitmap, kNumGfxSheets > gfx_bitmaps
Definition game_data.h:87