yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
asar_integration_test.cc
Go to the documentation of this file.
1#include <gtest/gtest.h>
2#include <filesystem>
3#include <fstream>
4
5#include "core/asar_wrapper.h"
6#include "app/rom.h"
7#include "absl/status/status.h"
8#include "testing.h"
9
10#include <gtest/gtest.h>
11#include <gmock/gmock.h>
12
13namespace yaze {
14namespace test {
15namespace integration {
16
17class AsarIntegrationTest : public ::testing::Test {
18 protected:
19 void SetUp() override {
20 wrapper_ = std::make_unique<core::AsarWrapper>();
21
22 // Create test directory
23 test_dir_ = std::filesystem::temp_directory_path() / "yaze_asar_integration";
24 std::filesystem::create_directories(test_dir_);
25
28 }
29
30 void TearDown() override {
31 try {
32 if (std::filesystem::exists(test_dir_)) {
33 std::filesystem::remove_all(test_dir_);
34 }
35 } catch (const std::exception& e) {
36 // Ignore cleanup errors
37 }
38 }
39
41 // Create a minimal SNES ROM structure
42 test_rom_.resize(1024 * 1024, 0); // 1MB ROM
43
44 // Add SNES header at 0x7FC0 (LoROM)
45 const uint32_t header_offset = 0x7FC0;
46
47 // ROM title (21 bytes)
48 std::string title = "YAZE TEST ROM ";
49 std::copy(title.begin(), title.end(), test_rom_.begin() + header_offset);
50
51 // Map mode (byte 21) - LoROM
52 test_rom_[header_offset + 21] = 0x20;
53
54 // Cartridge type (byte 22)
55 test_rom_[header_offset + 22] = 0x00;
56
57 // ROM size (byte 23) - 1MB
58 test_rom_[header_offset + 23] = 0x0A;
59
60 // SRAM size (byte 24)
61 test_rom_[header_offset + 24] = 0x00;
62
63 // Country code (byte 25)
64 test_rom_[header_offset + 25] = 0x01;
65
66 // Developer ID (byte 26)
67 test_rom_[header_offset + 26] = 0x00;
68
69 // Version (byte 27)
70 test_rom_[header_offset + 27] = 0x00;
71
72 // Calculate and set checksum complement and checksum
73 uint16_t checksum = 0;
74 for (size_t i = 0; i < test_rom_.size(); ++i) {
75 if (i != header_offset + 28 && i != header_offset + 29 &&
76 i != header_offset + 30 && i != header_offset + 31) {
77 checksum += test_rom_[i];
78 }
79 }
80
81 uint16_t checksum_complement = checksum ^ 0xFFFF;
82 test_rom_[header_offset + 28] = checksum_complement & 0xFF;
83 test_rom_[header_offset + 29] = (checksum_complement >> 8) & 0xFF;
84 test_rom_[header_offset + 30] = checksum & 0xFF;
85 test_rom_[header_offset + 31] = (checksum >> 8) & 0xFF;
86
87 // Add some code at the reset vector location
88 const uint32_t reset_vector_offset = 0x8000;
89 test_rom_[reset_vector_offset] = 0x18; // CLC
90 test_rom_[reset_vector_offset + 1] = 0xFB; // XCE
91 test_rom_[reset_vector_offset + 2] = 0x4C; // JMP abs
92 test_rom_[reset_vector_offset + 3] = 0x00; // $8000
93 test_rom_[reset_vector_offset + 4] = 0x80;
94 }
95
97 // Create comprehensive test assembly
98 comprehensive_asm_path_ = test_dir_ / "comprehensive_test.asm";
99 std::ofstream comp_file(comprehensive_asm_path_);
100 comp_file << R"(
101; Comprehensive Asar test for Yaze integration
102!addr = $7E0000
103
104; Test basic assembly
105org $008000
106main_entry:
107 sei ; Disable interrupts
108 clc ; Clear carry
109 xce ; Switch to native mode
110
111 ; Set up stack
112 rep #$30 ; 16-bit A and X/Y
113 ldx #$1FFF
114 txs
115
116 ; Test data writing
117 lda #$1234
118 sta !addr
119
120 ; Call subroutines
121 jsr init_graphics
122 jsr init_sound
124 ; Main loop
125main_loop:
126 jsr update_game
127 jsr wait_vblank
128 bra main_loop
129
130; Graphics initialization
131init_graphics:
132 pha
133 phx
134 phy
135
136 ; Set up PPU registers
137 sep #$20 ; 8-bit A
138 lda #$80
139 sta $2100 ; Force blank
140
141 ; Clear VRAM
142 rep #$20 ; 16-bit A
143 lda #$8000
144 sta $2116 ; VRAM address
145
146 lda #$0000
147 ldx #$8000
148clear_vram_loop:
149 sta $2118 ; Write to VRAM
150 dex
151 bne clear_vram_loop
152
153 ply
154 plx
155 pla
156 rts
157
158; Sound initialization
159init_sound:
160 pha
161
162 ; Initialize APU
163 sep #$20 ; 8-bit A
164 lda #$00
165 sta $2140 ; APU port 0
166 sta $2141 ; APU port 1
167 sta $2142 ; APU port 2
168 sta $2143 ; APU port 3
169
170 pla
171 rts
172
173; Game update routine
174update_game:
175 pha
176
177 ; Read controller
178 lda $4212 ; PPU status
179 and #$01
180 bne update_game ; Wait for vblank end
181
182 lda $4016 ; Controller 1
183 ; Process input here
184
185 pla
186 rts
187
188; Wait for vertical blank
189wait_vblank:
190 pha
191wait_vb_loop:
192 lda $4212 ; PPU status
193 and #$80
194 beq wait_vb_loop ; Wait for vblank
195 pla
196 rts
197
198; Data tables
199org $00A000
200graphics_data:
201 incbin "test_graphics.bin"
202
203sound_data:
204 db $00, $01, $02, $03, $04, $05, $06, $07
205 db $08, $09, $0A, $0B, $0C, $0D, $0E, $0F
206
207; String data for testing
208text_data:
209 db "YAZE INTEGRATION TEST", $00
210
211; Math functions
212calculate_distance:
213 ; Input: A = x1, X = y1, stack = x2, y2
214 ; Output: A = distance
215 pha
216 phx
217
218 ; Calculate dx = x2 - x1
219 pla ; Get x1
220 pha ; Save it back
221 ; ... distance calculation here
222
223 plx
224 pla
225 rts
226
227; Interrupt vectors
228org $00FFE0
229 dw $0000 ; Reserved
230 dw $0000 ; Reserved
231 dw $0000 ; Reserved
232 dw $0000 ; Reserved
233 dw $0000 ; Reserved
234 dw $0000 ; Reserved
235 dw $0000 ; Reserved
236 dw $0000 ; Reserved
237 dw main_entry ; RESET vector
238 dw $0000 ; Reserved
239 dw $0000 ; Reserved
240 dw $0000 ; Reserved
241 dw $0000 ; NMI vector
242 dw main_entry ; RESET vector
243 dw $0000 ; IRQ vector
244)";
245 comp_file.close();
246
247 // Create test graphics binary
248 std::ofstream gfx_file(test_dir_ / "test_graphics.bin", std::ios::binary);
249 std::vector<uint8_t> graphics_data(2048, 0x55); // Test pattern
250 gfx_file.write(reinterpret_cast<char*>(graphics_data.data()), graphics_data.size());
251 gfx_file.close();
252
253 // Create advanced assembly with macros and includes
254 advanced_asm_path_ = test_dir_ / "advanced_test.asm";
255 std::ofstream adv_file(advanced_asm_path_);
256 adv_file << R"(
257; Advanced Asar features test
258!ram_addr = $7E1000
259
260; Macro definitions
261macro move_block(source, dest, size)
262 rep #$30
263 ldx #<size>-1
264loop:
265 lda <source>,x
266 sta <dest>,x
267 dex
268 bpl loop
269endmacro
270
271macro set_ppu_register(register, value)
272 sep #$20
273 lda #<value>
274 sta <register>
275endmacro
276
277; Test code with macros
278org $008000
279advanced_entry:
280 %set_ppu_register($2100, $8F) ; Set forced blank
281
282 ; Use block move macro
283 %move_block($008100, !ram_addr, 256)
284
285 ; Conditional assembly
286 if !test_mode == 1
287 jsr debug_routine
288 endif
289
290 ; Loop with labels
291 ldx #$10
292test_loop:
293 lda test_data,x
294 sta !ram_addr,x
295 dex
296 bpl test_loop
297
298 rts
299
300debug_routine:
301 ; Debug code
302 rts
303
304test_data:
305 db $FF, $FE, $FD, $FC, $FB, $FA, $F9, $F8
306 db $F7, $F6, $F5, $F4, $F3, $F2, $F1, $F0
307)";
308 adv_file.close();
309
310 // Create error test assembly
311 error_asm_path_ = test_dir_ / "error_test.asm";
312 std::ofstream err_file(error_asm_path_);
313 err_file << R"(
314; Assembly with intentional errors for testing error handling
315org $008000
316error_test:
317 invalid_opcode ; This should cause an error
318 lda unknown_label ; This should cause an error
319 sta $999999 ; Invalid address
320 bra ; Missing operand
321)";
322 err_file.close();
323 }
324
325 std::unique_ptr<core::AsarWrapper> wrapper_;
326 std::filesystem::path test_dir_;
327 std::filesystem::path comprehensive_asm_path_;
328 std::filesystem::path advanced_asm_path_;
329 std::filesystem::path error_asm_path_;
330 std::vector<uint8_t> test_rom_;
331};
332
333TEST_F(AsarIntegrationTest, FullWorkflowIntegration) {
334 // Initialize Asar
335 ASSERT_TRUE(wrapper_->Initialize().ok());
336
337 // Test ROM loading and patching workflow
338 std::vector<uint8_t> rom_copy = test_rom_;
339 size_t original_size = rom_copy.size();
340
341 // Apply comprehensive patch
342 auto patch_result = wrapper_->ApplyPatch(comprehensive_asm_path_.string(), rom_copy);
343 ASSERT_TRUE(patch_result.ok()) << patch_result.status().message();
344
345 const auto& result = patch_result.value();
346 EXPECT_TRUE(result.success) << "Patch failed with errors: "
347 << testing::PrintToString(result.errors);
348
349 // Verify ROM was modified correctly
350 EXPECT_NE(rom_copy, test_rom_);
351 EXPECT_GT(result.rom_size, 0);
352
353 // Verify symbols were extracted
354 EXPECT_GT(result.symbols.size(), 0);
355
356 // Check for specific expected symbols
357 bool found_main_entry = false;
358 bool found_init_graphics = false;
359 bool found_init_sound = false;
360
361 for (const auto& symbol : result.symbols) {
362 if (symbol.name == "main_entry") {
363 found_main_entry = true;
364 EXPECT_EQ(symbol.address, 0x008000);
365 } else if (symbol.name == "init_graphics") {
366 found_init_graphics = true;
367 } else if (symbol.name == "init_sound") {
368 found_init_sound = true;
369 }
370 }
371
372 EXPECT_TRUE(found_main_entry) << "main_entry symbol not found";
373 EXPECT_TRUE(found_init_graphics) << "init_graphics symbol not found";
374 EXPECT_TRUE(found_init_sound) << "init_sound symbol not found";
375}
376
377TEST_F(AsarIntegrationTest, AdvancedFeaturesIntegration) {
378 ASSERT_TRUE(wrapper_->Initialize().ok());
379
380 // Test advanced assembly features (macros, conditionals, etc.)
381 std::vector<uint8_t> rom_copy = test_rom_;
382
383 auto patch_result = wrapper_->ApplyPatch(advanced_asm_path_.string(), rom_copy);
384 ASSERT_TRUE(patch_result.ok()) << patch_result.status().message();
385
386 const auto& result = patch_result.value();
387 EXPECT_TRUE(result.success) << "Advanced patch failed: "
388 << testing::PrintToString(result.errors);
389
390 // Verify symbols from advanced assembly
391 bool found_advanced_entry = false;
392 bool found_test_loop = false;
393
394 for (const auto& symbol : result.symbols) {
395 if (symbol.name == "advanced_entry") {
396 found_advanced_entry = true;
397 } else if (symbol.name == "test_loop") {
398 found_test_loop = true;
399 }
400 }
401
402 EXPECT_TRUE(found_advanced_entry);
403 EXPECT_TRUE(found_test_loop);
404}
405
406TEST_F(AsarIntegrationTest, ErrorHandlingIntegration) {
407 ASSERT_TRUE(wrapper_->Initialize().ok());
408
409 // Test error handling with intentionally broken assembly
410 std::vector<uint8_t> rom_copy = test_rom_;
411
412 auto patch_result = wrapper_->ApplyPatch(error_asm_path_.string(), rom_copy);
413
414 // Should fail due to errors in assembly
415 EXPECT_FALSE(patch_result.ok());
416
417 // Verify error message contains useful information
418 EXPECT_THAT(patch_result.status().message(),
419 testing::AnyOf(
420 testing::HasSubstr("invalid"),
421 testing::HasSubstr("unknown"),
422 testing::HasSubstr("error")));
423}
424
425TEST_F(AsarIntegrationTest, SymbolExtractionWorkflow) {
426 ASSERT_TRUE(wrapper_->Initialize().ok());
427
428 // Extract symbols without applying patch
429 auto symbols_result = wrapper_->ExtractSymbols(comprehensive_asm_path_.string());
430 ASSERT_TRUE(symbols_result.ok()) << symbols_result.status().message();
431
432 const auto& symbols = symbols_result.value();
433 EXPECT_GT(symbols.size(), 0);
434
435 // Test symbol table operations
436 std::vector<uint8_t> rom_copy = test_rom_;
437 auto patch_result = wrapper_->ApplyPatch(comprehensive_asm_path_.string(), rom_copy);
438 ASSERT_TRUE(patch_result.ok());
439
440 // Test symbol lookup by name
441 auto main_symbol = wrapper_->FindSymbol("main_entry");
442 ASSERT_TRUE(main_symbol.has_value());
443 EXPECT_EQ(main_symbol->name, "main_entry");
444 EXPECT_EQ(main_symbol->address, 0x008000);
445
446 // Test symbols at address lookup
447 auto symbols_at_main = wrapper_->GetSymbolsAtAddress(0x008000);
448 EXPECT_GT(symbols_at_main.size(), 0);
449
450 bool found_main_at_address = false;
451 for (const auto& symbol : symbols_at_main) {
452 if (symbol.name == "main_entry") {
453 found_main_at_address = true;
454 break;
455 }
456 }
457 EXPECT_TRUE(found_main_at_address);
458}
459
460TEST_F(AsarIntegrationTest, MultipleOperationsIntegration) {
461 ASSERT_TRUE(wrapper_->Initialize().ok());
462
463 // Test multiple patch operations on the same wrapper instance
464 std::vector<uint8_t> rom_copy1 = test_rom_;
465 std::vector<uint8_t> rom_copy2 = test_rom_;
466
467 // First patch
468 auto result1 = wrapper_->ApplyPatch(comprehensive_asm_path_.string(), rom_copy1);
469 ASSERT_TRUE(result1.ok());
470 EXPECT_TRUE(result1->success);
471
472 // Reset and apply different patch
473 wrapper_->Reset();
474 auto result2 = wrapper_->ApplyPatch(advanced_asm_path_.string(), rom_copy2);
475 ASSERT_TRUE(result2.ok());
476 EXPECT_TRUE(result2->success);
477
478 // Verify that symbol tables are different
479 EXPECT_NE(result1->symbols.size(), result2->symbols.size());
480}
481
482TEST_F(AsarIntegrationTest, PatchFromStringIntegration) {
483 ASSERT_TRUE(wrapper_->Initialize().ok());
484
485 std::string patch_content = R"(
486org $009000
487string_patch_test:
488 lda #$42
489 sta $7E2000
490 jsr subroutine_test
491 rts
492
493subroutine_test:
494 lda #$FF
495 sta $7E2001
496 rts
497)";
498
499 std::vector<uint8_t> rom_copy = test_rom_;
500 auto result = wrapper_->ApplyPatchFromString(patch_content, rom_copy, test_dir_.string());
501
502 ASSERT_TRUE(result.ok()) << result.status().message();
503 EXPECT_TRUE(result->success);
504 EXPECT_GT(result->symbols.size(), 0);
505
506 // Check for expected symbols
507 bool found_string_patch = false;
508 bool found_subroutine = false;
509
510 for (const auto& symbol : result->symbols) {
511 if (symbol.name == "string_patch_test") {
512 found_string_patch = true;
513 EXPECT_EQ(symbol.address, 0x009000);
514 } else if (symbol.name == "subroutine_test") {
515 found_subroutine = true;
516 }
517 }
518
519 EXPECT_TRUE(found_string_patch);
520 EXPECT_TRUE(found_subroutine);
521}
522
523TEST_F(AsarIntegrationTest, LargeRomHandling) {
524 ASSERT_TRUE(wrapper_->Initialize().ok());
525
526 // Create a larger ROM for testing
527 std::vector<uint8_t> large_rom(4 * 1024 * 1024, 0); // 4MB ROM
528
529 // Set up basic SNES header
530 const uint32_t header_offset = 0x7FC0;
531 std::string title = "LARGE ROM TEST ";
532 std::copy(title.begin(), title.end(), large_rom.begin() + header_offset);
533 large_rom[header_offset + 21] = 0x20; // LoROM
534 large_rom[header_offset + 23] = 0x0C; // 4MB
535
536 auto result = wrapper_->ApplyPatch(comprehensive_asm_path_.string(), large_rom);
537 ASSERT_TRUE(result.ok());
538 EXPECT_TRUE(result->success);
539 EXPECT_EQ(large_rom.size(), result->rom_size);
540}
541
542} // namespace integration
543} // namespace test
544} // namespace yaze
std::unique_ptr< core::AsarWrapper > wrapper_
TEST_F(AsarIntegrationTest, FullWorkflowIntegration)
Main namespace for the application.
Definition controller.cc:20