1
1
//! Tests for the data files from https://github.com/horizon-eda/horizon/
2
//!
3
//! Horizon is an app Electronic Design Automation.  It has SVG templates with specially
4
//! named elements; the app extracts their geometries and renders GUI widgets instead of
5
//! those elements.  So, it is critical that the geometries get computed accurately.
6
//!
7
//! Horizon's build system pre-computes the geometries of the SVG templates' elements, and
8
//! stores them in JSON files.  You can see the SVGs and the .subs JSON files in the
9
//! tests/fixtures/horizon in the librsvg source tree.
10
//!
11
//! This test file has machinery to load the SVG templates, and the JSON files with the
12
//! expected geometries.  The tests check that librsvg computes the same geometries every
13
//! time.
14

            
15
use anyhow::{Context, Result};
16
use rsvg::tests_only::Rect;
17
use rsvg::{CairoRenderer, LengthUnit, Loader};
18
use serde::Deserialize;
19
use std::collections::BTreeMap;
20
use std::fs;
21
use std::path::Path;
22

            
23
// Copy of cairo::Rectangle
24
//
25
// Somehow I can't make serde's "remote" work here, in combination with the BTreeMap below...
26
730
#[derive(Copy, Clone, Deserialize, Debug, PartialEq)]
27
struct Rectangle {
28
    x: f64,
29
    y: f64,
30
    width: f64,
31
    height: f64,
32
}
33

            
34
impl From<Rectangle> for Rect {
35
54
    fn from(r: Rectangle) -> Rect {
36
54
        Rect {
37
54
            x0: r.x,
38
54
            y0: r.y,
39
54
            x1: r.x + r.width,
40
54
            y1: r.y + r.height,
41
        }
42
54
    }
43
}
44

            
45
8
#[derive(Deserialize)]
46
struct Geometries(BTreeMap<String, Rectangle>);
47

            
48
4
fn read_geometries(path: &Path) -> Result<Geometries> {
49
4
    let contents = fs::read_to_string(path).context(format!("could not read {:?}", path))?;
50
4
    serde_json::from_str(&contents).context(format!("could not parse JSON from {:?}", path))
51
4
}
52

            
53
// We create a struct with the id and geometry so that
54
// assert_eq!() in the tests will print out the element name for failures.
55
#[derive(Debug, PartialEq)]
56
struct Element {
57
    id: String,
58
    geom: Rect,
59
}
60

            
61
macro_rules! assert_rectangles_approx_eq {
62
    ($id:expr, $expected:expr, $computed:expr) => {
63
53
        if !$expected.approx_eq(&$computed) {
64
            eprintln!(
65
                "assertion failed: rectangles are not approximately equal for id={}",
66
                $id
67
            );
68
            eprintln!("  expected: {:?}", $expected);
69
            eprintln!("  computed: {:?}", $computed);
70
            panic!();
71
        }
72
    };
73
}
74

            
75
4
fn test(svg_filename: &str) {
76
4
    let mut geometries_filename = String::from(svg_filename);
77
4
    geometries_filename.push_str(".subs");
78

            
79
    let geometries =
80
4
        read_geometries(Path::new(&geometries_filename)).expect("reading geometries JSON");
81

            
82
112
    let handle = Loader::new()
83
        .read_path(svg_filename)
84
        .expect("reading geometries SVG");
85
112
    let renderer = CairoRenderer::new(&handle);
86
112
    let dimensions = renderer.intrinsic_dimensions();
87
4
    let (svg_width, svg_height) = renderer
88
        .intrinsic_size_in_pixels()
89
        .expect("intrinsic size in pixels");
90

            
91
4
    assert!(matches!(dimensions.width.unit, LengthUnit::Px));
92
4
    assert!(matches!(dimensions.height.unit, LengthUnit::Px));
93
4
    assert_eq!(dimensions.width.length, svg_width);
94
4
    assert_eq!(dimensions.height.length, svg_height);
95

            
96
4
    for (id, expected) in geometries.0.iter() {
97
54
        println!("id: {}", id);
98
55
        let expected = Element {
99
56
            id: String::from(id),
100
55
            geom: Rect::from(*expected),
101
        };
102

            
103
55
        let viewport = cairo::Rectangle::new(0.0, 0.0, svg_width, svg_height);
104

            
105
54
        let (geometry, _) = renderer
106
54
            .geometry_for_layer(Some(id), &viewport)
107
            .unwrap_or_else(|_| panic!("getting geometry for {}", id));
108

            
109
53
        let computed = Element {
110
53
            id: String::from(id),
111
53
            geom: geometry.into(),
112
        };
113

            
114
53
        assert_rectangles_approx_eq!(id, expected.geom, computed.geom);
115
53
    }
116
4
}
117

            
118
#[test]
119
2
fn dual() {
120
1
    test("tests/fixtures/geometries/dual.svg");
121
2
}
122

            
123
#[test]
124
2
fn grid() {
125
1
    test("tests/fixtures/geometries/grid.svg");
126
2
}
127

            
128
#[test]
129
2
fn quad() {
130
1
    test("tests/fixtures/geometries/quad.svg");
131
2
}
132

            
133
#[test]
134
2
fn single() {
135
1
    test("tests/fixtures/geometries/single.svg");
136
2
}