From 1eadd5bdf7169e173f6de73dd6f434b7bd389d84 Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 25 Apr 2023 20:34:47 +0100 Subject: [PATCH] make it sounder --- mgba-test-runner/src/main.rs | 107 ++++++++++++++++++--------------- mgba-test-runner/src/runner.rs | 15 +++-- 2 files changed, 67 insertions(+), 55 deletions(-) diff --git a/mgba-test-runner/src/main.rs b/mgba-test-runner/src/main.rs index ac07b1d2..a733b4ee 100644 --- a/mgba-test-runner/src/main.rs +++ b/mgba-test-runner/src/main.rs @@ -9,16 +9,19 @@ use image::GenericImage; use io::Write; use regex::Regex; use runner::VideoBuffer; +use std::cell::Cell; use std::io; use std::path::Path; +use std::rc::Rc; -#[derive(PartialEq, Eq, Debug, Clone)] +#[derive(PartialEq, Eq, Debug, Clone, Copy)] enum Status { Running, Failed, Success, } +#[derive(Clone, Copy, PartialEq, Eq, Debug)] enum Timing { None, WaitFor(i32), @@ -28,77 +31,81 @@ enum Timing { const TEST_RUNNER_TAG: u16 = 785; fn test_file(file_to_run: &str) -> Status { - let mut finished = Status::Running; + let finished = Rc::new(Cell::new(Status::Running)); let debug_reader_mutex = Regex::new(r"(?s)^\[(.*)\] GBA Debug: (.*)$").unwrap(); let tagged_cycles_reader = Regex::new(r"Cycles: (\d*) Tag: (\d*)").unwrap(); let mut mgba = runner::MGBA::new(file_to_run).unwrap(); - let video_buffer = mgba.get_video_buffer(); - let mut number_of_cycles = Timing::None; - mgba.set_logger(|message| { - if let Some(captures) = debug_reader_mutex.captures(message) { - let log_level = &captures[1]; - let out = &captures[2]; + { + let finished = finished.clone(); + let video_buffer = mgba.get_video_buffer(); + let number_of_cycles = Cell::new(Timing::None); - if out.starts_with("image:") { - let image_path = out.strip_prefix("image:").unwrap(); - match check_image_match(image_path, &video_buffer) { - Err(e) => { - println!("[failed]"); - println!("{}", e); - finished = Status::Failed; + mgba.set_logger(move |message| { + if let Some(captures) = debug_reader_mutex.captures(message) { + let log_level = &captures[1]; + let out = &captures[2]; + + if out.starts_with("image:") { + let image_path = out.strip_prefix("image:").unwrap(); + match check_image_match(image_path, &video_buffer) { + Err(e) => { + println!("[failed]"); + println!("{}", e); + finished.set(Status::Failed); + } + Ok(_) => {} } - Ok(_) => {} - } - } else if out.ends_with("...") { - print!("{}", out); - io::stdout().flush().expect("can't flush stdout"); - } else if out.starts_with("Cycles: ") { - if let Some(captures) = tagged_cycles_reader.captures(out) { - let num_cycles: i32 = captures[1].parse().unwrap(); - let tag: u16 = captures[2].parse().unwrap(); + } else if out.ends_with("...") { + print!("{}", out); + io::stdout().flush().expect("can't flush stdout"); + } else if out.starts_with("Cycles: ") { + if let Some(captures) = tagged_cycles_reader.captures(out) { + let num_cycles: i32 = captures[1].parse().unwrap(); + let tag: u16 = captures[2].parse().unwrap(); - if tag == TEST_RUNNER_TAG { - number_of_cycles = match number_of_cycles { - Timing::WaitFor(n) => Timing::Difference(num_cycles - n), - Timing::None => Timing::WaitFor(num_cycles), - Timing::Difference(_) => Timing::WaitFor(num_cycles), - }; + if tag == TEST_RUNNER_TAG { + number_of_cycles.set(match number_of_cycles.get() { + Timing::WaitFor(n) => Timing::Difference(num_cycles - n), + Timing::None => Timing::WaitFor(num_cycles), + Timing::Difference(_) => Timing::WaitFor(num_cycles), + }); + } + } + } else if out == "[ok]" { + if let Timing::Difference(cycles) = number_of_cycles.get() { + println!( + "[ok: {} c ≈ {} s]", + cycles, + ((cycles as f64 / (16.78 * 1_000_000.0)) * 100.0).round() / 100.0 + ); + } else { + println!("{}", out); } - } - } else if out == "[ok]" { - if let Timing::Difference(cycles) = number_of_cycles { - println!( - "[ok: {} c ≈ {} s]", - cycles, - ((cycles as f64 / (16.78 * 1_000_000.0)) * 100.0).round() / 100.0 - ); } else { println!("{}", out); } - } else { - println!("{}", out); - } - if log_level == "FATAL" { - finished = Status::Failed; - } + if log_level == "FATAL" { + finished.set(Status::Failed); + } - if out == "Tests finished successfully" { - finished = Status::Success; + if out == "Tests finished successfully" { + finished.set(Status::Success); + } } - } - }); + }); + } loop { mgba.advance_frame(); - if finished != Status::Running { + if finished.get() != Status::Running { break; } } - return finished; + return finished.get(); } fn main() -> Result<(), Error> { diff --git a/mgba-test-runner/src/runner.rs b/mgba-test-runner/src/runner.rs index 44b35e36..85a1640f 100644 --- a/mgba-test-runner/src/runner.rs +++ b/mgba-test-runner/src/runner.rs @@ -3,6 +3,7 @@ use crate::bindings; use std::ffi::c_void; use std::ffi::CStr; use std::ffi::CString; +use std::marker::PhantomData; use std::os::raw::c_char; #[allow( @@ -12,8 +13,9 @@ use std::os::raw::c_char; non_snake_case )] -pub struct MGBA { +pub struct MGBA<'a> { mgba: *mut bindings::MGBA, + _phantom: PhantomData<&'a ()>, } pub struct VideoBuffer { @@ -34,14 +36,17 @@ impl VideoBuffer { } } -impl MGBA { +impl<'a> MGBA<'a> { pub fn new(filename: &str) -> Result { let c_str = CString::new(filename).expect("should be able to make cstring from filename"); let mgba = unsafe { bindings::new_runner(c_str.as_ptr() as *mut c_char) }; if mgba.is_null() { Err(anyhow::anyhow!("could not create core")) } else { - Ok(MGBA { mgba }) + Ok(MGBA { + mgba, + _phantom: PhantomData, + }) } } @@ -57,7 +62,7 @@ impl MGBA { pub fn advance_frame(&mut self) { unsafe { bindings::advance_frame(self.mgba) } } - pub fn set_logger(&mut self, mut logger: impl FnMut(&str)) { + pub fn set_logger(&mut self, logger: impl Fn(&str) + 'a) { unsafe { let callback = generate_c_callback(move |message: *mut c_char| { logger( @@ -99,7 +104,7 @@ extern "C" fn drop_box(data: *mut c_void) { } } -impl Drop for MGBA { +impl Drop for MGBA<'_> { fn drop(&mut self) { unsafe { bindings::free_runner(self.mgba) } }