Merge branch 'main' into release-0.3.0

This commit is contained in:
2026-07-31 10:16:03 +02:00
12 changed files with 804 additions and 67 deletions
+197 -15
View File
@@ -326,7 +326,7 @@ macro_rules! graph_topology_tests {
assert_eq!(
vertices.iter().filter(|&x| *x == v).count(),
1,
"unexpected vertex {v:?} from the iterator"
"unexpected vertex {v:?} from iterator"
);
}
}
@@ -367,7 +367,7 @@ macro_rules! graph_topology_tests {
.iter()
.position(|w| *w == v)
.expect(&format!(
"unexpected adjacent vertex {v:?} of {:?} from the iterator",
"unexpected adjacent vertex {v:?} of {:?} from iterator",
vertices[4]
));
expected_adjacency.swap_remove(i);
@@ -375,7 +375,7 @@ macro_rules! graph_topology_tests {
assert_eq!(
expected_adjacency.len(),
0,
"expected adjacent vertices {:?} of {:?} were not matched by the iterator",
"expected adjacent vertices {:?} of {:?} were not matched by iterator",
expected_adjacency,
vertices[4]
);
@@ -512,7 +512,7 @@ macro_rules! graph_topology_tests {
assert_eq!(
edges.iter().filter(|&&(f, _, _)| f == e).count(),
1,
"unexpected edge {e:?} from the iterator"
"unexpected edge {e:?} from iterator"
);
}
}
@@ -546,14 +546,14 @@ macro_rules! graph_topology_tests {
.iter()
.position(|f| *f == e)
.expect(&format!(
"unexpected incident edge {e:?} of vertex {:?} from the iterator",
"unexpected incident edge {e:?} of vertex {:?} from iterator",
vertices[i]
));
expected.swap_remove(pos);
}
assert!(
expected.is_empty(),
"expected incident edges {:?} of vertex {:?} were not matched by the iterator",
"expected incident edges {:?} of vertex {:?} were not matched by iterator",
expected,
vertices[i]
);
@@ -600,7 +600,7 @@ macro_rules! graph_topology_tests {
}
assert!(
expected.is_empty(),
"expected incident edges {:?} of vertex {:?} were not matched by the iterator",
"expected incident edges {:?} of vertex {:?} were not matched by iterator",
expected,
vertices[i]
);
@@ -636,14 +636,14 @@ macro_rules! graph_topology_tests {
.iter()
.position(|(v, e)| *v == incidence.0 && *e == incidence.1)
.expect(&format!(
"unexpected incidence {incidence:?} of vertex {:?} from the iterator",
"unexpected incidence {incidence:?} of vertex {:?} from iterator",
vertices[i]
));
expected.swap_remove(pos);
}
assert!(
expected.is_empty(),
"expected incidences {:?} of vertex {:?} were not matched by the iterator",
"expected incidences {:?} of vertex {:?} were not matched by iterator",
expected,
vertices[i]
);
@@ -738,6 +738,131 @@ macro_rules! graph_topology_tests {
}
}
#[test]
fn incidence_cursor_empty() {
use $crate::traits::{GraphTopology, IncidenceCursor};
let mut graph = <$T>::new();
let v = graph.add_vertex();
let mut cursor = graph.incidence_cursor(v);
assert_eq!(
cursor.next(&graph),
None,
"incidence cursor of vertex with degree 0 should immediately be exhausted"
);
}
#[test]
fn incidence_cursor() {
use $crate::traits::{GraphTopology, IncidenceCursor};
let (graph, vertices, _, incidences) = make_test_graph();
for i in 0..10 {
let mut expected = incidences[i].clone();
let mut cursor = graph.incidence_cursor(vertices[i]);
while let Some(incidence) = cursor.next(&graph) {
let pos = expected
.iter()
.position(|(v, e)| *v == incidence.0 && *e == incidence.1)
.expect(&format!(
"unexpected incidence {incidence:?} of vertex {:?} from cursor",
vertices[i]
));
expected.swap_remove(pos);
}
assert!(
expected.is_empty(),
"expected incidences {:?} of vertex {:?} were not matched by cursor",
expected,
vertices[i]
);
}
}
#[test]
fn incidence_cursor_loop_edge() {
use $crate::traits::{GraphTopology, IncidenceCursor};
let mut graph = <$T>::new();
let v = graph.add_vertex();
let e = graph.add_edge(v, v);
let mut cursor = graph.incidence_cursor(v);
assert_eq!(
cursor.next(&graph),
Some((v, e)),
"vertex should be adjacent to itself"
);
assert_eq!(
cursor.next(&graph),
Some((v, e)),
"vertex should be adjacent to itself twice"
);
assert_eq!(
cursor.next(&graph),
None,
"too many incidences from cursor"
);
}
#[test]
fn incidence_cursor_multiple_edges() {
use $crate::traits::{GraphTopology, IncidenceCursor};
let k = 3;
let mut graph = <$T>::new();
let vertices = [graph.add_vertex(), graph.add_vertex()];
let mut edges = Vec::new();
for _ in 0..k {
edges.push(graph.add_edge(vertices[0], vertices[1]));
}
for i in 0..2 {
let mut cursor = graph.incidence_cursor(vertices[i]);
for j in 0..k {
let current = cursor.next(&graph).expect(&format!(
"incidence {j} missing, expected {k} incidences for vertex {:?}",
vertices[i]
));
assert_eq!(
current.0,
vertices[1 - i],
"unexpected adjacent vertex of vertex {:?} in incidence {j}",
vertices[i]
);
assert_eq!(
edges.iter().filter(|e| **e == current.1).count(),
1,
"unexpected incident edge {:?} of vertex {:?}",
current.1,
vertices[i],
);
}
assert_eq!(
cursor.next(&graph),
None,
"too many incidences of {:?} from cursor",
vertices[i]
);
}
}
#[test]
fn incidence_cursor_copy() {
use $crate::traits::{GraphTopology, IncidenceCursor};
// Constructs a graph with two vertices connected to `v`.
let mut graph = <$T>::new();
let v = graph.add_vertex();
for _ in 0..2 {
let u = graph.add_vertex();
graph.add_edge(u, v);
}
let mut c1 = graph.incidence_cursor(v);
assert!(c1.next(&graph).is_some(), "expected first incidence");
// Copies cursor mid-traversal.
let mut c2 = c1;
// Continues iteration with original cursor.
assert!(c1.next(&graph).is_some(), "expected second incidence from original cursor");
assert!(c1.next(&graph).is_none(), "expected original cursor to be exhausted");
// Replays from the copy point with the copied cursor.
assert!(c2.next(&graph).is_some(), "expected second incidence from copied cursor");
assert!(c2.next(&graph).is_none(), "expected copied cursor to be exhausted");
}
#[test]
fn incident_vertices_incidences_consistency() {
use $crate::traits::GraphTopology;
@@ -1131,8 +1256,7 @@ macro_rules! graph_topology_deletion_tests {
for i in 0..10 {
let mut expected: Vec<_> = incidences[i]
.iter()
.filter(|(_, e)| *e != edges[2].0)
.map(|(_, e)| *e)
.filter_map(|(_, e)| (*e != edges[2].0).then_some(*e))
.collect();
assert_eq!(
graph.incident_edges(vertices[i]).count(),
@@ -1165,8 +1289,7 @@ macro_rules! graph_topology_deletion_tests {
for i in [0, 1, 3, 4, 5, 6, 7, 8, 9] {
let mut expected: Vec<_> = incidences[i]
.iter()
.filter(|(v, _)| *v != vertices[2])
.map(|(_, e)| *e)
.filter_map(|(v, e)| (*v != vertices[2]).then_some(*e))
.collect();
assert_eq!(
graph.incident_edges(vertices[i]).count(),
@@ -1241,14 +1364,73 @@ macro_rules! graph_topology_deletion_tests {
.iter()
.position(|(u, e)| *u == incidence.0 && *e == incidence.1)
.expect(&format!(
"unexpected incidence {incidence:?} of vertex {:?} after delete",
"unexpected incidence {incidence:?} of vertex {:?} from iterator after delete",
v
));
expected.swap_remove(pos);
}
assert!(
expected.is_empty(),
"expected incidences {:?} of vertex {:?} not matched after delete",
"expected incidences {:?} of vertex {:?} not matched by iterator after delete",
expected,
v
);
}
#[test]
fn incidence_cursor_after_delete_vertex() {
use $crate::traits::GraphTopologyDeletion;
let (mut graph, vertices, _, incidences) = make_test_graph();
graph.delete_vertex(vertices[2]);
for i in [0, 1, 3, 4, 5, 6, 7, 8, 9] {
let remaining = incidences[i]
.iter()
.filter(|(v, _)| *v != vertices[2])
.cloned()
.collect();
assert_vertex_incidence_cursor(&graph, vertices[i], remaining);
}
}
#[test]
fn incidence_cursor_after_delete_edge() {
use $crate::traits::GraphTopologyDeletion;
let (mut graph, vertices, edges, incidences) = make_test_graph();
// Deletes the edge from vertices[1] to vertices[2].
graph.delete_edge(edges[2].0);
for i in 0..10 {
let remaining = incidences[i]
.iter()
.filter(|(_, e)| *e != edges[2].0)
.cloned()
.collect();
assert_vertex_incidence_cursor(&graph, vertices[i], remaining);
}
}
fn assert_vertex_incidence_cursor(
graph: &$T,
v: <$T as $crate::traits::GraphTopology>::Vertex,
mut expected: Vec<(
<$T as $crate::traits::GraphTopology>::Vertex,
<$T as $crate::traits::GraphTopology>::Edge,
)>,
) {
use $crate::traits::IncidenceCursor;
let mut cursor = graph.incidence_cursor(v);
while let Some(incidence) = cursor.next(graph) {
let pos = expected
.iter()
.position(|(u, e)| *u == incidence.0 && *e == incidence.1)
.expect(&format!(
"unexpected incidence {incidence:?} of vertex {:?} from cursor after delete",
v
));
expected.swap_remove(pos);
}
assert!(
expected.is_empty(),
"expected incidences {:?} of vertex {:?} not matched by cursor after delete",
expected,
v
);
+5 -21
View File
@@ -48,7 +48,7 @@ macro_rules! entity_map_tests {
}
#[test]
fn extend_to_new_vertices() {
fn expand_to_new_vertices() {
use $crate::traits::GraphTopology;
let mut graph = <$T>::new();
graph.add_vertex();
@@ -59,21 +59,21 @@ macro_rules! entity_map_tests {
}
assert!(
map.capacity() < graph.vertex_capacity(),
"precondition: map is stale before extend"
"precondition: map is stale before expand"
);
map.extend(graph.vertex_capacity());
map.expand(graph.vertex_capacity());
assert_eq!(map.capacity(), graph.vertex_capacity());
}
#[test]
fn extend_does_not_overwrite_existing_values() {
fn expand_does_not_overwrite_existing_values() {
use $crate::traits::GraphTopology;
let mut graph = <$T>::new();
let v = graph.add_vertex();
let mut map = graph.vertex_map(0);
map[v] = 5;
graph.add_vertex();
map.extend(graph.vertex_capacity());
map.expand(graph.vertex_capacity());
assert_eq!(map[v], 5);
}
};
@@ -97,22 +97,6 @@ macro_rules! entity_map_deletion_tests {
assert_eq!(map[v1], 1);
}
#[test]
fn capacity_does_not_shrink_after_delete() {
use $crate::traits::GraphTopology;
use $crate::traits::GraphTopologyDeletion;
let mut graph = <$T>::new();
let v1 = graph.add_vertex();
let v2 = graph.add_vertex();
let mut map = graph.vertex_map(0);
map[v1] = 5;
let capacity_before = graph.vertex_capacity();
graph.delete_vertex(v2);
map.extend(graph.vertex_capacity());
assert_eq!(map.capacity(), capacity_before);
assert_eq!(map[v1], 5);
}
#[test]
fn reused_slot_returns_old_value() {
use $crate::traits::GraphTopology;