lightcraft/crates/raw/tests/robust.rs

105 lines
4.0 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! Malformed-input robustness: decoding random or corrupted files must never panic.
use lightcraft_raw::{
BlackLevel, Cfa, ColorData, DngCompression, DngWriteOptions, Method, OpcodeLists, Orientation, RawData, RawFormat, RawImage, Rect,
};
use proptest::prelude::*;
fn sample(comp: DngCompression) -> Vec<u8> {
let (w, h) = (24usize, 16usize);
let data: Vec<u16> = (0..w * h).map(|i| (i * 97 % 4000) as u16).collect();
let raw = RawImage {
format: RawFormat::Dng,
width: w,
height: h,
cpp: 1,
data: RawData::U16(data),
cfa: Some(Cfa::bayer("RGGB").unwrap()),
bits: 12,
black: BlackLevel::uniform(64.0),
white: vec![4095.0],
active_area: Rect::new(0, 0, w, h),
crop: Rect::new(0, 0, w, h),
orientation: Orientation::Normal,
color: ColorData::default(),
wb_multipliers: None,
linearized: false,
opcodes: OpcodeLists::default(),
metadata: Default::default(),
};
lightcraft_raw::write_dng(&raw, &DngWriteOptions { compression: comp, ..Default::default() }).unwrap()
}
fn exercise(bytes: &[u8]) {
let _ = lightcraft_raw::probe(bytes);
let _ = lightcraft_raw::embedded_preview(bytes);
if let Ok(img) = lightcraft_raw::decode(bytes)
&& img.width * img.height <= 1 << 16
{
let _ = img.develop(Method::Ahd);
let xy = lightcraft_raw::color::as_shot_white_xy(&img);
let _ = lightcraft_raw::color::camera_transform(&img, xy);
}
}
proptest! {
#![proptest_config(ProptestConfig { cases: 400, .. ProptestConfig::default() })]
#[test]
fn mutated_dngs_never_panic(flips in proptest::collection::vec((any::<usize>(), any::<u8>()), 1..24), cut in any::<usize>(), lj in any::<bool>()) {
let mut data = sample(if lj { DngCompression::Lj92 { tile: 16 } } else { DngCompression::Uncompressed });
for (i, v) in flips {
let n = data.len();
data[i % n] = v;
}
let keep = if cut % 3 == 0 { cut % data.len() } else { data.len() };
exercise(&data[..keep]);
}
#[test]
fn random_tiff_like_bytes_never_panic(mut data in proptest::collection::vec(any::<u8>(), 8..700), kind in 0usize..4) {
let heads: [&[u8]; 4] = [b"II*\0\x08\0\0\0", b"MM\0*\0\0\0\x08", b"II*\0\x10\0\0\0CR\x02\0", b"II+\0\x08\0\0\0"];
let h = heads[kind];
let n = h.len().min(data.len());
data[..n].copy_from_slice(&h[..n]);
exercise(&data);
}
#[test]
fn random_lj92_never_panics(data in proptest::collection::vec(any::<u8>(), 0..400)) {
let mut s = vec![0xff, 0xd8, 0xff, 0xc3, 0, 11, 12, 0, 8, 0, 8, 1, 1, 0x11, 0];
s.extend_from_slice(&data);
let _ = lightcraft_raw::ljpeg::decode(&s, 1 << 20);
}
}
#[test]
fn truncation_at_every_length() {
for comp in [DngCompression::Uncompressed, DngCompression::Lj92 { tile: 16 }] {
let d = sample(comp);
for n in (0..d.len()).step_by(7) {
exercise(&d[..n]);
}
}
}
#[test]
fn hostile_dimensions_are_rejected_quickly() {
// a DNG claiming a 60000 × 60000 16-bit image with 10 bytes of data
use lightcraft_tiff::tags as t;
use lightcraft_tiff::{IfdBuilder, ImageData, TiffWriter, Value};
let mut ifd = IfdBuilder::new();
ifd.set(t::DNG_VERSION, Value::Byte(vec![1, 4, 0, 0]));
ifd.set(t::IMAGE_WIDTH, Value::Long(vec![60000]));
ifd.set(t::IMAGE_LENGTH, Value::Long(vec![60000]));
ifd.set(t::BITS_PER_SAMPLE, Value::Short(vec![16]));
ifd.set(t::PHOTOMETRIC, Value::Short(vec![32803]));
ifd.set(t::CFA_PATTERN_EP, Value::Byte(vec![0, 1, 1, 2]));
ifd.set(t::COMPRESSION, Value::Short(vec![7]));
ifd.set_image(ImageData::Tiles { tile_width: 60000, tile_height: 60000, tiles: vec![vec![0xff, 0xd8, 0xff, 0xd9]] });
let bytes = TiffWriter::default().write(&[ifd]).unwrap();
let t0 = std::time::Instant::now();
assert!(lightcraft_raw::decode(&bytes).is_err());
assert!(t0.elapsed().as_secs_f64() < 2.0);
}