Fix 26 bugs, edge cases, and consistency issues from fifth audit pass
Critical: undo toast now trashes only batch output files (not entire dir), JPEG scanline write errors propagated, selective metadata write result returned. High: zero-dimension guards in ResizeConfig/fit_within, negative aspect ratio rejection, FM integration toggle infinite recursion guard, saturating counter arithmetic in executor. Medium: PNG compression level passed to oxipng, pct mode updates job_config, external file loading updates step indicator, CLI undo removes history entries, watch config write failures reported, fast-copy path reads image dimensions for rename templates, discovery excludes unprocessable formats (heic/svg/ico/jxl), CLI warns on invalid algorithm/overwrite values, resolve_collision trailing dot fix, generation guards on all preview threads to cancel stale results, default DPI aligned to 0, watermark text width uses char count not byte length. Low: binary path escaped in Nautilus extension, file dialog filter aligned with discovery, reset_wizard clears preset_mode and output_dir.
This commit is contained in:
@@ -42,3 +42,84 @@ fn privacy_mode_strips_gps() {
|
||||
strip_metadata(&input, &output, &MetadataConfig::Privacy).unwrap();
|
||||
assert!(output.exists());
|
||||
}
|
||||
|
||||
fn create_test_png(path: &Path) {
|
||||
let img = image::RgbaImage::from_fn(100, 80, |x, y| {
|
||||
image::Rgba([(x % 256) as u8, (y % 256) as u8, 128, 255])
|
||||
});
|
||||
img.save_with_format(path, image::ImageFormat::Png).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strip_png_metadata_produces_valid_png() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let input = dir.path().join("test.png");
|
||||
let output = dir.path().join("stripped.png");
|
||||
create_test_png(&input);
|
||||
|
||||
strip_metadata(&input, &output, &MetadataConfig::StripAll).unwrap();
|
||||
assert!(output.exists());
|
||||
// Output must be a valid PNG that can be opened
|
||||
let img = image::open(&output).unwrap();
|
||||
assert_eq!(img.width(), 100);
|
||||
assert_eq!(img.height(), 80);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strip_png_removes_text_chunks() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let input = dir.path().join("test.png");
|
||||
let output = dir.path().join("stripped.png");
|
||||
create_test_png(&input);
|
||||
|
||||
strip_metadata(&input, &output, &MetadataConfig::StripAll).unwrap();
|
||||
// Read output and verify no tEXt chunks remain
|
||||
let data = std::fs::read(&output).unwrap();
|
||||
let mut pos = 8; // skip PNG signature
|
||||
while pos + 12 <= data.len() {
|
||||
let chunk_len = u32::from_be_bytes([data[pos], data[pos+1], data[pos+2], data[pos+3]]) as usize;
|
||||
let chunk_type = &data[pos+4..pos+8];
|
||||
assert_ne!(chunk_type, b"tEXt", "tEXt chunk should be stripped");
|
||||
assert_ne!(chunk_type, b"iTXt", "iTXt chunk should be stripped");
|
||||
assert_ne!(chunk_type, b"zTXt", "zTXt chunk should be stripped");
|
||||
pos += 12 + chunk_len;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strip_png_output_smaller_or_equal() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let input = dir.path().join("test.png");
|
||||
let output = dir.path().join("stripped.png");
|
||||
create_test_png(&input);
|
||||
|
||||
let input_size = std::fs::metadata(&input).unwrap().len();
|
||||
strip_metadata(&input, &output, &MetadataConfig::StripAll).unwrap();
|
||||
let output_size = std::fs::metadata(&output).unwrap().len();
|
||||
assert!(output_size <= input_size);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strip_jpeg_removes_app1_exif() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let input = dir.path().join("test.jpg");
|
||||
let output = dir.path().join("stripped.jpg");
|
||||
create_test_jpeg(&input);
|
||||
|
||||
strip_metadata(&input, &output, &MetadataConfig::StripAll).unwrap();
|
||||
// Verify no APP1 (0xFFE1) markers remain
|
||||
let data = std::fs::read(&output).unwrap();
|
||||
let mut i = 2; // skip SOI
|
||||
while i + 1 < data.len() {
|
||||
if data[i] != 0xFF { break; }
|
||||
let marker = data[i + 1];
|
||||
if marker == 0xDA { break; } // SOS - rest is image data
|
||||
assert_ne!(marker, 0xE1, "APP1/EXIF marker should be stripped");
|
||||
if i + 3 < data.len() {
|
||||
let len = ((data[i + 2] as usize) << 8) | (data[i + 3] as usize);
|
||||
i += 2 + len;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user