use crate::{
BoundedAcyclicDiGraph, BoundedAcyclicGraphError, BoundedAcyclicStableDiGraph,
BoundedGraphError, FixedEdgeCount,
};
use petgraph::graph::NodeIndex;
#[test]
fn test_acyclic_prevents_simple_cycle() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<10>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
assert!(graph.add_edge(n1, n2, ()).is_ok());
match graph.add_edge(n2, n1, ()) {
Err(BoundedAcyclicGraphError::Acyclic(_)) => {} other => panic!("Expected Acyclic error, got: {:?}", other),
}
}
#[test]
fn test_acyclic_prevents_longer_cycle() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<10>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
let n3 = graph.add_node(FixedEdgeCount::empty());
let n4 = graph.add_node(FixedEdgeCount::empty());
assert!(graph.add_edge(n1, n2, ()).is_ok());
assert!(graph.add_edge(n2, n3, ()).is_ok());
assert!(graph.add_edge(n3, n4, ()).is_ok());
assert!(matches!(
graph.add_edge(n4, n1, ()),
Err(BoundedAcyclicGraphError::Acyclic(_))
));
assert!(matches!(
graph.add_edge(n4, n2, ()),
Err(BoundedAcyclicGraphError::Acyclic(_))
));
}
#[test]
fn test_acyclic_allows_valid_edges() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<10>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
let n3 = graph.add_node(FixedEdgeCount::empty());
assert!(graph.add_edge(n1, n2, ()).is_ok());
assert!(graph.add_edge(n1, n3, ()).is_ok());
assert_eq!(graph.edge_count(), 2);
}
#[test]
fn test_bounded_constraint_checked_before_acyclic() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<1>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
let n3 = graph.add_node(FixedEdgeCount::empty());
assert!(graph.add_edge(n1, n2, ()).is_ok());
assert!(matches!(
graph.add_edge(n1, n3, ()),
Err(BoundedAcyclicGraphError::Bounded(
BoundedGraphError::EdgeRejected {
source_rejected: true,
target_rejected: false,
..
}
))
));
}
#[test]
fn test_both_constraints_respected() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<2>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
let n3 = graph.add_node(FixedEdgeCount::empty());
assert!(graph.add_edge(n1, n2, ()).is_ok());
assert!(graph.add_edge(n2, n3, ()).is_ok());
assert!(matches!(
graph.add_edge(n3, n1, ()),
Err(BoundedAcyclicGraphError::Acyclic(_))
));
let n4 = graph.add_node(FixedEdgeCount::empty());
assert!(graph.add_edge(n1, n4, ()).is_ok());
let n5 = graph.add_node(FixedEdgeCount::empty());
assert!(matches!(
graph.add_edge(n1, n5, ()),
Err(BoundedAcyclicGraphError::Bounded(
BoundedGraphError::EdgeRejected {
source_rejected: true,
target_rejected: false,
..
}
))
));
}
#[test]
fn test_asymmetric_bounds_with_acyclic() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<2, 5>, ()>::new();
let hub = graph.add_node(FixedEdgeCount::empty());
let spoke1 = graph.add_node(FixedEdgeCount::empty());
let spoke2 = graph.add_node(FixedEdgeCount::empty());
let spoke3 = graph.add_node(FixedEdgeCount::empty());
assert!(graph.add_edge(hub, spoke1, ()).is_ok());
assert!(graph.add_edge(hub, spoke2, ()).is_ok());
assert!(graph.add_edge(hub, spoke3, ()).is_ok());
assert!(matches!(
graph.add_edge(spoke1, hub, ()),
Err(BoundedAcyclicGraphError::Acyclic(_))
));
}
#[test]
fn test_can_add_edge_checks_both_constraints() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<2>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
let n3 = graph.add_node(FixedEdgeCount::empty());
assert!(graph.can_add_edge(n1, n2));
assert!(graph.can_add_edge(n2, n3));
graph.add_edge(n1, n2, ()).unwrap();
assert!(!graph.can_add_edge(n2, n1));
graph.add_edge(n1, n3, ()).unwrap();
let n4 = graph.add_node(FixedEdgeCount::empty());
assert!(!graph.can_add_edge(n1, n4));
}
#[test]
fn test_update_edge_existing() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<5>, i32>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
let e1 = graph.add_edge(n1, n2, 10).unwrap();
let e2 = graph.update_edge(n1, n2, 20).unwrap();
assert_eq!(e1, e2);
}
#[test]
fn test_update_edge_new_respects_constraints() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<5>, i32>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
graph.add_edge(n1, n2, 10).unwrap();
assert!(matches!(
graph.update_edge(n2, n1, 20),
Err(BoundedAcyclicGraphError::Acyclic(_))
));
}
#[test]
fn test_remove_edge_allows_new_edges() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<1>, &str>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
let n3 = graph.add_node(FixedEdgeCount::empty());
let e = graph.add_edge(n1, n2, "a->b").unwrap();
assert!(matches!(
graph.add_edge(n1, n3, "a->c"),
Err(BoundedAcyclicGraphError::Bounded(
BoundedGraphError::EdgeRejected {
source_rejected: true,
target_rejected: false,
..
}
))
));
assert_eq!(graph.remove_edge(e), Some("a->b"));
assert_eq!(graph.edge_count(), 0);
assert!(graph.add_edge(n1, n3, "a->c").is_ok());
assert_eq!(graph.edge_count(), 1);
}
#[test]
fn test_remove_node_updates_graph() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<5>, &str>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
let n3 = graph.add_node(FixedEdgeCount::empty());
graph.add_edge(n1, n2, "a->b").unwrap();
graph.add_edge(n2, n3, "b->c").unwrap();
assert_eq!(graph.node_count(), 3);
assert_eq!(graph.edge_count(), 2);
assert!(graph.remove_node(n2).is_some());
assert_eq!(graph.node_count(), 2);
assert_eq!(graph.edge_count(), 0); }
#[test]
fn test_remove_edge_maintains_acyclicity() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<5>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
let n3 = graph.add_node(FixedEdgeCount::empty());
graph.add_edge(n1, n2, ()).unwrap();
let e = graph.add_edge(n2, n3, ()).unwrap();
assert!(matches!(
graph.add_edge(n3, n1, ()),
Err(BoundedAcyclicGraphError::Acyclic(_))
));
assert!(graph.remove_edge(e).is_some());
assert_eq!(graph.edge_count(), 1);
assert!(graph.add_edge(n3, n2, ()).is_ok());
assert!(graph.add_edge(n3, n1, ()).is_ok());
assert!(matches!(
graph.add_edge(n2, n1, ()),
Err(BoundedAcyclicGraphError::Acyclic(_))
));
}
#[test]
fn test_remove_node_maintains_acyclicity() {
let mut graph = BoundedAcyclicStableDiGraph::<FixedEdgeCount<5>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
let n3 = graph.add_node(FixedEdgeCount::empty());
let n4 = graph.add_node(FixedEdgeCount::empty());
graph.add_edge(n1, n2, ()).unwrap();
graph.add_edge(n2, n3, ()).unwrap();
graph.add_edge(n3, n4, ()).unwrap();
assert!(matches!(
graph.add_edge(n4, n1, ()),
Err(BoundedAcyclicGraphError::Acyclic(_))
));
assert!(graph.remove_node(n2).is_some());
assert_eq!(graph.node_count(), 3);
assert_eq!(graph.edge_count(), 1);
assert!(graph.add_edge(n4, n1, ()).is_ok());
assert!(matches!(
graph.add_edge(n1, n3, ()),
Err(BoundedAcyclicGraphError::Acyclic(_))
));
assert!(matches!(
graph.add_edge(n1, n4, ()),
Err(BoundedAcyclicGraphError::Acyclic(_))
));
}
#[test]
fn test_remove_node_allows_new_topology() {
let mut graph = BoundedAcyclicStableDiGraph::<FixedEdgeCount<5>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
let n3 = graph.add_node(FixedEdgeCount::empty());
graph.add_edge(n1, n2, ()).unwrap();
graph.add_edge(n2, n3, ()).unwrap();
assert!(matches!(
graph.add_edge(n3, n1, ()),
Err(BoundedAcyclicGraphError::Acyclic(_))
));
graph.remove_node(n2).unwrap();
assert!(graph.add_edge(n3, n1, ()).is_ok());
}
#[test]
fn test_remove_nonexistent_edge() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<5>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
let e = graph.add_edge(n1, n2, ()).unwrap();
assert!(graph.remove_edge(e).is_some());
assert!(graph.remove_edge(e).is_none());
}
#[test]
fn test_remove_nonexistent_node() {
let mut graph = BoundedAcyclicStableDiGraph::<FixedEdgeCount<5>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
assert!(graph.remove_node(n1).is_some());
assert!(graph.remove_node(n1).is_none());
}
#[test]
fn test_topological_ordering() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<5>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
let n3 = graph.add_node(FixedEdgeCount::empty());
graph.add_edge(n1, n2, ()).unwrap();
graph.add_edge(n2, n3, ()).unwrap();
let topo_order: Vec<_> = graph.topological_iter().collect();
let pos1 = topo_order.iter().position(|&n| n == n1).unwrap();
let pos2 = topo_order.iter().position(|&n| n == n2).unwrap();
let pos3 = topo_order.iter().position(|&n| n == n3).unwrap();
assert!(pos1 < pos2);
assert!(pos2 < pos3);
}
#[test]
fn test_topological_position_methods() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<5>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
graph.add_edge(n1, n2, ()).unwrap();
let pos1 = graph.get_position(n1);
let pos2 = graph.get_position(n2);
assert!(pos1.is_some());
assert!(pos2.is_some());
assert_eq!(graph.at_position(pos1.unwrap()), Some(n1));
assert_eq!(graph.at_position(pos2.unwrap()), Some(n2));
}
#[test]
fn test_stable_graph_acyclic() {
let mut graph = BoundedAcyclicStableDiGraph::<FixedEdgeCount<5>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
assert!(graph.add_edge(n1, n2, ()).is_ok());
graph.remove_node(n1);
let n3 = graph.add_node(FixedEdgeCount::empty());
assert!(graph.add_edge(n2, n3, ()).is_ok());
}
#[test]
fn test_complex_dag_structure() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<10>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
let n3 = graph.add_node(FixedEdgeCount::empty());
let n4 = graph.add_node(FixedEdgeCount::empty());
let n5 = graph.add_node(FixedEdgeCount::empty());
let n6 = graph.add_node(FixedEdgeCount::empty());
assert!(graph.add_edge(n1, n2, ()).is_ok());
assert!(graph.add_edge(n1, n3, ()).is_ok());
assert!(graph.add_edge(n2, n4, ()).is_ok());
assert!(graph.add_edge(n2, n5, ()).is_ok());
assert!(graph.add_edge(n3, n4, ()).is_ok());
assert!(graph.add_edge(n3, n5, ()).is_ok());
assert!(graph.add_edge(n4, n6, ()).is_ok());
assert!(graph.add_edge(n5, n6, ()).is_ok());
assert_eq!(graph.edge_count(), 8);
assert!(matches!(
graph.add_edge(n6, n1, ()),
Err(BoundedAcyclicGraphError::Acyclic(_))
));
}
#[test]
fn test_error_display_messages() {
use petgraph::acyclic::AcyclicEdgeError;
use petgraph::graph::NodeIndex;
let err1 = BoundedAcyclicGraphError::<u32>::Bounded(BoundedGraphError::source_rejected(
NodeIndex::new(0),
NodeIndex::new(1),
));
let err2 = BoundedAcyclicGraphError::<u32>::Bounded(BoundedGraphError::target_rejected(
NodeIndex::new(0),
NodeIndex::new(1),
));
let err3 = BoundedAcyclicGraphError::<u32>::Bounded(BoundedGraphError::both_rejected(
NodeIndex::new(0),
NodeIndex::new(1),
));
let err4 = BoundedAcyclicGraphError::<u32>::Bounded(BoundedGraphError::not_found(
Some(NodeIndex::new(0)),
None,
));
let err5 = BoundedAcyclicGraphError::<u32>::Acyclic(AcyclicEdgeError::SelfLoop);
assert_eq!(err1.to_string(), "source node 0 rejected the outgoing edge");
assert_eq!(err2.to_string(), "target node 1 rejected the incoming edge");
assert_eq!(
err3.to_string(),
"both nodes rejected the edge (source: 0, target: 1)"
);
assert_eq!(err4.to_string(), "source node 0 not found");
assert_eq!(err5.to_string(), "adding edge would create a self-loop");
}
#[test]
fn test_self_loop_prevention() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<10>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
assert!(matches!(
graph.add_edge(n1, n1, ()),
Err(BoundedAcyclicGraphError::Acyclic(_))
));
}
#[test]
fn test_empty_graph() {
let graph = BoundedAcyclicDiGraph::<FixedEdgeCount<5>, ()>::new();
assert_eq!(graph.node_count(), 0);
assert_eq!(graph.edge_count(), 0);
}
#[test]
fn test_default_construction() {
let graph: BoundedAcyclicDiGraph<FixedEdgeCount<5>, ()> = Default::default();
assert_eq!(graph.node_count(), 0);
assert_eq!(graph.edge_count(), 0);
}
#[test]
fn test_debug_format() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<5>, i32>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
graph.add_edge(n1, n2, 100).unwrap();
let debug_str = format!("{:?}", graph);
assert!(debug_str.contains("BoundedAcyclicGraph"));
}
#[test]
fn test_inner_access() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<5>, i32>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
let e = graph.add_edge(n1, n2, 42).unwrap();
let inner = graph.inner();
assert_eq!(*inner.edge_weight(e).unwrap(), 42);
assert_eq!(inner.node_count(), 2);
assert_eq!(inner.edge_count(), 1);
}
#[test]
fn test_as_acyclic() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<5>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
graph.add_edge(n1, n2, ()).unwrap();
let acyclic = graph.as_acyclic();
assert!(acyclic.is_valid_edge(n1, n2));
assert!(!acyclic.is_valid_edge(n2, n1)); }
#[test]
fn test_update_edge_existing_edge() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<5>, String>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
let e1 = graph.add_edge(n1, n2, "original".to_string()).unwrap();
assert_eq!(graph.edge_count(), 1);
let e2 = graph.update_edge(n1, n2, "updated".to_string()).unwrap();
assert_eq!(e1, e2); assert_eq!(graph.edge_count(), 1);
let weight = graph.inner().edge_weight(e2).unwrap();
assert_eq!(weight, "updated");
}
#[test]
fn test_update_edge_new_edge_violates_acyclic() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<5>, i32>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
graph.add_edge(n1, n2, 10).unwrap();
let result = graph.update_edge(n2, n1, 20);
assert!(matches!(result, Err(BoundedAcyclicGraphError::Acyclic(_))));
}
#[test]
fn test_add_edge_with_nonexistent_source_node() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<5>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let invalid = NodeIndex::new(999);
let result = graph.add_edge(invalid, n1, ());
assert!(matches!(
result,
Err(BoundedAcyclicGraphError::Bounded(
BoundedGraphError::NodeNotFound {
source: None,
target: Some(_)
}
))
));
}
#[test]
fn test_add_edge_with_nonexistent_target_node() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<5>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let invalid = NodeIndex::new(999);
let result = graph.add_edge(n1, invalid, ());
assert!(matches!(
result,
Err(BoundedAcyclicGraphError::Bounded(
BoundedGraphError::NodeNotFound {
source: Some(_),
target: None
}
))
));
}
#[test]
fn test_can_add_edge_with_invalid_nodes() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<5>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let invalid = NodeIndex::new(999);
assert!(!graph.can_add_edge(invalid, n1));
assert!(!graph.can_add_edge(n1, invalid));
assert!(!graph.can_add_edge(invalid, invalid));
}
#[test]
fn test_topological_methods() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<5>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
let n3 = graph.add_node(FixedEdgeCount::empty());
graph.add_edge(n1, n2, ()).unwrap();
graph.add_edge(n2, n3, ()).unwrap();
let pos1 = graph.get_position(n1);
let pos2 = graph.get_position(n2);
let pos3 = graph.get_position(n3);
assert!(pos1.is_some());
assert!(pos2.is_some());
assert!(pos3.is_some());
assert_eq!(graph.at_position(pos1.unwrap()), Some(n1));
assert_eq!(graph.at_position(pos2.unwrap()), Some(n2));
assert_eq!(graph.at_position(pos3.unwrap()), Some(n3));
}
#[test]
fn test_clone_graph() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<5>, i32>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
graph.add_edge(n1, n2, 42).unwrap();
let cloned = graph.clone();
assert_eq!(graph.node_count(), cloned.node_count());
assert_eq!(graph.edge_count(), cloned.edge_count());
let edge = cloned
.inner()
.find_edge(n1, n2)
.expect("Edge not found in clone");
assert_eq!(*cloned.inner().edge_weight(edge).unwrap(), 42);
}
#[test]
fn test_bounded_acyclic_error_variants() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<1>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
let n3 = graph.add_node(FixedEdgeCount::empty());
graph.add_edge(n1, n2, ()).unwrap();
let result = graph.add_edge(n1, n3, ());
assert!(matches!(
result,
Err(BoundedAcyclicGraphError::Bounded(
BoundedGraphError::EdgeRejected {
source_rejected: true,
target_rejected: false,
..
}
))
));
}
#[test]
fn test_target_node_full_error() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<1, 1>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
let n3 = graph.add_node(FixedEdgeCount::empty());
graph.add_edge(n1, n3, ()).unwrap();
let result = graph.add_edge(n2, n3, ());
assert!(
matches!(
result,
Err(BoundedAcyclicGraphError::Bounded(
BoundedGraphError::EdgeRejected {
source_rejected: false,
target_rejected: true,
..
}
))
),
"Expected target_rejected, got: {:?}",
result
);
}
#[test]
fn test_both_nodes_full_error() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<1, 1>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
let n3 = graph.add_node(FixedEdgeCount::empty());
let n4 = graph.add_node(FixedEdgeCount::empty());
graph.add_edge(n2, n1, ()).unwrap();
graph.add_edge(n3, n4, ()).unwrap();
let result = graph.add_edge(n2, n4, ());
assert!(matches!(
result,
Err(BoundedAcyclicGraphError::Bounded(
BoundedGraphError::EdgeRejected {
source_rejected: true,
target_rejected: true,
..
}
))
));
}
#[test]
fn test_can_add_edge_with_nonexistent_nodes() {
let graph = BoundedAcyclicDiGraph::<FixedEdgeCount<5>, ()>::new();
let n1 = NodeIndex::new(0);
let n2 = NodeIndex::new(1);
assert!(!graph.can_add_edge(n1, n2));
}
#[test]
fn test_can_add_edge_returns_false_for_cycle() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<5>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
graph.add_edge(n1, n2, ()).unwrap();
assert!(!graph.can_add_edge(n2, n1));
}
#[test]
fn test_can_add_edge_returns_false_when_source_full() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<1>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
let n3 = graph.add_node(FixedEdgeCount::empty());
graph.add_edge(n1, n2, ()).unwrap();
assert!(!graph.can_add_edge(n1, n3));
}
#[test]
fn test_update_edge_with_nonexistent_edge_adds_it() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<5>, String>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
let edge_idx = graph.update_edge(n1, n2, "new edge".to_string()).unwrap();
assert_eq!(graph.edge_count(), 1);
assert_eq!(graph.inner().edge_weight(edge_idx).unwrap(), "new edge");
}
#[test]
fn test_update_edge_violating_acyclic_fails() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<5>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
graph.add_edge(n1, n2, ()).unwrap();
let result = graph.update_edge(n2, n1, ());
assert!(matches!(result, Err(BoundedAcyclicGraphError::Acyclic(_))));
}
#[test]
fn test_update_edge_violating_bounded_fails() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<1>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
let n3 = graph.add_node(FixedEdgeCount::empty());
graph.add_edge(n1, n2, ()).unwrap();
let result = graph.update_edge(n1, n3, ());
assert!(matches!(
result,
Err(BoundedAcyclicGraphError::Bounded(
BoundedGraphError::EdgeRejected {
source_rejected: true,
target_rejected: false,
..
}
))
));
}
#[test]
fn test_at_position_with_various_positions() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<5>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
let n3 = graph.add_node(FixedEdgeCount::empty());
graph.add_edge(n1, n2, ()).unwrap();
graph.add_edge(n2, n3, ()).unwrap();
let pos1 = graph.get_position(n1).unwrap();
let pos2 = graph.get_position(n2).unwrap();
let pos3 = graph.get_position(n3).unwrap();
assert_eq!(graph.at_position(pos1), Some(n1));
assert_eq!(graph.at_position(pos2), Some(n2));
assert_eq!(graph.at_position(pos3), Some(n3));
}
#[test]
fn test_stable_graph_remove_edge_returns_none_for_invalid() {
let mut graph = BoundedAcyclicStableDiGraph::<FixedEdgeCount<5>, ()>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
let edge = graph.add_edge(n1, n2, ()).unwrap();
assert!(graph.remove_edge(edge).is_some());
assert!(graph.remove_edge(edge).is_none());
}
#[test]
fn test_stable_graph_operations_with_complex_topology() {
let mut graph = BoundedAcyclicStableDiGraph::<FixedEdgeCount<10>, i32>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
let n3 = graph.add_node(FixedEdgeCount::empty());
let n4 = graph.add_node(FixedEdgeCount::empty());
graph.add_edge(n1, n2, 10).unwrap();
graph.add_edge(n1, n3, 20).unwrap();
graph.add_edge(n2, n4, 30).unwrap();
graph.add_edge(n3, n4, 40).unwrap();
assert_eq!(graph.node_count(), 4);
assert_eq!(graph.edge_count(), 4);
graph.remove_node(n2);
assert_eq!(graph.node_count(), 3);
assert_eq!(graph.edge_count(), 2);
let n5 = graph.add_node(FixedEdgeCount::empty());
graph.add_edge(n3, n5, 50).unwrap();
assert_eq!(graph.edge_count(), 3);
}
#[test]
fn test_edge_weight_with_complex_type() {
#[derive(Debug, Clone, PartialEq)]
struct ComplexWeight {
value: i32,
label: String,
}
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<5>, ComplexWeight>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
let weight = ComplexWeight {
value: 42,
label: "test".to_string(),
};
let edge = graph.add_edge(n1, n2, weight.clone()).unwrap();
assert_eq!(graph.inner().edge_weight(edge).unwrap(), &weight);
}
#[test]
fn test_empty_graph_operations() {
let graph = BoundedAcyclicDiGraph::<FixedEdgeCount<5>, ()>::new();
assert_eq!(graph.node_count(), 0);
assert_eq!(graph.edge_count(), 0);
let nodes: Vec<_> = graph.topological_iter().collect();
assert_eq!(nodes.len(), 0);
}
#[test]
fn test_single_node_graph() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<5>, i32>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
assert_eq!(graph.node_count(), 1);
assert_eq!(graph.edge_count(), 0);
let pos = graph.get_position(n1);
assert!(pos.is_some());
assert_eq!(graph.at_position(pos.unwrap()), Some(n1));
}
#[test]
fn test_multiple_parallel_edges_attempt() {
let mut graph = BoundedAcyclicDiGraph::<FixedEdgeCount<5>, String>::new();
let n1 = graph.add_node(FixedEdgeCount::empty());
let n2 = graph.add_node(FixedEdgeCount::empty());
let e1 = graph.add_edge(n1, n2, "first".to_string()).unwrap();
let e2 = graph.update_edge(n1, n2, "second".to_string()).unwrap();
assert_eq!(e1, e2);
assert_eq!(graph.edge_count(), 1);
assert_eq!(graph.inner().edge_weight(e2).unwrap(), "second");
}
#[test]
fn test_try_from_graph_success_dag() {
use petgraph::Graph;
use std::convert::TryFrom;
let mut pg = Graph::new();
let n1 = pg.add_node(FixedEdgeCount::<3, 3>::empty());
let n2 = pg.add_node(FixedEdgeCount::<3, 3>::empty());
let n3 = pg.add_node(FixedEdgeCount::<3, 3>::empty());
pg.add_edge(n1, n2, ());
pg.add_edge(n2, n3, ());
let dag = BoundedAcyclicDiGraph::try_from(pg).unwrap();
assert_eq!(dag.node_count(), 3);
assert_eq!(dag.edge_count(), 2);
}
#[test]
fn test_try_from_graph_with_cycle() {
use petgraph::Graph;
use std::convert::TryFrom;
let mut pg = Graph::new();
let n1 = pg.add_node(FixedEdgeCount::<3, 3>::empty());
let n2 = pg.add_node(FixedEdgeCount::<3, 3>::empty());
let n3 = pg.add_node(FixedEdgeCount::<3, 3>::empty());
pg.add_edge(n1, n2, ());
pg.add_edge(n2, n3, ());
pg.add_edge(n3, n1, ());
let result = BoundedAcyclicDiGraph::try_from(pg);
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
BoundedAcyclicGraphError::Acyclic(_)
));
}
#[test]
fn test_try_from_graph_violates_bounded_constraints() {
use petgraph::Graph;
use std::convert::TryFrom;
let mut pg = Graph::new();
let n1 = pg.add_node(FixedEdgeCount::<2, 2>::empty()); let n2 = pg.add_node(FixedEdgeCount::<2, 2>::empty());
let n3 = pg.add_node(FixedEdgeCount::<2, 2>::empty());
let n4 = pg.add_node(FixedEdgeCount::<2, 2>::empty());
pg.add_edge(n1, n2, ());
pg.add_edge(n1, n3, ());
pg.add_edge(n1, n4, ());
let result = BoundedAcyclicDiGraph::try_from(pg);
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
BoundedAcyclicGraphError::Bounded(_)
));
}
#[test]
fn test_try_from_graph_empty_dag() {
use petgraph::Graph;
use std::convert::TryFrom;
let pg = Graph::<FixedEdgeCount<2, 2>, ()>::new();
let dag = BoundedAcyclicDiGraph::try_from(pg).unwrap();
assert_eq!(dag.node_count(), 0);
assert_eq!(dag.edge_count(), 0);
}
#[test]
fn test_try_from_graph_complex_dag() {
use petgraph::Graph;
use std::convert::TryFrom;
let mut pg = Graph::new();
let n1 = pg.add_node(FixedEdgeCount::<5, 5>::empty());
let n2 = pg.add_node(FixedEdgeCount::<5, 5>::empty());
let n3 = pg.add_node(FixedEdgeCount::<5, 5>::empty());
let n4 = pg.add_node(FixedEdgeCount::<5, 5>::empty());
let n5 = pg.add_node(FixedEdgeCount::<5, 5>::empty());
pg.add_edge(n1, n2, ());
pg.add_edge(n1, n3, ());
pg.add_edge(n2, n4, ());
pg.add_edge(n3, n4, ());
pg.add_edge(n2, n5, ());
let dag = BoundedAcyclicDiGraph::try_from(pg).unwrap();
assert_eq!(dag.edge_count(), 5);
}
#[test]
fn test_try_from_stable_graph_success() {
use petgraph::stable_graph::StableGraph;
use std::convert::TryFrom;
let mut pg = StableGraph::new();
let n1 = pg.add_node(FixedEdgeCount::<3, 3>::empty());
let n2 = pg.add_node(FixedEdgeCount::<3, 3>::empty());
pg.add_edge(n1, n2, ());
let dag = BoundedAcyclicStableDiGraph::try_from(pg).unwrap();
assert_eq!(dag.node_count(), 2);
assert_eq!(dag.edge_count(), 1);
}
#[test]
fn test_try_from_stable_graph_with_cycle() {
use petgraph::stable_graph::StableGraph;
use std::convert::TryFrom;
let mut pg = StableGraph::new();
let n1 = pg.add_node(FixedEdgeCount::<3, 3>::empty());
let n2 = pg.add_node(FixedEdgeCount::<3, 3>::empty());
pg.add_edge(n1, n2, ());
pg.add_edge(n2, n1, ());
let result = BoundedAcyclicStableDiGraph::try_from(pg);
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
BoundedAcyclicGraphError::Acyclic(_)
));
}
#[test]
fn test_try_from_stable_graph_violates_capacity() {
use petgraph::stable_graph::StableGraph;
use std::convert::TryFrom;
let mut pg = StableGraph::new();
let n1 = pg.add_node(FixedEdgeCount::<1, 1>::empty()); let n2 = pg.add_node(FixedEdgeCount::<1, 1>::empty());
let n3 = pg.add_node(FixedEdgeCount::<1, 1>::empty());
pg.add_edge(n1, n2, ());
pg.add_edge(n1, n3, ());
let result = BoundedAcyclicStableDiGraph::try_from(pg);
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
BoundedAcyclicGraphError::Bounded(_)
));
}
#[test]
fn test_try_from_preserves_topological_order() {
use petgraph::Graph;
use std::convert::TryFrom;
let mut pg = Graph::new();
let n1 = pg.add_node(FixedEdgeCount::<3, 3>::empty());
let n2 = pg.add_node(FixedEdgeCount::<3, 3>::empty());
let n3 = pg.add_node(FixedEdgeCount::<3, 3>::empty());
pg.add_edge(n1, n2, ());
pg.add_edge(n2, n3, ());
let dag = BoundedAcyclicDiGraph::try_from(pg).unwrap();
let topo_order: Vec<_> = dag.topological_iter().collect();
assert_eq!(topo_order.len(), 3);
let n1_pos = topo_order.iter().position(|&n| n == n1).unwrap();
let n2_pos = topo_order.iter().position(|&n| n == n2).unwrap();
let n3_pos = topo_order.iter().position(|&n| n == n3).unwrap();
assert!(n1_pos < n2_pos);
assert!(n2_pos < n3_pos);
}
#[test]
fn test_try_from_preserves_node_and_edge_data() {
use petgraph::Graph;
use std::convert::TryFrom;
let mut pg = Graph::new();
let n1 = pg.add_node(FixedEdgeCount::<3, 3, String>::new("Node1".to_string()));
let n2 = pg.add_node(FixedEdgeCount::<3, 3, String>::new("Node2".to_string()));
pg.add_edge(n1, n2, "EdgeData");
let dag = BoundedAcyclicDiGraph::try_from(pg).unwrap();
assert_eq!(dag.inner().node_weight(n1).unwrap().data, "Node1");
assert_eq!(dag.inner().node_weight(n2).unwrap().data, "Node2");
let edge = dag.inner().find_edge(n1, n2).unwrap();
assert_eq!(*dag.inner().edge_weight(edge).unwrap(), "EdgeData");
}
#[test]
fn test_try_from_self_loop_fails() {
use petgraph::Graph;
use std::convert::TryFrom;
let mut pg = Graph::new();
let n1 = pg.add_node(FixedEdgeCount::<3, 3>::empty());
pg.add_edge(n1, n1, ());
let result = BoundedAcyclicDiGraph::try_from(pg);
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
BoundedAcyclicGraphError::Acyclic(_)
));
}