Extract asserts from Clone tests to be reusable

This commit is contained in:
2026-09-25 08:36:30 +02:00
parent 0dea90ceff
commit d438a4288a
3 changed files with 147 additions and 77 deletions
+140
View File
@@ -338,3 +338,143 @@ where
}
path
}
/// Asserts that the two graphs are isomorphic.
///
/// `vertices` must contain all vertices of the `original` graph, and `vertex_map` must for any
/// vertex in the `original` graph return the corresponding vertex in the `other` graph.
///
/// `edges` must contain all edges and their endpoints of the `original` graph, and `edge_map` must
/// for any edge in the `original` graph return the corresponding edge in the `other` graph.
pub fn assert_isomorphic<G: GraphTopology, H: GraphTopology>(
original: &G,
other: &H,
vertices: &[G::Vertex],
vertex_map: impl Fn(G::Vertex) -> H::Vertex,
edges: &[(G::Edge, G::Vertex, G::Vertex)],
edge_map: impl Fn(G::Edge) -> H::Edge,
) where
G::Vertex: Debug,
G::Edge: Debug,
H::Vertex: Debug,
H::Edge: Debug,
{
assert_eq!(
original.vertex_count(),
vertices.len(),
"precondition: all vertices of the original must be provided"
);
assert_eq!(
original.edge_count(),
edges.len(),
"precondition: all edges of the original must be provided"
);
assert_eq!(
other.vertex_count(),
original.vertex_count(),
"other graph should have the same vertex count as the original"
);
assert_eq!(
other.edge_count(),
original.edge_count(),
"other graph should have the same edge count as the original"
);
for &v in vertices.iter() {
assert_eq!(
other.degree(vertex_map(v)),
original.degree(v),
"vertex {v:?} should have the same degree in the other graph as in the original"
);
let mut expected: Vec<_> = original.incidences(v).collect();
for incidence in other.incidences(vertex_map(v)) {
let pos = expected
.iter()
.position(|x| {
vertex_map(x.vertex) == incidence.vertex && edge_map(x.edge) == incidence.edge
})
.expect(&format!(
"unexpected incidence {incidence:?} of vertex {v:?} in the other graph"
));
expected.swap_remove(pos);
}
assert!(
expected.is_empty(),
"expected incidences {expected:?} of vertex {v:?} are missing in the other graph"
);
}
for &(e, u, v) in edges.iter() {
assert_eq!(
other.incident_vertices(edge_map(e)),
(vertex_map(u), vertex_map(v)),
"edge {e:?} should have the same incident vertices in the other graph as in the original"
);
}
}
/// Asserts that mutation of the two graphs happens independently.
///
/// `vertices` must contain all vertices of the `original` graph, which must not be empty, and
/// `vertex_map` must for any vertex in the `original` graph return the corresponding vertex in the
/// `other` graph.
pub fn assert_mutation_independent<G: GraphTopologyAddition, H: GraphTopologyAddition>(
original: &mut G,
other: &mut H,
vertices: &[G::Vertex],
vertex_map: impl Fn(G::Vertex) -> H::Vertex,
) {
let edge_count = original.edge_count();
assert_eq!(
original.vertex_count(),
vertices.len(),
"precondition: all vertices of the original must be provided"
);
assert!(
!vertices.is_empty(),
"precondition: at least one vertex must be provided"
);
let u = other.add_vertex();
other.add_edge(vertex_map(vertices[0]), u);
assert_eq!(
other.vertex_count(),
vertices.len() + 1,
"precondition: vertex count of other graph"
);
assert_eq!(
other.edge_count(),
edge_count + 1,
"precondition: edge count of other graph"
);
assert_eq!(
original.vertex_count(),
vertices.len(),
"mutating the other graph should not affect the vertex count of the original"
);
assert_eq!(
original.edge_count(),
edge_count,
"mutating the other graph should not affect the edge count of the original"
);
let v = original.add_vertex();
original.add_edge(vertices[0], v);
assert_eq!(
original.vertex_count(),
vertices.len() + 1,
"precondition: vertex count of original"
);
assert_eq!(
original.edge_count(),
edge_count + 1,
"precondition: edge count of original"
);
assert_eq!(
other.vertex_count(),
vertices.len() + 1,
"mutating the original should not affect the vertex count of the other graph"
);
assert_eq!(
other.edge_count(),
edge_count + 1,
"mutating the original should not affect the edge count of the other graph"
);
}
@@ -1,3 +1,4 @@
use crate::testing::fixtures;
use crate::testing::fixtures::MakeTestGraph;
use crate::traits::GraphTopologyAddition;
@@ -6,7 +7,7 @@ use crate::traits::GraphTopologyAddition;
macro_rules! graph_topology_addition_clone_tests {
($T:ty) => {
$crate::graph_topology_addition_clone_tests!(@wrap $T,
clone_independent,
clone_mutation_independent,
);
};
@@ -20,37 +21,8 @@ macro_rules! graph_topology_addition_clone_tests {
};
}
pub fn clone_independent<G: Clone + GraphTopologyAddition>() {
let (mut graph, [u, v], _) = G::single_edge();
pub fn clone_mutation_independent<G: Clone + GraphTopologyAddition>() {
let (mut graph, vertices, _) = G::single_edge();
let mut clone = graph.clone();
let w = clone.add_vertex();
clone.add_edge(u, w);
assert_eq!(clone.vertex_count(), 3, "precondition: vertex count of clone");
assert_eq!(clone.edge_count(), 2, "precondition: edge count of clone");
assert_eq!(
graph.vertex_count(),
2,
"mutating the clone should not affect the vertex count of the original"
);
assert_eq!(
graph.edge_count(),
1,
"mutating the clone should not affect the edge count of the original"
);
let x = graph.add_vertex();
graph.add_edge(v, x);
assert_eq!(graph.vertex_count(), 3, "precondition: vertex count of original");
assert_eq!(graph.edge_count(), 2, "precondition: edge count of original");
assert_eq!(
clone.vertex_count(),
3,
"mutating the original should not affect the vertex count of the clone"
);
assert_eq!(
clone.edge_count(),
2,
"mutating the original should not affect the edge count of the clone"
);
fixtures::assert_mutation_independent(&mut graph, &mut clone, &vertices, |v| v);
}
+2 -44
View File
@@ -1,5 +1,6 @@
use std::fmt::Debug;
use crate::testing::fixtures;
use crate::testing::fixtures::MakeTestGraph;
#[doc(hidden)]
@@ -7,7 +8,6 @@ use crate::testing::fixtures::MakeTestGraph;
macro_rules! graph_topology_clone_tests {
($T:ty) => {
$crate::graph_topology_clone_tests!(@wrap $T,
clone_preserves_vertex_and_edge_count,
clone_handles_interchangeable,
);
};
@@ -22,21 +22,6 @@ macro_rules! graph_topology_clone_tests {
};
}
pub fn clone_preserves_vertex_and_edge_count<G: Clone + MakeTestGraph>() {
let (graph, ..) = G::standard();
let clone = graph.clone();
assert_eq!(
clone.vertex_count(),
graph.vertex_count(),
"clone should have the same vertex count as the original"
);
assert_eq!(
clone.edge_count(),
graph.edge_count(),
"clone should have the same edge count as the original"
);
}
/// Queries the clone using vertex and edge handles obtained from the original graph, and asserts
/// that the results are identical to querying the original with those same handles. This is only
/// meaningful because Vertex and Edge handles are not tied to a particular graph instance.
@@ -47,32 +32,5 @@ where
{
let (graph, vertices, edges, _) = G::standard();
let clone = graph.clone();
for &v in vertices.iter() {
assert_eq!(
clone.degree(v),
graph.degree(v),
"vertex {v:?} should have the same degree in the clone as in the original"
);
let mut expected: Vec<_> = graph.incidences(v).collect();
for incidence in clone.incidences(v) {
let pos = expected
.iter()
.position(|x| *x == incidence)
.expect(&format!(
"unexpected incidence {incidence:?} of vertex {v:?} in the clone"
));
expected.swap_remove(pos);
}
assert!(
expected.is_empty(),
"expected incidences {expected:?} of vertex {v:?} are missing in the clone"
);
}
for &(e, u, v) in edges.iter() {
assert_eq!(
clone.incident_vertices(e),
(u, v),
"edge {e:?} should have the same incident vertices in the clone as in the original"
);
}
fixtures::assert_isomorphic(&graph, &clone, &vertices, |v| v, &edges, |e| e);
}