Lines
34.57 %
Functions
31.58 %
Branches
10.19 %
use float_cmp::approx_eq;
use gio::glib::Bytes;
use gio::MemoryInputStream;
use predicates::prelude::*;
use predicates::reflection::{Case, Child, PredicateReflection, Product};
use std::cmp;
use std::fmt;
use rsvg::{CairoRenderer, Length, Loader, LoadingError, SvgHandle};
/// 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, width: Length, height: Length) -> DetailPredicate<Self> {
DetailPredicate::<Self> {
p: self,
d: Detail::Size(Dimensions {
w: width,
h: height,
}),
}
fn svg_from_bytes(data: &[u8]) -> Result<SvgHandle, LoadingError> {
let bytes = Bytes::from(data);
let stream = MemoryInputStream::from_bytes(&bytes);
Loader::new().read_stream(&stream, None::<&gio::File>, None::<&gio::Cancellable>)
impl Predicate<[u8]> for SvgPredicate {
fn eval(&self, data: &[u8]) -> bool {
svg_from_bytes(data).is_ok()
fn find_case<'a>(&'a self, _expected: bool, data: &[u8]) -> Option<Case<'a>> {
match svg_from_bytes(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
pub struct DetailPredicate<SvgPredicate> {
p: SvgPredicate,
d: Detail,
enum Detail {
Size(Dimensions),
/// SVG's dimensions
struct Dimensions {
w: Length,
h: Length,
impl Dimensions {
pub fn width(&self) -> f64 {
self.w.length
pub fn height(&self) -> f64 {
self.h.length
impl fmt::Display for Dimensions {
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 {}
impl DetailPredicate<SvgPredicate> {
fn eval_doc(&self, handle: &SvgHandle) -> bool {
match &self.d {
Detail::Size(d) => {
let renderer = CairoRenderer::new(handle);
let dimensions = renderer.intrinsic_dimensions();
(dimensions.width, dimensions.height) == (d.w, d.h)
fn find_case_for_doc<'a>(&'a self, expected: bool, handle: &SvgHandle) -> Option<Case<'a>> {
if self.eval_doc(handle) == expected {
let product = self.product_for_doc(handle);
Some(Case::new(Some(self), false).add_product(product))
} else {
None
fn product_for_doc(&self, handle: &SvgHandle) -> Product {
Detail::Size(_) => {
Product::new(
"actual size",
format!(
"width={:?}, height={:?}",
dimensions.width, dimensions.height
),
impl Predicate<[u8]> for DetailPredicate<SvgPredicate> {
Ok(handle) => self.eval_doc(&handle),
_ => false,
fn find_case<'a>(&'a self, expected: bool, data: &[u8]) -> Option<Case<'a>> {
Ok(handle) => self.find_case_for_doc(expected, &handle),
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> {
Detail::Size(d) => write!(f, "is an SVG sized {}", d),