use std::fmt::Debug; use grapherity::algorithms; use grapherity::maps::ElementMap; use grapherity::testing::fixtures; use grapherity::testing::fixtures::MakeTestGraph; use grapherity::traits::GraphTopology; macro_rules! dfs_tests { ($T:ty) => { dfs_tests!(@wrap $T, dfs_single_vertex, dfs_disconnected, dfs, dfs_visited_single_vertex, dfs_visited_disconnected, dfs_visited, dfs_find_source, dfs_find_disconnected, dfs_find, dfs_find_where_source_matches, dfs_find_where_disconnected, dfs_find_where_no_match, dfs_find_where_adjacent, dfs_find_where, dfs_find_path_source_equals_target, dfs_find_path_disconnected, dfs_find_path_adjacent, dfs_find_path, dfs_find_path_where_source_matches, dfs_find_path_where_disconnected, dfs_find_path_where_no_match, dfs_find_path_where, ); }; (@wrap $T:ty, $($name:ident),* $(,)?) => { $( #[test] fn $name() { super::$name::<$T>(); } )* }; } mod append_graph { use grapherity::models::AppendGraph; dfs_tests!(AppendGraph); } mod graph { use grapherity::models::Graph; dfs_tests!(Graph); } fn dfs_single_vertex() where G::Vertex: Debug, { let (graph, v) = G::single_vertex(); let result = algorithms::dfs(&graph, v); assert!(result.visited[v], "source vertex should be visited"); assert_eq!( result.predecessors[v], None, "unexpected predecessor of source vertex" ); } fn dfs_disconnected() where G::Vertex: Debug, { let (graph, vertices) = G::disconnected(); let result = algorithms::dfs(&graph, vertices[0]); assert!( result.visited[vertices[0]], "source vertex should be visited" ); assert_eq!( result.predecessors[vertices[0]], None, "unexpected predecessor of source vertex" ); for &v in &vertices[1..3] { assert!( !result.visited[v], "disconnected vertex {v:?} should not be visited" ); assert_eq!( result.predecessors[v], None, "disconnected vertex {v:?} should have no predecessor" ); } } fn dfs() where G::Vertex: Debug, { let (graph, vertices, _, _) = G::standard(); let result = algorithms::dfs(&graph, vertices[0]); assert_dfs_visited::(&result.visited, &vertices); assert_dfs_predecessors(&graph, &result.visited, &result.predecessors, &vertices); } fn dfs_visited_single_vertex() { let (graph, v) = G::single_vertex(); let visited = algorithms::dfs_visited(&graph, v); assert!(visited[v], "source vertex should be visited"); } fn dfs_visited_disconnected() where G::Vertex: Debug, { let (graph, vertices) = G::disconnected(); let visited = algorithms::dfs_visited(&graph, vertices[0]); assert!(visited[vertices[0]], "source vertex should be visited"); for &v in &vertices[1..3] { assert!( !visited[v], "disconnected vertex {v:?} should not be visited" ); } } fn dfs_visited() where G::Vertex: Debug, { let (graph, vertices, _, _) = G::standard(); let visited = algorithms::dfs_visited(&graph, vertices[0]); assert_dfs_visited::(&visited, &vertices); } fn dfs_find_source() { let (graph, v) = G::single_vertex(); assert!( algorithms::dfs_find(&graph, v, v), "source should find itself" ); } fn dfs_find_disconnected() { let (graph, vertices) = G::disconnected(); assert!( !algorithms::dfs_find(&graph, vertices[0], vertices[1]), "disconnected target should not be found" ); } fn dfs_find() where G::Vertex: Debug, { let (graph, vertices, _, _) = G::standard(); for i in 0..10 { assert!( algorithms::dfs_find(&graph, vertices[0], vertices[i]), "vertex {:?} should be reachable from {:?}", vertices[i], vertices[0] ); } } fn dfs_find_where_source_matches() where G::Vertex: Debug, { let (graph, v) = G::single_vertex(); assert_eq!( algorithms::dfs_find_where(&graph, v, |u| u == v), Some(v), "source should find itself" ); } fn dfs_find_where_disconnected() where G::Vertex: Debug, { let (graph, vertices) = G::disconnected(); assert_eq!( algorithms::dfs_find_where(&graph, vertices[0], |v| v == vertices[1]), None, "disconnected vertex {:?} should not be found", vertices[1] ); } fn dfs_find_where_no_match() where G::Vertex: Debug, { let (graph, vertices, _, _) = G::standard(); assert_eq!( algorithms::dfs_find_where(&graph, vertices[0], |_| false), None, "no vertex should match an always-false predicate" ); } fn dfs_find_where_adjacent() where G::Vertex: Debug, { let (graph, vertices, _, _) = G::standard(); assert_eq!( algorithms::dfs_find_where(&graph, vertices[0], |v| v == vertices[1]), Some(vertices[1]), "expected to find adjacent vertex" ); } fn dfs_find_where() where G::Vertex: Debug, { let (graph, vertices, _, _) = G::standard(); assert_eq!( algorithms::dfs_find_where(&graph, vertices[0], |v| v == vertices[9]), Some(vertices[9]), "expected to find connected vertex" ); } fn dfs_find_path_source_equals_target() where G::Edge: Debug, { let (graph, v) = G::single_vertex(); assert_eq!( algorithms::dfs_find_path(&graph, v, v), Some(vec![]), "path from source to itself should be empty" ); } fn dfs_find_path_disconnected() where G::Edge: Debug, { let (graph, vertices) = G::disconnected(); assert_eq!( algorithms::dfs_find_path(&graph, vertices[0], vertices[1]), None, "no path should exist to disconnected vertex" ); } fn dfs_find_path_adjacent() where G::Edge: Debug, { let (graph, vertices, e) = G::single_edge(); let path = algorithms::dfs_find_path(&graph, vertices[0], vertices[1]) .expect("path should exist between adjacent vertices"); assert_eq!(path, [e], "path should contain only the connecting edge"); } fn dfs_find_path() where G::Vertex: Debug, G::Edge: Debug, { let (graph, vertices, _, _) = G::standard(); let path = algorithms::dfs_find_path(&graph, vertices[0], vertices[9]); fixtures::assert_valid_path(&graph, path, vertices[0], &[vertices[9]]); } fn dfs_find_path_where_source_matches() where G::Edge: Debug, { let (graph, v) = G::single_vertex(); assert_eq!( algorithms::dfs_find_path_where(&graph, v, |u| u == v), Some(vec![]), "path from source to itself should be empty" ); } fn dfs_find_path_where_disconnected() where G::Edge: Debug, { let (graph, vertices) = G::disconnected(); assert_eq!( algorithms::dfs_find_path_where(&graph, vertices[0], |v| v == vertices[1]), None, "no path should exist to disconnected vertex" ); } fn dfs_find_path_where_no_match() where G::Edge: Debug, { let (graph, vertices, _, _) = G::standard(); assert_eq!( algorithms::dfs_find_path_where(&graph, vertices[0], |_| false), None, "no path should exist when predicate never matches" ); } fn dfs_find_path_where() where G::Vertex: Debug, G::Edge: Debug, { let (graph, vertices, _, _) = G::standard(); let path = algorithms::dfs_find_path_where(&graph, vertices[0], |v| v == vertices[9]); _ = fixtures::assert_valid_path(&graph, path, vertices[0], &[vertices[9]]); } fn assert_dfs_visited( visited: &ElementMap, vertices: &[G::Vertex], ) where G::Vertex: Debug, { for i in 0..10 { assert!( visited[vertices[i]], "vertex {:?} should be visited", vertices[i] ); } } fn assert_dfs_predecessors( graph: &G, visited: &ElementMap, predecessors: &ElementMap>, vertices: &[G::Vertex], ) where G::Vertex: Debug, { assert_eq!( predecessors[vertices[0]], None, "source should have no predecessor" ); for i in 1..10 { let v = vertices[i]; let p = predecessors[v].expect(&format!("vertex {v:?} should have a predecessor")); assert!( visited[p], "predecessor {p:?} of vertex {v:?} should be visited" ); assert!( graph.are_adjacent(v, p), "predecessor {p:?} of vertex {v:?} should be adjacent" ); } }