1use pixelflow_core::Frame;
4
5#[derive(Clone, Copy, Debug, PartialEq)]
7pub struct GoldenTolerance {
8 pub u8_abs: u8,
10 pub u16_abs: u16,
12 pub f32_abs: f32,
14}
15
16pub const EXACT_GOLDEN_TOLERANCE: GoldenTolerance = GoldenTolerance {
18 u8_abs: 0,
19 u16_abs: 0,
20 f32_abs: 0.0,
21};
22
23pub fn assert_plane_u8_near(
25 frame: &Frame,
26 plane_index: usize,
27 expected: &[&[u8]],
28 tolerance: GoldenTolerance,
29) {
30 let plane = frame
31 .plane::<u8>(plane_index)
32 .expect("frame plane should have u8 storage");
33 assert_eq!(plane.height(), expected.len(), "plane height mismatch");
34
35 for (y, expected_row) in expected.iter().enumerate() {
36 let row = plane.row(y).expect("row should exist");
37 assert_eq!(
38 row.len(),
39 expected_row.len(),
40 "plane row width mismatch at row {y}"
41 );
42
43 for (x, (&actual, &want)) in row.iter().zip(expected_row.iter()).enumerate() {
44 let delta = actual.abs_diff(want);
45 assert!(
46 delta <= tolerance.u8_abs,
47 "sample mismatch at plane {plane_index} ({x}, {y}): expected {want}, got {actual}, tolerance {}",
48 tolerance.u8_abs
49 );
50 }
51 }
52}
53
54pub fn assert_plane_u16_near(
56 frame: &Frame,
57 plane_index: usize,
58 expected: &[&[u16]],
59 tolerance: GoldenTolerance,
60) {
61 let plane = frame
62 .plane::<u16>(plane_index)
63 .expect("frame plane should have u16 storage");
64 assert_eq!(plane.height(), expected.len(), "plane height mismatch");
65
66 for (y, expected_row) in expected.iter().enumerate() {
67 let row = plane.row(y).expect("row should exist");
68 assert_eq!(
69 row.len(),
70 expected_row.len(),
71 "plane row width mismatch at row {y}"
72 );
73
74 for (x, (&actual, &want)) in row.iter().zip(expected_row.iter()).enumerate() {
75 let delta = actual.abs_diff(want);
76 assert!(
77 delta <= tolerance.u16_abs,
78 "sample mismatch at plane {plane_index} ({x}, {y}): expected {want}, got {actual}, tolerance {}",
79 tolerance.u16_abs
80 );
81 }
82 }
83}
84
85pub fn assert_plane_f32_near(
87 frame: &Frame,
88 plane_index: usize,
89 expected: &[&[f32]],
90 tolerance: GoldenTolerance,
91) {
92 let plane = frame
93 .plane::<f32>(plane_index)
94 .expect("frame plane should have f32 storage");
95 assert_eq!(plane.height(), expected.len(), "plane height mismatch");
96
97 for (y, expected_row) in expected.iter().enumerate() {
98 let row = plane.row(y).expect("row should exist");
99 assert_eq!(
100 row.len(),
101 expected_row.len(),
102 "plane row width mismatch at row {y}"
103 );
104
105 for (x, (&actual, &want)) in row.iter().zip(expected_row.iter()).enumerate() {
106 let delta = (actual - want).abs();
107 assert!(
108 delta <= tolerance.f32_abs,
109 "sample mismatch at plane {plane_index} ({x}, {y}): expected {want}, got {actual}, tolerance {}",
110 tolerance.f32_abs
111 );
112 }
113 }
114}
115
116pub fn assert_frame_u8_near(
118 frame: &Frame,
119 expected_planes: &[&[&[u8]]],
120 tolerance: GoldenTolerance,
121) {
122 assert_eq!(
123 frame.format().planes().len(),
124 expected_planes.len(),
125 "plane count mismatch"
126 );
127 for (plane_index, expected) in expected_planes.iter().enumerate() {
128 assert_plane_u8_near(frame, plane_index, expected, tolerance);
129 }
130}
131
132pub fn assert_frame_u16_near(
134 frame: &Frame,
135 expected_planes: &[&[&[u16]]],
136 tolerance: GoldenTolerance,
137) {
138 assert_eq!(
139 frame.format().planes().len(),
140 expected_planes.len(),
141 "plane count mismatch"
142 );
143 for (plane_index, expected) in expected_planes.iter().enumerate() {
144 assert_plane_u16_near(frame, plane_index, expected, tolerance);
145 }
146}
147
148pub fn assert_frame_f32_near(
150 frame: &Frame,
151 expected_planes: &[&[&[f32]]],
152 tolerance: GoldenTolerance,
153) {
154 assert_eq!(
155 frame.format().planes().len(),
156 expected_planes.len(),
157 "plane count mismatch"
158 );
159 for (plane_index, expected) in expected_planes.iter().enumerate() {
160 assert_plane_f32_near(frame, plane_index, expected, tolerance);
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use crate::{synthetic_f32_frame, synthetic_u8_frame, synthetic_u16_frame};
167
168 use super::{
169 EXACT_GOLDEN_TOLERANCE, GoldenTolerance, assert_frame_f32_near, assert_frame_u8_near,
170 assert_frame_u16_near, assert_plane_u8_near,
171 };
172
173 #[test]
174 fn exact_golden_tolerance_is_zero_for_all_storage_types() {
175 assert_eq!(EXACT_GOLDEN_TOLERANCE.u8_abs, 0);
176 assert_eq!(EXACT_GOLDEN_TOLERANCE.u16_abs, 0);
177 assert_eq!(EXACT_GOLDEN_TOLERANCE.f32_abs, 0.0);
178 }
179
180 #[test]
181 fn plane_u8_assertion_accepts_declared_tolerance() {
182 let frame = synthetic_u8_frame("gray8", 2, 1, |_plane, x, _y| {
183 u8::try_from(x).expect("fixture sample fits u8")
184 })
185 .expect("synthetic frame should build");
186
187 assert_plane_u8_near(
188 &frame,
189 0,
190 &[&[1, 2]],
191 GoldenTolerance {
192 u8_abs: 1,
193 u16_abs: 0,
194 f32_abs: 0.0,
195 },
196 );
197 }
198
199 #[test]
200 fn frame_u8_assertion_checks_all_planes() {
201 let frame = synthetic_u8_frame("yuv444p8", 2, 1, |plane, x, _y| match plane {
202 0 => match x {
203 0 => 1,
204 1 => 2,
205 _ => unreachable!("fixture width is 2"),
206 },
207 1 => match x {
208 0 => 3,
209 1 => 4,
210 _ => unreachable!("fixture width is 2"),
211 },
212 2 => match x {
213 0 => 5,
214 1 => 6,
215 _ => unreachable!("fixture width is 2"),
216 },
217 _ => unreachable!("yuv444p8 has exactly 3 planes"),
218 })
219 .expect("synthetic frame should build");
220
221 let p0 = [&[1_u8, 2][..]];
222 let p1 = [&[3_u8, 4][..]];
223 let p2 = [&[5_u8, 6][..]];
224 let expected = [&p0[..], &p1[..], &p2[..]];
225
226 assert_frame_u8_near(&frame, &expected, EXACT_GOLDEN_TOLERANCE);
227 }
228
229 #[test]
230 fn frame_u16_assertion_checks_all_planes() {
231 let frame = synthetic_u16_frame("gray10", 2, 1, |_plane, x, _y| match x {
232 0 => 100,
233 1 => 101,
234 _ => unreachable!("fixture width is 2"),
235 })
236 .expect("synthetic frame should build");
237 let p0 = [&[100_u16, 101][..]];
238 let expected = [&p0[..]];
239
240 assert_frame_u16_near(&frame, &expected, EXACT_GOLDEN_TOLERANCE);
241 }
242
243 #[test]
244 fn frame_f32_assertion_checks_all_planes() {
245 let frame = synthetic_f32_frame("grayf32", 2, 1, |_plane, x, _y| match x {
246 0 => 0.25,
247 1 => 1.25,
248 _ => unreachable!("fixture width is 2"),
249 })
250 .expect("synthetic frame should build");
251 let p0 = [&[0.25_f32, 1.25][..]];
252 let expected = [&p0[..]];
253
254 assert_frame_f32_near(&frame, &expected, EXACT_GOLDEN_TOLERANCE);
255 }
256}