Files
grapherity/tests/dfs.rs
T

360 lines
9.1 KiB
Rust

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<G: MakeTestGraph>()
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<G: MakeTestGraph>()
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<G: MakeTestGraph>()
where
G::Vertex: Debug,
{
let (graph, vertices, _, _) = G::standard();
let result = algorithms::dfs(&graph, vertices[0]);
assert_dfs_visited::<G>(&result.visited, &vertices);
assert_dfs_predecessors(&graph, &result.visited, &result.predecessors, &vertices);
}
fn dfs_visited_single_vertex<G: MakeTestGraph>() {
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<G: MakeTestGraph>()
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<G: MakeTestGraph>()
where
G::Vertex: Debug,
{
let (graph, vertices, _, _) = G::standard();
let visited = algorithms::dfs_visited(&graph, vertices[0]);
assert_dfs_visited::<G>(&visited, &vertices);
}
fn dfs_find_source<G: MakeTestGraph>() {
let (graph, v) = G::single_vertex();
assert!(
algorithms::dfs_find(&graph, v, v),
"source should find itself"
);
}
fn dfs_find_disconnected<G: MakeTestGraph>() {
let (graph, vertices) = G::disconnected();
assert!(
!algorithms::dfs_find(&graph, vertices[0], vertices[1]),
"disconnected target should not be found"
);
}
fn dfs_find<G: MakeTestGraph>()
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<G: MakeTestGraph>()
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<G: MakeTestGraph>()
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<G: MakeTestGraph>()
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<G: MakeTestGraph>()
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<G: MakeTestGraph>()
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<G: MakeTestGraph>()
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<G: MakeTestGraph>()
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<G: MakeTestGraph>()
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<G: MakeTestGraph>()
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<G: MakeTestGraph>()
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<G: MakeTestGraph>()
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<G: MakeTestGraph>()
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<G: MakeTestGraph>()
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<G: GraphTopology>(
visited: &ElementMap<G::Vertex, bool>,
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<G: GraphTopology>(
graph: &G,
visited: &ElementMap<G::Vertex, bool>,
predecessors: &ElementMap<G::Vertex, Option<G::Vertex>>,
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"
);
}
}