1/** \file
2 * \brief Tests for the ogdf::SvgPrinter
3 *
4 * \author Tilo Wiedera
5 *
6 * \par License:
7 * This file is part of the Open Graph Drawing Framework (OGDF).
8 *
9 * \par
10 * Copyright (C)<br>
11 * See README.md in the OGDF root directory for details.
12 *
13 * \par
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * Version 2 or 3 as published by the Free Software Foundation;
17 * see the file LICENSE.txt included in the packaging of this file
18 * for details.
19 *
20 * \par
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * \par
27 * You should have received a copy of the GNU General Public
28 * License along with this program; if not, see
29 * http://www.gnu.org/copyleft/gpl.html
30 */
31
32#include <regex>
33#include <ogdf/lib/pugixml/pugixml.h>
34#include <ogdf/fileformats/GraphIO.h>
35#include <ogdf/basic/graph_generators.h>
36#include <testing.h>
37
38void createDocument(GraphAttributes attr, pugi::xml_document &doc, GraphIO::SVGSettings *settings = nullptr, bool reassignPositions = true) {
39 std::ostringstream write;
40
41 if(reassignPositions) {
42 int i = 0;
43 for(node v : attr.constGraph().nodes) {
44 attr.x(v) = attr.y(v) = i++ * 100;
45 attr.width(v) = attr.height(v) = 10;
46 }
47 }
48
49 if(settings == nullptr) {
50 GraphIO::drawSVG(attr, write);
51 } else {
52 GraphIO::drawSVG(attr, write, *settings);
53 }
54
55 pugi::xml_parse_result result = doc.load_string(write.str().c_str());
56 AssertThat((bool) result, IsTrue());
57}
58
59go_bandit([](){
60describe("GraphIO", []() {
61describe("SVG", []() {
62 std::unique_ptr<Graph> graph;
63 int numberOfNodes = 42;
64
65 before_each([&](){
66 graph.reset(new Graph);
67 randomBiconnectedGraph(*graph, numberOfNodes, 3*numberOfNodes);
68 });
69
70 it("is well-formed", [&]() {
71 GraphAttributes attr(*graph);
72 pugi::xml_document doc;
73 createDocument(attr, doc);
74
75 pugi::xml_node svg = doc.child("svg");
76 AssertThat((bool) svg, IsTrue());
77 AssertThat(svg.attribute("viewBox").empty(), IsFalse());
78
79 AssertThat(static_cast<int>(svg.select_nodes("//rect").size()), Equals(graph->numberOfNodes()));
80 AssertThat(static_cast<int>(svg.select_nodes("//path").size()), Equals(graph->numberOfEdges()));
81 });
82
83 it("supports 3D", [&]() {
84 GraphAttributes attr(*graph,
85 GraphAttributes::nodeGraphics |
86 GraphAttributes::nodeStyle |
87 GraphAttributes::edgeGraphics |
88 GraphAttributes::threeD |
89 GraphAttributes::nodeLabel |
90 GraphAttributes::nodeLabelPosition);
91 List<node> nodes;
92 graph->allNodes(nodes);
93 nodes.permute();
94 int i = 0;
95
96 for(node v : nodes) {
97 attr.fillColor(v) = Color::Name::Gray;
98 attr.x(v) = randomNumber(0, numberOfNodes*5);
99 attr.y(v) = randomNumber(0, numberOfNodes*5);
100 attr.label(v) = to_string(i);
101 attr.z(v) = i++;
102 }
103
104 List<int> expected;
105 for(i = 0; i < numberOfNodes; i++) {
106 expected.pushBack(i);
107 }
108
109 pugi::xml_document doc;
110 createDocument(attr, doc);
111 pugi::xpath_node_set xmlNodes = doc.select_nodes("//text");
112 AssertThat(static_cast<int>(xmlNodes.size()), Equals(graph->numberOfNodes()));
113
114 for(pugi::xpath_node xmlNode : xmlNodes) {
115 int index = xmlNode.node().text().as_int();
116 AssertThat(expected.search(index).valid(), IsTrue());
117 expected.removeFirst(index);
118 }
119 });
120
121 it("respects the requested size", [&]() {
122 GraphAttributes attr(*graph);
123 GraphIO::SVGSettings settings;
124 settings.width("100%");
125 settings.height("700px");
126
127 pugi::xml_document doc;
128 createDocument(attr, doc, &settings);
129 pugi::xml_node svg = doc.child("svg");
130
131 AssertThat(std::string("100%") == svg.attribute("width").value(), IsTrue());
132 AssertThat(std::string("700px") == svg.attribute("height").value(), IsTrue());
133 });
134
135 it("doesn't set a default size", [&]() {
136 GraphAttributes attr(*graph);
137
138 pugi::xml_document doc;
139 createDocument(attr, doc);
140 pugi::xml_node svg = doc.child("svg");
141
142 AssertThat(svg.attribute("width").empty(), IsTrue());
143 AssertThat(svg.attribute("height").empty(), IsTrue());
144 });
145
146 it("supports fill color", [&]() {
147 GraphAttributes attr(*graph, GraphAttributes::nodeGraphics | GraphAttributes::nodeStyle);
148
149 for(node v : graph->nodes) {
150 attr.fillColor(v) = "#0ACDC0";
151 }
152
153 pugi::xml_document doc;
154 createDocument(attr, doc);
155
156 AssertThat(static_cast<int>(doc.select_nodes(".//*[@fill='#0ACDC0']").size()), Equals(graph->numberOfNodes()));
157 });
158
159 it("supports stroke color", [&]() {
160 GraphAttributes attr(*graph, GraphAttributes::nodeGraphics | GraphAttributes::nodeStyle);
161
162 for(node v : graph->nodes) {
163 attr.strokeColor(v) = "#0ACDC0";
164 }
165
166 pugi::xml_document doc;
167 createDocument(attr, doc);
168
169 AssertThat(static_cast<int>(doc.select_nodes(".//*[@stroke='#0ACDC0']").size()), Equals(graph->numberOfNodes()));
170 });
171
172 it("sets the viewBox", [&]() {
173 const double tolerance = 1;
174 GraphAttributes attr(*graph, GraphAttributes::nodeGraphics | GraphAttributes::nodeStyle);
175
176 GraphIO::SVGSettings settings;
177 settings.margin(10);
178
179 for(node v : graph->nodes) {
180 double dx = attr.width(v)/2 + settings.margin() + tolerance;
181 double dy = attr.height(v)/2 + settings.margin() + tolerance;
182 attr.x(v) = randomDouble(dx, 100 - dx);
183 attr.y(v) = randomDouble(dy, 100 - dy);
184 }
185
186 pugi::xml_document doc;
187 createDocument(attr, doc, &settings, false);
188
189 std::string viewBox = doc.child("svg").attribute("viewBox").value();
190 std::stringstream ss(viewBox);
191
192 double xmin, xmax, ymin, ymax;
193
194 ss >> xmin;
195 ss >> ymin;
196 ss >> xmax;
197 ss >> ymax;
198
199 xmax += xmin;
200 ymax += ymin;
201
202 AssertThat(xmin, IsGreaterThan(0) || Equals(0));
203 AssertThat(ymin, IsGreaterThan(0) || Equals(0));
204 AssertThat(xmax, IsLessThan(100) || Equals(100));
205 AssertThat(ymax, IsLessThan(100) || Equals(100));
206
207 AssertThat(xmin, IsLessThan(xmax));
208 AssertThat(ymin, IsLessThan(ymax));
209 });
210
211 it("draws clusters", [&]() {
212 ClusterGraph clusterGraph(*graph);
213 randomClusterGraph(clusterGraph, *graph, 10);
214 ClusterGraphAttributes attr(clusterGraph, GraphAttributes::nodeGraphics | GraphAttributes::nodeStyle);
215
216 for(node v : graph->nodes) {
217 attr.shape(v) = Shape::Octagon;
218 }
219
220 std::ostringstream write;
221 GraphIO::drawSVG(attr, write);
222 pugi::xml_document doc;
223 pugi::xml_parse_result result = doc.load_string(write.str().c_str());
224
225 AssertThat((bool) result, IsTrue());
226 AssertThat(static_cast<int>(doc.select_nodes(".//rect").size()), Equals(clusterGraph.numberOfClusters() - 1));
227 });
228
229 it("supports arrow heads", [&]() {
230 GraphAttributes attr(*graph, GraphAttributes::nodeGraphics | GraphAttributes::edgeGraphics | GraphAttributes::edgeArrow);
231
232 pugi::xml_document doc;
233 createDocument(attr, doc);
234
235 AssertThat(static_cast<int>(doc.select_nodes(".//polygon").size()), Equals(graph->numberOfEdges()));
236 });
237
238 it("supports two arrows per edge", [&]() {
239 GraphAttributes attr(*graph, GraphAttributes::nodeGraphics | GraphAttributes::edgeGraphics | GraphAttributes::edgeArrow);
240
241 for(edge e : graph->edges) {
242 attr.arrowType(e) = EdgeArrow::Both;
243 }
244
245 pugi::xml_document doc;
246 createDocument(attr, doc);
247
248 AssertThat(static_cast<int>(doc.select_nodes(".//polygon").size()), Equals(graph->numberOfEdges() * 2));
249 });
250});
251});
252});
253