pixelflow-test-support 0.1.0

Deterministic test helpers for PixelFlow filters, plugins, and embedders.
Documentation
//! Golden-frame assertion helpers.

use pixelflow_core::Frame;

/// Absolute per-sample tolerance for deterministic golden-frame comparisons.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct GoldenTolerance {
    /// Maximum allowed absolute difference for `u8` samples.
    pub u8_abs: u8,
    /// Maximum allowed absolute difference for `u16` samples.
    pub u16_abs: u16,
    /// Maximum allowed absolute difference for `f32` samples.
    pub f32_abs: f32,
}

/// Exact comparison tolerance for structural filters such as crop and trim.
pub const EXACT_GOLDEN_TOLERANCE: GoldenTolerance = GoldenTolerance {
    u8_abs: 0,
    u16_abs: 0,
    f32_abs: 0.0,
};

/// Asserts one `u8` plane matches expected rows within tolerance.
pub fn assert_plane_u8_near(
    frame: &Frame,
    plane_index: usize,
    expected: &[&[u8]],
    tolerance: GoldenTolerance,
) {
    let plane = frame
        .plane::<u8>(plane_index)
        .expect("frame plane should have u8 storage");
    assert_eq!(plane.height(), expected.len(), "plane height mismatch");

    for (y, expected_row) in expected.iter().enumerate() {
        let row = plane.row(y).expect("row should exist");
        assert_eq!(
            row.len(),
            expected_row.len(),
            "plane row width mismatch at row {y}"
        );

        for (x, (&actual, &want)) in row.iter().zip(expected_row.iter()).enumerate() {
            let delta = actual.abs_diff(want);
            assert!(
                delta <= tolerance.u8_abs,
                "sample mismatch at plane {plane_index} ({x}, {y}): expected {want}, got {actual}, tolerance {}",
                tolerance.u8_abs
            );
        }
    }
}

/// Asserts one `u16` plane matches expected rows within tolerance.
pub fn assert_plane_u16_near(
    frame: &Frame,
    plane_index: usize,
    expected: &[&[u16]],
    tolerance: GoldenTolerance,
) {
    let plane = frame
        .plane::<u16>(plane_index)
        .expect("frame plane should have u16 storage");
    assert_eq!(plane.height(), expected.len(), "plane height mismatch");

    for (y, expected_row) in expected.iter().enumerate() {
        let row = plane.row(y).expect("row should exist");
        assert_eq!(
            row.len(),
            expected_row.len(),
            "plane row width mismatch at row {y}"
        );

        for (x, (&actual, &want)) in row.iter().zip(expected_row.iter()).enumerate() {
            let delta = actual.abs_diff(want);
            assert!(
                delta <= tolerance.u16_abs,
                "sample mismatch at plane {plane_index} ({x}, {y}): expected {want}, got {actual}, tolerance {}",
                tolerance.u16_abs
            );
        }
    }
}

/// Asserts one `f32` plane matches expected rows within tolerance.
pub fn assert_plane_f32_near(
    frame: &Frame,
    plane_index: usize,
    expected: &[&[f32]],
    tolerance: GoldenTolerance,
) {
    let plane = frame
        .plane::<f32>(plane_index)
        .expect("frame plane should have f32 storage");
    assert_eq!(plane.height(), expected.len(), "plane height mismatch");

    for (y, expected_row) in expected.iter().enumerate() {
        let row = plane.row(y).expect("row should exist");
        assert_eq!(
            row.len(),
            expected_row.len(),
            "plane row width mismatch at row {y}"
        );

        for (x, (&actual, &want)) in row.iter().zip(expected_row.iter()).enumerate() {
            let delta = (actual - want).abs();
            assert!(
                delta <= tolerance.f32_abs,
                "sample mismatch at plane {plane_index} ({x}, {y}): expected {want}, got {actual}, tolerance {}",
                tolerance.f32_abs
            );
        }
    }
}

/// Asserts all `u8` planes match expected rows within tolerance.
pub fn assert_frame_u8_near(
    frame: &Frame,
    expected_planes: &[&[&[u8]]],
    tolerance: GoldenTolerance,
) {
    assert_eq!(
        frame.format().planes().len(),
        expected_planes.len(),
        "plane count mismatch"
    );
    for (plane_index, expected) in expected_planes.iter().enumerate() {
        assert_plane_u8_near(frame, plane_index, expected, tolerance);
    }
}

