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).
349 lines
9.6 KiB
Rust
349 lines
9.6 KiB
Rust
use std::fmt::Debug;
|
|
use std::hash::Hash;
|
|
|
|
use grapherity::algorithms;
|
|
use grapherity::algorithms::DijkstraResult;
|
|
use grapherity::maps::ElementMap;
|
|
use grapherity::testing::fixtures::MakeTestGraph;
|
|
use grapherity::traits::{GraphTopology, Incidence};
|
|
|
|
macro_rules! dijkstra_tests {
|
|
($T:ty) => {
|
|
dijkstra_tests!(@wrap $T,
|
|
dijkstra_single_vertex,
|
|
dijkstra_disconnected,
|
|
dijkstra_zero_weight_loop,
|
|
dijkstra,
|
|
dijkstra_distances,
|
|
dijkstra_unweighted_single_vertex,
|
|
dijkstra_unweighted_disconnected,
|
|
dijkstra_unweighted,
|
|
dijkstra_distances_unweighted_single_vertex,
|
|
dijkstra_distances_unweighted_disconnected,
|
|
dijkstra_distances_unweighted,
|
|
);
|
|
};
|
|
|
|
(@wrap $T:ty, $($name:ident),* $(,)?) => {
|
|
$(
|
|
#[test]
|
|
fn $name() {
|
|
super::$name::<$T>();
|
|
}
|
|
)*
|
|
};
|
|
}
|
|
|
|
mod append_graph {
|
|
use grapherity::models::AppendGraph;
|
|
|
|
dijkstra_tests!(AppendGraph);
|
|
}
|
|
|
|
mod graph {
|
|
use grapherity::models::Graph;
|
|
|
|
dijkstra_tests!(Graph);
|
|
}
|
|
|
|
fn dijkstra_single_vertex<G: MakeTestGraph>()
|
|
where
|
|
G::Vertex: Debug + Hash,
|
|
{
|
|
let (graph, v) = G::single_vertex();
|
|
let result = algorithms::dijkstra(&graph, v, |_| panic!("unexpected call of weight functor"));
|
|
assert_single_vertex::<G>(&result, v);
|
|
}
|
|
|
|
fn dijkstra_disconnected<G: MakeTestGraph>()
|
|
where
|
|
G::Vertex: Debug + Hash,
|
|
{
|
|
let (graph, vertices) = G::disconnected();
|
|
let result = algorithms::dijkstra(&graph, vertices[0], |_| {
|
|
panic!("unexpected call of weight functor")
|
|
});
|
|
assert_single_vertex::<G>(&result, vertices[0]);
|
|
assert_disconnected::<G>(&result, &vertices[1..3]);
|
|
}
|
|
|
|
fn dijkstra_zero_weight_loop<G: MakeTestGraph>()
|
|
where
|
|
G::Vertex: Debug + Hash,
|
|
{
|
|
let (graph, vertices, _, weights) = make_two_edge_path_with_zero_weight_loops::<G>();
|
|
let result = algorithms::dijkstra(&graph, vertices[0], |e| weights[e]);
|
|
assert_single_vertex::<G>(&result, vertices[0]);
|
|
for i in 1..3 {
|
|
assert_eq!(
|
|
result.predecessors[vertices[i]],
|
|
Some(vertices[i - 1]),
|
|
"unexpected predecessor of vertex {:?}",
|
|
vertices[i]
|
|
);
|
|
assert_eq!(
|
|
result.distances[vertices[i]],
|
|
Some(i.try_into().unwrap()),
|
|
"unexpected distance of vertex {:?}",
|
|
vertices[i]
|
|
);
|
|
}
|
|
}
|
|
|
|
fn dijkstra<G: MakeTestGraph>()
|
|
where
|
|
G::Vertex: Debug + Hash,
|
|
{
|
|
let (graph, vertices, _, _, weights) = make_test_graph_weighted::<G>();
|
|
let result = algorithms::dijkstra(&graph, vertices[0], |e| weights[e]);
|
|
assert_test_graph::<G>(&result, &vertices);
|
|
}
|
|
|
|
fn dijkstra_distances<G: MakeTestGraph>()
|
|
where
|
|
G::Vertex: Debug + Hash,
|
|
{
|
|
let (graph, vertices, _, _, weights) = make_test_graph_weighted::<G>();
|
|
let distances = algorithms::dijkstra_distances(&graph, vertices[0], |e| weights[e]);
|
|
assert_distances_test_graph::<G>(&distances, &vertices);
|
|
}
|
|
|
|
fn dijkstra_unweighted_single_vertex<G: MakeTestGraph>()
|
|
where
|
|
G::Vertex: Debug + Hash,
|
|
{
|
|
let (graph, v) = G::single_vertex();
|
|
let result = algorithms::dijkstra_unweighted(&graph, v);
|
|
assert_single_vertex::<G>(&result, v)
|
|
}
|
|
|
|
fn dijkstra_unweighted_disconnected<G: MakeTestGraph>()
|
|
where
|
|
G::Vertex: Debug + Hash,
|
|
{
|
|
let (graph, vertices) = G::disconnected();
|
|
let result = algorithms::dijkstra_unweighted(&graph, vertices[0]);
|
|
assert_single_vertex::<G>(&result, vertices[0]);
|
|
assert_disconnected::<G>(&result, &vertices[1..3]);
|
|
}
|
|
|
|
fn dijkstra_unweighted<G: MakeTestGraph>()
|
|
where
|
|
G::Vertex: Debug + Hash,
|
|
{
|
|
let (graph, vertices, _, _) = G::standard();
|
|
let result = algorithms::dijkstra_unweighted(&graph, vertices[0]);
|
|
assert_unweighted_test_graph::<G>(&result, &vertices);
|
|
}
|
|
|
|
fn dijkstra_distances_unweighted_single_vertex<G: MakeTestGraph>()
|
|
where
|
|
G::Vertex: Hash,
|
|
{
|
|
let (graph, v) = G::single_vertex();
|
|
let distances = algorithms::dijkstra_distances_unweighted(&graph, v);
|
|
assert_distances_single_vertex::<G>(&distances, v);
|
|
}
|
|
|
|
fn dijkstra_distances_unweighted_disconnected<G: MakeTestGraph>()
|
|
where
|
|
G::Vertex: Debug + Hash,
|
|
{
|
|
let (graph, vertices) = G::disconnected();
|
|
let distances = algorithms::dijkstra_distances_unweighted(&graph, vertices[0]);
|
|
assert_distances_single_vertex::<G>(&distances, vertices[0]);
|
|
assert_distances_disconnected::<G>(&distances, &vertices[1..3]);
|
|
}
|
|
|
|
fn dijkstra_distances_unweighted<G: MakeTestGraph>()
|
|
where
|
|
G::Vertex: Debug + Hash,
|
|
{
|
|
let (graph, vertices, _, _) = G::standard();
|
|
let distances = algorithms::dijkstra_distances_unweighted(&graph, vertices[0]);
|
|
assert_distances_unweighted_test_graph::<G>(&distances, &vertices);
|
|
}
|
|
|
|
fn assert_single_vertex<G: GraphTopology>(result: &DijkstraResult<G::Vertex>, v: G::Vertex)
|
|
where
|
|
G::Vertex: Debug,
|
|
{
|
|
assert_distances_single_vertex::<G>(&result.distances, v);
|
|
assert_eq!(
|
|
result.predecessors[v], None,
|
|
"unexpected predecessor of source vertex",
|
|
);
|
|
}
|
|
|
|
fn assert_distances_single_vertex<G: GraphTopology>(
|
|
distances: &ElementMap<G::Vertex, Option<u32>>,
|
|
v: G::Vertex,
|
|
) {
|
|
assert_eq!(
|
|
distances[v],
|
|
Some(0),
|
|
"unexpected distance of source vertex"
|
|
);
|
|
}
|
|
|
|
fn assert_disconnected<G: GraphTopology>(result: &DijkstraResult<G::Vertex>, vertices: &[G::Vertex])
|
|
where
|
|
G::Vertex: Debug,
|
|
{
|
|
assert_distances_disconnected::<G>(&result.distances, &vertices);
|
|
for &v in vertices {
|
|
assert_eq!(
|
|
result.predecessors[v], None,
|
|
"unexpected predecessor of disconnected vertex {v:?}",
|
|
);
|
|
}
|
|
}
|
|
|
|
fn assert_distances_disconnected<G: GraphTopology>(
|
|
distances: &ElementMap<G::Vertex, Option<u32>>,
|
|
vertices: &[G::Vertex],
|
|
) where
|
|
G::Vertex: Debug,
|
|
{
|
|
for &v in vertices {
|
|
assert_eq!(
|
|
distances[v], None,
|
|
"unexpected distance of disconnected vertex {v:?}",
|
|
);
|
|
}
|
|
}
|
|
|
|
fn assert_test_graph<G: GraphTopology>(result: &DijkstraResult<G::Vertex>, vertices: &[G::Vertex])
|
|
where
|
|
G::Vertex: Debug,
|
|
{
|
|
assert_distances_test_graph::<G>(&result.distances, &vertices);
|
|
let expected_predecessors_from_v0 = [
|
|
vec![None],
|
|
vec![Some(vertices[0])],
|
|
vec![Some(vertices[4])],
|
|
vec![Some(vertices[1])],
|
|
vec![Some(vertices[7])],
|
|
vec![Some(vertices[9])],
|
|
vec![Some(vertices[3])],
|
|
vec![Some(vertices[9])],
|
|
vec![Some(vertices[7])],
|
|
vec![Some(vertices[6])],
|
|
];
|
|
for i in 0..10 {
|
|
assert!(
|
|
expected_predecessors_from_v0[i].contains(&result.predecessors[vertices[i]]),
|
|
"unexpected predecessor {:?} of {:?}",
|
|
result.predecessors[vertices[i]],
|
|
vertices[i]
|
|
);
|
|
}
|
|
}
|
|
|
|
fn assert_distances_test_graph<G: GraphTopology>(
|
|
distances: &ElementMap<G::Vertex, Option<u32>>,
|
|
vertices: &[G::Vertex],
|
|
) where
|
|
G::Vertex: Debug,
|
|
{
|
|
let expected_distances_from_v0 = [
|
|
Some(0),
|
|
Some(1),
|
|
Some(65),
|
|
Some(4),
|
|
Some(58),
|
|
Some(43),
|
|
Some(14),
|
|
Some(46),
|
|
Some(145),
|
|
Some(29),
|
|
];
|
|
for i in 0..10 {
|
|
assert_eq!(
|
|
distances[vertices[i]], expected_distances_from_v0[i],
|
|
"unexpected distance from {:?} to {:?}",
|
|
vertices[0], vertices[i]
|
|
);
|
|
}
|
|
}
|
|
|
|
fn assert_unweighted_test_graph<G: GraphTopology>(
|
|
result: &DijkstraResult<G::Vertex>,
|
|
vertices: &[G::Vertex],
|
|
) where
|
|
G::Vertex: Debug,
|
|
{
|
|
assert_distances_unweighted_test_graph::<G>(&result.distances, &vertices);
|
|
let expected_predecessors_from_v0 = [
|
|
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 0..10 {
|
|
assert!(
|
|
expected_predecessors_from_v0[i].contains(&result.predecessors[vertices[i]]),
|
|
"unexpected predecessor {:?} of {:?}",
|
|
result.predecessors[vertices[i]],
|
|
vertices[i]
|
|
);
|
|
}
|
|
}
|
|
|
|
fn assert_distances_unweighted_test_graph<G: GraphTopology>(
|
|
distances: &ElementMap<G::Vertex, Option<u32>>,
|
|
vertices: &[G::Vertex],
|
|
) where
|
|
G::Vertex: Debug,
|
|
{
|
|
let expected_distances_from_v0 = [
|
|
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_distances_from_v0[i],
|
|
"unexpected distance from {:?} to {:?}",
|
|
vertices[0], vertices[i]
|
|
);
|
|
}
|
|
}
|
|
|
|
fn make_test_graph_weighted<G: MakeTestGraph>() -> (
|
|
G,
|
|
[G::Vertex; 10],
|
|
[(G::Edge, G::Vertex, G::Vertex); 18],
|
|
[Vec<Incidence<G::Vertex, G::Edge>>; 10],
|
|
ElementMap<G::Edge, u32>,
|
|
) {
|
|
let (graph, vertices, edges, incidences) = G::standard();
|
|
let mut weights = graph.edge_map(99);
|
|
for i in [1, 3, 7, 10, 12, 14, 15, 17] {
|
|
weights[edges[i].0] = i.try_into().unwrap();
|
|
}
|
|
(graph, vertices, edges, incidences, weights)
|
|
}
|
|
|
|
fn make_two_edge_path_with_zero_weight_loops<G: MakeTestGraph>()
|
|
-> (G, [G::Vertex; 3], [G::Edge; 4], ElementMap<G::Edge, u32>) {
|
|
let (graph, vertices, edges) = G::two_edge_path_with_loops();
|
|
let mut weights = graph.edge_map(1);
|
|
weights[edges[2]] = 0;
|
|
weights[edges[3]] = 0;
|
|
(graph, vertices, edges, weights)
|
|
}
|