diff --git a/Cargo.toml b/Cargo.toml index f727369..4eeb037 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ criterion = { version = "0.5.1", features = ["html_reports"] } rmp-serde = "1.1.1" rstest = "0.21.0" itertools = "0.13.0" +insta = "1.39.0" [[bench]] name = "bench_main" diff --git a/src/render.rs b/src/render.rs index 3178f6a..4fbf707 100644 --- a/src/render.rs +++ b/src/render.rs @@ -142,3 +142,168 @@ impl EdgeStyle { } } } + +#[cfg(test)] +mod test { + use std::fmt::Display; + + use rstest::{fixture, rstest}; + + use crate::{Hierarchy, LinkMut, PortGraph, PortMut, PortView, Weights}; + + use super::{DotFormat, MermaidFormat}; + + /// A simple flat graph with some nodes and edges. + #[fixture] + fn flat_graph() -> ( + &'static str, + PortGraph, + Option, + Option>, + ) { + let mut graph = PortGraph::new(); + let n1 = graph.add_node(3, 2); + let n2 = graph.add_node(1, 0); + let n3 = graph.add_node(1, 0); + graph.link_nodes(n1, 0, n2, 0).unwrap(); + graph.link_nodes(n1, 1, n3, 0).unwrap(); + ("flat", graph, None, None) + } + + #[fixture] + fn hierarchy_graph() -> ( + &'static str, + PortGraph, + Option, + Option>, + ) { + let mut graph = PortGraph::new(); + let n1 = graph.add_node(3, 2); + let n2 = graph.add_node(0, 1); + let n3 = graph.add_node(1, 0); + graph.link_nodes(n2, 0, n3, 0).unwrap(); + + let mut hier = Hierarchy::new(); + hier.push_child(n2, n1).unwrap(); + hier.push_child(n3, n1).unwrap(); + + ("hierarchy", graph, Some(hier), None) + } + + /// A hierarchical graph with edges between different regions. + #[fixture] + fn hierarchy_interregional_graph() -> ( + &'static str, + PortGraph, + Option, + Option>, + ) { + let mut graph = PortGraph::new(); + let n1 = graph.add_node(3, 2); + let n2 = graph.add_node(0, 1); + let n3 = graph.add_node(1, 0); + let n4 = graph.add_node(1, 1); + let n5 = graph.add_node(1, 1); + graph.link_nodes(n2, 0, n3, 0).unwrap(); + graph.link_nodes(n4, 0, n5, 0).unwrap(); + + let mut hier = Hierarchy::new(); + hier.push_child(n2, n1).unwrap(); + hier.push_child(n3, n1).unwrap(); + hier.push_child(n4, n2).unwrap(); + hier.push_child(n5, n3).unwrap(); + + ("hierarchy_interregional", graph, Some(hier), None) + } + + #[fixture] + fn weighted_graph() -> ( + &'static str, + PortGraph, + Option, + Option>, + ) { + let mut graph = PortGraph::new(); + let n1 = graph.add_node(0, 2); + let n2 = graph.add_node(1, 0); + let n3 = graph.add_node(1, 0); + let p10 = graph.output(n1, 0).unwrap(); + let p11 = graph.output(n1, 1).unwrap(); + let p20 = graph.input(n2, 0).unwrap(); + let p30 = graph.input(n3, 0).unwrap(); + + graph.link_ports(p10, p20).unwrap(); + graph.link_ports(p11, p30).unwrap(); + + let mut weights = Weights::new(); + weights[n1] = "node1".to_string(); + weights[n2] = "node2".to_string(); + weights[n3] = "node3".to_string(); + weights[p10] = "out 0".to_string(); + weights[p11] = "out 1".to_string(); + weights[p20] = "in 0".to_string(); + weights[p30] = "in 0".to_string(); + + ("weighted", graph, None, Some(weights)) + } + + #[rstest] + #[case::flat(flat_graph())] + #[case::hierarchy(hierarchy_graph())] + #[case::interregional(hierarchy_interregional_graph())] + #[case::weighted(weighted_graph())] + #[cfg_attr(miri, ignore)] // Opening files is not supported in (isolated) miri + fn mermaid_output( + #[case] graph_elems: ( + &str, + impl MermaidFormat, + Option, + Option>, + ), + ) { + let (name, graph, hierarchy, weights) = graph_elems; + let mermaid = match (hierarchy, weights) { + (Some(h), Some(w)) => graph + .mermaid_format() + .with_hierarchy(&h) + .with_weights(&w) + .finish(), + (Some(h), None) => graph.mermaid_format().with_hierarchy(&h).finish(), + (None, Some(w)) => graph.mermaid_format().with_weights(&w).finish(), + (None, None) => graph.mermaid_string(), + }; + + let name = format!("{}__mermaid", name); + insta::assert_snapshot!(name, mermaid); + } + + #[rstest] + #[case::flat(flat_graph())] + #[case::hierarchy(hierarchy_graph())] + #[case::interregional(hierarchy_interregional_graph())] + #[case::weighted(weighted_graph())] + #[cfg_attr(miri, ignore)] // Opening files is not supported in (isolated) miri + fn dot_output( + #[case] graph_elems: ( + &str, + impl DotFormat, + Option, + Option>, + ), + ) { + let (name, graph, hierarchy, weights) = graph_elems; + let dot = match (hierarchy, weights) { + (Some(h), Some(w)) => graph + .dot_format() + .with_hierarchy(&h) + .with_weights(&w) + .finish(), + (Some(h), None) => graph.dot_format().with_hierarchy(&h).finish(), + (None, Some(w)) => graph.dot_format().with_weights(&w).finish(), + (None, None) => graph.dot_string(), + }; + + let name = format!("{}__dot", name); + insta::assert_snapshot!(name, dot); + } +} diff --git a/src/render/dot.rs b/src/render/dot.rs index 5a2b6ec..9813072 100644 --- a/src/render/dot.rs +++ b/src/render/dot.rs @@ -286,97 +286,3 @@ struct PortCellStrings { /// The label to show for the port. pub label: String, } - -#[cfg(test)] -mod tests { - use crate::{LinkMut, PortGraph, PortMut, PortView}; - - use super::*; - - #[test] - fn test_dot_string() { - let mut graph = PortGraph::new(); - let n1 = graph.add_node(3, 2); - let n2 = graph.add_node(1, 0); - let n3 = graph.add_node(1, 0); - graph.link_nodes(n1, 0, n2, 0).unwrap(); - graph.link_nodes(n1, 1, n3, 0).unwrap(); - - let dot = &graph.dot_string(); - let expected = r#"digraph { -0 [shape=plain label=<
012
0
01
>] -0:out0 -> 1:in0 [style=""] -0:out1 -> 2:in0 [style=""] -1 [shape=plain label=<
0
1
>] -2 [shape=plain label=<
0
2
>] -} -"#; - assert_eq!(dot, expected, "\n{}\n{}\n", dot, expected); - } - - #[test] - fn test_hier_dot_string() { - let mut graph = PortGraph::new(); - let n1 = graph.add_node(3, 2); - let n2 = graph.add_node(1, 0); - let n3 = graph.add_node(1, 0); - graph.link_nodes(n1, 0, n2, 0).unwrap(); - graph.link_nodes(n1, 1, n3, 0).unwrap(); - - let mut hier = Hierarchy::new(); - - hier.push_child(n2, n1).unwrap(); - hier.push_child(n3, n1).unwrap(); - let dot = graph.dot_format().with_hierarchy(&hier).finish(); - let expected = r#"digraph { -0 [shape=plain label=<
012
0
01
>] -0:out0 -> 1:in0 [style=""] -0:out1 -> 2:in0 [style=""] -1 [shape=plain label=<
0
1
>] -2 [shape=plain label=<
0
2
>] -hier0 [shape=plain label="0"] -hier0 -> hier1 [style = "dashed"] -hier0 -> hier2 [style = "dashed"] -hier1 [shape=plain label="1"] -hier2 [shape=plain label="2"] -} -"#; - assert_eq!(dot, expected, "\n{}\n{}\n", dot, expected); - } - - #[test] - fn test_dot_string_weighted() { - let mut graph = PortGraph::new(); - let n1 = graph.add_node(0, 2); - let n2 = graph.add_node(1, 0); - let n3 = graph.add_node(1, 0); - let p10 = graph.output(n1, 0).unwrap(); - let p11 = graph.output(n1, 1).unwrap(); - let p20 = graph.input(n2, 0).unwrap(); - let p30 = graph.input(n3, 0).unwrap(); - - graph.link_ports(p10, p20).unwrap(); - graph.link_ports(p11, p30).unwrap(); - - let mut weights = Weights::new(); - weights[n1] = "node1".to_string(); - weights[n2] = "node2".to_string(); - weights[n3] = "node3".to_string(); - weights[p10] = "out 0".to_string(); - weights[p11] = "out 1".to_string(); - weights[p20] = "in 0".to_string(); - weights[p30] = "in 0".to_string(); - - let dot = graph.dot_format().with_weights(&weights).finish(); - println!("\n{}\n", dot); - let expected = r#"digraph { -0 [shape=plain label=<
node1
0: out 01: out 1
>] -0:out0 -> 1:in0 [style=""] -0:out1 -> 2:in0 [style=""] -1 [shape=plain label=<
0: in 0
node2
>] -2 [shape=plain label=<
0: in 0
node3
>] -} -"#; - assert_eq!(dot, expected, "\n{}\n{}\n", dot, expected); - } -} diff --git a/src/render/mermaid.rs b/src/render/mermaid.rs index 7ec2c88..9cf7fc5 100644 --- a/src/render/mermaid.rs +++ b/src/render/mermaid.rs @@ -332,84 +332,3 @@ pub fn encode_label(id: &str, label: &str) -> String { } format!("\"{}\"", label.replace('"', "#quot;").replace('\n', "
")) } - -#[cfg(test)] -mod tests { - use crate::{LinkMut, PortGraph, PortMut, PortView}; - - use super::*; - - #[test] - fn test_mermaid_string() { - let mut graph = PortGraph::new(); - let n1 = graph.add_node(3, 2); - let n2 = graph.add_node(1, 0); - let n3 = graph.add_node(1, 0); - graph.link_nodes(n1, 0, n2, 0).unwrap(); - graph.link_nodes(n1, 1, n3, 0).unwrap(); - - let mermaid = &graph.mermaid_string(); - let expected = r#"graph LR - 0[0] - 0-->1 - 0-->2 - 1[1] - 2[2] -"#; - assert_eq!(mermaid, expected, "\n{}\n{}\n", mermaid, expected); - } - - #[test] - fn test_hier_mermaid_string() { - let mut graph = PortGraph::new(); - let n1 = graph.add_node(3, 2); - let n2 = graph.add_node(0, 1); - let n3 = graph.add_node(1, 0); - graph.link_nodes(n2, 0, n3, 0).unwrap(); - - let mut hier = Hierarchy::new(); - - hier.push_child(n2, n1).unwrap(); - hier.push_child(n3, n1).unwrap(); - let mermaid = graph.mermaid_format().with_hierarchy(&hier).finish(); - let expected = r#"graph LR - subgraph 0 [0] - direction LR - 1[1] - 1-->2 - 2[2] - end -"#; - assert_eq!(mermaid, expected, "\n{}\n{}\n", mermaid, expected); - } - - #[test] - fn test_mermaid_string_weighted() { - let mut graph = PortGraph::new(); - let n1 = graph.add_node(0, 2); - let n2 = graph.add_node(1, 0); - let n3 = graph.add_node(1, 0); - let p10 = graph.output(n1, 0).unwrap(); - let p11 = graph.output(n1, 1).unwrap(); - let p20 = graph.input(n2, 0).unwrap(); - let p30 = graph.input(n3, 0).unwrap(); - - graph.link_ports(p10, p20).unwrap(); - graph.link_ports(p11, p30).unwrap(); - - let mut weights: Weights = Weights::new(); - weights[n1] = "node1".to_string(); - weights[n2] = "node2".to_string(); - weights[n3] = "node3".to_string(); - - let mermaid = graph.mermaid_format().with_weights(&weights).finish(); - let expected = r#"graph LR - 0["node1"] - 0-->1 - 0-->2 - 1["node2"] - 2["node3"] -"#; - assert_eq!(mermaid, expected, "\n{}\n{}\n", mermaid, expected); - } -} diff --git a/src/snapshots/portgraph__render__test__flat__dot.snap b/src/snapshots/portgraph__render__test__flat__dot.snap new file mode 100644 index 0000000..7797202 --- /dev/null +++ b/src/snapshots/portgraph__render__test__flat__dot.snap @@ -0,0 +1,11 @@ +--- +source: src/render.rs +expression: dot +--- +digraph { +0 [shape=plain label=<
012
0
01
>] +0:out0 -> 1:in0 [style=""] +0:out1 -> 2:in0 [style=""] +1 [shape=plain label=<
0
1
>] +2 [shape=plain label=<
0
2
>] +} diff --git a/src/snapshots/portgraph__render__test__flat__mermaid.snap b/src/snapshots/portgraph__render__test__flat__mermaid.snap new file mode 100644 index 0000000..d412abd --- /dev/null +++ b/src/snapshots/portgraph__render__test__flat__mermaid.snap @@ -0,0 +1,10 @@ +--- +source: src/render.rs +expression: mermaid +--- +graph LR + 0[0] + 0-->1 + 0-->2 + 1[1] + 2[2] diff --git a/src/snapshots/portgraph__render__test__hierarchy__dot.snap b/src/snapshots/portgraph__render__test__hierarchy__dot.snap new file mode 100644 index 0000000..03724a9 --- /dev/null +++ b/src/snapshots/portgraph__render__test__hierarchy__dot.snap @@ -0,0 +1,15 @@ +--- +source: src/render.rs +expression: dot +--- +digraph { +0 [shape=plain label=<
012
0
01
>] +1 [shape=plain label=<
1
0
>] +1:out0 -> 2:in0 [style=""] +2 [shape=plain label=<
0
2
>] +hier0 [shape=plain label="0"] +hier0 -> hier1 [style = "dashed"] +hier0 -> hier2 [style = "dashed"] +hier1 [shape=plain label="1"] +hier2 [shape=plain label="2"] +} diff --git a/src/snapshots/portgraph__render__test__hierarchy__mermaid.snap b/src/snapshots/portgraph__render__test__hierarchy__mermaid.snap new file mode 100644 index 0000000..ded2f90 --- /dev/null +++ b/src/snapshots/portgraph__render__test__hierarchy__mermaid.snap @@ -0,0 +1,11 @@ +--- +source: src/render.rs +expression: mermaid +--- +graph LR + subgraph 0 [0] + direction LR + 1[1] + 1-->2 + 2[2] + end diff --git a/src/snapshots/portgraph__render__test__hierarchy_interregional__dot.snap b/src/snapshots/portgraph__render__test__hierarchy_interregional__dot.snap new file mode 100644 index 0000000..1c38315 --- /dev/null +++ b/src/snapshots/portgraph__render__test__hierarchy_interregional__dot.snap @@ -0,0 +1,22 @@ +--- +source: src/render.rs +expression: dot +--- +digraph { +0 [shape=plain label=<
012
0
01
>] +1 [shape=plain label=<
1
0
>] +1:out0 -> 2:in0 [style=""] +2 [shape=plain label=<
0
2
>] +3 [shape=plain label=<
0
3
0
>] +3:out0 -> 4:in0 [style=""] +4 [shape=plain label=<
0
4
0
>] +hier0 [shape=plain label="0"] +hier0 -> hier1 [style = "dashed"] +hier0 -> hier2 [style = "dashed"] +hier1 [shape=plain label="1"] +hier1 -> hier3 [style = "dashed"] +hier2 [shape=plain label="2"] +hier2 -> hier4 [style = "dashed"] +hier3 [shape=plain label="3"] +hier4 [shape=plain label="4"] +} diff --git a/src/snapshots/portgraph__render__test__hierarchy_interregional__mermaid.snap b/src/snapshots/portgraph__render__test__hierarchy_interregional__mermaid.snap new file mode 100644 index 0000000..6788a53 --- /dev/null +++ b/src/snapshots/portgraph__render__test__hierarchy_interregional__mermaid.snap @@ -0,0 +1,18 @@ +--- +source: src/render.rs +expression: mermaid +--- +graph LR + subgraph 0 [0] + direction LR + subgraph 1 [1] + direction LR + 3[3] + 3-->4 + end + 1-->2 + subgraph 2 [2] + direction LR + 4[4] + end + end diff --git a/src/snapshots/portgraph__render__test__weighted__dot.snap b/src/snapshots/portgraph__render__test__weighted__dot.snap new file mode 100644 index 0000000..6a52a16 --- /dev/null +++ b/src/snapshots/portgraph__render__test__weighted__dot.snap @@ -0,0 +1,11 @@ +--- +source: src/render.rs +expression: dot +--- +digraph { +0 [shape=plain label=<
node1
0: out 01: out 1
>] +0:out0 -> 1:in0 [style=""] +0:out1 -> 2:in0 [style=""] +1 [shape=plain label=<
0: in 0
node2
>] +2 [shape=plain label=<
0: in 0
node3
>] +} diff --git a/src/snapshots/portgraph__render__test__weighted__mermaid.snap b/src/snapshots/portgraph__render__test__weighted__mermaid.snap new file mode 100644 index 0000000..d0b31a6 --- /dev/null +++ b/src/snapshots/portgraph__render__test__weighted__mermaid.snap @@ -0,0 +1,10 @@ +--- +source: src/render.rs +expression: mermaid +--- +graph LR + 0["node1"] + 0-->1 + 0-->2 + 1["node2"] + 2["node3"]