From 84d2dc2f5041d2b02e93f5659269bda41cc85bae Mon Sep 17 00:00:00 2001 From: RunDevelopment Date: Wed, 26 Nov 2025 14:19:27 +0100 Subject: [PATCH] Add test for file variations --- Cargo.toml | 1 + tests/reference.rs | 80 +++++++++++++++++++++++++++++++++++++++------- 2 files changed, 69 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c3bd6e8..f049272 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,4 +33,5 @@ zip = { version = "5.1.1", default-features = false, features = ["deflate"], opt [dev-dependencies] image = { version = "0.25.8", default-features = false, features = ["png", "tiff"] } +tempfile = "3.23.0" walkdir = "2.5.0" diff --git a/tests/reference.rs b/tests/reference.rs index 5175366..b17ca00 100644 --- a/tests/reference.rs +++ b/tests/reference.rs @@ -1,6 +1,21 @@ +use std::path::PathBuf; + use image::ColorType; +use tempfile::tempdir; use walkdir::WalkDir; +fn iter_decoding_images() -> impl Iterator { + WalkDir::new("tests/images") + .into_iter() + .filter_map(|entry| entry.ok()) + .filter(|entry| { + entry.file_type().is_file() + && entry.path().extension().unwrap() != "png" + && entry.path().extension().unwrap() != "tiff" + }) + .map(|entry| entry.path().to_owned()) +} + /// Test decoding of all test images in `tests/images/` against reference images /// (either PNG or TIFF). /// @@ -23,20 +38,14 @@ fn test_decoding() { let bless = std::env::var("BLESS").is_ok(); let mut errors = vec![]; - for entry in WalkDir::new("tests/images") { - let entry = entry.unwrap(); - if !entry.file_type().is_file() - || entry.path().extension().unwrap() == "png" - || entry.path().extension().unwrap() == "tiff" - { - continue; - } + for img_path in iter_decoding_images() { + let img_path = img_path.as_path(); let mut add_error = |e: &str| { - errors.push(format!("{}: {}", entry.path().display(), e)); + errors.push(format!("{}: {}", img_path.display(), e)); }; - let img = match image::open(entry.path()) { + let img = match image::open(img_path) { Ok(i) => i, Err(e) => { add_error(&format!("Cannot decode image: {e}")); @@ -48,7 +57,7 @@ fn test_decoding() { ColorType::Rgb32F | ColorType::Rgba32F => "tiff", _ => "png", }; - let ref_path = &entry.path().with_extension(ref_format); + let ref_path = &img_path.with_extension(ref_format); let save_reference = || { if bless { @@ -74,7 +83,7 @@ fn test_decoding() { img, reference, "Image {} does not match reference", - entry.path().display() + img_path.display() ); } @@ -82,3 +91,50 @@ fn test_decoding() { panic!("Decoding errors:\n{}", errors.join("\n")); } } + +/// This test takes valid images from `tests/images/` and applies various +/// modifications to them (truncation, extension with garbage data, byte mutations) +/// and ensures that decoding them does not panic. +#[test] +fn test_decoding_variations() { + image_extras::register(); + + let tmp_dir = tempdir().unwrap(); + + for img_path in iter_decoding_images() { + let img_path = img_path.as_path(); + let ext = img_path.extension().unwrap().to_string_lossy().to_string(); + + let bytes = std::fs::read(img_path).unwrap(); + + // test truncations + for i in (0..1000.min(bytes.len())).step_by(37) { + let shorter_path = tmp_dir.path().join(format!("truncation.{ext}")); + std::fs::write(&shorter_path, &bytes[..i]).unwrap(); + _ = image::open(shorter_path); // just check it doesn't panic + } + + // test extension + let mut longer = bytes.clone(); + longer.extend_from_slice(b"I am garbage data at the end of the file!"); + let longer_path = tmp_dir.path().join(format!("extension.{ext}")); + std::fs::write(&longer_path, &longer).unwrap(); + _ = image::open(longer_path); // just check it doesn't panic + + // test mutations + let mut mutations_buffer = bytes.clone().into_boxed_slice(); + for i in 0..16.min(mutations_buffer.len()) { + let mask = 0b0101_0101; + mutations_buffer[i] ^= mask; + + let shorter_path = tmp_dir.path().join(format!("mutations.{ext}")); + std::fs::write(&shorter_path, &bytes[..i]).unwrap(); + _ = image::open(shorter_path); // just check it doesn't panic + + // undo mutation + mutations_buffer[i] ^= mask; + } + } + + tmp_dir.close().unwrap(); +}