Raw-rs: run tests in parallel (#1968)

* Run raw-rs tests in parallel

* cargo fmt

* Fix clippy warnings

* Use Fast CompressionType to speed up the tests

* Fix benches

* Use cfg_attr instead of cfg.

---------

Co-authored-by: hypercube <0hypercube@gmail.com>
Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Elbert Ronnie 2024-09-20 16:39:58 +05:30 committed by GitHub
parent e352c7fa71
commit d68f91ccca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 67 additions and 38 deletions

5
Cargo.lock generated
View file

@ -4829,8 +4829,9 @@ dependencies = [
"build-camera-data",
"image 0.25.2",
"libraw-rs",
"num_enum 0.7.3",
"reqwest 0.12.7",
"num_enum 0.7.2",
"rayon",
"reqwest 0.12.5",
"tag-derive",
"thiserror",
]

View file

@ -13,7 +13,7 @@ repository = "https://github.com/GraphiteEditor/Graphite/tree/master/libraries/r
documentation = "https://docs.rs/raw-rs"
[features]
raw-rs-tests = ["dep:image", "dep:libraw-rs", "dep:reqwest"]
raw-rs-tests = ["dep:image", "dep:libraw-rs", "dep:reqwest", "dep:rayon"]
[dependencies]
# Local dependencies
@ -33,3 +33,4 @@ reqwest = { workspace = true, optional = true }
# Optional dependencies (should be dev dependencies, but Cargo currently doesn't allow optional dev dependencies)
libraw-rs = { version = "0.0.4", optional = true }
rayon = { version = "1.10.0", optional = true }

View file

@ -6,11 +6,13 @@ use raw_rs::RawImage;
use image::codecs::png::{CompressionType, FilterType, PngEncoder};
use image::{ColorType, ImageEncoder};
use libraw::Processor;
use rayon::prelude::*;
use std::collections::HashMap;
use std::fmt::Write;
use std::fs::{create_dir, metadata, read_dir, File};
use std::io::{BufWriter, Cursor, Read};
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration;
const TEST_FILES: [&str; 3] = ["ILCE-7M3-ARW2.3.5-blossoms.arw", "ILCE-7RM4-ARW2.3.5-kestrel.arw", "ILCE-6000-ARW2.3.1-windsock.arw"];
@ -21,52 +23,74 @@ const BASE_PATH: &str = "./tests/images/";
fn test_images_match_with_libraw() {
download_images();
let mut failed_tests = 0;
read_dir(BASE_PATH)
let paths: Vec<_> = read_dir(BASE_PATH)
.unwrap()
.map(|dir_entry| dir_entry.unwrap().path())
.filter(|path| path.is_file() && path.file_name().map(|file_name| file_name != ".gitkeep").unwrap_or(false))
.for_each(|path| {
let mut f = File::open(&path).unwrap();
let mut content = vec![];
f.read_to_end(&mut content).unwrap();
.collect();
print!("{} => ", path.display());
let failed_tests = if std::env::var("RAW_RS_TEST_RUN_SEQUENTIALLY").is_ok() {
let mut failed_tests = 0;
let raw_image = match test_raw_data(&content) {
Err(err_msg) => {
failed_tests += 1;
return println!("{}", err_msg);
}
Ok(raw_image) => raw_image,
};
// TODO: The code below is kept commented because raw data to final image processing is
// incomplete. Remove this once it is done.
// if let Err(err_msg) = test_final_image(&content, raw_image) {
// failed_tests += 1;
// return println!("{}", err_msg);
// };
println!("Passed");
// TODO: Remove this later
let mut image = raw_rs::process_8bit(raw_image);
store_image(&path, "raw_rs", &mut image.data, image.width, image.height);
let processor = Processor::new();
let libraw_image = processor.process_8bit(&content).unwrap();
let mut data = Vec::from_iter(libraw_image.iter().copied());
store_image(&path, "libraw_rs", &mut data[..], libraw_image.width() as usize, libraw_image.height() as usize);
paths.iter().for_each(|path| {
if !test_image(path) {
failed_tests += 1;
}
});
failed_tests
} else {
let failed_tests = AtomicUsize::new(0);
paths.par_iter().for_each(|path| {
if !test_image(path) {
failed_tests.fetch_add(1, Ordering::SeqCst);
}
});
failed_tests.load(Ordering::SeqCst)
};
if failed_tests != 0 {
panic!("{} images have failed the tests", failed_tests);
}
}
fn test_image(path: &Path) -> bool {
let mut f = File::open(path).unwrap();
let mut content = vec![];
f.read_to_end(&mut content).unwrap();
let raw_image = match test_raw_data(&content) {
Err(err_msg) => {
println!("{} => {}", path.display(), err_msg);
return false;
}
Ok(raw_image) => raw_image,
};
// TODO: The code below is kept commented because raw data to final image processing is
// incomplete. Remove this once it is done.
// if let Err(err_msg) = test_final_image(&content, raw_image) {
// failed_tests += 1;
// return println!("{}", err_msg);
// };
println!("{} => Passed", path.display());
// TODO: Remove this later
let mut image = raw_rs::process_8bit(raw_image);
store_image(path, "raw_rs", &mut image.data, image.width, image.height);
let processor = Processor::new();
let libraw_image = processor.process_8bit(&content).unwrap();
let mut data = Vec::from_iter(libraw_image.iter().copied());
store_image(path, "libraw_rs", &mut data[..], libraw_image.width() as usize, libraw_image.height() as usize);
true
}
fn store_image(path: &Path, suffix: &str, data: &mut [u8], width: usize, height: usize) {
let mut output_path = PathBuf::new();
if let Some(parent) = path.parent() {
@ -85,7 +109,7 @@ fn store_image(path: &Path, suffix: &str, data: &mut [u8], width: usize, height:
output_path.set_extension("png");
let file = BufWriter::new(File::create(output_path).unwrap());
let png_encoder = PngEncoder::new_with_quality(file, CompressionType::Best, FilterType::Adaptive);
let png_encoder = PngEncoder::new_with_quality(file, CompressionType::Fast, FilterType::Adaptive);
png_encoder.write_image(data, width as u32, height as u32, ColorType::Rgb8.into()).unwrap();
}

View file

@ -42,6 +42,8 @@ fn compile_to_proto(c: &mut Criterion) {
#[cfg_attr(all(feature = "iai", not(feature = "criterion")), library_benchmark)]
#[cfg_attr(all(feature = "iai", not(feature="criterion")), benches::with_setup(args = ["isometric-fountain", "painted-dreams", "procedural-string-lights", "red-dress", "valley-of-spires"], setup = load_from_name))]
// Note that this can not be disabled with a `#[cfg(...)]` because this causes a compile error.
// Therefore negated condition is used in `#[cfg_attr(...)]` with the attribute `cfg(any())` that is always false.
pub fn iai_compile_to_proto(_input: NodeNetwork) {
#[cfg(all(feature = "iai", not(feature = "criterion")))]
black_box(compile(_input));
@ -57,5 +59,6 @@ library_benchmark_group!(name = compile_group; benchmarks = iai_compile_to_proto
#[cfg(all(not(feature = "criterion"), feature = "iai"))]
main!(library_benchmark_groups = compile_group);
// An empty main function so the crate compiles with no features enabled.
#[cfg(all(not(feature = "criterion"), not(feature = "iai")))]
fn main() {}