diff --git a/src/models/frozen_graph.rs b/src/models/frozen_graph.rs index b2980dc..0b94f47 100644 --- a/src/models/frozen_graph.rs +++ b/src/models/frozen_graph.rs @@ -248,3 +248,70 @@ impl GraphTopology for FrozenGraph { } } } + +#[cfg(test)] +mod trait_tests { + use super::FrozenGraph; + + crate::graph_topology_tests!(FrozenGraph); +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::testing::fixtures::MakeTestGraph; + use crate::traits::GraphTopology; + + #[test] + fn incident_vertices_opposite_handle() { + let (graph, [v0, v1], e) = FrozenGraph::single_edge(); + let f = Edge(graph.incidences[e.0].opposite); + 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:?}" + ); + } + + #[test] + fn csr_sentinel_and_offsets_standard() { + let (graph, ..) = FrozenGraph::standard(); + assert_eq!( + *graph.vertices.last().unwrap(), + graph.incidences.len(), + "sentinel should equal total number of incidences" + ); + assert!( + graph.vertices.windows(2).all(|w| w[0] <= w[1]), + "incidence offsets of vertices should be monotonically non-decreasing" + ); + } + + #[test] + fn csr_sentinel_and_offsets_empty() { + let graph = FrozenGraph::empty(); + assert_eq!( + graph.vertices, + vec![0], + "empty graph should have a single sentinel entry of 0" + ); + assert!( + graph.incidences.is_empty(), + "empty graph should have no incidences" + ); + } + + #[test] + fn opposite_symmetry() { + let (graph, ..) = FrozenGraph::standard(); + for i in 0..graph.incidences.len() { + let opp = graph.incidences[i].opposite; + assert_ne!(opp, i, "incidence should not equal its own opposite"); + assert_eq!( + graph.incidences[opp].opposite, i, + "opposite of opposite of incidence should equal the incidence" + ); + } + } +} diff --git a/src/testing/fixtures.rs b/src/testing/fixtures.rs index eaa259c..3b02869 100644 --- a/src/testing/fixtures.rs +++ b/src/testing/fixtures.rs @@ -1,7 +1,9 @@ -use crate::maps::ElementMap; -use crate::traits::{GraphTopology, GraphTopologyAddition, Incidence}; use std::fmt::Debug; +use crate::maps::ElementMap; +use crate::models::{AppendGraph, FrozenGraph}; +use crate::traits::{GraphTopology, GraphTopologyAddition, Incidence}; + pub trait MakeTestGraph: GraphTopology + Sized { fn standard() -> ( Self, @@ -165,6 +167,89 @@ impl MakeTestGraph for G { } } +impl MakeTestGraph for FrozenGraph { + fn standard() -> ( + Self, + [Self::Vertex; 10], + [(Self::Edge, Self::Vertex, Self::Vertex); 18], + [Vec>; 10], + ) { + let (build, vertices, edges, incidences) = AppendGraph::standard(); + let (graph, vertex_map, edge_map) = FrozenGraph::from_graph(&build); + ( + graph, + vertices.map(|v| vertex_map[v]), + edges.map(|(e, u, v)| (edge_map[e], vertex_map[u], vertex_map[v])), + incidences.map(|vec| { + vec.iter() + .map(|i| Incidence { + vertex: vertex_map[i.vertex], + edge: edge_map[i.edge], + }) + .collect() + }), + ) + } + + fn empty() -> Self { + FrozenGraph::from(&AppendGraph::empty()) + } + + fn single_vertex() -> (Self, Self::Vertex) { + let (build, v) = AppendGraph::single_vertex(); + let (graph, vertex_map, _) = FrozenGraph::from_graph(&build); + (graph, vertex_map[v]) + } + + fn single_edge() -> (Self, [Self::Vertex; 2], Self::Edge) { + let (build, vertices, e) = AppendGraph::single_edge(); + let (graph, vertex_map, edge_map) = FrozenGraph::from_graph(&build); + (graph, vertices.map(|v| vertex_map[v]), edge_map[e]) + } + + fn disconnected() -> (Self, [Self::Vertex; 3]) { + let (build, vertices) = AppendGraph::disconnected(); + let (graph, vertex_map, _) = FrozenGraph::from_graph(&build); + (graph, vertices.map(|v| vertex_map[v])) + } + + fn loop_edge() -> (Self, Self::Vertex, Self::Edge) { + let (build, v, e) = AppendGraph::loop_edge(); + let (graph, vertex_map, edge_map) = FrozenGraph::from_graph(&build); + (graph, vertex_map[v], edge_map[e]) + } + + fn multiple_edges() -> (Self, [Self::Vertex; 2], [Self::Edge; K]) { + let (build, vertices, edges) = AppendGraph::multiple_edges(); + let (graph, vertex_map, edge_map) = FrozenGraph::from_graph(&build); + ( + graph, + vertices.map(|v| vertex_map[v]), + edges.map(|e| edge_map[e]), + ) + } + + fn two_edge_path() -> (Self, [Self::Vertex; 3], [Self::Edge; 2]) { + let (build, vertices, edges) = AppendGraph::two_edge_path(); + let (graph, vertex_map, edge_map) = FrozenGraph::from_graph(&build); + ( + graph, + vertices.map(|v| vertex_map[v]), + edges.map(|e| edge_map[e]), + ) + } + + fn two_edge_path_with_loops() -> (Self, [Self::Vertex; 3], [Self::Edge; 4]) { + let (build, vertices, edges) = AppendGraph::two_edge_path_with_loops(); + let (graph, vertex_map, edge_map) = FrozenGraph::from_graph(&build); + ( + graph, + vertices.map(|v| vertex_map[v]), + edges.map(|e| edge_map[e]), + ) + } +} + pub fn assert_standard_unweighted_distances_v0(actual_for: F, vertices: &[V]) where V: Debug + Copy, diff --git a/tests/bfs.rs b/tests/bfs.rs index 90f63e2..53a1dfe 100644 --- a/tests/bfs.rs +++ b/tests/bfs.rs @@ -41,6 +41,12 @@ macro_rules! bfs_tests { }; } +mod frozen_graph { + use grapherity::models::FrozenGraph; + + bfs_tests!(FrozenGraph); +} + mod append_graph { use grapherity::models::AppendGraph; diff --git a/tests/dfs.rs b/tests/dfs.rs index 6e35afc..d49bbcb 100644 --- a/tests/dfs.rs +++ b/tests/dfs.rs @@ -44,6 +44,12 @@ macro_rules! dfs_tests { }; } +mod frozen_graph { + use grapherity::models::FrozenGraph; + + dfs_tests!(FrozenGraph); +} + mod append_graph { use grapherity::models::AppendGraph; diff --git a/tests/dijkstra.rs b/tests/dijkstra.rs index f7c5423..802098a 100644 --- a/tests/dijkstra.rs +++ b/tests/dijkstra.rs @@ -35,6 +35,12 @@ macro_rules! dijkstra_tests { }; } +mod frozen_graph { + use grapherity::models::FrozenGraph; + + dijkstra_tests!(FrozenGraph); +} + mod append_graph { use grapherity::models::AppendGraph;