mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-07-07 15:55:00 +00:00
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:
parent
e352c7fa71
commit
d68f91ccca
4 changed files with 67 additions and 38 deletions
5
Cargo.lock
generated
5
Cargo.lock
generated
|
@ -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",
|
||||
]
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue