use std::path::Path;
use super::helpers::{
decoded_png_rgb_crc, load_cgb_rom_with_model, load_gb_rom_with_model, run_frames_and_crc,
save_screen_png,
};
use crate::gb::bus::GbBus;
use crate::gb::console::Gb;
use crate::gb::model::{CgbModel, DmgModel};
const ASHIEPAWS_DIR: &str = "roms/gb/automated_tests/ashiepaws";
const RESULT_SCREEN_FRAMES: u32 = 300;
#[derive(Clone, Copy, Debug)]
enum HardwareMode {
Dmg,
Cgb,
}
#[derive(Clone, Copy, Debug)]
struct AshiepawsCase {
capture_name: &'static str,
rom_name: &'static str,
reference_png_name: &'static str,
hardware: HardwareMode,
expected_screen_crc: u32,
reference_png_crc: u32,
}
const BULLY_DMG: AshiepawsCase = AshiepawsCase {
capture_name: "bully_dmg",
rom_name: "bully.gb",
reference_png_name: "bully.png",
hardware: HardwareMode::Dmg,
expected_screen_crc: 0xB93F87C5,
reference_png_crc: 0xB93F87C5,
};
const BULLY_CGB: AshiepawsCase = AshiepawsCase {
capture_name: "bully_cgb",
rom_name: "bully.gb",
reference_png_name: "bully.png",
hardware: HardwareMode::Cgb,
expected_screen_crc: 0xB93F87C5,
reference_png_crc: 0xB93F87C5,
};
const STRIKETHROUGH_DMG: AshiepawsCase = AshiepawsCase {
capture_name: "strikethrough_dmg",
rom_name: "strikethrough.gb",
reference_png_name: "strikethrough.png",
hardware: HardwareMode::Dmg,
expected_screen_crc: 0xCCD4C45D,
reference_png_crc: 0x02BCDCAB,
};
#[test]
fn bully_matches_shootout_reference_on_dmg() {
assert_case_matches_reviewed_crc(BULLY_DMG);
}
#[test]
fn bully_matches_shootout_reference_on_cgb() {
assert_case_matches_reviewed_crc(BULLY_CGB);
}
#[test]
fn strikethrough_matches_reviewed_baseline_on_dmg() {
assert_case_matches_reviewed_crc(STRIKETHROUGH_DMG);
}
#[test]
fn bully_reference_png_matches_locked_crc() {
assert_reference_png_matches_locked_crc(BULLY_DMG);
}
#[test]
fn strikethrough_reference_png_matches_locked_crc() {
assert_reference_png_matches_locked_crc(STRIKETHROUGH_DMG);
}
#[test]
#[ignore = "screenshot helper - run manually with NESER_CAPTURE_SCREEN=1 for baseline review"]
fn capture_ashiepaws_screenshots() {
for case in [BULLY_DMG, BULLY_CGB, STRIKETHROUGH_DMG] {
let crc = run_case_frames_and_crc(case, RESULT_SCREEN_FRAMES);
println!(
"[ashiepaws] {} {:?} reference CRC={:#010X}",
case.rom_name,
case.hardware,
decoded_png_rgb_crc(&reference_png_path(case.reference_png_name))
);
println!(
"[ashiepaws] {} {:?} frame {RESULT_SCREEN_FRAMES} CRC={crc:#010X}",
case.rom_name, case.hardware
);
}
}
fn assert_case_matches_reviewed_crc(case: AshiepawsCase) {
let crc = run_case_and_crc(case);
assert_eq!(
crc, case.expected_screen_crc,
"{} {:?} frame {RESULT_SCREEN_FRAMES} CRC mismatch: got {crc:#010X}, expected {:#010X}",
case.rom_name, case.hardware, case.expected_screen_crc
);
}
fn assert_reference_png_matches_locked_crc(case: AshiepawsCase) {
let crc = decoded_png_rgb_crc(&reference_png_path(case.reference_png_name));
assert_eq!(
crc, case.reference_png_crc,
"{} reference PNG CRC mismatch: got {crc:#010X}, expected {:#010X}",
case.reference_png_name, case.reference_png_crc
);
}
fn run_case_and_crc(case: AshiepawsCase) -> u32 {
run_case_frames_and_crc(case, RESULT_SCREEN_FRAMES)
}
fn run_case_frames_and_crc(case: AshiepawsCase, frames: u32) -> u32 {
match case.hardware {
HardwareMode::Dmg => {
let mut gb = load_gb_rom_with_model(&rom_path(case.rom_name), DmgModel::DmgB);
run_loaded_case_and_crc(&mut gb, case, frames)
}
HardwareMode::Cgb => {
let mut gb = load_cgb_rom_with_model(&rom_path(case.rom_name), CgbModel::CgbE);
run_loaded_case_and_crc(&mut gb, case, frames)
}
}
}
fn run_loaded_case_and_crc<B: GbBus>(gb: &mut Gb<B>, case: AshiepawsCase, frames: u32) -> u32 {
let crc = run_frames_and_crc(gb, frames);
capture_screen_if_requested(gb, case, frames, crc);
crc
}
fn capture_screen_if_requested<B: GbBus>(gb: &Gb<B>, case: AshiepawsCase, frames: u32, crc: u32) {
if std::env::var_os("NESER_CAPTURE_SCREEN").is_none() {
return;
}
let dir = Path::new("target/ashiepaws-captures");
std::fs::create_dir_all(dir).expect("create ashiepaws capture directory");
let path = dir.join(format!("{}_frame_{frames}.png", case.capture_name));
save_screen_png(gb, path.to_str().expect("valid ashiepaws capture path"));
println!(
"[ashiepaws] {} {:?} frame {frames}: CRC={crc:#010X}, PNG saved to {path:?}",
case.rom_name, case.hardware
);
}
fn rom_path(rom_name: &str) -> String {
format!("{ASHIEPAWS_DIR}/{rom_name}")
}
fn reference_png_path(png_name: &str) -> std::path::PathBuf {
Path::new(ASHIEPAWS_DIR).join(png_name)
}