[librsvg: 8/11] Implement PngPredicate.with_contents() using tests::reference_utils




commit 54e67f1d7ed916097aa1a672d584546df927d17c
Author: Sven Neumann <sven svenfoo org>
Date:   Tue Jan 26 13:46:36 2021 +0100

    Implement PngPredicate.with_contents() using tests::reference_utils

 src/surface_utils/compare_surfaces.rs | 21 +++++++++++
 tests/src/predicates/png.rs           | 69 ++++++++++++++++++++++-------------
 tests/src/reference_utils.rs          | 38 ++++++++++++-------
 3 files changed, 89 insertions(+), 39 deletions(-)
---
diff --git a/src/surface_utils/compare_surfaces.rs b/src/surface_utils/compare_surfaces.rs
index 47be0661..13b702e4 100644
--- a/src/surface_utils/compare_surfaces.rs
+++ b/src/surface_utils/compare_surfaces.rs
@@ -1,3 +1,5 @@
+use std::fmt;
+
 use super::{
     iterators::Pixels,
     shared_surface::{SharedImageSurface, SurfaceType},
@@ -17,6 +19,25 @@ pub struct Diff {
     pub surface: SharedImageSurface,
 }
 
+impl fmt::Display for BufferDiff {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            BufferDiff::DifferentSizes => write!(f, "different sizes"),
+            BufferDiff::Diff(diff) => diff.fmt(f),
+        }
+    }
+}
+
+impl fmt::Display for Diff {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(
+            f,
+            "{} pixels are different, with a maximum difference of {}",
+            self.num_pixels_changed, self.max_diff
+        )
+    }
+}
+
 #[inline]
 fn emphasize(p: &Pixel) -> Pixel {
     let emphasize_component = |c| {
diff --git a/tests/src/predicates/png.rs b/tests/src/predicates/png.rs
index 3e9dd898..c4375d7e 100644
--- a/tests/src/predicates/png.rs
+++ b/tests/src/predicates/png.rs
@@ -3,8 +3,14 @@ extern crate png;
 use predicates::prelude::*;
 use predicates::reflection::{Case, Child, PredicateReflection, Product};
 use std::fmt;
+use std::io::BufReader;
 use std::path::{Path, PathBuf};
 
+use librsvg::surface_utils::compare_surfaces::BufferDiff;
+use librsvg::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};
+
+use crate::reference_utils::{surface_from_png, Compare, Deviation, Reference};
+
 /// Checks that the variable of type [u8] can be parsed as a PNG file.
 #[derive(Debug)]
 pub struct PngPredicate {}
@@ -15,12 +21,9 @@ impl PngPredicate {
     }
 
     pub fn with_contents<P: AsRef<Path>>(self: Self, reference: P) -> ReferencePredicate<Self> {
-        let mut reference_path = PathBuf::new();
-        reference_path.push(reference);
-        ReferencePredicate::<Self> {
-            p: self,
-            reference_path,
-        }
+        let mut path = PathBuf::new();
+        path.push(reference);
+        ReferencePredicate::<Self> { p: self, path }
     }
 }
 
