[librsvg: 8/15] tests: Add tests for other output formats of rsvg-convert



commit f07b3c935c1a19f8bca2893669f0217662a60a01
Author: Sven Neumann <sven svenfoo org>
Date:   Mon Feb 10 08:23:33 2020 +0100

    tests: Add tests for other output formats of rsvg-convert

 tests/Makefile.am                  |   4 +-
 tests/src/cmdline/mod.rs           |   2 +-
 tests/src/cmdline/png_predicate.rs |  69 ---------------
 tests/src/cmdline/predicates.rs    | 171 +++++++++++++++++++++++++++++++++++++
 tests/src/cmdline/rsvg_convert.rs  | 131 ++++++++++++++++++----------
 5 files changed, 259 insertions(+), 118 deletions(-)
---
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 5167de3e..ccb79d10 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -4,8 +4,8 @@ test_sources =                          \
        Cargo.toml                      \
        src/main.rs                     \
        src/cmdline/mod.rs              \
-       src/cmdline/rsvg_convert.rs     \
-       src/cmdline/png_predicate.rs
+       src/cmdline/predicates.rs       \
+       src/cmdline/rsvg_convert.rs
 
 EXTRA_DIST +=          \
        $(test_sources) \
diff --git a/tests/src/cmdline/mod.rs b/tests/src/cmdline/mod.rs
index 1647c326..a6d65a08 100644
--- a/tests/src/cmdline/mod.rs
+++ b/tests/src/cmdline/mod.rs
@@ -1,2 +1,2 @@
-mod png_predicate;
+mod predicates;
 mod rsvg_convert;
diff --git a/tests/src/cmdline/predicates.rs b/tests/src/cmdline/predicates.rs
new file mode 100644
index 00000000..5a685e23
--- /dev/null
+++ b/tests/src/cmdline/predicates.rs
@@ -0,0 +1,171 @@
+extern crate png;
+extern crate predicates;
+
+pub mod file {
+
+    use predicates::boolean::AndPredicate;
+    use predicates::prelude::*;
+    use predicates::reflection::{Case, Child, PredicateReflection, Product};
+    use predicates::str::{ContainsPredicate, StartsWithPredicate};
+
+    use std::fmt;
+
+    /// Checks that the variable of type [u8] looks like a PDF file.
+    /// Actually it only looks at the very first bytes.
+    #[derive(Debug)]
+    pub struct PdfPredicate {}
+
+    impl PdfPredicate {
+        fn not_a_pdf<'a>(&'a self, reason: &'static str) -> Option<Case<'a>> {
+            Some(Case::new(Some(self), false).add_product(Product::new("not a PDF", reason)))
+        }
+    }
+
+    impl Predicate<[u8]> for PdfPredicate {
+        fn eval(&self, data: &[u8]) -> bool {
+            match data.get(0..5) {
+                Some(head) => head == b"%PDF-",
+                None => false,
+            }
+        }
+
+        fn find_case<'a>(&'a self, _expected: bool, data: &[u8]) -> Option<Case<'a>> {
+            match data.get(0..5) {
+                Some(head) => {
+                    if head == b"%PDF-" {
+                        None
+                    } else {
+                        self.not_a_pdf("header mismatch")
+                    }
+                }
+                None => self.not_a_pdf("too short"),
+            }
+        }
+    }
+
+    impl PredicateReflection for PdfPredicate {}
+
+    impl fmt::Display for PdfPredicate {
+        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+            write!(f, "has PDF header")
+        }
+    }
+
+    /// Checks that the variable of type [u8] can be parsed as a PNG file.
+    #[derive(Debug)]
+    pub struct PngPredicate {}
+
+    impl PngPredicate {
+        pub fn with_size(self: Self, w: u32, h: u32) -> SizePredicate<Self> {
+            SizePredicate::<Self> { p: self, w, h }
+        }
+    }
+
+    impl Predicate<[u8]> for PngPredicate {
+        fn eval(&self, data: &[u8]) -> bool {
+            let decoder = png::Decoder::new(data);
+            decoder.read_info().is_ok()
+        }
+
+        fn find_case<'a>(&'a self, _expected: bool, data: &[u8]) -> Option<Case<'a>> {
+            let decoder = png::Decoder::new(data);
+            match decoder.read_info() {
+                Ok(_) => None,
+                Err(e) => Some(Case::new(Some(self), false).add_product(Product::new("Error", e))),
+            }
+        }
+    }
+
+    impl PredicateReflection for PngPredicate {}
+
+    impl fmt::Display for PngPredicate {
+        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+            write!(f, "is a PNG")
+        }
+    }
+
+    /// Extends a PngPredicate by a check for a given size of the PNG file.
+    #[derive(Debug)]
+    pub struct SizePredicate<PngPredicate> {
+        p: PngPredicate,
+        w: u32,
+        h: u32,
+    }
+
+    impl SizePredicate<PngPredicate> {
+        fn eval_info(&self, info: &png::OutputInfo) -> bool {
+            info.width == self.w && info.height == self.h
+        }
+
+        fn find_case_for_info<'a>(
+            &'a self,
+            expected: bool,
+            info: &png::OutputInfo,
+        ) -> Option<Case<'a>> {
+            if self.eval_info(info) == expected {
+                let product = self.product_for_info(info);
+                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)
+        }
+    }
+
+    impl Predicate<[u8]> for SizePredicate<PngPredicate> {
+        fn eval(&self, data: &[u8]) -> bool {
+            let decoder = png::Decoder::new(data);
+            match decoder.read_info() {
+                Ok((info, _)) => self.eval_info(&info),
+                _ => 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),
+                Err(e) => Some(Case::new(Some(self), false).add_product(Product::new("Error", e))),
+            }
+        }
+    }
+
+    impl PredicateReflection for SizePredicate<PngPredicate> {
+        fn children<'a>(&'a self) -> Box<dyn Iterator<Item = Child<'a>> + 'a> {
+            let params = vec![Child::new("predicate", &self.p)];
+            Box::new(params.into_iter())
+        }
+    }
+
+    impl fmt::Display for SizePredicate<PngPredicate> {
+        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+            write!(f, "is a PNG with size {} x {}", self.w, self.h)
+        }
+    }
+
+    /// Predicates to check that some output ([u8]) is of a certain file type
+
+    pub fn is_png() -> PngPredicate {
+        PngPredicate {}
+    }
+
+    pub fn is_ps() -> StartsWithPredicate {
+        predicate::str::starts_with("%!PS-Adobe-3.0\n")
+    }
+
+    pub fn is_eps() -> StartsWithPredicate {
+        predicate::str::starts_with("%!PS-Adobe-3.0 EPSF-3.0\n")
+    }
+
+    pub fn is_pdf() -> PdfPredicate {
+        PdfPredicate {}
+    }
+
+    pub fn is_svg() -> AndPredicate<StartsWithPredicate, ContainsPredicate, str> {
+        predicate::str::starts_with("<?xml ").and(predicate::str::contains("<svg "))
+    }
+}
diff --git a/tests/src/cmdline/rsvg_convert.rs b/tests/src/cmdline/rsvg_convert.rs
index 87fc2a8a..1566accf 100644
--- a/tests/src/cmdline/rsvg_convert.rs
+++ b/tests/src/cmdline/rsvg_convert.rs
@@ -1,7 +1,7 @@
 extern crate assert_cmd;
 extern crate predicates;
 
