diff --git a/mgba-test-runner/Cargo.lock b/mgba-test-runner/Cargo.lock index 0d9c8e6d..03812702 100644 --- a/mgba-test-runner/Cargo.lock +++ b/mgba-test-runner/Cargo.lock @@ -1,5 +1,17 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + [[package]] name = "aho-corasick" version = "0.7.15" @@ -35,6 +47,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + [[package]] name = "bindgen" version = "0.53.3" @@ -65,6 +83,18 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "bytemuck" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bed57e2090563b83ba8f83366628ce535a7584c9afa4c9fc0612a03925c6df58" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + [[package]] name = "cc" version = "1.0.67" @@ -121,6 +151,81 @@ dependencies = [ "vec_map", ] +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "crc32fast" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" +dependencies = [ + "cfg-if 1.0.0", + "lazy_static", +] + +[[package]] +name = "deflate" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" +dependencies = [ + "adler32", + "byteorder", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + [[package]] name = "env_logger" version = "0.7.1" @@ -134,6 +239,16 @@ dependencies = [ "termcolor", ] +[[package]] +name = "gif" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a668f699973d0f573d15749b7002a9ac9e1f9c6b220e7b165601334c173d8de" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "glob" version = "0.3.0" @@ -158,6 +273,25 @@ dependencies = [ "quick-error", ] +[[package]] +name = "image" +version = "0.23.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "gif", + "jpeg-decoder", + "num-iter", + "num-rational", + "num-traits", + "png", + "scoped_threadpool", + "tiff", +] + [[package]] name = "jobserver" version = "0.1.21" @@ -167,6 +301,15 @@ dependencies = [ "libc", ] +[[package]] +name = "jpeg-decoder" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2" +dependencies = [ + "rayon", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -210,6 +353,15 @@ version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +[[package]] +name = "memoffset" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" +dependencies = [ + "autocfg", +] + [[package]] name = "mgba-test-runner" version = "0.1.0" @@ -217,9 +369,29 @@ dependencies = [ "anyhow", "bindgen", "cc", + "image", "regex", ] +[[package]] +name = "miniz_oxide" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" +dependencies = [ + "adler32", +] + +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + [[package]] name = "nom" version = "5.1.2" @@ -230,12 +402,75 @@ dependencies = [ "version_check", ] +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "peeking_take_while" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +[[package]] +name = "png" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" +dependencies = [ + "bitflags", + "crc32fast", + "deflate", + "miniz_oxide 0.3.7", +] + [[package]] name = "proc-macro2" version = "1.0.27" @@ -260,6 +495,31 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rayon" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + [[package]] name = "regex" version = "1.4.5" @@ -283,6 +543,18 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "scoped_threadpool" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "shlex" version = "0.1.1" @@ -313,6 +585,17 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "tiff" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437" +dependencies = [ + "jpeg-decoder", + "miniz_oxide 0.4.4", + "weezl", +] + [[package]] name = "unicode-width" version = "0.1.8" @@ -337,6 +620,12 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +[[package]] +name = "weezl" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b77fdfd5a253be4ab714e4ffa3c49caf146b4de743e97510c0656cf90f1e8e" + [[package]] name = "which" version = "3.1.1" diff --git a/mgba-test-runner/Cargo.toml b/mgba-test-runner/Cargo.toml index 73a58841..2f775c9a 100644 --- a/mgba-test-runner/Cargo.toml +++ b/mgba-test-runner/Cargo.toml @@ -9,6 +9,7 @@ edition = "2018" [dependencies] regex = "1" anyhow = "1.0" +image = "0.23" [build-dependencies] cc = { version = "1.0", features = ["parallel"] } diff --git a/mgba-test-runner/src/main.rs b/mgba-test-runner/src/main.rs index 645cb5f8..856ac5ad 100644 --- a/mgba-test-runner/src/main.rs +++ b/mgba-test-runner/src/main.rs @@ -2,8 +2,10 @@ mod runner; use anyhow::{anyhow, Error}; +use image::io::Reader; use io::Write; use regex::Regex; +use runner::VideoBuffer; use std::cell::RefCell; use std::io; use std::path::Path; @@ -20,13 +22,27 @@ fn test_file(file_to_run: &str) -> Status { let finished = Rc::new(RefCell::new(Status::Running)); let debug_reader_mutex = Regex::new(r"^\[(.*)\] GBA Debug: (.*)$").unwrap(); + let mut mgba = runner::MGBA::new(file_to_run); + let video_buffer = mgba.get_video_buffer(); + let fin_closure = Rc::clone(&finished); runner::set_logger(Box::new(move |message| { if let Some(captures) = debug_reader_mutex.captures(message) { let log_level = &captures[1]; let out = &captures[2]; - if out.ends_with("...") { + 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); + let mut done = fin_closure.borrow_mut(); + *done = Status::Failed; + } + Ok(_) => {} + } + } else if out.ends_with("...") { print!("{}", out); io::stdout().flush().expect("can't flush stdout"); } else { @@ -45,8 +61,6 @@ fn test_file(file_to_run: &str) -> Status { } })); - let mut mgba = runner::MGBA::new(file_to_run); - loop { mgba.advance_frame(); let done = finished.borrow(); @@ -81,9 +95,42 @@ fn main() -> Result<(), Error> { fn gba_colour_to_rgba(colour: u32) -> [u8; 4] { [ - (((((colour) << 3) & 0xF8) * 0x21) >> 5) as u8, - (((((colour) >> 2) & 0xF8) * 0x21) >> 5) as u8, - (((((colour) >> 7) & 0xF8) * 0x21) >> 5) as u8, + ((colour >> 0) & 0xFF) as u8, + ((colour >> 8) & 0xFF) as u8, + ((colour >> 16) & 0xFF) as u8, 255, ] } + +fn rgba_to_gba_to_rgba(c: [u8; 4]) -> [u8; 4] { + let mut n = c.clone(); + n.iter_mut() + .for_each(|a| *a = ((((*a as u32 >> 3) << 3) * 0x21) >> 5) as u8); + n +} + +fn check_image_match(image_path: &str, video_buffer: &VideoBuffer) -> Result<(), Error> { + let expected_image = Reader::open(image_path)?.decode()?; + let expected = expected_image + .as_rgba8() + .ok_or(anyhow!("cannot convert to rgba8"))?; + + let (buf_dim_x, buf_dim_y) = video_buffer.get_size(); + let (exp_dim_x, exp_dim_y) = expected.dimensions(); + if (buf_dim_x != exp_dim_x) || (buf_dim_y != exp_dim_y) { + return Err(anyhow!("image sizes do not match")); + } + for y in 0..buf_dim_y { + for x in 0..buf_dim_x { + let video_pixel = video_buffer.get_pixel(x, y); + let image_pixel = expected.get_pixel(x, y); + let video_pixel = gba_colour_to_rgba(video_pixel); + let image_pixel = rgba_to_gba_to_rgba(image_pixel.0); + if image_pixel != video_pixel { + return Err(anyhow!("images do not match")); + } + } + } + + Ok(()) +}