|
1 | 1 | package eu.rekawek.coffeegb.core.integration.mealybug; |
2 | 2 |
|
| 3 | +import eu.rekawek.coffeegb.core.Gameboy; |
3 | 4 | import eu.rekawek.coffeegb.core.GameboyType; |
| 5 | +import eu.rekawek.coffeegb.core.integration.support.ImageTestRunner; |
4 | 6 | import org.junit.Test; |
5 | 7 | import org.junit.runner.RunWith; |
6 | 8 | import org.junit.runners.Parameterized; |
7 | 9 |
|
8 | 10 | import java.io.File; |
9 | | -import java.nio.file.Path; |
10 | 11 | import java.nio.file.Paths; |
11 | 12 | import java.util.ArrayList; |
12 | | -import java.util.Arrays; |
13 | 13 | import java.util.Collection; |
14 | 14 | import java.util.List; |
| 15 | +import java.util.Map; |
15 | 16 |
|
16 | | -import static eu.rekawek.coffeegb.core.integration.support.RomTestUtils.testRomWithImage; |
| 17 | +import static org.junit.Assert.assertTrue; |
17 | 18 |
|
18 | 19 | /** |
19 | | - * Mealybug Tearoom tests: mid-scanline writes to the PPU registers (SCX/SCY, LCDC bits, BGP) |
20 | | - * during mode 3. The expected images were captured from real hardware (DMG-CPU B where the |
21 | | - * revisions differ, DMG-blob otherwise); a test passes when the frame at the LD B,B breakpoint |
22 | | - * is pixel-identical. |
| 20 | + * Mealybug Tearoom tests: mid-scanline writes to the PPU registers during mode 3, compared |
| 21 | + * against photos of a real DMG-CPU B (DMG-blob where no CPU B photo exists). The tests run |
| 22 | + * with the FAST_FORWARD boot because they reuse the boot ROM's VRAM leftovers (the (r) logo |
| 23 | + * tile) as sprite data. |
| 24 | + * |
| 25 | + * <p>Each ROM asserts that the number of differing pixels does not exceed its known |
| 26 | + * baseline, so the pixel-exact tests are locked at 0 and the rest cannot regress silently. |
| 27 | + * When an accuracy change improves a score, tighten the baseline here. |
23 | 28 | */ |
24 | 29 | @RunWith(Parameterized.class) |
25 | 30 | public class MealybugRomTest { |
26 | 31 |
|
27 | | - private final Path rom; |
| 32 | + private static final Map<String, Integer> BASELINES = Map.ofEntries( |
| 33 | + Map.entry("m2_win_en_toggle.gb", 0), |
| 34 | + Map.entry("m3_bgp_change.gb", 21), |
| 35 | + Map.entry("m3_bgp_change_sprites.gb", 20), |
| 36 | + Map.entry("m3_lcdc_bg_en_change.gb", 11), |
| 37 | + Map.entry("m3_lcdc_bg_map_change.gb", 192), |
| 38 | + Map.entry("m3_lcdc_obj_en_change.gb", 2), |
| 39 | + Map.entry("m3_lcdc_obj_en_change_variant.gb", 54), |
| 40 | + Map.entry("m3_lcdc_obj_size_change.gb", 360), |
| 41 | + Map.entry("m3_lcdc_obj_size_change_scx.gb", 230), |
| 42 | + Map.entry("m3_lcdc_tile_sel_change.gb", 776), |
| 43 | + Map.entry("m3_lcdc_tile_sel_win_change.gb", 1062), |
| 44 | + Map.entry("m3_lcdc_win_en_change_multiple.gb", 8316), |
| 45 | + Map.entry("m3_lcdc_win_en_change_multiple_wx.gb", 15756), |
| 46 | + Map.entry("m3_lcdc_win_map_change.gb", 212), |
| 47 | + Map.entry("m3_obp0_change.gb", 0), |
| 48 | + Map.entry("m3_scx_high_5_bits.gb", 84), |
| 49 | + Map.entry("m3_scx_low_3_bits.gb", 0), |
| 50 | + Map.entry("m3_scy_change.gb", 4269), |
| 51 | + Map.entry("m3_window_timing.gb", 3), |
| 52 | + Map.entry("m3_window_timing_wx_0.gb", 130), |
| 53 | + Map.entry("m3_wx_4_change.gb", 229), |
| 54 | + Map.entry("m3_wx_4_change_sprites.gb", 10), |
| 55 | + Map.entry("m3_wx_5_change.gb", 638), |
| 56 | + Map.entry("m3_wx_6_change.gb", 13799)); |
28 | 57 |
|
29 | | - public MealybugRomTest(String name, Path rom) { |
| 58 | + private final File rom; |
| 59 | + |
| 60 | + private final int baseline; |
| 61 | + |
| 62 | + public MealybugRomTest(String name, File rom, int baseline) { |
30 | 63 | this.rom = rom; |
| 64 | + this.baseline = baseline; |
31 | 65 | } |
32 | 66 |
|
33 | 67 | @Parameterized.Parameters(name = "{0}") |
34 | 68 | public static Collection<Object[]> data() { |
35 | 69 | File dir = Paths.get("src/test/resources/roms/mealybug").toFile(); |
36 | | - File[] roms = dir.listFiles((d, n) -> n.endsWith(".gb")); |
37 | | - Arrays.sort(roms); |
38 | 70 | List<Object[]> params = new ArrayList<>(); |
39 | | - for (File rom : roms) { |
40 | | - // ROMs without an expected image are CGB-only variants |
41 | | - if (new File(dir, rom.getName().replace(".gb", ".png")).isFile()) { |
42 | | - params.add(new Object[]{rom.getName(), rom.toPath()}); |
| 71 | + for (Map.Entry<String, Integer> e : BASELINES.entrySet()) { |
| 72 | + File rom = new File(dir, e.getKey()); |
| 73 | + if (rom.isFile()) { |
| 74 | + params.add(new Object[]{e.getKey(), rom, e.getValue()}); |
43 | 75 | } |
44 | 76 | } |
| 77 | + params.sort((a, b) -> ((String) a[0]).compareTo((String) b[0])); |
45 | 78 | return params; |
46 | 79 | } |
47 | 80 |
|
48 | 81 | @Test |
49 | 82 | public void test() throws Exception { |
50 | | - // FAST_FORWARD: the tests reuse the boot ROM's VRAM leftovers (the (r) logo tile) |
51 | | - testRomWithImage(rom, GameboyType.DMG, eu.rekawek.coffeegb.core.Gameboy.BootstrapMode.FAST_FORWARD); |
| 83 | + ImageTestRunner runner = |
| 84 | + new ImageTestRunner(rom, GameboyType.DMG, Gameboy.BootstrapMode.FAST_FORWARD); |
| 85 | + ImageTestRunner.TestResult result = runner.runTest(); |
| 86 | + int[] actual = result.getResultRGB(); |
| 87 | + int[] expected = result.getExpectedRGB(); |
| 88 | + int diff = 0; |
| 89 | + for (int i = 0; i < expected.length; i++) { |
| 90 | + if (actual[i] != expected[i]) { |
| 91 | + diff++; |
| 92 | + } |
| 93 | + } |
| 94 | + assertTrue("diff pixels " + diff + " exceeds the known baseline " + baseline |
| 95 | + + " - an accuracy regression", diff <= baseline); |
52 | 96 | } |
53 | 97 | } |
0 commit comments