-use crate::cmdline::png_predicate;
+use crate::cmdline::predicates::file;
 
 use assert_cmd::Command;
 use predicates::prelude::*;
@@ -11,17 +11,17 @@ use std::path::Path;
 // The goal is to test the code in rsvg-convert, not the entire library.
 //
 //  - all command-line options are accepted
-//  - size of the output (should be sufficient to do that for PNG)
-//  - command-line options that affect size (width, height, zoom, resolution)
-//  - limit on output size (32767 pixels)
-//  - output formats (PNG, PDF, PS, EPS, SVG), okay to ignore XML and recording
-//  - multi-page output (for PDF)
+//  - size of the output (should be sufficient to do that for PNG) ✔
+//  - command-line options that affect size (width, height, zoom, resolution) ✔
+//  - limit on output size (32767 pixels) ✔
+//  - output formats (PNG, PDF, PS, EPS, SVG), okay to ignore XML and recording ✔
+//  - multi-page output (for PDF) ✔
 //  - handling of SOURCE_DATA_EPOCH environment variable for PDF output
 //  - handling of background color option
 //  - support for optional CSS stylesheet
-//  - error handling for missing SVG dimensions
+//  - error handling for missing SVG dimensions ✔
 //  - error handling for export lookup ID
-//  - error handling for invalid input
+//  - error handling for invalid input ✔
 
 struct RsvgConvert {}
 
@@ -55,7 +55,7 @@ fn converts_svg_from_stdin_to_png() {
     RsvgConvert::new_with_input(input)
         .assert()
         .success()
-        .stdout(png_predicate::has_size(200, 100));
+        .stdout(file::is_png());
 }
 
 #[test]
