#[derive(Clone, Debug)]
pub struct GoldenImageConfig {
pub pixel_tolerance: u8,
pub max_diff_percent: f32,
pub update_on_mismatch: bool,
}
impl Default for GoldenImageConfig {
fn default() -> Self {
Self {
pixel_tolerance: 3,
max_diff_percent: 0.1,
update_on_mismatch: false,
}
}
}
#[derive(Clone, Debug)]
pub struct GoldenImageResult {
pub passed: bool,
pub diff_percent: f32,
pub diff_count: u64,
pub total_pixels: u64,
}
pub struct GoldenImageComparator;
impl GoldenImageComparator {
pub fn compare(
actual: &[u8],
expected: &[u8],
config: &GoldenImageConfig,
) -> GoldenImageResult {
if actual.len() != expected.len() {
return GoldenImageResult {
passed: false,
diff_percent: 100.0,
diff_count: actual.len() as u64 / 4,
total_pixels: actual.len() as u64 / 4,
};
}
let total_pixels = (actual.len() / 4) as u64;
if total_pixels == 0 {
return GoldenImageResult {
passed: true,
diff_percent: 0.0,
diff_count: 0,
total_pixels: 0,
};
}
let mut diff_count = 0u64;
for i in 0..(actual.len() / 4) {
let base = i * 4;
let mut pixel_differs = false;
for ch in 0..3 {
if actual[base + ch].abs_diff(expected[base + ch]) > config.pixel_tolerance {
pixel_differs = true;
break;
}
}
if pixel_differs {
diff_count += 1;
}
}
let diff_percent = (diff_count as f32 / total_pixels as f32) * 100.0;
GoldenImageResult {
passed: diff_percent <= config.max_diff_percent,
diff_percent,
diff_count,
total_pixels,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn golden_image_identical() {
let pixels = vec![255u8; 400]; let config = GoldenImageConfig::default();
let result = GoldenImageComparator::compare(&pixels, &pixels, &config);
assert!(result.passed);
assert_eq!(result.diff_percent, 0.0);
}
#[test]
fn golden_image_detects_difference() {
let mut actual = vec![255u8; 400];
let expected = vec![255u8; 400];
actual[0] = 0;
let config = GoldenImageConfig::default();
let result = GoldenImageComparator::compare(&actual, &expected, &config);
assert!(!result.passed);
assert!(result.diff_percent > 0.0);
}
#[test]
fn golden_image_tolerance() {
let mut actual = vec![255u8; 400];
let expected = vec![255u8; 400];
actual[0] = 253; let config = GoldenImageConfig::default();
let result = GoldenImageComparator::compare(&actual, &expected, &config);
assert!(result.passed);
}
#[test]
fn golden_image_different_sizes() {
let actual = vec![255u8; 400];
let expected = vec![255u8; 800];
let config = GoldenImageConfig::default();
let result = GoldenImageComparator::compare(&actual, &expected, &config);
assert!(!result.passed);
assert_eq!(result.diff_percent, 100.0);
}
#[test]
fn golden_image_empty() {
let config = GoldenImageConfig::default();
let result = GoldenImageComparator::compare(&[], &[], &config);
assert!(result.passed);
}
}