From 8324f93ebde16737478953d3efd1c924dfaf18f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20M=C3=BCller?= Date: Thu, 30 Apr 2026 17:41:53 +0200 Subject: [PATCH] Add graph topology test helper macros to deduplicate test code --- src/lib.rs | 2 + src/models/append_graph.rs | 274 +------------ src/models/graph.rs | 523 +------------------------ src/testing.rs | 1 + src/testing/graph_topology_testing.rs | 538 ++++++++++++++++++++++++++ 5 files changed, 547 insertions(+), 791 deletions(-) create mode 100644 src/testing.rs create mode 100644 src/testing/graph_topology_testing.rs diff --git a/src/lib.rs b/src/lib.rs index 2c999c7..43bdd7f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ pub mod algorithms; pub mod models; pub mod traits; + +mod testing; diff --git a/src/models/append_graph.rs b/src/models/append_graph.rs index 7868ae7..03e7904 100644 --- a/src/models/append_graph.rs +++ b/src/models/append_graph.rs @@ -121,276 +121,6 @@ impl GraphTopology for AppendGraph { mod tests { use super::*; - #[test] - fn add_vertex() { - let mut graph = AppendGraph::new(); - let v = graph.add_vertex(); - assert_ne!(graph.add_vertex(), v, "unexpected duplicate vertex"); - } - - #[test] - fn vertex_count_empty() { - let graph = AppendGraph::new(); - assert_eq!(graph.vertex_count(), 0, "unexpected vertex count"); - } - - #[test] - fn vertex_count() { - let (graph, _) = make_test_graph(); - assert_eq!(graph.vertex_count(), 10, "unexpected vertex count"); - } - - #[test] - fn add_edge() { - let mut graph = AppendGraph::new(); - let v1 = graph.add_vertex(); - let v2 = graph.add_vertex(); - let e = graph.add_edge(v1, v2); - assert_ne!(graph.add_edge(v1, v2), e, "unexpected duplicate edge"); - } - - #[test] - fn edge_count_empty() { - let graph = AppendGraph::new(); - assert_eq!(graph.edge_count(), 0, "unexpected edge count"); - } - - #[test] - fn edge_count() { - let (graph, _) = make_test_graph(); - assert_eq!(graph.edge_count(), 14, "unexpected edge count"); - } - - #[test] - fn degree_zero() { - let mut graph = AppendGraph::new(); - let v = graph.add_vertex(); - assert_eq!(graph.degree(v), 0, "unexpected non-zero degree"); - } - - #[test] - fn degree() { - let (graph, vertices) = make_test_graph(); - let expected_degrees = [1, 4, 4, 2, 4, 2, 3, 3, 2, 3]; - for i in 0..graph.vertex_count() { - assert_eq!( - graph.degree(vertices[i]), - expected_degrees[i], - "unexpected degree of {:?}", - vertices[i] - ); - } - } - - #[test] - fn are_adjacent_vertex_self() { - let mut graph = AppendGraph::new(); - let v = graph.add_vertex(); - assert!( - !graph.are_adjacent(v, v), - "should not be adjacent to itself" - ); - } - - #[test] - fn are_adjacent_single_edge() { - let mut graph = AppendGraph::new(); - let v1 = graph.add_vertex(); - let v2 = graph.add_vertex(); - assert!(!graph.are_adjacent(v1, v2), "should not be adjacent"); - assert!(!graph.are_adjacent(v2, v1), "should not be adjacent"); - graph.add_edge(v1, v2); - assert!(graph.are_adjacent(v1, v2), "should be adjacent"); - assert!(graph.are_adjacent(v2, v1), "should be adjacent"); - } - - #[test] - fn are_adjacent() { - let (graph, vertices) = make_test_graph(); - assert!( - graph.are_adjacent(vertices[0], vertices[1]), - "expected {:?} and {:?} to be adjacent", - vertices[0], - vertices[1] - ); - assert!( - graph.are_adjacent(vertices[9], vertices[5]), - "expected {:?} and {:?} to be adjacent", - vertices[9], - vertices[5] - ); - assert!( - !graph.are_adjacent(vertices[9], vertices[3]), - "unexpected adjacency of {:?} and {:?}", - vertices[9], - vertices[3] - ); - - for i in 0..graph.vertex_count() { - let exp = match i { - 2 => continue, - 1 | 4 | 5 | 6 => true, - _ => false, - }; - assert_eq!( - graph.are_adjacent(vertices[2], vertices[i]), - exp, - "unexpected adjacency of {:?} and {:?}", - vertices[2], - vertices[i] - ); - assert_eq!( - graph.are_adjacent(vertices[i], vertices[2]), - exp, - "unexpected adjacency of {:?} and {:?}", - vertices[i], - vertices[2] - ); - } - } - - #[test] - fn vertices_empty() { - let graph = AppendGraph::new(); - assert_eq!( - graph.vertices().count(), - 0, - "vertex iterator of empty graph should have no elements" - ); - } - - #[test] - fn vertices() { - let (graph, vertices) = make_test_graph(); - assert_eq!(graph.vertices().count(), 10, "unexpected vertex count"); - - // Expects each vertex to appear exactly once. - for v in graph.vertices() { - assert_eq!( - vertices.iter().filter(|&x| *x == v).count(), - 1, - "unexpected vertex {v:?} from the iterator" - ); - } - } - - #[test] - fn neighbors_empty() { - let mut graph = AppendGraph::new(); - let vertex = graph.add_vertex(); - assert_eq!( - graph.neighbors(vertex).count(), - 0, - "neighbor iterator of vertex with degree 0 should have no elements" - ); - } - - #[test] - fn neighbors() { - let (graph, vertices) = make_test_graph(); - // Checks neighbors of vertex 4. - assert_eq!( - graph.neighbors(vertices[4]).count(), - 4, - "unexpected neighbor count" - ); - - // Expects each neighbor to appear exactly once. This will not work if there are multiple edges. - let neighbors = vec![vertices[1], vertices[2], vertices[7], vertices[8]]; - for v in graph.neighbors(vertices[4]) { - assert_eq!( - neighbors.iter().filter(|&x| *x == v).count(), - 1, - "unexpected neighbor {v:?} of {:?} from the iterator", - vertices[4] - ); - } - } - - #[test] - fn loop_edge() { - let mut graph = AppendGraph::new(); - let v = graph.add_vertex(); - graph.add_edge(v, v); - assert_eq!(graph.vertex_count(), 1, "unexpected vertex count"); - assert_eq!(graph.edge_count(), 1, "unexpected edge count"); - assert_eq!(graph.degree(v), 2, "unexpected degree"); - assert!( - graph.are_adjacent(v, v), - "vertex with loop edge should be adjacent to itself" - ); - let mut neighbors = graph.neighbors(v); - assert_eq!( - neighbors.next(), - Some(v), - "vertex should be neighbor of itself" - ); - assert_eq!( - neighbors.next(), - Some(v), - "vertex should be neighbor of itself twice" - ); - assert_eq!(neighbors.next(), None, "too many neighbors from iterator"); - } - - #[test] - fn multiple_edges() { - let k = 3; - let mut graph = AppendGraph::new(); - let vertices = [graph.add_vertex(), graph.add_vertex()]; - for _ in 0..k { - graph.add_edge(vertices[0], vertices[1]); - } - assert_eq!( - graph.vertex_count(), - vertices.len(), - "unexpected vertex count" - ); - assert_eq!(graph.edge_count(), k, "unexpected edge count"); - for v in vertices { - assert_eq!(graph.degree(v), k, "unexpected degree of {v:?}"); - } - assert!( - graph.are_adjacent(vertices[0], vertices[1]), - "should be adjacent" - ); - for i in 0..2 { - let mut neighbors = graph.neighbors(vertices[i]); - for j in 0..k { - assert_eq!( - neighbors.next(), - Some(vertices[1 - i]), - "neighbor {j} of vertex {:?} should be {:?}", - vertices[i], - vertices[1 - i] - ); - } - assert_eq!( - neighbors.next(), - None, - "too many neighbors of {:?} from iterator", - vertices[i] - ); - } - } - - fn make_test_graph() -> (AppendGraph, [Vertex; 10]) { - let mut graph = AppendGraph::new(); - let vertices: [Vertex; 10] = core::array::from_fn(|_| graph.add_vertex()); - graph.add_edge(vertices[0], vertices[1]); - graph.add_edge(vertices[1], vertices[2]); - graph.add_edge(vertices[1], vertices[3]); - graph.add_edge(vertices[1], vertices[4]); - graph.add_edge(vertices[2], vertices[4]); - graph.add_edge(vertices[2], vertices[5]); - graph.add_edge(vertices[2], vertices[6]); - graph.add_edge(vertices[3], vertices[6]); - graph.add_edge(vertices[4], vertices[7]); - graph.add_edge(vertices[4], vertices[8]); - graph.add_edge(vertices[5], vertices[9]); - graph.add_edge(vertices[6], vertices[9]); - graph.add_edge(vertices[7], vertices[8]); - graph.add_edge(vertices[7], vertices[9]); - (graph, vertices) - } + crate::graph_topology_test_fixtures!(AppendGraph); + crate::graph_topology_tests!(AppendGraph); } diff --git a/src/models/graph.rs b/src/models/graph.rs index 4f6e16f..22f8dea 100644 --- a/src/models/graph.rs +++ b/src/models/graph.rs @@ -112,7 +112,7 @@ impl Graph { } // Updates the source vertex incidence list after the incidence "e" was deleted from the - // incidences arena. "next" is the next incidence after "e" in the source vertex incidence list. + // incidence arena. "next" is the next incidence after "e" in the source vertex incidence list. fn update_incidence_list( &mut self, e: Edge, @@ -237,494 +237,9 @@ impl GraphTopologyDeletion for Graph { mod tests { use super::*; - #[test] - fn add_vertex() { - let mut graph = Graph::new(); - let v = graph.add_vertex(); - assert_ne!(graph.add_vertex(), v, "unexpected duplicate vertex"); - } - - #[test] - fn vertex_count_empty() { - let graph = Graph::new(); - assert_eq!(graph.vertex_count(), 0, "unexpected vertex count"); - } - - #[test] - fn vertex_count() { - let (graph, _) = make_test_graph(); - assert_eq!(graph.vertex_count(), 10, "unexpected vertex count"); - } - - #[test] - fn add_edge() { - let mut graph = Graph::new(); - let v1 = graph.add_vertex(); - let v2 = graph.add_vertex(); - let e = graph.add_edge(v1, v2); - assert_ne!(graph.add_edge(v1, v2), e, "unexpected duplicate edge"); - } - - #[test] - fn edge_count_empty() { - let graph = Graph::new(); - assert_eq!(graph.edge_count(), 0, "unexpected edge count"); - } - - #[test] - fn edge_count() { - let (graph, _) = make_test_graph(); - assert_eq!(graph.edge_count(), 14, "unexpected edge count"); - } - - #[test] - fn degree_zero() { - let mut graph = Graph::new(); - let v = graph.add_vertex(); - assert_eq!(graph.degree(v), 0, "unexpected non-zero degree"); - } - - #[test] - fn degree() { - let (graph, vertices) = make_test_graph(); - let expected_degrees = [1, 4, 4, 2, 4, 2, 3, 3, 2, 3]; - for i in 0..graph.vertex_count() { - assert_eq!( - graph.degree(vertices[i]), - expected_degrees[i], - "unexpected degree of {:?}", - vertices[i] - ); - } - } - - #[test] - fn are_adjacent_vertex_self() { - let mut graph = Graph::new(); - let v = graph.add_vertex(); - assert!( - !graph.are_adjacent(v, v), - "should not be adjacent to itself" - ); - } - - #[test] - fn are_adjacent_single_edge() { - let mut graph = Graph::new(); - let v1 = graph.add_vertex(); - let v2 = graph.add_vertex(); - assert!(!graph.are_adjacent(v1, v2), "should not be adjacent"); - assert!(!graph.are_adjacent(v2, v1), "should not be adjacent"); - graph.add_edge(v1, v2); - assert!(graph.are_adjacent(v1, v2), "should be adjacent"); - assert!(graph.are_adjacent(v2, v1), "should be adjacent"); - } - - #[test] - fn are_adjacent() { - let (graph, vertices) = make_test_graph(); - assert!( - graph.are_adjacent(vertices[0], vertices[1]), - "expected {:?} and {:?} to be adjacent", - vertices[0], - vertices[1] - ); - assert!( - graph.are_adjacent(vertices[9], vertices[5]), - "expected {:?} and {:?} to be adjacent", - vertices[9], - vertices[5] - ); - assert!( - !graph.are_adjacent(vertices[9], vertices[3]), - "unexpected adjacency of {:?} and {:?}", - vertices[9], - vertices[3] - ); - - for i in 0..graph.vertex_count() { - let exp = match i { - 2 => continue, - 1 | 4 | 5 | 6 => true, - _ => false, - }; - assert_eq!( - graph.are_adjacent(vertices[2], vertices[i]), - exp, - "unexpected adjacency of {:?} and {:?}", - vertices[2], - vertices[i] - ); - assert_eq!( - graph.are_adjacent(vertices[i], vertices[2]), - exp, - "unexpected adjacency of {:?} and {:?}", - vertices[i], - vertices[2] - ); - } - } - - #[test] - fn vertices_empty() { - let graph = Graph::new(); - assert_eq!( - graph.vertices().count(), - 0, - "vertex iterator of empty graph should have no elements" - ); - } - - #[test] - fn vertices() { - let (graph, vertices) = make_test_graph(); - assert_eq!(graph.vertices().count(), 10, "unexpected vertex count"); - - // Expects each vertex to appear exactly once. - for v in graph.vertices() { - assert_eq!( - vertices.iter().filter(|&x| *x == v).count(), - 1, - "unexpected vertex {v:?} from the iterator" - ); - } - } - - #[test] - fn neighbors_empty() { - let mut graph = Graph::new(); - let vertex = graph.add_vertex(); - assert_eq!( - graph.neighbors(vertex).count(), - 0, - "neighbor iterator of vertex with degree 0 should have no elements" - ); - } - - #[test] - fn neighbors() { - let (graph, vertices) = make_test_graph(); - // Checks neighbors of vertex 4. - assert_eq!( - graph.neighbors(vertices[4]).count(), - 4, - "unexpected neighbor count" - ); - - // Expects each neighbor to appear exactly once. This will not work if there are multiple edges. - let neighbors = vec![vertices[1], vertices[2], vertices[7], vertices[8]]; - for v in graph.neighbors(vertices[4]) { - assert_eq!( - neighbors.iter().filter(|&x| *x == v).count(), - 1, - "unexpected neighbor {v:?} of {:?} from the iterator", - vertices[4] - ); - } - } - - #[test] - fn loop_edge() { - let mut graph = Graph::new(); - let v = graph.add_vertex(); - graph.add_edge(v, v); - assert_eq!(graph.vertex_count(), 1, "unexpected vertex count"); - assert_eq!(graph.edge_count(), 1, "unexpected edge count"); - assert_eq!(graph.degree(v), 2, "unexpected degree"); - assert!( - graph.are_adjacent(v, v), - "vertex with loop edge should be adjacent to itself" - ); - let mut neighbors = graph.neighbors(v); - assert_eq!( - neighbors.next(), - Some(v), - "vertex should be neighbor of itself" - ); - assert_eq!( - neighbors.next(), - Some(v), - "vertex should be neighbor of itself twice" - ); - assert_eq!(neighbors.next(), None, "too many neighbors from iterator"); - } - - #[test] - fn multiple_edges() { - let k = 3; - let mut graph = Graph::new(); - let vertices = [graph.add_vertex(), graph.add_vertex()]; - for _ in 0..k { - graph.add_edge(vertices[0], vertices[1]); - } - assert_eq!( - graph.vertex_count(), - vertices.len(), - "unexpected vertex count" - ); - assert_eq!(graph.edge_count(), k, "unexpected edge count"); - for v in vertices { - assert_eq!(graph.degree(v), k, "unexpected degree of {v:?}"); - } - assert!( - graph.are_adjacent(vertices[0], vertices[1]), - "should be adjacent" - ); - for i in 0..2 { - let mut neighbors = graph.neighbors(vertices[i]); - for j in 0..k { - assert_eq!( - neighbors.next(), - Some(vertices[1 - i]), - "neighbor {j} of vertex {:?} should be {:?}", - vertices[i], - vertices[1 - i] - ); - } - assert_eq!( - neighbors.next(), - None, - "too many neighbors of {:?} from iterator", - vertices[i] - ); - } - } - - #[test] - fn delete_vertex() { - let mut graph = Graph::new(); - let v = graph.add_vertex(); - assert_eq!( - graph.vertex_count(), - 1, - "unexpected vertex count before delete" - ); - graph.delete_vertex(v); - assert_eq!( - graph.vertex_count(), - 0, - "unexpected vertex count after delete" - ); - assert_ne!( - graph.add_vertex(), - v, - "unexpected duplicate vertex after delete" - ); - } - - #[test] - fn delete_vertex_loop() { - let mut graph = Graph::new(); - let v = graph.add_vertex(); - graph.add_edge(v, v); - assert_eq!( - graph.vertex_count(), - 1, - "unexpected vertex count before delete" - ); - assert_eq!(graph.edge_count(), 1, "unexpected edge count before delete"); - assert!( - graph.are_adjacent(v, v), - "expected vertex to be self-adjacent before delete" - ); - assert_eq!(graph.degree(v), 2, "unexpected vertex degree before delete"); - graph.delete_vertex(v); - assert_eq!( - graph.vertex_count(), - 0, - "unexpected vertex count after delete" - ); - assert_eq!(graph.edge_count(), 0, "unexpected edge count after delete"); - assert_ne!( - graph.add_vertex(), - v, - "unexpected duplicate vertex after delete" - ); - } - - #[test] - fn delete_vertex_invalid_index() { - let mut graph = Graph::new(); - let v = graph.add_vertex(); - graph.delete_vertex(v); - let result = std::panic::catch_unwind(move || graph.delete_vertex(v)); - assert!(result.is_err(), "second deletion should panic"); - } - - #[test] - fn delete_vertex_connected() { - let (mut graph, vertices) = make_test_graph(); - assert_eq!( - graph.vertex_count(), - 10, - "unexpected vertex count before delete" - ); - assert_eq!( - graph.edge_count(), - 14, - "unexpected edge count before delete" - ); - graph.delete_vertex(vertices[2]); - assert_eq!( - graph.vertex_count(), - 9, - "unexpected vertex count after delete" - ); - assert_eq!(graph.edge_count(), 10, "unexpected edge count after delete"); - let expected_edges = [ - (vertices[0], vertices[1]), - (vertices[1], vertices[3]), - (vertices[1], vertices[4]), - (vertices[3], vertices[6]), - (vertices[4], vertices[7]), - (vertices[4], vertices[8]), - (vertices[5], vertices[9]), - (vertices[6], vertices[9]), - (vertices[7], vertices[8]), - (vertices[7], vertices[9]), - ]; - for (v, u) in expected_edges { - assert!( - graph.are_adjacent(v, u), - "expected {v:?} and {u:?} to be adjacent after delete" - ); - } - for v in [vertices[1], vertices[4], vertices[5], vertices[6]] { - assert!( - !graph.neighbors(v).any(|u| u == vertices[2]), - "unexpected adjacency of {v:?} to deleted vertex {:?}", - vertices[2] - ); - } - } - - #[test] - fn delete_edge() { - let mut graph = Graph::new(); - let v1 = graph.add_vertex(); - let v2 = graph.add_vertex(); - let e = graph.add_edge(v1, v2); - assert_eq!( - graph.vertex_count(), - 2, - "unexpected vertex count before delete" - ); - assert_eq!(graph.edge_count(), 1, "unexpected edge count before delete"); - assert!( - graph.are_adjacent(v1, v2), - "expected vertices to be adjacent before delete" - ); - assert_eq!( - graph.degree(v1), - 1, - "unexpected vertex degree before delete" - ); - assert_eq!( - graph.degree(v2), - 1, - "unexpected vertex degree before delete" - ); - graph.delete_edge(e); - assert_eq!( - graph.vertex_count(), - 2, - "unexpected vertex count after delete" - ); - assert_eq!(graph.edge_count(), 0, "unexpected edge count after delete"); - assert!( - !graph.are_adjacent(v1, v2), - "unexpected adjacency after delete" - ); - assert_eq!(graph.degree(v1), 0, "unexpected vertex degree after delete"); - assert_eq!(graph.degree(v2), 0, "unexpected vertex degree after delete"); - assert_ne!( - graph.add_edge(v1, v2), - e, - "unexpected duplicate edge after delete" - ); - } - - #[test] - fn delete_edge_loop() { - let mut graph = Graph::new(); - let v = graph.add_vertex(); - let e = graph.add_edge(v, v); - assert_eq!( - graph.vertex_count(), - 1, - "unexpected vertex count before delete" - ); - assert_eq!(graph.edge_count(), 1, "unexpected edge count before delete"); - assert!( - graph.are_adjacent(v, v), - "expected vertex to be self-adjacent before delete" - ); - assert_eq!(graph.degree(v), 2, "unexpected vertex degree before delete"); - graph.delete_edge(e); - assert_eq!( - graph.vertex_count(), - 1, - "unexpected vertex count after delete" - ); - assert_eq!(graph.edge_count(), 0, "unexpected edge count after delete"); - assert!( - !graph.are_adjacent(v, v), - "unexpected adjacency after delete" - ); - assert_eq!(graph.degree(v), 0, "unexpected vertex degree after delete"); - assert_ne!( - graph.add_edge(v, v), - e, - "unexpected duplicate edge after delete" - ); - } - - #[test] - fn delete_edge_multiple() { - let mut graph = Graph::new(); - let v1 = graph.add_vertex(); - let v2 = graph.add_vertex(); - let e = graph.add_edge(v1, v2); - graph.add_edge(v1, v2); - assert_eq!( - graph.vertex_count(), - 2, - "unexpected vertex count before delete" - ); - assert_eq!(graph.edge_count(), 2, "unexpected edge count before delete"); - assert!( - graph.are_adjacent(v1, v2), - "expected vertices to be adjacent before delete" - ); - assert_eq!( - graph.degree(v1), - 2, - "unexpected vertex degree before delete" - ); - assert_eq!( - graph.degree(v2), - 2, - "unexpected vertex degree before delete" - ); - graph.delete_edge(e); - assert_eq!( - graph.vertex_count(), - 2, - "unexpected vertex count after delete" - ); - assert_eq!(graph.edge_count(), 1, "unexpected edge count after delete"); - assert!( - graph.are_adjacent(v1, v2), - "expected vertices to be adjacent after delete" - ); - assert_eq!(graph.degree(v1), 1, "unexpected vertex degree after delete"); - assert_eq!(graph.degree(v2), 1, "unexpected vertex degree after delete"); - assert_ne!( - graph.add_edge(v1, v2), - e, - "unexpected duplicate edge after delete" - ); - } + crate::graph_topology_test_fixtures!(Graph); + crate::graph_topology_tests!(Graph); + crate::graph_topology_deletion_tests!(Graph); #[test] fn delete_edge_paired_index() { @@ -753,34 +268,4 @@ mod tests { assert_eq!(graph.edge_count(), 0, "unexpected edge count after delete"); } - #[test] - fn delete_edge_invalid_index() { - let mut graph = Graph::new(); - let v1 = graph.add_vertex(); - let v2 = graph.add_vertex(); - let e = graph.add_edge(v1, v2); - graph.delete_edge(e); - let result = std::panic::catch_unwind(move || graph.delete_edge(e)); - assert!(result.is_err(), "second deletion should panic"); - } - - fn make_test_graph() -> (Graph, [Vertex; 10]) { - let mut graph = Graph::new(); - let vertices: [Vertex; 10] = core::array::from_fn(|_| graph.add_vertex()); - graph.add_edge(vertices[0], vertices[1]); - graph.add_edge(vertices[1], vertices[2]); - graph.add_edge(vertices[1], vertices[3]); - graph.add_edge(vertices[1], vertices[4]); - graph.add_edge(vertices[2], vertices[4]); - graph.add_edge(vertices[2], vertices[5]); - graph.add_edge(vertices[2], vertices[6]); - graph.add_edge(vertices[3], vertices[6]); - graph.add_edge(vertices[4], vertices[7]); - graph.add_edge(vertices[4], vertices[8]); - graph.add_edge(vertices[5], vertices[9]); - graph.add_edge(vertices[6], vertices[9]); - graph.add_edge(vertices[7], vertices[8]); - graph.add_edge(vertices[7], vertices[9]); - (graph, vertices) - } } diff --git a/src/testing.rs b/src/testing.rs new file mode 100644 index 0000000..ecf3963 --- /dev/null +++ b/src/testing.rs @@ -0,0 +1 @@ +pub(crate) mod graph_topology_testing; diff --git a/src/testing/graph_topology_testing.rs b/src/testing/graph_topology_testing.rs new file mode 100644 index 0000000..ad29576 --- /dev/null +++ b/src/testing/graph_topology_testing.rs @@ -0,0 +1,538 @@ +#[cfg(test)] +#[macro_export] +macro_rules! graph_topology_test_fixtures { + ($T:ty) => { + fn make_test_graph() -> ($T, [<$T as $crate::traits::GraphTopology>::Vertex; 10]) { + let mut graph = <$T>::new(); + let vertices: [<$T as $crate::traits::GraphTopology>::Vertex; 10] = + core::array::from_fn(|_| graph.add_vertex()); + graph.add_edge(vertices[0], vertices[1]); + graph.add_edge(vertices[1], vertices[2]); + graph.add_edge(vertices[1], vertices[3]); + graph.add_edge(vertices[1], vertices[4]); + graph.add_edge(vertices[2], vertices[4]); + graph.add_edge(vertices[2], vertices[5]); + graph.add_edge(vertices[2], vertices[6]); + graph.add_edge(vertices[3], vertices[6]); + graph.add_edge(vertices[4], vertices[7]); + graph.add_edge(vertices[4], vertices[8]); + graph.add_edge(vertices[5], vertices[9]); + graph.add_edge(vertices[6], vertices[9]); + graph.add_edge(vertices[7], vertices[8]); + graph.add_edge(vertices[7], vertices[9]); + (graph, vertices) + } + }; +} + +#[cfg(test)] +#[macro_export] +macro_rules! graph_topology_tests { + ($T:ty) => { + #[test] + fn add_vertex() { + let mut graph = <$T>::new(); + let v = graph.add_vertex(); + assert_ne!(graph.add_vertex(), v, "unexpected duplicate vertex"); + } + + #[test] + fn vertex_count_empty() { + let graph = <$T>::new(); + assert_eq!(graph.vertex_count(), 0, "unexpected vertex count"); + } + + #[test] + fn vertex_count() { + let (graph, _) = make_test_graph(); + assert_eq!(graph.vertex_count(), 10, "unexpected vertex count"); + } + + #[test] + fn add_edge() { + let mut graph = <$T>::new(); + let v1 = graph.add_vertex(); + let v2 = graph.add_vertex(); + let e = graph.add_edge(v1, v2); + assert_ne!(graph.add_edge(v1, v2), e, "unexpected duplicate edge"); + } + + #[test] + fn edge_count_empty() { + let graph = <$T>::new(); + assert_eq!(graph.edge_count(), 0, "unexpected edge count"); + } + + #[test] + fn edge_count() { + let (graph, _) = make_test_graph(); + assert_eq!(graph.edge_count(), 14, "unexpected edge count"); + } + + #[test] + fn degree_zero() { + let mut graph = <$T>::new(); + let v = graph.add_vertex(); + assert_eq!(graph.degree(v), 0, "unexpected non-zero degree"); + } + + #[test] + fn degree() { + let (graph, vertices) = make_test_graph(); + let expected_degrees = [1, 4, 4, 2, 4, 2, 3, 3, 2, 3]; + for i in 0..graph.vertex_count() { + assert_eq!( + graph.degree(vertices[i]), + expected_degrees[i], + "unexpected degree of {:?}", + vertices[i] + ); + } + } + + #[test] + fn are_adjacent_vertex_self() { + let mut graph = <$T>::new(); + let v = graph.add_vertex(); + assert!( + !graph.are_adjacent(v, v), + "should not be adjacent to itself" + ); + } + + #[test] + fn are_adjacent_single_edge() { + let mut graph = <$T>::new(); + let v1 = graph.add_vertex(); + let v2 = graph.add_vertex(); + assert!(!graph.are_adjacent(v1, v2), "should not be adjacent"); + assert!(!graph.are_adjacent(v2, v1), "should not be adjacent"); + graph.add_edge(v1, v2); + assert!(graph.are_adjacent(v1, v2), "should be adjacent"); + assert!(graph.are_adjacent(v2, v1), "should be adjacent"); + } + + #[test] + fn are_adjacent() { + let (graph, vertices) = make_test_graph(); + assert!( + graph.are_adjacent(vertices[0], vertices[1]), + "expected {:?} and {:?} to be adjacent", + vertices[0], + vertices[1] + ); + assert!( + graph.are_adjacent(vertices[9], vertices[5]), + "expected {:?} and {:?} to be adjacent", + vertices[9], + vertices[5] + ); + assert!( + !graph.are_adjacent(vertices[9], vertices[3]), + "unexpected adjacency of {:?} and {:?}", + vertices[9], + vertices[3] + ); + + for i in 0..graph.vertex_count() { + let exp = match i { + 2 => continue, + 1 | 4 | 5 | 6 => true, + _ => false, + }; + assert_eq!( + graph.are_adjacent(vertices[2], vertices[i]), + exp, + "unexpected adjacency of {:?} and {:?}", + vertices[2], + vertices[i] + ); + assert_eq!( + graph.are_adjacent(vertices[i], vertices[2]), + exp, + "unexpected adjacency of {:?} and {:?}", + vertices[i], + vertices[2] + ); + } + } + + #[test] + fn vertices_empty() { + let graph = <$T>::new(); + assert_eq!( + graph.vertices().count(), + 0, + "vertex iterator of empty graph should have no elements" + ); + } + + #[test] + fn vertices() { + let (graph, vertices) = make_test_graph(); + assert_eq!(graph.vertices().count(), 10, "unexpected vertex count"); + + // Expects each vertex to appear exactly once. + for v in graph.vertices() { + assert_eq!( + vertices.iter().filter(|&x| *x == v).count(), + 1, + "unexpected vertex {v:?} from the iterator" + ); + } + } + + #[test] + fn neighbors_empty() { + let mut graph = <$T>::new(); + let vertex = graph.add_vertex(); + assert_eq!( + graph.neighbors(vertex).count(), + 0, + "neighbor iterator of vertex with degree 0 should have no elements" + ); + } + + #[test] + fn neighbors() { + let (graph, vertices) = make_test_graph(); + // Checks neighbors of vertex 4. + assert_eq!( + graph.neighbors(vertices[4]).count(), + 4, + "unexpected neighbor count" + ); + + // Expects each neighbor to appear exactly once. This will not work if there are multiple edges. + let neighbors = vec![vertices[1], vertices[2], vertices[7], vertices[8]]; + for v in graph.neighbors(vertices[4]) { + assert_eq!( + neighbors.iter().filter(|&x| *x == v).count(), + 1, + "unexpected neighbor {v:?} of {:?} from the iterator", + vertices[4] + ); + } + } + + #[test] + fn loop_edge() { + let mut graph = <$T>::new(); + let v = graph.add_vertex(); + graph.add_edge(v, v); + assert_eq!(graph.vertex_count(), 1, "unexpected vertex count"); + assert_eq!(graph.edge_count(), 1, "unexpected edge count"); + assert_eq!(graph.degree(v), 2, "unexpected degree"); + assert!( + graph.are_adjacent(v, v), + "vertex with loop edge should be adjacent to itself" + ); + let mut neighbors = graph.neighbors(v); + assert_eq!( + neighbors.next(), + Some(v), + "vertex should be neighbor of itself" + ); + assert_eq!( + neighbors.next(), + Some(v), + "vertex should be neighbor of itself twice" + ); + assert_eq!(neighbors.next(), None, "too many neighbors from iterator"); + } + + #[test] + fn multiple_edges() { + let k = 3; + let mut graph = <$T>::new(); + let vertices = [graph.add_vertex(), graph.add_vertex()]; + for _ in 0..k { + graph.add_edge(vertices[0], vertices[1]); + } + assert_eq!( + graph.vertex_count(), + vertices.len(), + "unexpected vertex count" + ); + assert_eq!(graph.edge_count(), k, "unexpected edge count"); + for v in vertices { + assert_eq!(graph.degree(v), k, "unexpected degree of {v:?}"); + } + assert!( + graph.are_adjacent(vertices[0], vertices[1]), + "should be adjacent" + ); + for i in 0..2 { + let mut neighbors = graph.neighbors(vertices[i]); + for j in 0..k { + assert_eq!( + neighbors.next(), + Some(vertices[1 - i]), + "neighbor {j} of vertex {:?} should be {:?}", + vertices[i], + vertices[1 - i] + ); + } + assert_eq!( + neighbors.next(), + None, + "too many neighbors of {:?} from iterator", + vertices[i] + ); + } + } + }; +} + +#[cfg(test)] +#[macro_export] +macro_rules! graph_topology_deletion_tests { + ($T:ty) => { + #[test] + fn delete_vertex() { + let mut graph = Graph::new(); + let v = graph.add_vertex(); + assert_eq!( + graph.vertex_count(), + 1, + "unexpected vertex count before delete" + ); + graph.delete_vertex(v); + assert_eq!( + graph.vertex_count(), + 0, + "unexpected vertex count after delete" + ); + assert_ne!( + graph.add_vertex(), + v, + "unexpected duplicate vertex after delete" + ); + } + + #[test] + fn delete_vertex_loop() { + let mut graph = Graph::new(); + let v = graph.add_vertex(); + graph.add_edge(v, v); + assert_eq!( + graph.vertex_count(), + 1, + "unexpected vertex count before delete" + ); + assert_eq!(graph.edge_count(), 1, "unexpected edge count before delete"); + assert!( + graph.are_adjacent(v, v), + "expected vertex to be self-adjacent before delete" + ); + assert_eq!(graph.degree(v), 2, "unexpected vertex degree before delete"); + graph.delete_vertex(v); + assert_eq!( + graph.vertex_count(), + 0, + "unexpected vertex count after delete" + ); + assert_eq!(graph.edge_count(), 0, "unexpected edge count after delete"); + assert_ne!( + graph.add_vertex(), + v, + "unexpected duplicate vertex after delete" + ); + } + + #[test] + fn delete_vertex_invalid_index() { + let mut graph = Graph::new(); + let v = graph.add_vertex(); + graph.delete_vertex(v); + let result = std::panic::catch_unwind(move || graph.delete_vertex(v)); + assert!(result.is_err(), "second deletion should panic"); + } + + #[test] + fn delete_vertex_connected() { + let (mut graph, vertices) = make_test_graph(); + assert_eq!( + graph.vertex_count(), + 10, + "unexpected vertex count before delete" + ); + assert_eq!( + graph.edge_count(), + 14, + "unexpected edge count before delete" + ); + graph.delete_vertex(vertices[2]); + assert_eq!( + graph.vertex_count(), + 9, + "unexpected vertex count after delete" + ); + assert_eq!(graph.edge_count(), 10, "unexpected edge count after delete"); + let expected_edges = [ + (vertices[0], vertices[1]), + (vertices[1], vertices[3]), + (vertices[1], vertices[4]), + (vertices[3], vertices[6]), + (vertices[4], vertices[7]), + (vertices[4], vertices[8]), + (vertices[5], vertices[9]), + (vertices[6], vertices[9]), + (vertices[7], vertices[8]), + (vertices[7], vertices[9]), + ]; + for (v, u) in expected_edges { + assert!( + graph.are_adjacent(v, u), + "expected {v:?} and {u:?} to be adjacent after delete" + ); + } + for v in [vertices[1], vertices[4], vertices[5], vertices[6]] { + assert!( + !graph.neighbors(v).any(|u| u == vertices[2]), + "unexpected adjacency of {v:?} to deleted vertex {:?}", + vertices[2] + ); + } + } + + #[test] + fn delete_edge() { + let mut graph = Graph::new(); + let v1 = graph.add_vertex(); + let v2 = graph.add_vertex(); + let e = graph.add_edge(v1, v2); + assert_eq!( + graph.vertex_count(), + 2, + "unexpected vertex count before delete" + ); + assert_eq!(graph.edge_count(), 1, "unexpected edge count before delete"); + assert!( + graph.are_adjacent(v1, v2), + "expected vertices to be adjacent before delete" + ); + assert_eq!( + graph.degree(v1), + 1, + "unexpected vertex degree before delete" + ); + assert_eq!( + graph.degree(v2), + 1, + "unexpected vertex degree before delete" + ); + graph.delete_edge(e); + assert_eq!( + graph.vertex_count(), + 2, + "unexpected vertex count after delete" + ); + assert_eq!(graph.edge_count(), 0, "unexpected edge count after delete"); + assert!( + !graph.are_adjacent(v1, v2), + "unexpected adjacency after delete" + ); + assert_eq!(graph.degree(v1), 0, "unexpected vertex degree after delete"); + assert_eq!(graph.degree(v2), 0, "unexpected vertex degree after delete"); + assert_ne!( + graph.add_edge(v1, v2), + e, + "unexpected duplicate edge after delete" + ); + } + + #[test] + fn delete_edge_loop() { + let mut graph = Graph::new(); + let v = graph.add_vertex(); + let e = graph.add_edge(v, v); + assert_eq!( + graph.vertex_count(), + 1, + "unexpected vertex count before delete" + ); + assert_eq!(graph.edge_count(), 1, "unexpected edge count before delete"); + assert!( + graph.are_adjacent(v, v), + "expected vertex to be self-adjacent before delete" + ); + assert_eq!(graph.degree(v), 2, "unexpected vertex degree before delete"); + graph.delete_edge(e); + assert_eq!( + graph.vertex_count(), + 1, + "unexpected vertex count after delete" + ); + assert_eq!(graph.edge_count(), 0, "unexpected edge count after delete"); + assert!( + !graph.are_adjacent(v, v), + "unexpected adjacency after delete" + ); + assert_eq!(graph.degree(v), 0, "unexpected vertex degree after delete"); + assert_ne!( + graph.add_edge(v, v), + e, + "unexpected duplicate edge after delete" + ); + } + + #[test] + fn delete_edge_multiple() { + let mut graph = Graph::new(); + let v1 = graph.add_vertex(); + let v2 = graph.add_vertex(); + let e = graph.add_edge(v1, v2); + graph.add_edge(v1, v2); + assert_eq!( + graph.vertex_count(), + 2, + "unexpected vertex count before delete" + ); + assert_eq!(graph.edge_count(), 2, "unexpected edge count before delete"); + assert!( + graph.are_adjacent(v1, v2), + "expected vertices to be adjacent before delete" + ); + assert_eq!( + graph.degree(v1), + 2, + "unexpected vertex degree before delete" + ); + assert_eq!( + graph.degree(v2), + 2, + "unexpected vertex degree before delete" + ); + graph.delete_edge(e); + assert_eq!( + graph.vertex_count(), + 2, + "unexpected vertex count after delete" + ); + assert_eq!(graph.edge_count(), 1, "unexpected edge count after delete"); + assert!( + graph.are_adjacent(v1, v2), + "expected vertices to be adjacent after delete" + ); + assert_eq!(graph.degree(v1), 1, "unexpected vertex degree after delete"); + assert_eq!(graph.degree(v2), 1, "unexpected vertex degree after delete"); + assert_ne!( + graph.add_edge(v1, v2), + e, + "unexpected duplicate edge after delete" + ); + } + + #[test] + fn delete_edge_invalid_index() { + let mut graph = Graph::new(); + let v1 = graph.add_vertex(); + let v2 = graph.add_vertex(); + let e = graph.add_edge(v1, v2); + graph.delete_edge(e); + let result = std::panic::catch_unwind(move || graph.delete_edge(e)); + assert!(result.is_err(), "second deletion should panic"); + } + }; +}