@@ -114,47 +117,63 @@ impl fmt::Display for SizePredicate<PngPredicate> {
 #[derive(Debug)]
 pub struct ReferencePredicate<PngPredicate> {
     p: PngPredicate,
-    reference_path: PathBuf,
+    path: PathBuf,
 }
 
 impl ReferencePredicate<PngPredicate> {
-    fn eval_info(&self, info: &png::OutputInfo) -> bool {
-        // info.width == self.w && info.height == self.h
-        false
+    fn diff_acceptable(diff: &BufferDiff) -> bool {
+        match diff {
+            BufferDiff::DifferentSizes => false,
+            BufferDiff::Diff(diff) => !diff.inacceptable(),
+        }
     }
 
-    fn find_case_for_info<'a>(
+    fn diff_surface(&self, surface: &SharedImageSurface) -> Option<BufferDiff> {
+        let reference = Reference::from_png(&self.path).unwrap();
+        if let Ok(diff) = reference.compare(&surface) {
+            if !Self::diff_acceptable(&diff) {
+                return Some(diff);
+            }
+        }
+        None
+    }
+
+    fn find_case_for_surface<'a>(
         &'a self,
         expected: bool,
-        info: &png::OutputInfo,
+        surface: &SharedImageSurface,
     ) -> Option<Case<'a>> {
-        if self.eval_info(info) == expected {
-            let product = self.product_for_info(info);
+        let diff = self.diff_surface(&surface);
+        if diff.is_some() != expected {
+            let product = self.product_for_diff(&diff.unwrap());
             Some(Case::new(Some(self), false).add_product(product))
         } else {
             None
         }
     }
 
-    fn product_for_info(&self, info: &png::OutputInfo) -> Product {
-        let actual_size = format!("{} x {}", info.width, info.height);
-        Product::new("actual size", actual_size)
+    fn product_for_diff(&self, diff: &BufferDiff) -> Product {
+        let difference = format!("{}", diff);
+        Product::new("images differ", difference)
     }
 }
 
 impl Predicate<[u8]> for ReferencePredicate<PngPredicate> {
     fn eval(&self, data: &[u8]) -> bool {
-        let decoder = png::Decoder::new(data);
-        match decoder.read_info() {
-            Ok((info, _)) => self.eval_info(&info),
-            _ => false,
+        if let Ok(surface) = surface_from_png(&mut BufReader::new(data)) {
+            let surface = SharedImageSurface::wrap(surface, SurfaceType::SRgb).unwrap();
+            self.diff_surface(&surface).is_some()
+        } else {
+            false
         }
     }
 
     fn find_case<'a>(&'a self, expected: bool, data: &[u8]) -> Option<Case<'a>> {
-        let decoder = png::Decoder::new(data);
-        match decoder.read_info() {
-            Ok((info, _)) => self.find_case_for_info(expected, &info),
+        match surface_from_png(&mut BufReader::new(data)) {
+            Ok(surface) => {
+                let surface = SharedImageSurface::wrap(surface, SurfaceType::SRgb).unwrap();
+                self.find_case_for_surface(expected, &surface)
+            }
             Err(e) => Some(Case::new(Some(self), false).add_product(Product::new("Error", e))),
         }
     }
@@ -172,7 +191,7 @@ impl fmt::Display for ReferencePredicate<PngPredicate> {
         write!(
             f,
             "is a PNG that matches the reference {}",
-            self.reference_path.display()
+            self.path.display()
         )
     }
 }
diff --git a/tests/src/reference_utils.rs b/tests/src/reference_utils.rs
index c1b6f399..2558eb0e 100644
--- a/tests/src/reference_utils.rs
+++ b/tests/src/reference_utils.rs
@@ -6,7 +6,7 @@
 use std::convert::TryFrom;
 use std::env;
 use std::fs::{self, File};
-use std::io::BufReader;
+use std::io::{BufReader, Read};
 use std::path::{Path, PathBuf};
 use std::sync::Once;
 
@@ -22,17 +22,8 @@ impl Reference {
     {
         let file = File::open(path).map_err(|e| cairo::IoError::Io(e))?;
         let mut reader = BufReader::new(file);
-        let png = cairo::ImageSurface::create_from_png(&mut reader)?;
-        let argb =
-            cairo::ImageSurface::create(cairo::Format::ARgb32, png.get_width(), png.get_height())?;
-        {
-            // convert to ARGB; the PNG may come as Rgb24
-            let cr = cairo::Context::new(&argb);
-            cr.set_source_surface(&png, 0.0, 0.0);
-            cr.paint();
-        }
-
-        Self::from_surface(argb)
+        let surface = surface_from_png(&mut reader)?;
+        Self::from_surface(surface)
     }
 
     pub fn from_surface(surface: cairo::ImageSurface) -> Result<Self, cairo::IoError> {
@@ -45,7 +36,7 @@ pub trait Compare {
     fn compare(self, surface: &SharedImageSurface) -> Result<BufferDiff, cairo::IoError>;
 }
 
-impl Compare for Reference {
+impl Compare for &Reference {
     fn compare(self, surface: &SharedImageSurface) -> Result<BufferDiff, cairo::IoError> {
         compare_surfaces(&self.0, surface).map_err(cairo::IoError::from)
     }
@@ -156,7 +147,7 @@ fn tolerable_difference() -> u8 {
     unsafe { TOLERANCE }
 }
 
-trait Deviation {
+pub trait Deviation {
     fn distinguishable(&self) -> bool;
     fn inacceptable(&self) -> bool;
 }
@@ -170,3 +161,22 @@ impl Deviation for Diff {
         self.max_diff > tolerable_difference()
     }
 }
+
+/// Creates a cairo::ImageSurface from a stream of PNG data.
+///
+/// The surface is converted to ARGB if needed. Use this helper function with `Reference`.
+pub fn surface_from_png<R>(stream: &mut R) -> Result<cairo::ImageSurface, cairo::IoError>
+where
+    R: Read,
+{
+    let png = cairo::ImageSurface::create_from_png(stream)?;
+    let argb =
+        cairo::ImageSurface::create(cairo::Format::ARgb32, png.get_width(), png.get_height())?;
+    {
+        // convert to ARGB; the PNG may come as Rgb24
+        let cr = cairo::Context::new(&argb);
+        cr.set_source_surface(&png, 0.0, 0.0);
+        cr.paint();
+    }
+    Ok(argb)
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]