diff options
author | Daniel Petri Rocha <daniel.petri@tum.de> | 2021-11-22 22:11:07 +0100 |
---|---|---|
committer | Daniel Petri Rocha <daniel.petri@tum.de> | 2021-11-22 22:46:16 +0100 |
commit | 338f88a4683bcb312d0e03d9732a7419602b6417 (patch) | |
tree | 15db127dbe30c6edb195bb109503517d976db366 /tests | |
parent | e5a4e80bb3677a69bf0f2405bac6ba62b513d1c3 (diff) | |
download | librsvg-338f88a4683bcb312d0e03d9732a7419602b6417.tar.gz |
SvgPredicate with libxml parser
Diffstat (limited to 'tests')
-rw-r--r-- | tests/src/cmdline/rsvg_convert.rs | 68 | ||||
-rw-r--r-- | tests/src/predicates/file.rs | 8 | ||||
-rw-r--r-- | tests/src/predicates/mod.rs | 1 | ||||
-rw-r--r-- | tests/src/predicates/svg.rs | 198 |
4 files changed, 270 insertions, 5 deletions
diff --git a/tests/src/cmdline/rsvg_convert.rs b/tests/src/cmdline/rsvg_convert.rs index 78c7c625..a1ad58cc 100644 --- a/tests/src/cmdline/rsvg_convert.rs +++ b/tests/src/cmdline/rsvg_convert.rs @@ -16,6 +16,7 @@ use predicates::str::*; use std::path::Path; use tempfile::Builder; use url::Url; +use librsvg::{Length, LengthUnit}; // What should be tested here? // The goal is to test the code in rsvg-convert, not the entire library. @@ -152,7 +153,72 @@ fn output_format_svg_short_option() { .arg("svg") .assert() .success() - .stdout(file::is_svg()); + .stdout(file::is_svg().with_svg_format()); +} + +#[cfg(system_deps_have_cairo_svg)] +#[test] +fn user_specified_width_and_height() { + RsvgConvert::new_with_input("tests/fixtures/dimensions/521-with-viewbox.svg") + .arg("--format") + .arg("svg") + .arg("--width") + .arg("42cm") + .arg("--height") + .arg("43cm") + .assert() + .success() + .stdout(file::is_svg().with_size( + Length::new(42.0, LengthUnit::Cm), + Length::new(43.0, LengthUnit::Cm) + ) + ); +} + +#[cfg(system_deps_have_cairo_svg)] +#[test] +fn user_specified_width_and_height_px_output() { + RsvgConvert::new_with_input("tests/fixtures/dimensions/521-with-viewbox.svg") + .arg("--format") + .arg("svg") + .arg("--width") + .arg("1920") + .arg("--height") + .arg("508mm") + .assert() + .success() + .stdout(file::is_svg().with_size( + Length::new(1920.0, LengthUnit::Px), + Length::new(1920.0, LengthUnit::Px) + ) + ); +} + +#[cfg(system_deps_have_cairo_svg)] +#[test] +fn user_specified_width_and_height_a4() { + RsvgConvert::new_with_input("tests/fixtures/dimensions/521-with-viewbox.svg") + .arg("--format") + .arg("svg") + .arg("--page-width") + .arg("210mm") + .arg("--page-height") + .arg("297mm") + .arg("--left") + .arg("1cm") + .arg("--top") + .arg("1cm") + .arg("--width") + .arg("190mm") + .arg("--height") + .arg("277mm") + .assert() + .success() + .stdout(file::is_svg().with_size( + Length::new(210.0, LengthUnit::Mm), + Length::new(297.0, LengthUnit::Mm) + ) + ); } #[test] diff --git a/tests/src/predicates/file.rs b/tests/src/predicates/file.rs index 1a026ce1..244835e0 100644 --- a/tests/src/predicates/file.rs +++ b/tests/src/predicates/file.rs @@ -1,9 +1,9 @@ -use predicates::boolean::AndPredicate; use predicates::prelude::*; -use predicates::str::{ContainsPredicate, StartsWithPredicate}; +use predicates::str::{StartsWithPredicate}; use crate::predicates::pdf::PdfPredicate; use crate::predicates::png::PngPredicate; +use crate::predicates::svg::SvgPredicate; /// Predicates to check that some output ([u8]) is of a certain file type @@ -23,6 +23,6 @@ pub fn is_pdf() -> PdfPredicate { PdfPredicate {} } -pub fn is_svg() -> AndPredicate<StartsWithPredicate, ContainsPredicate, str> { - predicate::str::starts_with("<?xml ").and(predicate::str::contains("<svg ")) +pub fn is_svg() -> SvgPredicate { + SvgPredicate {} } diff --git a/tests/src/predicates/mod.rs b/tests/src/predicates/mod.rs index 04e8daa5..81716b29 100644 --- a/tests/src/predicates/mod.rs +++ b/tests/src/predicates/mod.rs @@ -3,6 +3,7 @@ extern crate predicates; pub mod file; mod pdf; mod png; +mod svg; use predicates::str; diff --git a/tests/src/predicates/svg.rs b/tests/src/predicates/svg.rs new file mode 100644 index 00000000..f9409126 --- /dev/null +++ b/tests/src/predicates/svg.rs @@ -0,0 +1,198 @@ +use librsvg::Length; +use predicates::boolean::AndPredicate; +use predicates::prelude::*; +use predicates::reflection::{Case, Child, PredicateReflection, Product}; +use predicates::str::StartsWithPredicate; +use predicates::str::*; +use std::cmp; +use std::fmt; +use std::str; + +use libxml::parser::Parser; +use libxml::xpath::Context; + +use librsvg::doctest_only::Both; +use librsvg::rsvg_convert_only::ULength; +use librsvg::Parse; + +/// Checks that the variable of type [u8] can be parsed as a SVG file. +#[derive(Debug)] +pub struct SvgPredicate {} + +impl SvgPredicate { + pub fn with_size(self: Self, width: Length, height: Length) -> DetailPredicate<Self> { + DetailPredicate::<Self> { + p: self, + d: Detail::Size(Dimensions { + w: width, + h: height, + }), + } + } + + pub fn with_svg_format( + self: Self, + ) -> AndPredicate<StartsWithPredicate, ContainsPredicate, str> { + predicate::str::starts_with("<?xml ").and(predicate::str::contains("<svg ")) + } +} + +impl Predicate<[u8]> for SvgPredicate { + fn eval(&self, data: &[u8]) -> bool { + str::from_utf8(data).is_ok() + } + + fn find_case<'a>(&'a self, _expected: bool, data: &[u8]) -> Option<Case<'a>> { + match str::from_utf8(data) { + Ok(_) => None, + Err(e) => Some(Case::new(Some(self), false).add_product(Product::new("Error", e))), + } + } +} + +impl PredicateReflection for SvgPredicate {} + +impl fmt::Display for SvgPredicate { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "is an SVG") + } +} + +/// Extends a SVG Predicate by a check for its size +#[derive(Debug)] +pub struct DetailPredicate<SvgPredicate> { + p: SvgPredicate, + d: Detail, +} + +#[derive(Debug)] +enum Detail { + Size(Dimensions), +} + +/// SVG's dimensions +#[derive(Debug)] +struct Dimensions { + w: Length, + h: Length, +} + +impl Dimensions { + pub fn width(self: &Self) -> f64 { + self.w.length + } + + pub fn height(self: &Self) -> f64 { + self.h.length + } +} + +impl fmt::Display for Dimensions { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{}{} x {}{}", + self.width(), + self.w.unit, + self.height(), + self.h.unit + ) + } +} + +impl cmp::PartialEq for Dimensions { + fn eq(&self, other: &Self) -> bool { + approx_eq!(f64, self.width(), other.width(), epsilon = 0.000_001) + && approx_eq!(f64, self.height(), other.height(), epsilon = 0.000_001) + && (self.w.unit == self.h.unit) + && (self.h.unit == other.h.unit) + && (other.h.unit == other.w.unit) + } +} + +impl cmp::Eq for Dimensions {} + +trait Details { + fn get_size(&self) -> Option<Dimensions>; +} + +impl DetailPredicate<SvgPredicate> { + fn eval_doc(&self, doc: &str) -> bool { + match &self.d { + Detail::Size(d) => doc.get_size() == Some(Dimensions { w: d.w, h: d.h }), + } + } + + fn find_case_for_doc<'a>(&'a self, expected: bool, doc: &str) -> Option<Case<'a>> { + if self.eval_doc(doc) == expected { + let product = self.product_for_doc(doc); + Some(Case::new(Some(self), false).add_product(product)) + } else { + None + } + } + + fn product_for_doc(&self, doc: &str) -> Product { + match &self.d { + Detail::Size(_) => Product::new( + "actual size", + match doc.get_size() { + Some(dim) => format!("{}", dim), + None => "None".to_string(), + }, + ), + } + } +} + +impl Details for &str { + fn get_size(self: &Self) -> Option<Dimensions> { + let parser = Parser::default(); + let doc = parser.parse_string(self).unwrap(); + let context = Context::new(&doc).unwrap(); + + let width = context.evaluate("//@width").unwrap().get_nodes_as_vec()[0].get_content(); + let height = context.evaluate("//@height").unwrap().get_nodes_as_vec()[0].get_content(); + + let parsed_w = ULength::<Both>::parse_str(&width).unwrap(); + let parsed_h = ULength::<Both>::parse_str(&height).unwrap(); + + let dim = Dimensions { + w: Length::new(parsed_w.length, parsed_w.unit), + h: Length::new(parsed_h.length, parsed_h.unit), + }; + + return Some(dim); + } +} + +impl Predicate<[u8]> for DetailPredicate<SvgPredicate> { + fn eval(&self, data: &[u8]) -> bool { + match str::from_utf8(data) { + Ok(doc) => self.eval_doc(&doc), + _ => false, + } + } + + fn find_case<'a>(&'a self, expected: bool, data: &[u8]) -> Option<Case<'a>> { + match str::from_utf8(data) { + Ok(doc) => self.find_case_for_doc(expected, &doc), + Err(e) => Some(Case::new(Some(self), false).add_product(Product::new("Error", e))), + } + } +} + +impl PredicateReflection for DetailPredicate<SvgPredicate> { + 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 DetailPredicate<SvgPredicate> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match &self.d { + Detail::Size(d) => write!(f, "is an SVG sized {}", d), + } + } +} |