]> git.lizzy.rs Git - rust.git/blob - src/libgraphviz/tests.rs
Auto merge of #61937 - AaronKutch:master, r=scottmcm
[rust.git] / src / libgraphviz / tests.rs
1 use NodeLabels::*;
2 use super::{Id, Labeller, Nodes, Edges, GraphWalk, render, Style};
3 use super::LabelText::{self, LabelStr, EscStr, HtmlStr};
4 use std::io;
5 use std::io::prelude::*;
6
7 /// each node is an index in a vector in the graph.
8 type Node = usize;
9 struct Edge {
10     from: usize,
11     to: usize,
12     label: &'static str,
13     style: Style,
14 }
15
16 fn edge(from: usize, to: usize, label: &'static str, style: Style) -> Edge {
17     Edge {
18         from,
19         to,
20         label,
21         style,
22     }
23 }
24
25 struct LabelledGraph {
26     /// The name for this graph. Used for labeling generated `digraph`.
27     name: &'static str,
28
29     /// Each node is an index into `node_labels`; these labels are
30     /// used as the label text for each node. (The node *names*,
31     /// which are unique identifiers, are derived from their index
32     /// in this array.)
33     ///
34     /// If a node maps to None here, then just use its name as its
35     /// text.
36     node_labels: Vec<Option<&'static str>>,
37
38     node_styles: Vec<Style>,
39
40     /// Each edge relates a from-index to a to-index along with a
41     /// label; `edges` collects them.
42     edges: Vec<Edge>,
43 }
44
45 // A simple wrapper around LabelledGraph that forces the labels to
46 // be emitted as EscStr.
47 struct LabelledGraphWithEscStrs {
48     graph: LabelledGraph,
49 }
50
51 enum NodeLabels<L> {
52     AllNodesLabelled(Vec<L>),
53     UnlabelledNodes(usize),
54     SomeNodesLabelled(Vec<Option<L>>),
55 }
56
57 type Trivial = NodeLabels<&'static str>;
58
59 impl NodeLabels<&'static str> {
60     fn to_opt_strs(self) -> Vec<Option<&'static str>> {
61         match self {
62             UnlabelledNodes(len) => vec![None; len],
63             AllNodesLabelled(lbls) => lbls.into_iter().map(|l| Some(l)).collect(),
64             SomeNodesLabelled(lbls) => lbls.into_iter().collect(),
65         }
66     }
67
68     fn len(&self) -> usize {
69         match self {
70             &UnlabelledNodes(len) => len,
71             &AllNodesLabelled(ref lbls) => lbls.len(),
72             &SomeNodesLabelled(ref lbls) => lbls.len(),
73         }
74     }
75 }
76
77 impl LabelledGraph {
78     fn new(name: &'static str,
79            node_labels: Trivial,
80            edges: Vec<Edge>,
81            node_styles: Option<Vec<Style>>)
82            -> LabelledGraph {
83         let count = node_labels.len();
84         LabelledGraph {
85             name,
86             node_labels: node_labels.to_opt_strs(),
87             edges,
88             node_styles: match node_styles {
89                 Some(nodes) => nodes,
90                 None => vec![Style::None; count],
91             },
92         }
93     }
94 }
95
96 impl LabelledGraphWithEscStrs {
97     fn new(name: &'static str,
98            node_labels: Trivial,
99            edges: Vec<Edge>)
100            -> LabelledGraphWithEscStrs {
101         LabelledGraphWithEscStrs { graph: LabelledGraph::new(name, node_labels, edges, None) }
102     }
103 }
104
105 fn id_name<'a>(n: &Node) -> Id<'a> {
106     Id::new(format!("N{}", *n)).unwrap()
107 }
108
109 impl<'a> Labeller<'a> for LabelledGraph {
110     type Node = Node;
111     type Edge = &'a Edge;
112     fn graph_id(&'a self) -> Id<'a> {
113         Id::new(self.name).unwrap()
114     }
115     fn node_id(&'a self, n: &Node) -> Id<'a> {
116         id_name(n)
117     }
118     fn node_label(&'a self, n: &Node) -> LabelText<'a> {
119         match self.node_labels[*n] {
120             Some(l) => LabelStr(l.into()),
121             None => LabelStr(id_name(n).name()),
122         }
123     }
124     fn edge_label(&'a self, e: &&'a Edge) -> LabelText<'a> {
125         LabelStr(e.label.into())
126     }
127     fn node_style(&'a self, n: &Node) -> Style {
128         self.node_styles[*n]
129     }
130     fn edge_style(&'a self, e: &&'a Edge) -> Style {
131         e.style
132     }
133 }
134
135 impl<'a> Labeller<'a> for LabelledGraphWithEscStrs {
136     type Node = Node;
137     type Edge = &'a Edge;
138     fn graph_id(&'a self) -> Id<'a> {
139         self.graph.graph_id()
140     }
141     fn node_id(&'a self, n: &Node) -> Id<'a> {
142         self.graph.node_id(n)
143     }
144     fn node_label(&'a self, n: &Node) -> LabelText<'a> {
145         match self.graph.node_label(n) {
146             LabelStr(s) | EscStr(s) | HtmlStr(s) => EscStr(s),
147         }
148     }
149     fn edge_label(&'a self, e: &&'a Edge) -> LabelText<'a> {
150         match self.graph.edge_label(e) {
151             LabelStr(s) | EscStr(s) | HtmlStr(s) => EscStr(s),
152         }
153     }
154 }
155
156 impl<'a> GraphWalk<'a> for LabelledGraph {
157     type Node = Node;
158     type Edge = &'a Edge;
159     fn nodes(&'a self) -> Nodes<'a, Node> {
160         (0..self.node_labels.len()).collect()
161     }
162     fn edges(&'a self) -> Edges<'a, &'a Edge> {
163         self.edges.iter().collect()
164     }
165     fn source(&'a self, edge: &&'a Edge) -> Node {
166         edge.from
167     }
168     fn target(&'a self, edge: &&'a Edge) -> Node {
169         edge.to
170     }
171 }
172
173 impl<'a> GraphWalk<'a> for LabelledGraphWithEscStrs {
174     type Node = Node;
175     type Edge = &'a Edge;
176     fn nodes(&'a self) -> Nodes<'a, Node> {
177         self.graph.nodes()
178     }
179     fn edges(&'a self) -> Edges<'a, &'a Edge> {
180         self.graph.edges()
181     }
182     fn source(&'a self, edge: &&'a Edge) -> Node {
183         edge.from
184     }
185     fn target(&'a self, edge: &&'a Edge) -> Node {
186         edge.to
187     }
188 }
189
190 fn test_input(g: LabelledGraph) -> io::Result<String> {
191     let mut writer = Vec::new();
192     render(&g, &mut writer).unwrap();
193     let mut s = String::new();
194     Read::read_to_string(&mut &*writer, &mut s)?;
195     Ok(s)
196 }
197
198 // All of the tests use raw-strings as the format for the expected outputs,
199 // so that you can cut-and-paste the content into a .dot file yourself to
200 // see what the graphviz visualizer would produce.
201
202 #[test]
203 fn empty_graph() {
204     let labels: Trivial = UnlabelledNodes(0);
205     let r = test_input(LabelledGraph::new("empty_graph", labels, vec![], None));
206     assert_eq!(r.unwrap(),
207 r#"digraph empty_graph {
208 }
209 "#);
210 }
211
212 #[test]
213 fn single_node() {
214     let labels: Trivial = UnlabelledNodes(1);
215     let r = test_input(LabelledGraph::new("single_node", labels, vec![], None));
216     assert_eq!(r.unwrap(),
217 r#"digraph single_node {
218     N0[label="N0"];
219 }
220 "#);
221 }
222
223 #[test]
224 fn single_node_with_style() {
225     let labels: Trivial = UnlabelledNodes(1);
226     let styles = Some(vec![Style::Dashed]);
227     let r = test_input(LabelledGraph::new("single_node", labels, vec![], styles));
228     assert_eq!(r.unwrap(),
229 r#"digraph single_node {
230     N0[label="N0"][style="dashed"];
231 }
232 "#);
233 }
234
235 #[test]
236 fn single_edge() {
237     let labels: Trivial = UnlabelledNodes(2);
238     let result = test_input(LabelledGraph::new("single_edge",
239                                                labels,
240                                                vec![edge(0, 1, "E", Style::None)],
241                                                None));
242     assert_eq!(result.unwrap(),
243 r#"digraph single_edge {
244     N0[label="N0"];
245     N1[label="N1"];
246     N0 -> N1[label="E"];
247 }
248 "#);
249 }
250
251 #[test]
252 fn single_edge_with_style() {
253     let labels: Trivial = UnlabelledNodes(2);
254     let result = test_input(LabelledGraph::new("single_edge",
255                                                labels,
256                                                vec![edge(0, 1, "E", Style::Bold)],
257                                                None));
258     assert_eq!(result.unwrap(),
259 r#"digraph single_edge {
260     N0[label="N0"];
261     N1[label="N1"];
262     N0 -> N1[label="E"][style="bold"];
263 }
264 "#);
265 }
266
267 #[test]
268 fn test_some_labelled() {
269     let labels: Trivial = SomeNodesLabelled(vec![Some("A"), None]);
270     let styles = Some(vec![Style::None, Style::Dotted]);
271     let result = test_input(LabelledGraph::new("test_some_labelled",
272                                                labels,
273                                                vec![edge(0, 1, "A-1", Style::None)],
274                                                styles));
275     assert_eq!(result.unwrap(),
276 r#"digraph test_some_labelled {
277     N0[label="A"];
278     N1[label="N1"][style="dotted"];
279     N0 -> N1[label="A-1"];
280 }
281 "#);
282 }
283
284 #[test]
285 fn single_cyclic_node() {
286     let labels: Trivial = UnlabelledNodes(1);
287     let r = test_input(LabelledGraph::new("single_cyclic_node",
288                                           labels,
289                                           vec![edge(0, 0, "E", Style::None)],
290                                           None));
291     assert_eq!(r.unwrap(),
292 r#"digraph single_cyclic_node {
293     N0[label="N0"];
294     N0 -> N0[label="E"];
295 }
296 "#);
297 }
298
299 #[test]
300 fn hasse_diagram() {
301     let labels = AllNodesLabelled(vec!["{x,y}", "{x}", "{y}", "{}"]);
302     let r = test_input(LabelledGraph::new("hasse_diagram",
303                                           labels,
304                                           vec![edge(0, 1, "", Style::None),
305                                                edge(0, 2, "", Style::None),
306                                                edge(1, 3, "", Style::None),
307                                                edge(2, 3, "", Style::None)],
308                                           None));
309     assert_eq!(r.unwrap(),
310 r#"digraph hasse_diagram {
311     N0[label="{x,y}"];
312     N1[label="{x}"];
313     N2[label="{y}"];
314     N3[label="{}"];
315     N0 -> N1[label=""];
316     N0 -> N2[label=""];
317     N1 -> N3[label=""];
318     N2 -> N3[label=""];
319 }
320 "#);
321 }
322
323 #[test]
324 fn left_aligned_text() {
325     let labels = AllNodesLabelled(vec![
326         "if test {\
327        \\l    branch1\
328        \\l} else {\
329        \\l    branch2\
330        \\l}\
331        \\lafterward\
332        \\l",
333         "branch1",
334         "branch2",
335         "afterward"]);
336
337     let mut writer = Vec::new();
338
339     let g = LabelledGraphWithEscStrs::new("syntax_tree",
340                                           labels,
341                                           vec![edge(0, 1, "then", Style::None),
342                                                edge(0, 2, "else", Style::None),
343                                                edge(1, 3, ";", Style::None),
344                                                edge(2, 3, ";", Style::None)]);
345
346     render(&g, &mut writer).unwrap();
347     let mut r = String::new();
348     Read::read_to_string(&mut &*writer, &mut r).unwrap();
349
350     assert_eq!(r,
351 r#"digraph syntax_tree {
352     N0[label="if test {\l    branch1\l} else {\l    branch2\l}\lafterward\l"];
353     N1[label="branch1"];
354     N2[label="branch2"];
355     N3[label="afterward"];
356     N0 -> N1[label="then"];
357     N0 -> N2[label="else"];
358     N1 -> N3[label=";"];
359     N2 -> N3[label=";"];
360 }
361 "#);
362 }
363
364 #[test]
365 fn simple_id_construction() {
366     let id1 = Id::new("hello");
367     match id1 {
368         Ok(_) => {}
369         Err(..) => panic!("'hello' is not a valid value for id anymore"),
370     }
371 }
372
373 #[test]
374 fn badly_formatted_id() {
375     let id2 = Id::new("Weird { struct : ure } !!!");
376     match id2 {
377         Ok(_) => panic!("graphviz id suddenly allows spaces, brackets and stuff"),
378         Err(..) => {}
379     }
380 }