summaryrefslogtreecommitdiff
path: root/src/test.rs
blob: 06a634d60f2f8ce4d197019c095018bc3467a494 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
use crate::{decode_png, decode_png_header, ColorType, Error};
use std::{fs, sync::Mutex};

#[derive(Debug)]
enum Flaw {
	ErrorFromValidPNG,
	NoErrorFromInvalidPNG,
	DecodedMismatch,
	ConvertedMismatch,
}

impl core::fmt::Display for Flaw {
	fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
		match self {
			Self::ErrorFromValidPNG => write!(f, "decoding valid PNG gives error"),
			Self::NoErrorFromInvalidPNG => write!(f, "decoding invalid PNG gives no error"),
			Self::DecodedMismatch => write!(f, "image incorrectly decoded"),
			Self::ConvertedMismatch => write!(f, "image incorrectly converted"),
		}
	}
}

const LARGE_BUF: Mutex<Vec<u8>> = Mutex::new(vec![]);
fn test_bytes(bytes: &[u8]) -> Result<(), Flaw> {
	let decoder = png::Decoder::new(bytes);
	let mut is_valid = true;
	if let Ok(mut reader) = decoder.read_info() {
		let mut png_buf = vec![0; reader.output_buffer_size()];
		if let Ok(png_header) = reader.next_frame(&mut png_buf) {
			let png_bytes = &png_buf[..png_header.buffer_size()];

			let mini_header = match decode_png_header(bytes) {
				Ok(h) => h,
				Err(Error::UnsupportedInterlace) => return Ok(()),
				Err(_) => return Err(Flaw::ErrorFromValidPNG),
			};
			let mut mini_buf = vec![0; mini_header.required_bytes_rgba8bpc()];
			let mut image =
				decode_png(bytes, &mut mini_buf).map_err(|_| Flaw::ErrorFromValidPNG)?;
			let mini_bytes = image.pixels();
			if png_bytes != mini_bytes {
				return Err(Flaw::DecodedMismatch);
			}
			let (_, mut data) = png_decoder::decode(bytes).unwrap();
			if matches!(image.color_type(), ColorType::Gray | ColorType::Rgb) {
				// pretend there's no tRNS chunk.
				// there shouldnt be one who the fucks stupid idea was that.
				for i in 0..data.len() / 4 {
					data[4 * i + 3] = 255;
				}
			}
			image.convert_to_rgba8bpc().unwrap();
			if data != image.pixels() {
				return Err(Flaw::ConvertedMismatch);
			}
		} else {
			is_valid = false;
		}
	} else {
		is_valid = false;
	}

	if !is_valid && decode_png(bytes, &mut LARGE_BUF.lock().unwrap()).is_ok() {
		return Err(Flaw::NoErrorFromInvalidPNG);
	}
	Ok(())
}

fn test_images_in_dir(dir: &str) {
	for entry in fs::read_dir(dir).unwrap() {
		let entry = entry.unwrap();
		let r#type = entry.file_type().unwrap();
		let path = entry.path().to_str().unwrap().to_string();
		if r#type.is_file() {
			if let Err(flaw) = test_bytes(&std::fs::read(&path).unwrap()) {
				panic!("flaw for file {path}: {flaw}");
			}
		} else if r#type.is_dir() {
			test_images_in_dir(&path);
		}
		println!("{path} ... \x1b[32mok\x1b[0m");
	}
}

#[test]
fn test_images() {
	*LARGE_BUF.lock().unwrap() = vec![0; 10 << 20];

	test_images_in_dir("test");
}

#[test]
fn test_bad_png() {
	let mut data = &b"hello"[..];
	let err = decode_png_header(&mut data).unwrap_err();
	assert!(matches!(err, Error::NotPng));
}
#[test]
fn test_buffer_too_small() {
	let png = &include_bytes!("../test/ouroboros.png")[..];
	let mut buffer = [0; 128];
	let err = decode_png(png, &mut buffer[..]).unwrap_err();
	assert!(matches!(err, Error::BufferTooSmall));
}