/// Asserts all `u16` planes match expected rows within tolerance.
pub fn assert_frame_u16_near(
    frame: &Frame,
    expected_planes: &[&[&[u16]]],
    tolerance: GoldenTolerance,
) {
    assert_eq!(
        frame.format().planes().len(),
        expected_planes.len(),
        "plane count mismatch"
    );
    for (plane_index, expected) in expected_planes.iter().enumerate() {
        assert_plane_u16_near(frame, plane_index, expected, tolerance);
    }
}

/// Asserts all `f32` planes match expected rows within tolerance.
pub fn assert_frame_f32_near(
    frame: &Frame,
    expected_planes: &[&[&[f32]]],
    tolerance: GoldenTolerance,
) {
    assert_eq!(
        frame.format().planes().len(),
        expected_planes.len(),
        "plane count mismatch"
    );
    for (plane_index, expected) in expected_planes.iter().enumerate() {
        assert_plane_f32_near(frame, plane_index, expected, tolerance);
    }
}

#[cfg(test)]
mod tests {
    use crate::{synthetic_f32_frame, synthetic_u8_frame, synthetic_u16_frame};

    use super::{
        EXACT_GOLDEN_TOLERANCE, GoldenTolerance, assert_frame_f32_near, assert_frame_u8_near,
        assert_frame_u16_near, assert_plane_u8_near,
    };

    #[test]
    fn exact_golden_tolerance_is_zero_for_all_storage_types() {
        assert_eq!(EXACT_GOLDEN_TOLERANCE.u8_abs, 0);
        assert_eq!(EXACT_GOLDEN_TOLERANCE.u16_abs, 0);
        assert_eq!(EXACT_GOLDEN_TOLERANCE.f32_abs, 0.0);
    }

    #[test]
    fn plane_u8_assertion_accepts_declared_tolerance() {
        let frame = synthetic_u8_frame("gray8", 2, 1, |_plane, x, _y| {
            u8::try_from(x).expect("fixture sample fits u8")
        })
        .expect("synthetic frame should build");

        assert_plane_u8_near(
            &frame,
            0,
            &[&[1, 2]],
            GoldenTolerance {
                u8_abs: 1,
                u16_abs: 0,
                f32_abs: 0.0,
            },
        );
    }

    #[test]
    fn frame_u8_assertion_checks_all_planes() {
        let frame = synthetic_u8_frame("yuv444p8", 2, 1, |plane, x, _y| match plane {
            0 => match x {
                0 => 1,
                1 => 2,
                _ => unreachable!("fixture width is 2"),
            },
            1 => match x {
                0 => 3,
                1 => 4,
                _ => unreachable!("fixture width is 2"),
            },
            2 => match x {
                0 => 5,
                1 => 6,
                _ => unreachable!("fixture width is 2"),
            },
            _ => unreachable!("yuv444p8 has exactly 3 planes"),
        })
        .expect("synthetic frame should build");

        let p0 = [&[1_u8, 2][..]];
        let p1 = [&[3_u8, 4][..]];
        let p2 = [&[5_u8, 6][..]];
        let expected = [&p0[..], &p1[..], &p2[..]];

        assert_frame_u8_near(&frame, &expected, EXACT_GOLDEN_TOLERANCE);
    }

    #[test]
    fn frame_u16_assertion_checks_all_planes() {
        let frame = synthetic_u16_frame("gray10", 2, 1, |_plane, x, _y| match x {
            0 => 100,
            1 => 101,
            _ => unreachable!("fixture width is 2"),
        })
        .expect("synthetic frame should build");
        let p0 = [&[100_u16, 101][..]];
        let expected = [&p0[..]];

        assert_frame_u16_near(&frame, &expected, EXACT_GOLDEN_TOLERANCE);
    }

    #[test]
    fn frame_f32_assertion_checks_all_planes() {
        let frame = synthetic_f32_frame("grayf32", 2, 1, |_plane, x, _y| match x {
            0 => 0.25,
            1 => 1.25,
            _ => unreachable!("fixture width is 2"),
        })
        .expect("synthetic frame should build");
        let p0 = [&[0.25_f32, 1.25][..]];
        let expected = [&p0[..]];

        assert_frame_f32_near(&frame, &expected, EXACT_GOLDEN_TOLERANCE);
    }
}