@@ -65,18 +65,7 @@ fn argument_is_input_filename() {
         .arg(input)
         .assert()
         .success()
-        .stdout(png_predicate::has_size(200, 100));
-}
-
-#[test]
-fn multiple_input_files_not_allowed_for_png_output() {
-    let input = Path::new("fixtures/dimensions/521-with-viewbox.svg");
-    RsvgConvert::new()
-        .arg(input)
-        .arg("foo.svg")
-        .assert()
-        .failure()
-        .stderr("Multiple SVG files are only allowed for PDF and (E)PS output.\n");
+        .stdout(file::is_png());
 }
 
 #[test]
@@ -86,7 +75,7 @@ fn output_format_png() {
         .arg("--format=png")
         .assert()
         .success()
-        .stdout(png_predicate::has_size(200, 100));
+        .stdout(file::is_png());
 }
 
 #[test]
@@ -96,7 +85,7 @@ fn output_format_ps() {
         .arg("--format=ps")
         .assert()
         .success()
-        .stdout(predicate::str::starts_with("%!PS-Adobe-3.0\n"));
+        .stdout(file::is_ps());
 }
 
 #[test]
@@ -106,7 +95,7 @@ fn output_format_eps() {
         .arg("--format=eps")
         .assert()
         .success()
-        .stdout(predicate::str::starts_with("%!PS-Adobe-3.0 EPSF-3.0\n"));
+        .stdout(file::is_eps());
 }
 
 #[test]
@@ -115,20 +104,19 @@ fn output_format_pdf() {
     RsvgConvert::new_with_input(input)
         .arg("--format=pdf")
         .assert()
-        .success();
-    // TODO: add a check for PDF output
+        .success()
+        .stdout(file::is_pdf());
 }
 
 #[test]
 fn output_format_svg_short_option() {
     let input = Path::new("fixtures/dimensions/521-with-viewbox.svg");
-    let svg_output = predicate::str::starts_with("<?xml ").and(predicate::str::contains("<svg "));
     RsvgConvert::new_with_input(input)
         .arg("-f")
         .arg("svg")
         .assert()
         .success()
-        .stdout(svg_output);
+        .stdout(file::is_svg());
 }
 
 #[test]
@@ -160,6 +148,57 @@ fn empty_svg_yields_error() {
         .stderr("The SVG stdin has no dimensions\n");
 }
 
