The tests only declare a list of tests in the macro, no longer the test functions themselves. This allows better tooling support (e.g. IDE and rustfmt).
308 lines
7.7 KiB
Rust
308 lines
7.7 KiB
Rust
use std::fmt::Debug;
|
|
|
|
use grapherity::algorithms;
|
|
use grapherity::maps::ElementMap;
|
|
use grapherity::testing::fixtures::MakeTestGraph;
|
|
use grapherity::traits::GraphTopology;
|
|
|
|
macro_rules! bfs_tests {
|
|
($T:ty) => {
|
|
bfs_tests!(@wrap $T,
|
|
bfs_single_vertex,
|
|
bfs_disconnected,
|
|
bfs,
|
|
bfs_distances_single_vertex,
|
|
bfs_distances_disconnected,
|
|
bfs_distances,
|
|
bfs_find_source,
|
|
bfs_find_disconnected,
|
|
bfs_find,
|
|
bfs_find_where_source_matches,
|
|
bfs_find_where_disconnected,
|
|
bfs_find_where_no_match,
|
|
bfs_find_where_nearest,
|
|
);
|
|
};
|
|
|
|
(@wrap $T:ty, $($name:ident),* $(,)?) => {
|
|
$(
|
|
#[test]
|
|
fn $name() {
|
|
super::$name::<$T>();
|
|
}
|
|
)*
|
|
};
|
|
}
|
|
|
|
mod append_graph {
|
|
use grapherity::models::AppendGraph;
|
|
|
|
bfs_tests!(AppendGraph);
|
|
}
|
|
|
|
mod graph {
|
|
use grapherity::models::Graph;
|
|
|
|
bfs_tests!(Graph);
|
|
}
|
|
|
|
fn bfs_single_vertex<G: MakeTestGraph>()
|
|
where
|
|
G::Vertex: Debug,
|
|
{
|
|
let (graph, v) = G::single_vertex();
|
|
let result = algorithms::bfs(&graph, v);
|
|
assert_eq!(
|
|
result.distances[v],
|
|
Some(0),
|
|
"unexpected distance of source vertex"
|
|
);
|
|
assert_eq!(
|
|
result.predecessors[v], None,
|
|
"unexpected predecessor of source vertex"
|
|
);
|
|
}
|
|
|
|
fn bfs_disconnected<G: MakeTestGraph>()
|
|
where
|
|
G::Vertex: Debug,
|
|
{
|
|
let (graph, vertices) = G::disconnected();
|
|
let result = algorithms::bfs(&graph, vertices[0]);
|
|
assert_eq!(
|
|
result.distances[vertices[0]],
|
|
Some(0),
|
|
"unexpected distance of source vertex"
|
|
);
|
|
assert_eq!(
|
|
result.predecessors[vertices[0]], None,
|
|
"unexpected predecessor of source vertex"
|
|
);
|
|
for &v in &vertices[1..3] {
|
|
assert_eq!(
|
|
result.distances[v], None,
|
|
"disconnected vertex {v:?} should have no distance"
|
|
);
|
|
assert_eq!(
|
|
result.predecessors[v], None,
|
|
"disconnected vertex {v:?} should have no predecessor"
|
|
);
|
|
}
|
|
}
|
|
|
|
fn bfs<G: MakeTestGraph>()
|
|
where
|
|
G::Vertex: Debug,
|
|
{
|
|
let (graph, vertices, _, _) = G::standard();
|
|
let result = algorithms::bfs(&graph, vertices[0]);
|
|
assert_bfs_distances::<G>(&result.distances, &vertices);
|
|
assert_bfs_predecessors::<G>(&result.predecessors, &vertices);
|
|
}
|
|
|
|
fn bfs_distances_single_vertex<G: MakeTestGraph>() {
|
|
let (graph, v) = G::single_vertex();
|
|
let distances = algorithms::bfs_distances(&graph, v);
|
|
assert_eq!(
|
|
distances[v],
|
|
Some(0),
|
|
"unexpected distance of source vertex"
|
|
);
|
|
}
|
|
|
|
fn bfs_distances_disconnected<G: MakeTestGraph>()
|
|
where
|
|
G::Vertex: Debug,
|
|
{
|
|
let (graph, vertices) = G::disconnected();
|
|
let distances = algorithms::bfs_distances(&graph, vertices[0]);
|
|
assert_eq!(
|
|
distances[vertices[0]],
|
|
Some(0),
|
|
"unexpected distance of source vertex"
|
|
);
|
|
for &v in &vertices[1..3] {
|
|
assert_eq!(
|
|
distances[v], None,
|
|
"disconnected vertex {v:?} should have no distance"
|
|
);
|
|
}
|
|
}
|
|
|
|
fn bfs_distances<G: MakeTestGraph>()
|
|
where
|
|
G::Vertex: Debug,
|
|
{
|
|
let (graph, vertices, _, _) = G::standard();
|
|
let distances = algorithms::bfs_distances(&graph, vertices[0]);
|
|
assert_bfs_distances::<G>(&distances, &vertices);
|
|
}
|
|
|
|
fn bfs_find_source<G: MakeTestGraph>() {
|
|
let (graph, v) = G::single_vertex();
|
|
assert_eq!(
|
|
algorithms::bfs_find(&graph, v, v),
|
|
Some(0),
|
|
"source should be found at distance 0"
|
|
);
|
|
}
|
|
|
|
fn bfs_find_disconnected<G: MakeTestGraph>() {
|
|
let (graph, vertices) = G::disconnected();
|
|
assert_eq!(
|
|
algorithms::bfs_find(&graph, vertices[0], vertices[1]),
|
|
None,
|
|
"disconnected target should not be found"
|
|
);
|
|
}
|
|
|
|
fn bfs_find<G: MakeTestGraph>()
|
|
where
|
|
G::Vertex: Debug,
|
|
{
|
|
let (graph, vertices, _, _) = G::standard();
|
|
let expected_distances = [
|
|
Some(0),
|
|
Some(1),
|
|
Some(2),
|
|
Some(2),
|
|
Some(2),
|
|
Some(3),
|
|
Some(3),
|
|
Some(3),
|
|
Some(3),
|
|
Some(4),
|
|
];
|
|
for i in 0..10 {
|
|
assert_eq!(
|
|
algorithms::bfs_find(&graph, vertices[0], vertices[i]),
|
|
expected_distances[i],
|
|
"unexpected distance from {:?} to {:?}",
|
|
vertices[0],
|
|
vertices[i]
|
|
);
|
|
}
|
|
}
|
|
|
|
fn bfs_find_where_source_matches<G: MakeTestGraph>()
|
|
where
|
|
G::Vertex: Debug,
|
|
{
|
|
let (graph, v) = G::single_vertex();
|
|
assert_eq!(
|
|
algorithms::bfs_find_where(&graph, v, |u| u == v),
|
|
Some((v, 0)),
|
|
"source should match with distance 0"
|
|
);
|
|
}
|
|
|
|
fn bfs_find_where_disconnected<G: MakeTestGraph>()
|
|
where
|
|
G::Vertex: Debug,
|
|
{
|
|
let (graph, vertices) = G::disconnected();
|
|
assert_eq!(
|
|
algorithms::bfs_find_where(&graph, vertices[0], |v| v == vertices[1]),
|
|
None,
|
|
"disconnected vertex {:?} should not be found",
|
|
vertices[1]
|
|
);
|
|
}
|
|
|
|
fn bfs_find_where_no_match<G: MakeTestGraph>()
|
|
where
|
|
G::Vertex: Debug,
|
|
{
|
|
let (graph, vertices, _, _) = G::standard();
|
|
assert_eq!(
|
|
algorithms::bfs_find_where(&graph, vertices[0], |_| false),
|
|
None,
|
|
"no vertex should match an always-false predicate"
|
|
);
|
|
}
|
|
|
|
fn bfs_find_where_nearest<G: MakeTestGraph>()
|
|
where
|
|
G::Vertex: Debug,
|
|
{
|
|
let (graph, vertices, _, _) = G::standard();
|
|
// vertices[5], vertices[6], vertices[7], vertices[8] are all at distance 3 from vertices[0].
|
|
// vertices[9] is at distance 4. Predicate matches vertices[7], vertices[8], vertices[9].
|
|
// BFS must return one of the distance-3 ones, not vertices[9].
|
|
let result = algorithms::bfs_find_where(&graph, vertices[0], |v| {
|
|
v == vertices[7] || v == vertices[8] || v == vertices[9]
|
|
});
|
|
assert!(result.is_some(), "expected a match");
|
|
let (found, distance) = result.unwrap();
|
|
assert_eq!(
|
|
distance, 3,
|
|
"unexpected distance to nearest matching vertex {found:?}",
|
|
);
|
|
assert!(
|
|
found == vertices[7] || found == vertices[8],
|
|
"unexpected nearest match vertex {found:?}, should be {:?} or {:?}",
|
|
vertices[7],
|
|
vertices[8]
|
|
);
|
|
}
|
|
|
|
fn assert_bfs_distances<G: GraphTopology>(
|
|
distances: &ElementMap<G::Vertex, Option<u32>>,
|
|
vertices: &[G::Vertex],
|
|
) where
|
|
G::Vertex: Debug,
|
|
{
|
|
let expected = [
|
|
Some(0),
|
|
Some(1),
|
|
Some(2),
|
|
Some(2),
|
|
Some(2),
|
|
Some(3),
|
|
Some(3),
|
|
Some(3),
|
|
Some(3),
|
|
Some(4),
|
|
];
|
|
for i in 0..10 {
|
|
assert_eq!(
|
|
distances[vertices[i]], expected[i],
|
|
"unexpected distance from {:?} to {:?}",
|
|
vertices[0], vertices[i]
|
|
);
|
|
}
|
|
}
|
|
|
|
fn assert_bfs_predecessors<G: GraphTopology>(
|
|
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"
|
|
);
|
|
// Each non-source vertex's predecessor must be adjacent and at distance one less.
|
|
let expected_predecessors = [
|
|
vec![None],
|
|
vec![Some(vertices[0])],
|
|
vec![Some(vertices[1])],
|
|
vec![Some(vertices[1])],
|
|
vec![Some(vertices[1])],
|
|
vec![Some(vertices[2])],
|
|
vec![Some(vertices[2]), Some(vertices[3])],
|
|
vec![Some(vertices[4])],
|
|
vec![Some(vertices[4])],
|
|
vec![Some(vertices[5]), Some(vertices[6]), Some(vertices[7])],
|
|
];
|
|
for i in 1..10 {
|
|
assert!(
|
|
expected_predecessors[i].contains(&predecessors[vertices[i]]),
|
|
"unexpected predecessor {:?} of {:?}",
|
|
predecessors[vertices[i]],
|
|
vertices[i]
|
|
);
|
|
}
|
|
}
|