make it sounder

This commit is contained in:
Corwin 2023-04-25 20:34:47 +01:00
parent d45486b189
commit 1eadd5bdf7
No known key found for this signature in database
2 changed files with 67 additions and 55 deletions

View file

@ -9,16 +9,19 @@ use image::GenericImage;
use io::Write; use io::Write;
use regex::Regex; use regex::Regex;
use runner::VideoBuffer; use runner::VideoBuffer;
use std::cell::Cell;
use std::io; use std::io;
use std::path::Path; use std::path::Path;
use std::rc::Rc;
#[derive(PartialEq, Eq, Debug, Clone)] #[derive(PartialEq, Eq, Debug, Clone, Copy)]
enum Status { enum Status {
Running, Running,
Failed, Failed,
Success, Success,
} }
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum Timing { enum Timing {
None, None,
WaitFor(i32), WaitFor(i32),
@ -28,77 +31,81 @@ enum Timing {
const TEST_RUNNER_TAG: u16 = 785; const TEST_RUNNER_TAG: u16 = 785;
fn test_file(file_to_run: &str) -> Status { 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 debug_reader_mutex = Regex::new(r"(?s)^\[(.*)\] GBA Debug: (.*)$").unwrap();
let tagged_cycles_reader = Regex::new(r"Cycles: (\d*) Tag: (\d*)").unwrap(); let tagged_cycles_reader = Regex::new(r"Cycles: (\d*) Tag: (\d*)").unwrap();
let mut mgba = runner::MGBA::new(file_to_run).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 finished = finished.clone();
let log_level = &captures[1]; let video_buffer = mgba.get_video_buffer();
let out = &captures[2]; let number_of_cycles = Cell::new(Timing::None);
if out.starts_with("image:") { mgba.set_logger(move |message| {
let image_path = out.strip_prefix("image:").unwrap(); if let Some(captures) = debug_reader_mutex.captures(message) {
match check_image_match(image_path, &video_buffer) { let log_level = &captures[1];
Err(e) => { let out = &captures[2];
println!("[failed]");
println!("{}", e); if out.starts_with("image:") {
finished = Status::Failed; 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);
} else if out.ends_with("...") { io::stdout().flush().expect("can't flush stdout");
print!("{}", out); } else if out.starts_with("Cycles: ") {
io::stdout().flush().expect("can't flush stdout"); if let Some(captures) = tagged_cycles_reader.captures(out) {
} else if out.starts_with("Cycles: ") { let num_cycles: i32 = captures[1].parse().unwrap();
if let Some(captures) = tagged_cycles_reader.captures(out) { let tag: u16 = captures[2].parse().unwrap();
let num_cycles: i32 = captures[1].parse().unwrap();
let tag: u16 = captures[2].parse().unwrap();
if tag == TEST_RUNNER_TAG { if tag == TEST_RUNNER_TAG {
number_of_cycles = match number_of_cycles { number_of_cycles.set(match number_of_cycles.get() {
Timing::WaitFor(n) => Timing::Difference(num_cycles - n), Timing::WaitFor(n) => Timing::Difference(num_cycles - n),
Timing::None => Timing::WaitFor(num_cycles), Timing::None => Timing::WaitFor(num_cycles),
Timing::Difference(_) => 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 { } else {
println!("{}", out); println!("{}", out);
} }
} else {
println!("{}", out);
}
if log_level == "FATAL" { if log_level == "FATAL" {
finished = Status::Failed; finished.set(Status::Failed);
} }
if out == "Tests finished successfully" { if out == "Tests finished successfully" {
finished = Status::Success; finished.set(Status::Success);
}
} }
} });
}); }
loop { loop {
mgba.advance_frame(); mgba.advance_frame();
if finished != Status::Running { if finished.get() != Status::Running {
break; break;
} }
} }
return finished; return finished.get();
} }
fn main() -> Result<(), Error> { fn main() -> Result<(), Error> {

View file

@ -3,6 +3,7 @@ use crate::bindings;
use std::ffi::c_void; use std::ffi::c_void;
use std::ffi::CStr; use std::ffi::CStr;
use std::ffi::CString; use std::ffi::CString;
use std::marker::PhantomData;
use std::os::raw::c_char; use std::os::raw::c_char;
#[allow( #[allow(
@ -12,8 +13,9 @@ use std::os::raw::c_char;
non_snake_case non_snake_case
)] )]
pub struct MGBA { pub struct MGBA<'a> {
mgba: *mut bindings::MGBA, mgba: *mut bindings::MGBA,
_phantom: PhantomData<&'a ()>,
} }
pub struct VideoBuffer { pub struct VideoBuffer {
@ -34,14 +36,17 @@ impl VideoBuffer {
} }
} }
impl MGBA { impl<'a> MGBA<'a> {
pub fn new(filename: &str) -> Result<Self, anyhow::Error> { pub fn new(filename: &str) -> Result<Self, anyhow::Error> {
let c_str = CString::new(filename).expect("should be able to make cstring from filename"); 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) }; let mgba = unsafe { bindings::new_runner(c_str.as_ptr() as *mut c_char) };
if mgba.is_null() { if mgba.is_null() {
Err(anyhow::anyhow!("could not create core")) Err(anyhow::anyhow!("could not create core"))
} else { } else {
Ok(MGBA { mgba }) Ok(MGBA {
mgba,
_phantom: PhantomData,
})
} }
} }
@ -57,7 +62,7 @@ impl MGBA {
pub fn advance_frame(&mut self) { pub fn advance_frame(&mut self) {
unsafe { bindings::advance_frame(self.mgba) } 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 { unsafe {
let callback = generate_c_callback(move |message: *mut c_char| { let callback = generate_c_callback(move |message: *mut c_char| {
logger( logger(
@ -99,7 +104,7 @@ extern "C" fn drop_box<T>(data: *mut c_void) {
} }
} }
impl Drop for MGBA { impl Drop for MGBA<'_> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { bindings::free_runner(self.mgba) } unsafe { bindings::free_runner(self.mgba) }
} }