use crate::{
BoundedGraph, EdgeBounds, ImmutableEdgeBounds, SimpleEdgeBounds, SymmetricFixedEdgeCount,
};
use petgraph::data::DataMapMut;
use petgraph::graph::DefaultIx;
#[derive(Debug, Clone)]
struct MutableLimitNode {
max_edges: usize, data: String, }
impl EdgeBounds for MutableLimitNode {
fn max_incoming_edges(&self) -> DefaultIx {
self.max_edges as DefaultIx
}
fn max_outgoing_edges(&self) -> DefaultIx {
self.max_edges as DefaultIx
}
}
impl SimpleEdgeBounds for MutableLimitNode {}
#[test]
fn test_symmetric_fixed_edge_count_mutation_safety() {
let mut graph = BoundedGraph::<SymmetricFixedEdgeCount<2, i32>, ()>::new();
let n1 = graph.add_node(SymmetricFixedEdgeCount::new(100));
let n2 = graph.add_node(SymmetricFixedEdgeCount::new(200));
assert_eq!(graph[n1].data, 100);
assert_eq!(graph[n2].data, 200);
assert!(graph.add_edge(n1, n2, ()).is_ok());
assert!(graph.add_edge(n1, n2, ()).is_ok());
let result = graph.add_edge(n1, n2, ());
assert!(result.is_err());
if let Some(node) = DataMapMut::node_weight_mut(&mut graph, n1) {
node.data = 999;
}
assert_eq!(graph[n1].data, 999);
let result = graph.add_edge(n1, n2, ());
assert!(
result.is_err(),
"SymmetricFixedEdgeCount is SAFE: Mutation didn't break limits!"
);
}
#[test]
fn test_mutable_limit_node_protected() {
let mut graph = BoundedGraph::<MutableLimitNode, ()>::new();
let n1 = graph.add_node(MutableLimitNode {
max_edges: 2,
data: "Node1".to_string(),
});
let n2 = graph.add_node(MutableLimitNode {
max_edges: 2,
data: "Node2".to_string(),
});
assert_eq!(graph[n1].data, "Node1");
assert_eq!(graph[n2].data, "Node2");
assert_eq!(graph[n1].max_edges, 2);
assert!(graph.add_edge(n1, n2, ()).is_ok());
assert!(graph.add_edge(n1, n2, ()).is_ok());
let result = graph.add_edge(n1, n2, ());
assert!(result.is_err());
let data_copy = graph[n1].data.clone();
assert_eq!(data_copy, "Node1");
let result = graph.add_edge(n1, n2, ());
assert!(result.is_err());
}
#[derive(Debug, Clone)]
struct SafeNodeWithData<const MAX_IN: usize, const MAX_OUT: usize> {
label: String,
value: i32,
}
impl<const MAX_IN: usize, const MAX_OUT: usize> EdgeBounds for SafeNodeWithData<MAX_IN, MAX_OUT> {
fn max_incoming_edges(&self) -> DefaultIx {
MAX_IN as DefaultIx
}
fn max_outgoing_edges(&self) -> DefaultIx {
MAX_OUT as DefaultIx
}
}
impl<const MAX_IN: usize, const MAX_OUT: usize> SimpleEdgeBounds
for SafeNodeWithData<MAX_IN, MAX_OUT>
{
}
impl<const MAX_IN: usize, const MAX_OUT: usize> ImmutableEdgeBounds
for SafeNodeWithData<MAX_IN, MAX_OUT>
{
}
#[test]
fn test_safe_mutation_with_immutable_edge_bounds() {
let mut graph = BoundedGraph::<SafeNodeWithData<2, 3>, ()>::new();
let n1 = graph.add_node(SafeNodeWithData {
label: "Node A".to_string(),
value: 100,
});
let n2 = graph.add_node(SafeNodeWithData {
label: "Node B".to_string(),
value: 200,
});
assert_eq!(graph[n1].label, "Node A");
assert_eq!(graph[n1].value, 100);
assert_eq!(graph[n2].label, "Node B");
assert_eq!(graph[n2].value, 200);
assert!(graph.add_edge(n1, n2, ()).is_ok());
assert!(graph.add_edge(n1, n2, ()).is_ok());
if let Some(node) = DataMapMut::node_weight_mut(&mut graph, n1) {
node.label = "Node A (modified)".to_string();
node.value = 999;
}
assert_eq!(graph[n1].label, "Node A (modified)");
assert_eq!(graph[n1].value, 999);
let result = graph.add_edge(n1, n2, ());
assert!(result.is_err());
let n3 = graph.add_node(SafeNodeWithData {
label: "Node C".to_string(),
value: 300,
});
assert!(graph.add_edge(n1, n3, ()).is_ok());
let result = graph.add_edge(n1, n3, ());
assert!(result.is_err());
}
#[test]
fn test_data_mutation_preserves_edge_limits() {
#[derive(Debug, Clone, PartialEq)]
struct NodeData {
count: i32,
name: String,
}
let mut graph = BoundedGraph::<SymmetricFixedEdgeCount<2, NodeData>, ()>::new();
let n1 = graph.add_node(SymmetricFixedEdgeCount::new(NodeData {
count: 0,
name: "test".to_string(),
}));
let n2 = graph.add_node(SymmetricFixedEdgeCount::new(NodeData {
count: 10,
name: "node2".to_string(),
}));
assert!(graph.add_edge(n1, n2, ()).is_ok());
assert!(graph.add_edge(n1, n2, ()).is_ok());
if let Some(node) = DataMapMut::node_weight_mut(&mut graph, n1) {
node.count += 100;
node.name = "mutated".to_string();
}
assert_eq!(graph[n1].count, 100);
assert_eq!(graph[n1].name, "mutated");
let result = graph.add_edge(n1, n2, ());
assert!(result.is_err());
}
#[test]
fn test_deref_mut_with_immutable_edge_bounds() {
use std::ops::DerefMut;
let mut graph = BoundedGraph::<SymmetricFixedEdgeCount<2, String>, ()>::new();
let n1 = graph.add_node(SymmetricFixedEdgeCount::new("Node1".to_string()));
let n2 = graph.add_node(SymmetricFixedEdgeCount::new("Node2".to_string()));
assert!(graph.add_edge(n1, n2, ()).is_ok());
assert!(graph.add_edge(n1, n2, ()).is_ok());
assert!(graph.add_edge(n1, n2, ()).is_err());
let underlying_graph = graph.deref_mut();
assert_eq!(underlying_graph.edge_count(), 2);
if let Some(node_weight) = underlying_graph.node_weight_mut(n1) {
node_weight.data = "Modified".to_string();
}
assert_eq!(graph[n1].data, "Modified");
assert!(graph.add_edge(n1, n2, ()).is_err());
}
#[test]
fn test_deref_mut_unavailable_for_mutable_bounds() {
let mut graph = BoundedGraph::<MutableLimitNode, ()>::new();
let n1 = graph.add_node(MutableLimitNode {
max_edges: 2,
data: "Node1".to_string(),
});
let _immutable_ref = &*graph;
assert_eq!(_immutable_ref.node_count(), 1);
assert_eq!(graph[n1].data, "Node1");
}