506 lines
15 KiB
Rust
506 lines
15 KiB
Rust
use std::fmt::Debug;
|
|
|
|
use crate::testing::fixtures::MakeTestGraph;
|
|
use crate::traits::{
|
|
GraphTopology, GraphTopologyAddition, GraphTopologyDeletion, Incidence, IncidenceCursor,
|
|
};
|
|
|
|
#[doc(hidden)]
|
|
#[macro_export]
|
|
macro_rules! graph_topology_deletion_tests {
|
|
($T:ty) => {
|
|
$crate::graph_topology_deletion_tests!(@wrap $T,
|
|
delete_vertex,
|
|
delete_vertex_loop,
|
|
delete_vertex_invalid_index,
|
|
delete_vertex_connected,
|
|
delete_edge,
|
|
delete_edge_loop,
|
|
delete_edge_multiple,
|
|
delete_edge_invalid_index,
|
|
vertices_after_delete,
|
|
incident_vertices_after_delete_edge,
|
|
edges_after_delete,
|
|
incident_edges_after_delete_edge,
|
|
incident_edges_after_delete_vertex,
|
|
incidences_after_delete_vertex,
|
|
incidences_after_delete_edge,
|
|
incidence_cursor_after_delete_vertex,
|
|
incidence_cursor_after_delete_edge,
|
|
);
|
|
};
|
|
|
|
(@wrap $T:ty, $($name:ident),* $(,)?) => {
|
|
$(
|
|
#[test]
|
|
fn $name() {
|
|
$crate::testing::graph_topology_deletion_testing::$name::<$T>();
|
|
}
|
|
)*
|
|
};
|
|
}
|
|
|
|
pub fn delete_vertex<G: GraphTopologyDeletion + GraphTopologyAddition>() {
|
|
let (mut graph, v) = G::single_vertex();
|
|
assert_eq!(
|
|
graph.vertex_count(),
|
|
1,
|
|
"unexpected vertex count before delete"
|
|
);
|
|
graph.delete_vertex(v);
|
|
assert_eq!(
|
|
graph.vertex_count(),
|
|
0,
|
|
"unexpected vertex count after delete"
|
|
);
|
|
}
|
|
|
|
pub fn delete_vertex_loop<G: GraphTopologyDeletion + GraphTopologyAddition>() {
|
|
let (mut graph, v, _) = G::loop_edge();
|
|
assert_eq!(
|
|
graph.vertex_count(),
|
|
1,
|
|
"unexpected vertex count before delete"
|
|
);
|
|
assert_eq!(graph.edge_count(), 1, "unexpected edge count before delete");
|
|
assert!(
|
|
graph.are_adjacent(v, v),
|
|
"expected vertex to be self-adjacent before delete"
|
|
);
|
|
assert_eq!(graph.degree(v), 2, "unexpected vertex degree before delete");
|
|
graph.delete_vertex(v);
|
|
assert_eq!(
|
|
graph.vertex_count(),
|
|
0,
|
|
"unexpected vertex count after delete"
|
|
);
|
|
assert_eq!(graph.edge_count(), 0, "unexpected edge count after delete");
|
|
}
|
|
|
|
pub fn delete_vertex_invalid_index<G: GraphTopologyDeletion + GraphTopologyAddition>() {
|
|
let (mut graph, v) = G::single_vertex();
|
|
graph.delete_vertex(v);
|
|
let result =
|
|
std::panic::catch_unwind(std::panic::AssertUnwindSafe(move || graph.delete_vertex(v)));
|
|
assert!(result.is_err(), "second deletion should panic");
|
|
}
|
|
|
|
pub fn delete_vertex_connected<G: GraphTopologyDeletion + GraphTopologyAddition>()
|
|
where
|
|
G::Vertex: Debug,
|
|
{
|
|
let (mut graph, vertices, _, _) = G::standard();
|
|
assert_eq!(
|
|
graph.vertex_count(),
|
|
10,
|
|
"unexpected vertex count before delete"
|
|
);
|
|
assert_eq!(
|
|
graph.edge_count(),
|
|
18,
|
|
"unexpected edge count before delete"
|
|
);
|
|
graph.delete_vertex(vertices[2]);
|
|
assert_eq!(
|
|
graph.vertex_count(),
|
|
9,
|
|
"unexpected vertex count after delete"
|
|
);
|
|
assert_eq!(graph.edge_count(), 12, "unexpected edge count after delete");
|
|
let expected_edges = [
|
|
(vertices[0], vertices[1]),
|
|
(vertices[1], vertices[3]),
|
|
(vertices[1], vertices[4]),
|
|
(vertices[3], vertices[6]),
|
|
(vertices[4], vertices[4]),
|
|
(vertices[4], vertices[7]),
|
|
(vertices[4], vertices[8]),
|
|
(vertices[5], vertices[9]),
|
|
(vertices[6], vertices[9]),
|
|
(vertices[7], vertices[8]),
|
|
(vertices[7], vertices[9]),
|
|
];
|
|
for (v, u) in expected_edges {
|
|
assert!(
|
|
graph.are_adjacent(v, u),
|
|
"expected {v:?} and {u:?} to be adjacent after delete"
|
|
);
|
|
}
|
|
for v in [vertices[1], vertices[4], vertices[5], vertices[6]] {
|
|
assert!(
|
|
!graph.adjacent_vertices(v).any(|u| u == vertices[2]),
|
|
"unexpected adjacency of {v:?} to deleted vertex {:?}",
|
|
vertices[2]
|
|
);
|
|
}
|
|
}
|
|
|
|
pub fn delete_edge<G: GraphTopologyDeletion + GraphTopologyAddition>() {
|
|
let (mut graph, [v0, v1], e) = G::single_edge();
|
|
assert_eq!(
|
|
graph.vertex_count(),
|
|
2,
|
|
"unexpected vertex count before delete"
|
|
);
|
|
assert_eq!(graph.edge_count(), 1, "unexpected edge count before delete");
|
|
assert!(
|
|
graph.are_adjacent(v0, v1),
|
|
"expected vertices to be adjacent before delete"
|
|
);
|
|
assert_eq!(
|
|
graph.degree(v0),
|
|
1,
|
|
"unexpected vertex degree before delete"
|
|
);
|
|
assert_eq!(
|
|
graph.degree(v1),
|
|
1,
|
|
"unexpected vertex degree before delete"
|
|
);
|
|
graph.delete_edge(e);
|
|
assert_eq!(
|
|
graph.vertex_count(),
|
|
2,
|
|
"unexpected vertex count after delete"
|
|
);
|
|
assert_eq!(graph.edge_count(), 0, "unexpected edge count after delete");
|
|
assert!(
|
|
!graph.are_adjacent(v0, v1),
|
|
"unexpected adjacency after delete"
|
|
);
|
|
assert_eq!(graph.degree(v0), 0, "unexpected vertex degree after delete");
|
|
assert_eq!(graph.degree(v1), 0, "unexpected vertex degree after delete");
|
|
}
|
|
|
|
pub fn delete_edge_loop<G: GraphTopologyDeletion + GraphTopologyAddition>() {
|
|
let (mut graph, v, e) = G::loop_edge();
|
|
assert_eq!(
|
|
graph.vertex_count(),
|
|
1,
|
|
"unexpected vertex count before delete"
|
|
);
|
|
assert_eq!(graph.edge_count(), 1, "unexpected edge count before delete");
|
|
assert!(
|
|
graph.are_adjacent(v, v),
|
|
"expected vertex to be self-adjacent before delete"
|
|
);
|
|
assert_eq!(graph.degree(v), 2, "unexpected vertex degree before delete");
|
|
graph.delete_edge(e);
|
|
assert_eq!(
|
|
graph.vertex_count(),
|
|
1,
|
|
"unexpected vertex count after delete"
|
|
);
|
|
assert_eq!(graph.edge_count(), 0, "unexpected edge count after delete");
|
|
assert!(
|
|
!graph.are_adjacent(v, v),
|
|
"unexpected adjacency after delete"
|
|
);
|
|
assert_eq!(graph.degree(v), 0, "unexpected vertex degree after delete");
|
|
}
|
|
|
|
pub fn delete_edge_multiple<G: GraphTopologyDeletion + GraphTopologyAddition>() {
|
|
const K: usize = 2;
|
|
let (mut graph, [v0, v1], edges) = G::multiple_edges::<K>();
|
|
assert_eq!(
|
|
graph.vertex_count(),
|
|
2,
|
|
"unexpected vertex count before delete"
|
|
);
|
|
assert_eq!(graph.edge_count(), K, "unexpected edge count before delete");
|
|
assert!(
|
|
graph.are_adjacent(v0, v1),
|
|
"expected vertices to be adjacent before delete"
|
|
);
|
|
assert_eq!(
|
|
graph.degree(v0),
|
|
K,
|
|
"unexpected vertex degree before delete"
|
|
);
|
|
assert_eq!(
|
|
graph.degree(v1),
|
|
K,
|
|
"unexpected vertex degree before delete"
|
|
);
|
|
graph.delete_edge(edges[0]);
|
|
assert_eq!(
|
|
graph.vertex_count(),
|
|
2,
|
|
"unexpected vertex count after delete"
|
|
);
|
|
assert_eq!(
|
|
graph.edge_count(),
|
|
K - 1,
|
|
"unexpected edge count after delete"
|
|
);
|
|
assert!(
|
|
graph.are_adjacent(v0, v1),
|
|
"expected vertices to be adjacent after delete"
|
|
);
|
|
assert_eq!(
|
|
graph.degree(v0),
|
|
K - 1,
|
|
"unexpected vertex degree after delete"
|
|
);
|
|
assert_eq!(
|
|
graph.degree(v1),
|
|
K - 1,
|
|
"unexpected vertex degree after delete"
|
|
);
|
|
}
|
|
|
|
pub fn delete_edge_invalid_index<G: GraphTopologyDeletion + GraphTopologyAddition>() {
|
|
let (mut graph, _, e) = G::single_edge();
|
|
graph.delete_edge(e);
|
|
let result =
|
|
std::panic::catch_unwind(std::panic::AssertUnwindSafe(move || graph.delete_edge(e)));
|
|
assert!(result.is_err(), "second deletion should panic");
|
|
}
|
|
|
|
pub fn vertices_after_delete<G: GraphTopologyDeletion + GraphTopologyAddition>()
|
|
where
|
|
G::Vertex: Debug,
|
|
{
|
|
let (mut graph, vertices, _, _) = G::standard();
|
|
let del = vertices[2];
|
|
graph.delete_vertex(del);
|
|
assert_eq!(
|
|
graph.vertex_count(),
|
|
9,
|
|
"unexpected vertex count after delete"
|
|
);
|
|
assert_eq!(
|
|
graph.vertices().count(),
|
|
9,
|
|
"unexpected vertex iterator count after delete"
|
|
);
|
|
for v in graph.vertices() {
|
|
assert_ne!(v, del, "deleted vertex {del:?} appeared in iterator");
|
|
}
|
|
for &v in vertices.iter().filter(|&&u| u != del) {
|
|
assert!(
|
|
graph.vertices().any(|u| u == v),
|
|
"expected vertex {v:?} missing from iterator after delete"
|
|
);
|
|
}
|
|
}
|
|
|
|
pub fn incident_vertices_after_delete_edge<G: GraphTopologyDeletion + GraphTopologyAddition>()
|
|
where
|
|
G::Vertex: Debug,
|
|
G::Edge: Debug,
|
|
{
|
|
let (mut graph, _, edges, _) = G::standard();
|
|
let del = edges[2].0;
|
|
graph.delete_edge(del);
|
|
for &(f, v0, v1) in edges.iter().filter(|&&(f, _, _)| f != del) {
|
|
let (u0, u1) = graph.incident_vertices(f);
|
|
assert!(
|
|
(u0 == v0 && u1 == v1) || (u0 == v1 && u1 == v0),
|
|
"unexpected incident vertices {u0:?} and {u1:?} for edge {f:?} after delete"
|
|
);
|
|
}
|
|
}
|
|
|
|
pub fn edges_after_delete<G: GraphTopologyDeletion + GraphTopologyAddition>()
|
|
where
|
|
G::Edge: Debug,
|
|
{
|
|
let (mut graph, vertices, edges, _) = G::standard();
|
|
graph.delete_vertex(vertices[2]);
|
|
assert_eq!(graph.edge_count(), 12, "unexpected edge count after delete");
|
|
assert_eq!(
|
|
graph.edges().count(),
|
|
12,
|
|
"unexpected edge iterator count after delete"
|
|
);
|
|
for i in [2, 5, 6, 7, 8, 9] {
|
|
assert!(
|
|
!graph.edges().any(|e| e == edges[i].0),
|
|
"deleted edge {:?} appeared in iterator",
|
|
edges[i].0
|
|
);
|
|
}
|
|
for i in [0, 1, 3, 4, 10, 11, 12, 13, 14, 15, 16, 17] {
|
|
assert!(
|
|
graph.edges().any(|e| e == edges[i].0),
|
|
"expected edge {:?} missing from iterator after delete",
|
|
edges[i].0
|
|
);
|
|
}
|
|
}
|
|
|
|
pub fn incident_edges_after_delete_edge<G: GraphTopologyDeletion + GraphTopologyAddition>()
|
|
where
|
|
G::Vertex: Debug,
|
|
G::Edge: Debug,
|
|
{
|
|
let (mut graph, vertices, edges, incidences) = G::standard();
|
|
let del = edges[2].0;
|
|
graph.delete_edge(del);
|
|
assert_eq!(vertices.len(), incidences.len());
|
|
for (&v, incs) in vertices.iter().zip(&incidences) {
|
|
let mut expected = incs
|
|
.iter()
|
|
.filter_map(|x| (x.edge != del).then_some(x.edge))
|
|
.collect();
|
|
assert_incident_edges_after_delete(&graph, v, &mut expected);
|
|
}
|
|
}
|
|
|
|
pub fn incident_edges_after_delete_vertex<G: GraphTopologyDeletion + GraphTopologyAddition>()
|
|
where
|
|
G::Vertex: Debug,
|
|
G::Edge: Debug,
|
|
{
|
|
let (mut graph, vertices, _, incidences) = G::standard();
|
|
let del = vertices[2];
|
|
graph.delete_vertex(del);
|
|
for (&v, incs) in vertices.iter().zip(&incidences).filter(|&(&u, _)| u != del) {
|
|
let mut expected = incs
|
|
.iter()
|
|
.filter_map(|x| (x.vertex != del).then_some(x.edge))
|
|
.collect();
|
|
assert_incident_edges_after_delete(&graph, v, &mut expected);
|
|
}
|
|
}
|
|
|
|
pub fn incidences_after_delete_vertex<G: GraphTopologyDeletion + GraphTopologyAddition>()
|
|
where
|
|
G::Vertex: Debug,
|
|
G::Edge: Debug,
|
|
{
|
|
let (mut graph, vertices, _, incidences) = G::standard();
|
|
let del = vertices[2];
|
|
graph.delete_vertex(del);
|
|
for (&v, incs) in vertices.iter().zip(&incidences).filter(|&(&u, _)| u != del) {
|
|
let remaining = incs.iter().filter(|x| x.vertex != del).cloned().collect();
|
|
assert_vertex_incidences(&graph, v, remaining);
|
|
}
|
|
}
|
|
|
|
pub fn incidences_after_delete_edge<G: GraphTopologyDeletion + GraphTopologyAddition>()
|
|
where
|
|
G::Vertex: Debug,
|
|
G::Edge: Debug,
|
|
{
|
|
let (mut graph, vertices, edges, incidences) = G::standard();
|
|
let del = edges[2].0;
|
|
graph.delete_edge(del);
|
|
assert_eq!(vertices.len(), incidences.len());
|
|
for (&v, incs) in vertices.iter().zip(&incidences) {
|
|
let remaining = incs.iter().filter(|x| x.edge != del).cloned().collect();
|
|
assert_vertex_incidences(&graph, v, remaining);
|
|
}
|
|
}
|
|
|
|
pub fn incidence_cursor_after_delete_vertex<G: GraphTopologyDeletion + GraphTopologyAddition>()
|
|
where
|
|
G::Vertex: Debug,
|
|
G::Edge: Debug,
|
|
{
|
|
let (mut graph, vertices, _, incidences) = G::standard();
|
|
let del = vertices[2];
|
|
graph.delete_vertex(del);
|
|
for (&v, incs) in vertices.iter().zip(&incidences).filter(|&(&u, _)| u != del) {
|
|
let remaining = incs.iter().filter(|x| x.vertex != del).cloned().collect();
|
|
assert_vertex_incidence_cursor(&graph, v, remaining);
|
|
}
|
|
}
|
|
|
|
pub fn incidence_cursor_after_delete_edge<G: GraphTopologyDeletion + GraphTopologyAddition>()
|
|
where
|
|
G::Vertex: Debug,
|
|
G::Edge: Debug,
|
|
{
|
|
let (mut graph, vertices, edges, incidences) = G::standard();
|
|
let del = edges[2].0;
|
|
graph.delete_edge(del);
|
|
assert_eq!(vertices.len(), incidences.len());
|
|
for (&v, incs) in vertices.iter().zip(&incidences) {
|
|
let remaining = incs.iter().filter(|x| x.edge != del).cloned().collect();
|
|
assert_vertex_incidence_cursor(&graph, v, remaining);
|
|
}
|
|
}
|
|
|
|
fn assert_vertex_incidences<G: GraphTopology>(
|
|
graph: &G,
|
|
v: G::Vertex,
|
|
mut expected: Vec<Incidence<G::Vertex, G::Edge>>,
|
|
) where
|
|
G::Vertex: Debug,
|
|
G::Edge: Debug,
|
|
{
|
|
assert_eq!(
|
|
graph.incidences(v).count(),
|
|
expected.len(),
|
|
"unexpected incidence count for vertex {v:?} after delete"
|
|
);
|
|
for incidence in graph.incidences(v) {
|
|
let pos = expected
|
|
.iter()
|
|
.position(|&x| x == incidence)
|
|
.unwrap_or_else(|| {
|
|
panic!(
|
|
"unexpected incidence {incidence:?} of vertex {v:?} from iterator after delete"
|
|
)
|
|
});
|
|
expected.swap_remove(pos);
|
|
}
|
|
assert!(
|
|
expected.is_empty(),
|
|
"expected incidences {expected:?} of vertex {v:?} not matched by iterator after delete"
|
|
);
|
|
}
|
|
|
|
fn assert_vertex_incidence_cursor<G: GraphTopology>(
|
|
graph: &G,
|
|
v: G::Vertex,
|
|
mut expected: Vec<Incidence<G::Vertex, G::Edge>>,
|
|
) where
|
|
G::Vertex: Debug,
|
|
G::Edge: Debug,
|
|
{
|
|
let mut cursor = graph.incidence_cursor(v);
|
|
while let Some(incidence) = cursor.next(graph) {
|
|
let pos = expected
|
|
.iter()
|
|
.position(|&x| x == incidence)
|
|
.unwrap_or_else(|| {
|
|
panic!(
|
|
"unexpected incidence {incidence:?} of vertex {v:?} from cursor after delete"
|
|
)
|
|
});
|
|
expected.swap_remove(pos);
|
|
}
|
|
assert!(
|
|
expected.is_empty(),
|
|
"expected incidences {expected:?} of vertex {v:?} not matched by cursor after delete"
|
|
);
|
|
}
|
|
|
|
fn assert_incident_edges_after_delete<G: GraphTopology>(
|
|
graph: &G,
|
|
v: G::Vertex,
|
|
expected: &mut Vec<G::Edge>,
|
|
) where
|
|
G::Vertex: Debug,
|
|
G::Edge: Debug,
|
|
{
|
|
assert_eq!(
|
|
graph.incident_edges(v).count(),
|
|
expected.len(),
|
|
"unexpected incident edge count for vertex {v:?} after delete"
|
|
);
|
|
for e in graph.incident_edges(v) {
|
|
let pos = expected.iter().position(|&f| f == e).unwrap_or_else(|| {
|
|
panic!("unexpected incident edge {e:?} of vertex {v:?} after delete")
|
|
});
|
|
expected.swap_remove(pos);
|
|
}
|
|
assert!(
|
|
expected.is_empty(),
|
|
"expected incident edges {expected:?} of vertex {v:?} not matched after delete"
|
|
);
|
|
}
|