blob: fcf204013ed67071d4eab06f102f435028725fa9 [file] [edit]
#![cfg(feature = "stable")]
mod common;
use common::graphs::FromDefault;
use petgraph_core::{
edge::{Direction, Undirected},
id::DefaultIx,
visit::{EdgeIndexable, EdgeRef, IntoEdgeReferences, IntoNodeReferences, NodeIndexable},
};
use petgraph_graph::{stable::StableGraph, Graph, NodeIndex};
use crate::common::graphs::GraphDoubleLink;
type GraphLink<N, E> = common::graphs::GraphLink<StableGraph<N, E, Undirected>>;
impl GraphLink<(), ()> {
fn new() -> Self {
Self::from_default()
}
}
type GraphDoubleSameDirection<N, E> =
common::graphs::GraphDoubleSameDirection<StableGraph<N, E, Undirected>>;
impl GraphDoubleSameDirection<(), ()> {
fn new() -> Self {
Self::from_default()
}
}
#[test]
fn node_indices() {
let mut graph = StableGraph::<(), (), Undirected>::with_capacity(0, 0);
let a = graph.add_node(());
let b = graph.add_node(());
let c = graph.add_node(());
graph.remove_node(b);
assert_eq!(graph.node_indices().collect::<Vec<_>>(), vec![a, c]);
}
#[test]
fn node_indices_stable() {
let mut graph = StableGraph::<i32, (), Undirected>::with_capacity(0, 0);
let a = graph.add_node(0);
let b = graph.add_node(1);
let c = graph.add_node(2);
graph.remove_node(b);
// even though we removed the `b` node, the index for `c` should remain the same
// This is what makes `StableGraph` special, `Graph` would've moved the last index (`c`) to the
// place of the removed node.
assert_eq!(graph.node_weight(a), Some(&0));
assert_eq!(graph.node_weight(b), None);
assert_eq!(graph.node_weight(c), Some(&2));
}
#[test]
fn node_indices_reclaim() {
let mut graph = StableGraph::<(), (), Undirected>::with_capacity(0, 0);
let a = graph.add_node(());
let b = graph.add_node(());
let c = graph.add_node(());
graph.remove_node(b);
let b2 = graph.add_node(());
assert_eq!(b, b2);
// `StableGraph` holds a list of unallocated nodes, if we create a new node and removed one, it
// should occupy the same index.
assert_eq!(graph.node_indices().collect::<Vec<_>>(), vec![a, b, c]);
}
#[test]
fn node_bound() {
let mut graph = StableGraph::<(), (), Undirected>::with_capacity(0, 0);
assert_eq!(graph.node_bound(), 0);
graph.add_node(());
let b = graph.add_node(());
assert_eq!(graph.node_bound(), 2);
graph.remove_node(b);
// `StableGraph` retains a list of unallocated nodes, so the bound should ignore those
// unallocated nodes.
assert_eq!(graph.node_bound(), 1);
}
#[test]
fn edge_indices() {
let mut graph = StableGraph::<(), (), Undirected>::with_capacity(0, 0);
let a = graph.add_node(());
let b = graph.add_node(());
let ab = graph.add_edge(a, b, ());
let ba = graph.add_edge(b, a, ());
graph.remove_edge(ba);
assert_eq!(graph.edge_indices().collect::<Vec<_>>(), vec![ab]);
}
#[test]
fn edge_indices_stable() {
let mut graph = StableGraph::<(), i32, Undirected>::with_capacity(0, 0);
let a = graph.add_node(());
let b = graph.add_node(());
let ab = graph.add_edge(a, b, 0);
let ba = graph.add_edge(b, a, 1);
graph.remove_edge(ab);
// same reason as `node_indices_stable` test, we remove the edge but the index for `ba` should
// remain the same
assert_eq!(graph.edge_weight(ba), Some(&1));
}
#[test]
fn edge_indices_reclaim() {
let mut graph = StableGraph::<(), (), Undirected>::with_capacity(0, 0);
let a = graph.add_node(());
let b = graph.add_node(());
let ab = graph.add_edge(a, b, ());
graph.add_edge(b, a, ());
graph.remove_edge(ab);
let ab2 = graph.add_edge(a, b, ());
assert_eq!(ab, ab2);
}
#[test]
fn edge_bound() {
let mut graph = StableGraph::<(), (), Undirected>::with_capacity(0, 0);
assert_eq!(graph.edge_bound(), 0);
let a = graph.add_node(());
let b = graph.add_node(());
graph.add_edge(a, b, ());
let ba = graph.add_edge(b, a, ());
assert_eq!(graph.edge_bound(), 2);
graph.remove_edge(ba);
// same reason as `node_bound` test, we remove the edge but the bound should ignore the
// unallocated edge
assert_eq!(graph.edge_bound(), 1);
}
#[test]
fn clear_edges() {
let mut graph = StableGraph::<(), (), Undirected>::with_capacity(0, 0);
let a = graph.add_node(());
let b = graph.add_node(());
graph.add_edge(a, b, ());
graph.add_edge(b, a, ());
graph.clear_edges();
assert_eq!(graph.edge_count(), 0);
assert_eq!(graph.edge_indices().count(), 0);
assert_eq!(graph.edge_references().count(), 0);
assert!(
graph
.node_indices()
.all(|index| graph.neighbors(index).count() == 0)
);
}
#[test]
fn edges() {
let GraphLink { graph, a, b, ab } = GraphLink::new();
assert_eq!(graph.edges(a).count(), 1);
assert_eq!(graph.edges(b).count(), 1);
assert_eq!(
graph
.edges(a)
.map(|reference| reference.id())
.collect::<Vec<_>>(),
vec![ab]
);
assert_eq!(
graph
.edges(b)
.map(|reference| reference.id())
.collect::<Vec<_>>(),
vec![ab]
);
}
#[test]
fn edges_multi() {
let GraphDoubleSameDirection {
graph,
a,
b,
ab1,
ab2,
} = GraphDoubleSameDirection::new();
assert_eq!(graph.edges(a).count(), 2);
assert_eq!(graph.edges(b).count(), 2);
assert_eq!(
graph
.edges(a)
.map(|reference| reference.id())
.collect::<Vec<_>>(),
vec![ab2, ab1]
);
assert_eq!(
graph
.edges(b)
.map(|reference| reference.id())
.collect::<Vec<_>>(),
vec![ab2, ab1]
);
}
#[test]
fn edges_multi_connecting() {
let GraphDoubleSameDirection {
graph,
a,
b,
ab1,
ab2,
} = GraphDoubleSameDirection::new();
assert_eq!(graph.edges_connecting(a, b).count(), 2);
assert_eq!(
graph
.edges_connecting(a, b)
.map(|reference| reference.id())
.collect::<Vec<_>>(),
vec![ab2, ab1]
);
assert_eq!(graph.edges_connecting(b, a).count(), 2);
assert_eq!(
graph
.edges_connecting(b, a)
.map(|reference| reference.id())
.collect::<Vec<_>>(),
vec![ab2, ab1]
);
}
#[test]
fn edges_directed() {
let GraphLink { graph, a, b, ab } = GraphLink::new();
assert_eq!(graph.edges_directed(a, Direction::Outgoing).count(), 1);
assert_eq!(graph.edges_directed(a, Direction::Incoming).count(), 1);
assert_eq!(graph.edges_directed(b, Direction::Outgoing).count(), 1);
assert_eq!(graph.edges_directed(b, Direction::Incoming).count(), 1);
assert_eq!(
graph
.edges_directed(a, Direction::Outgoing)
.map(|reference| reference.id())
.collect::<Vec<_>>(),
vec![ab]
);
assert_eq!(
graph
.edges_directed(a, Direction::Incoming)
.map(|reference| reference.id())
.collect::<Vec<_>>(),
vec![ab]
);
assert_eq!(
graph
.edges_directed(b, Direction::Outgoing)
.map(|reference| reference.id())
.collect::<Vec<_>>(),
vec![ab]
);
assert_eq!(
graph
.edges_directed(b, Direction::Incoming)
.map(|reference| reference.id())
.collect::<Vec<_>>(),
vec![ab]
);
}
#[test]
fn neighbours() {
let GraphLink { graph, a, b, .. } = GraphLink::new();
assert_eq!(graph.neighbors(a).count(), 1);
assert_eq!(graph.neighbors(b).count(), 1);
assert_eq!(graph.neighbors(a).collect::<Vec<_>>(), vec![b]);
assert_eq!(graph.neighbors(b).collect::<Vec<_>>(), vec![a]);
}
#[test]
fn neighbours_directed() {
let GraphLink { graph, a, b, .. } = GraphLink::new();
assert_eq!(graph.neighbors_directed(a, Direction::Outgoing).count(), 1);
assert_eq!(graph.neighbors_directed(a, Direction::Incoming).count(), 1);
assert_eq!(graph.neighbors_directed(b, Direction::Outgoing).count(), 1);
assert_eq!(graph.neighbors_directed(b, Direction::Incoming).count(), 1);
assert_eq!(
graph
.neighbors_directed(a, Direction::Outgoing)
.collect::<Vec<_>>(),
vec![b]
);
assert_eq!(
graph
.neighbors_directed(a, Direction::Incoming)
.collect::<Vec<_>>(),
vec![b]
);
assert_eq!(
graph
.neighbors_directed(b, Direction::Outgoing)
.collect::<Vec<_>>(),
vec![a]
);
assert_eq!(
graph
.neighbors_directed(b, Direction::Incoming)
.collect::<Vec<_>>(),
vec![a]
);
}
#[test]
fn node_references() {
let GraphLink { graph, a, b, .. } = GraphLink::new();
assert_eq!(graph.node_references().count(), 2);
assert_eq!(
graph
.node_references()
.map(|(index, _)| index)
.collect::<Vec<_>>(),
vec![a, b]
);
}
#[test]
fn edge_references() {
let GraphLink { graph, a, b, ab } = GraphLink::new();
assert_eq!(graph.edge_references().count(), 1);
assert_eq!(
graph
.edge_references()
.map(|edge| (edge.source(), edge.target(), edge.id()))
.collect::<Vec<_>>(),
vec![(a, b, ab)]
);
}
#[test]
fn nodes_mut() {
let GraphLink {
mut graph, a, b, ..
} = GraphLink::<i32, ()>::from_default();
assert_eq!(graph.node_weights_mut().count(), 2);
for (index, weight) in graph.node_weights_mut().enumerate() {
*weight = i32::try_from(index).expect("should be able to convert") + 1;
}
assert_eq!(graph.node_weight(a), Some(&1));
assert_eq!(graph.node_weight(b), Some(&2));
}
#[test]
fn edges_mut() {
let GraphLink { mut graph, ab, .. } = GraphLink::<(), i32>::from_default();
assert_eq!(graph.edge_weights_mut().count(), 1);
for (index, weight) in graph.edge_weights_mut().enumerate() {
*weight = i32::try_from(index).expect("should be able to convert") + 1;
}
assert_eq!(graph.edge_weight(ab), Some(&1));
}
#[cfg(feature = "std")]
#[test]
fn access_removed_node() {
let GraphLink {
mut graph, a, b, ..
} = GraphLink::new();
// we don't need to do the move shenanigans here because StableGraph does not move indices when
// a node is removed.
graph.remove_node(a);
let result = std::panic::catch_unwind(|| {
let access = graph[a];
core::hint::black_box(&access);
});
result.expect_err("Accessing removed node should panic");
}
#[cfg(feature = "std")]
#[test]
fn add_node_out_of_bounds() {
let mut graph = StableGraph::<(), (), Undirected, u8>::with_capacity(0, 0);
// fill up the graph
for _ in 0..255 {
graph.add_node(());
}
let result = std::panic::catch_unwind(move || {
graph.add_node(());
});
result.expect_err("Creating more than Ix::MAX nodes should panic");
}
#[cfg(feature = "std")]
#[test]
fn add_edge_out_of_bounds() {
let mut graph = StableGraph::<(), (), Undirected, u8>::with_capacity(0, 0);
let a = graph.add_node(());
let b = graph.add_node(());
// fill up the graph
for _ in 0..255 {
graph.add_edge(a, b, ());
}
let result = std::panic::catch_unwind(move || {
graph.add_edge(a, b, ());
});
result.expect_err("Creating more than Ix::MAX edges should panic");
}
#[test]
fn from() {
let mut stable =
GraphDoubleLink::<StableGraph<i32, i32, Undirected, DefaultIx>, DefaultIx>::from_default();
let mut normal =
GraphDoubleLink::<Graph<i32, i32, Undirected, DefaultIx>, DefaultIx>::from_default();
// permute both the same way, nodes are tagged with their index (starting from 1)
// and edges are tagged with their index, but negative (starting from -1)
for (index, weight) in stable.graph.node_weights_mut().enumerate() {
*weight = i32::try_from(index + 1).expect("too many nodes");
}
for (index, weight) in normal.graph.node_weights_mut().enumerate() {
*weight = i32::try_from(index + 1).expect("too many nodes");
}
for (index, weight) in stable.graph.edge_weights_mut().enumerate() {
*weight = -i32::try_from(index + 1).expect("too many edges");
}
for (index, weight) in normal.graph.edge_weights_mut().enumerate() {
*weight = -i32::try_from(index + 1).expect("too many edges");
}
// now remove the middle node (b) from both
stable.graph.remove_node(stable.b);
normal.graph.remove_node(normal.b);
// convert the stable graph to a normal graph
let converted: Graph<_, _, _, _> = Graph::from(stable.graph);
// assert that all edges and nodes are the same
assert!(
converted
.node_references()
.eq(normal.graph.node_references())
);
assert!(
converted
.edge_references()
.eq(normal.graph.edge_references())
);
}
#[test]
fn into() {
let stable =
GraphDoubleLink::<StableGraph<i32, i32, Undirected, DefaultIx>, DefaultIx>::from_default();
let normal =
GraphDoubleLink::<Graph<i32, i32, Undirected, DefaultIx>, DefaultIx>::from_default();
// here we don't permute, because the conversion from graph to stable cannot handle holes that
// we created, as it shuffles indices in that case
let converted: StableGraph<_, _, _, _> = normal.graph.into();
// assert that all edges and nodes are the same
assert!(
converted
.node_references()
.eq(stable.graph.node_references())
);
assert!(
converted
.edge_references()
.eq(stable.graph.edge_references())
);
}