+#[test]
+fn multiple_input_files_not_allowed_for_png_output() {
+    let one = Path::new("fixtures/dimensions/521-with-viewbox.svg");
+    let two = Path::new("fixtures/dimensions/sub-rect-no-unit.svg");
+    RsvgConvert::new()
+        .arg(one)
+        .arg(two)
+        .assert()
+        .failure()
+        .stderr("Multiple SVG files are only allowed for PDF and (E)PS output.\n");
+}
+
+#[test]
+fn multiple_input_files_accepted_for_eps_output() {
+    let one = Path::new("fixtures/dimensions/521-with-viewbox.svg");
+    let two = Path::new("fixtures/dimensions/sub-rect-no-unit.svg");
+    RsvgConvert::new()
+        .arg("--format=eps")
+        .arg(one)
+        .arg(two)
+        .assert()
+        .success()
+        .stdout(file::is_eps());
+}
+
+#[test]
+fn multiple_input_files_accepted_for_pdf_output() {
+    let one = Path::new("fixtures/dimensions/521-with-viewbox.svg");
+    let two = Path::new("fixtures/dimensions/sub-rect-no-unit.svg");
+    RsvgConvert::new()
+        .arg("--format=pdf")
+        .arg(one)
+        .arg(two)
+        .assert()
+        .success()
+        .stdout(file::is_pdf());
+}
+
+#[test]
+fn multiple_input_files_accepted_for_ps_output() {
+    let one = Path::new("fixtures/dimensions/521-with-viewbox.svg");
+    let two = Path::new("fixtures/dimensions/sub-rect-no-unit.svg");
+    RsvgConvert::new()
+        .arg("--format=ps")
+        .arg(one)
+        .arg(two)
+        .assert()
+        .success()
+        .stdout(file::is_ps());
+}
+
 #[test]
 fn width_option() {
     let input = Path::new("fixtures/dimensions/521-with-viewbox.svg");
@@ -167,7 +206,7 @@ fn width_option() {
         .arg("--width=300")
         .assert()
         .success()
-        .stdout(png_predicate::has_size(300, 150));
+        .stdout(file::is_png().with_size(300, 150));
 }
 
 #[test]
@@ -177,7 +216,7 @@ fn height_option() {
         .arg("--height=200")
         .assert()
         .success()
-        .stdout(png_predicate::has_size(400, 200));
+        .stdout(file::is_png().with_size(400, 200));
 }
 
 #[test]
@@ -188,7 +227,7 @@ fn width_and_height_options() {
         .arg("--height=200")
         .assert()
         .success()
-        .stdout(png_predicate::has_size(300, 200));
+        .stdout(file::is_png().with_size(300, 200));
 }
 
 #[test]
@@ -198,7 +237,7 @@ fn zoom_factor() {
         .arg("--zoom=0.8")
         .assert()
         .success()
-        .stdout(png_predicate::has_size(160, 80));
+        .stdout(file::is_png().with_size(160, 80));
 }
 
 // TODO: Is this a bug in rsvg-convert or the desired behavior ?
@@ -222,7 +261,7 @@ fn zoom_factor_and_larger_size() {
         .arg("--zoom=1.5")
         .assert()
         .success()
-        .stdout(png_predicate::has_size(300, 150));
+        .stdout(file::is_png().with_size(300, 150));
 }
 
 #[test]
@@ -234,7 +273,7 @@ fn zoom_factor_and_smaller_size() {
         .arg("--zoom=3.5")
         .assert()
         .success()
-        .stdout(png_predicate::has_size(400, 200));
+        .stdout(file::is_png().with_size(400, 200));
 }
 
 #[test]
@@ -244,7 +283,7 @@ fn x_zoom_option() {
         .arg("--x-zoom=2")
         .assert()
         .success()
-        .stdout(png_predicate::has_size(400, 100));
+        .stdout(file::is_png().with_size(400, 100));
 }
 
 #[test]
@@ -255,7 +294,7 @@ fn x_short_option() {
         .arg("2.0")
         .assert()
         .success()
-        .stdout(png_predicate::has_size(400, 100));
+        .stdout(file::is_png().with_size(400, 100));
 }
 
 #[test]
@@ -265,7 +304,7 @@ fn y_zoom_option() {
         .arg("--y-zoom=2.0")
         .assert()
         .success()
-        .stdout(png_predicate::has_size(200, 200));
+        .stdout(file::is_png().with_size(200, 200));
 }
 
 #[test]
@@ -276,7 +315,7 @@ fn y_short_option() {
         .arg("2")
         .assert()
         .success()
-        .stdout(png_predicate::has_size(200, 200));
+        .stdout(file::is_png().with_size(200, 200));
 }
 
 #[test]
@@ -298,7 +337,7 @@ fn default_resolution_is_90dpi() {
     RsvgConvert::new_with_input(input)
         .assert()
         .success()
-        .stdout(png_predicate::has_size(262, 184));
+        .stdout(file::is_png().with_size(262, 184));
 }
 
 #[test]
@@ -308,7 +347,7 @@ fn x_resolution() {
         .arg("--dpi-x=300")
         .assert()
         .success()
-        .stdout(png_predicate::has_size(874, 184));
+        .stdout(file::is_png().with_size(874, 184));
 }
 
 #[test]
@@ -319,7 +358,7 @@ fn x_resolution_short_option() {
         .arg("45")
         .assert()
         .success()
-        .stdout(png_predicate::has_size(131, 184));
+        .stdout(file::is_png().with_size(131, 184));
 }
 
 #[test]
@@ -329,7 +368,7 @@ fn y_resolution() {
         .arg("--dpi-y=300")
         .assert()
         .success()
-        .stdout(png_predicate::has_size(262, 614));
+        .stdout(file::is_png().with_size(262, 614));
 }
 
 #[test]
@@ -340,7 +379,7 @@ fn y_resolution_short_option() {
         .arg("45")
         .assert()
         .success()
-        .stdout(png_predicate::has_size(262, 92));
+        .stdout(file::is_png().with_size(262, 92));
 }
 
 #[test]
@@ -351,7 +390,7 @@ fn x_and_y_resolution() {
         .arg("--dpi-y=150")
         .assert()
         .success()
-        .stdout(png_predicate::has_size(874, 307));
+        .stdout(file::is_png().with_size(874, 307));
 }
 
 #[test]
@@ -362,7 +401,7 @@ fn default_is_used_for_zero_resolution() {
         .arg("--dpi-y=0")
         .assert()
         .success()
-        .stdout(png_predicate::has_size(262, 184));
+        .stdout(file::is_png().with_size(262, 184));
 }
 
 #[test]
@@ -373,7 +412,7 @@ fn default_is_used_for_negative_resolution() {
         .arg("--dpi-y=-100")
         .assert()
         .success()
-        .stdout(png_predicate::has_size(262, 184));
+        .stdout(file::is_png().with_size(262, 184));
 }
 
 #[test]


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