From 59d9c2b8a785c488072f217e264508dd30c83e0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20M=C3=BCller?= Date: Thu, 6 Aug 2026 23:06:52 +0200 Subject: [PATCH] Add ElementMap::reserve to replace ElementMap::expand, update tests ElementMap tests are now self contained unit tests, without any graph context. The only exception is the repurposed test "reused_slot_returns_old_value", which is not suited for GraphTopologyDeletion, but actually specific for Graph. --- src/maps.rs | 70 +++++++++++++++++++--- src/models/graph.rs | 23 ++++++++ src/testing.rs | 1 - src/testing/maps_testing.rs | 115 ------------------------------------ tests/maps.rs | 12 ---- 5 files changed, 85 insertions(+), 136 deletions(-) delete mode 100644 src/testing/maps_testing.rs delete mode 100644 tests/maps.rs diff --git a/src/maps.rs b/src/maps.rs index f6ddf0f..ac7dbdc 100644 --- a/src/maps.rs +++ b/src/maps.rs @@ -41,15 +41,13 @@ impl ElementMap { self.data.capacity() } - /// Expands the internal data storage capacity of the map to `capacity`, Does nothing if - /// capacity is already sufficient. + /// Reserves capacity for at least `additional` more data entries. Does nothing if capacity is + /// already sufficient. /// - /// Use this before writing data for new graph entities to avoid incremental growth on the first - /// write to each new entity. - pub fn expand(&mut self, capacity: usize) { - if capacity > self.data.len() { - self.data.resize(capacity, self.default.clone()); - } + /// Use this before writing data for new graph elements to avoid incremental growth on the first + /// write to each new element. + pub fn reserve(&mut self, additional: usize) { + self.data.reserve(additional); } } @@ -75,3 +73,59 @@ impl IndexMut for ElementMap { &mut self.data[i] } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn initial_values_are_default() { + let map = ElementMap::new(42, |e| e, 0); + assert_eq!(map[0], 42, "initial read at 0 should be default"); + assert_eq!(map[5], 42, "initial read at 5 should be default"); + } + + #[test] + fn reserve_increases_capacity_without_initializing() { + let mut map = ElementMap::new(0, |e| e, 0); + let capacity_before = map.capacity(); + map.reserve(capacity_before + 10); + assert!( + map.capacity() > capacity_before, + "expected sufficient capacity increase after reserve" + ); + assert_eq!(map[5], 0, "expected read beyond length to return default"); + } + + #[test] + fn reserve_prevents_reallocation_on_write() { + let mut map = ElementMap::new(0, |e| e, 0); + map.reserve(10); + let capacity_before = map.capacity(); + for i in 0..10 { + map[i] = i as i32; + } + assert_eq!( + map.capacity(), + capacity_before, + "writes within reserved capacity should not reallocate" + ); + } + + #[test] + fn reserve_does_not_overwrite_existing_values() { + let mut map = ElementMap::new(0, |e| e, 0); + map[0] = 5; + let capacity_before = map.capacity(); + map.reserve(capacity_before + 10); + assert_eq!(map[0], 5, "reserve should not change existing data"); + } + + #[test] + fn write_and_read() { + let mut map = ElementMap::new(0, |e| e, 0); + map[1] = 7; + assert_eq!(map[1], 7, "unexpected read value after write"); + assert_eq!(map[0], 0, "initial read should be default"); + } +} diff --git a/src/models/graph.rs b/src/models/graph.rs index c47ec36..e584cf8 100644 --- a/src/models/graph.rs +++ b/src/models/graph.rs @@ -420,4 +420,27 @@ mod tests { graph.delete_edge(f); assert_eq!(graph.edge_count(), 0, "unexpected edge count after delete"); } + + #[test] + fn reused_slot_returns_old_value() { + let mut graph = Graph::new(); + graph.add_vertex(); + let v1 = graph.add_vertex(); + let mut map = graph.vertex_map(0); + map[v1] = 99; + graph.delete_vertex(v1); + let v2 = graph.add_vertex(); + assert_eq!( + v1.arr_idx(), + v2.arr_idx(), + "precondition: new vertex {v1:?} should reuse slot of deleted vertex {v2:?}" + ); + // ElementMap uses raw indices, not vertex identity. A new vertex v2 reusing the slot + // of previously deleted v1 sees the old value. Callers must reinitialize stale slots + // after deletion. + assert_eq!( + map[v2], 99, + "new vertex reusing slot of deleted vertex should return old value" + ); + } } diff --git a/src/testing.rs b/src/testing.rs index 124eff5..d815794 100644 --- a/src/testing.rs +++ b/src/testing.rs @@ -4,4 +4,3 @@ pub(crate) mod bfs_testing; pub(crate) mod dfs_testing; pub(crate) mod dijkstra_testing; pub(crate) mod graph_topology_testing; -pub(crate) mod maps_testing; diff --git a/src/testing/maps_testing.rs b/src/testing/maps_testing.rs deleted file mode 100644 index daa3b67..0000000 --- a/src/testing/maps_testing.rs +++ /dev/null @@ -1,115 +0,0 @@ -#[doc(hidden)] -#[macro_export] -macro_rules! entity_map_tests { - ($T:ty) => { - #[test] - fn initial_values_are_default() { - use $crate::traits::{GraphTopology, GraphTopologyAddition}; - let mut graph = <$T>::new(); - let v1 = graph.add_vertex(); - let v2 = graph.add_vertex(); - let map = graph.vertex_map(42); - assert_eq!(map[v1], 42); - assert_eq!(map[v2], 42); - } - - #[test] - fn write_and_read() { - use $crate::traits::{GraphTopology, GraphTopologyAddition}; - let mut graph = <$T>::new(); - let v1 = graph.add_vertex(); - let v2 = graph.add_vertex(); - let mut map = graph.vertex_map(0); - map[v1] = 7; - assert_eq!(map[v1], 7); - assert_eq!(map[v2], 0); - } - - #[test] - fn lazy_growth_on_read() { - use $crate::traits::{GraphTopology, GraphTopologyAddition}; - let mut graph = <$T>::new(); - graph.add_vertex(); - let map = graph.vertex_map(99); - let v = graph.add_vertex(); - assert_eq!(map[v], 99); - } - - #[test] - fn lazy_growth_on_write() { - use $crate::traits::{GraphTopology, GraphTopologyAddition}; - let mut graph = <$T>::new(); - let v1 = graph.add_vertex(); - let mut map = graph.vertex_map(0); - let v2 = graph.add_vertex(); - map[v2] = 7; - assert_eq!(map[v1], 0); - assert_eq!(map[v2], 7); - } - - #[test] - fn expand_to_new_vertices() { - use $crate::traits::{GraphTopology, GraphTopologyAddition}; - let mut graph = <$T>::new(); - graph.add_vertex(); - let mut map = graph.vertex_map(42); - let capacity_before = map.capacity(); - while graph.vertex_capacity() <= capacity_before { - graph.add_vertex(); - } - assert!( - map.capacity() < graph.vertex_capacity(), - "precondition: map is stale before expand" - ); - map.expand(graph.vertex_capacity()); - assert_eq!(map.capacity(), graph.vertex_capacity()); - } - - #[test] - fn expand_does_not_overwrite_existing_values() { - use $crate::traits::{GraphTopology, GraphTopologyAddition}; - let mut graph = <$T>::new(); - let v = graph.add_vertex(); - let mut map = graph.vertex_map(0); - map[v] = 5; - graph.add_vertex(); - map.expand(graph.vertex_capacity()); - assert_eq!(map[v], 5); - } - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! entity_map_deletion_tests { - ($T:ty) => { - #[test] - fn surviving_vertex_readable_after_delete() { - use $crate::traits::{GraphTopology, GraphTopologyAddition, 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] = 1; - map[v2] = 2; - graph.delete_vertex(v2); - assert_eq!(map[v1], 1); - } - - #[test] - fn reused_slot_returns_old_value() { - use $crate::traits::{GraphTopology, GraphTopologyAddition, GraphTopologyDeletion}; - let mut graph = <$T>::new(); - graph.add_vertex(); - let v1 = graph.add_vertex(); - let mut map = graph.vertex_map(0); - map[v1] = 99; - graph.delete_vertex(v1); - let v2 = graph.add_vertex(); - // EntityMap uses raw indices, not vertex identity. A new vertex v2 reusing the slot - // of previously deleted v1 sees the old value. Callers must reinitialize stale slots - // after deletion. - assert_eq!(map[v2], 99); - } - }; -} diff --git a/tests/maps.rs b/tests/maps.rs deleted file mode 100644 index 73fe87d..0000000 --- a/tests/maps.rs +++ /dev/null @@ -1,12 +0,0 @@ -mod append_graph_entity_map_tests { - use grapherity::models::AppendGraph; - - grapherity::entity_map_tests!(AppendGraph); -} - -mod graph_entity_map_tests { - use grapherity::models::Graph; - - grapherity::entity_map_tests!(Graph); - grapherity::entity_map_deletion_tests!(Graph); -}