use petgraph::{
csr::IndexType,
dot::{Config, Dot},
Direction::Incoming,
};
use crate::{BoundedGraph, EdgeBounds, SimpleEdgeBounds};
#[test]
fn edge_number_bounded_node() {
#[derive(Debug, Default)]
pub struct BoundedNodeIndex {
name: String,
inputs: usize,
outputs: usize,
}
impl BoundedNodeIndex {
pub fn new(name: String, inputs: usize, outputs: usize) -> Self {
Self {
name,
inputs,
outputs,
}
}
}
impl<Ix: IndexType> EdgeBounds<Ix> for BoundedNodeIndex {
fn max_incoming_edges(&self) -> Ix {
Ix::new(self.inputs)
}
fn max_outgoing_edges(&self) -> Ix {
Ix::new(self.outputs)
}
}
impl SimpleEdgeBounds for BoundedNodeIndex {}
let mut deps = BoundedGraph::<BoundedNodeIndex, &str>::new();
let pg = deps.add_node(BoundedNodeIndex::new(
"petgraph".to_string(),
0usize,
3usize,
));
let fb = deps.add_node(BoundedNodeIndex::new(
"fixedbitset".to_string(),
4usize,
0usize,
));
let qc = deps.add_node(BoundedNodeIndex::new(
"quickcheck".to_string(),
1usize,
2usize,
));
let rand = deps.add_node(BoundedNodeIndex::new("rand".to_string(), 1usize, 2usize));
let libc = deps.add_node(BoundedNodeIndex::new("libc".to_string(), 1usize, 2usize));
deps.extend_with_edges(&[(pg, fb), (pg, qc), (qc, rand), (rand, libc), (qc, libc)]);
println!("{:?}", Dot::with_config(&*deps, &[Config::EdgeNoLabel]));
assert_eq!(deps[pg].name, "petgraph");
assert_eq!(deps.edge_count(), 4); assert_eq!(deps.edges_directed(libc, Incoming).count(), 1);
deps.update_edge(qc, fb, "").unwrap();
assert_eq!(deps.edge_count(), 5);
}
use crate::FixedEdgeCount;
#[test]
fn advanced_node() {
let mut deps = BoundedGraph::<FixedEdgeCount<3>, &str>::new();
let pg = deps.add_node(FixedEdgeCount::default());
let fb = deps.add_node(FixedEdgeCount::default());
let qc = deps.add_node(FixedEdgeCount::default());
deps.extend_with_edges(&[(pg, fb), (pg, qc)]);
println!("{:?}", Dot::with_config(&*deps, &[Config::EdgeNoLabel]));
assert_eq!(deps.node_count(), 3);
assert_eq!(deps.edge_count(), 2);
}
#[test]
fn test_edge_bounds_at_limit() {
use crate::BoundedNode;
use petgraph::graph::DefaultIx;
use petgraph::Direction;
#[derive(Debug)]
pub struct SimpleBoundedNode {
max_in: usize,
max_out: usize,
}
impl<Ix: IndexType> EdgeBounds<Ix> for SimpleBoundedNode {
fn max_incoming_edges(&self) -> Ix {
Ix::new(self.max_in)
}
fn max_outgoing_edges(&self) -> Ix {
Ix::new(self.max_out)
}
}
impl SimpleEdgeBounds for SimpleBoundedNode {}
let node = SimpleBoundedNode {
max_in: 3,
max_out: 2,
};
assert!(
!<SimpleBoundedNode as BoundedNode<DefaultIx>>::can_add_edge(
&node,
Direction::Incoming,
3,
&node
)
);
assert!(
!<SimpleBoundedNode as BoundedNode<DefaultIx>>::can_add_edge(
&node,
Direction::Outgoing,
2,
&node
)
);
assert!(<SimpleBoundedNode as BoundedNode<DefaultIx>>::can_add_edge(
&node,
Direction::Incoming,
2,
&node
));
assert!(<SimpleBoundedNode as BoundedNode<DefaultIx>>::can_add_edge(
&node,
Direction::Outgoing,
1,
&node
));
assert!(<SimpleBoundedNode as BoundedNode<DefaultIx>>::can_add_edge(
&node,
Direction::Incoming,
0,
&node
));
assert!(<SimpleBoundedNode as BoundedNode<DefaultIx>>::can_add_edge(
&node,
Direction::Outgoing,
0,
&node
));
}
#[test]
fn test_relational_constraints_with_other_node() {
use crate::{BoundedGraph, BoundedNode, ImmutableEdgeBounds};
use petgraph::Direction;
#[derive(Debug, Clone, PartialEq)]
enum NodeRole {
Server,
Client,
Peer,
}
#[derive(Debug, Clone)]
struct RoleBasedNode {
role: NodeRole,
max_connections: usize,
}
impl RoleBasedNode {
fn new(role: NodeRole, max_connections: usize) -> Self {
Self {
role,
max_connections,
}
}
fn can_connect_to(&self, other: &Self) -> bool {
match (&self.role, &other.role) {
(NodeRole::Server, NodeRole::Client) => true,
(NodeRole::Server, NodeRole::Peer) => true,
(NodeRole::Client, NodeRole::Server) => true,
(NodeRole::Peer, NodeRole::Server) => true,
(NodeRole::Peer, NodeRole::Peer) => true,
_ => false,
}
}
}
impl BoundedNode for RoleBasedNode {
fn can_add_edge(
&self,
dir: Direction,
existing_edge_count: usize,
other_node: &Self,
) -> bool {
if existing_edge_count >= self.max_connections {
return false;
}
match dir {
Direction::Outgoing => self.can_connect_to(other_node),
Direction::Incoming => other_node.can_connect_to(self),
}
}
}
impl ImmutableEdgeBounds for RoleBasedNode {}
let mut graph = BoundedGraph::<RoleBasedNode, ()>::new();
let server1 = graph.add_node(RoleBasedNode::new(NodeRole::Server, 10));
let server2 = graph.add_node(RoleBasedNode::new(NodeRole::Server, 10));
let client1 = graph.add_node(RoleBasedNode::new(NodeRole::Client, 3));
let client2 = graph.add_node(RoleBasedNode::new(NodeRole::Client, 3));
let peer1 = graph.add_node(RoleBasedNode::new(NodeRole::Peer, 5));
let peer2 = graph.add_node(RoleBasedNode::new(NodeRole::Peer, 5));
assert!(graph.add_edge(server1, client1, ()).is_ok()); assert!(graph.add_edge(server1, client2, ()).is_ok());
assert!(graph.add_edge(client1, server2, ()).is_ok());
let result = graph.add_edge(client1, client2, ());
assert!(
result.is_err(),
"Client->Client should be rejected by role rules"
);
assert!(graph.add_edge(server1, peer1, ()).is_ok());
assert!(graph.add_edge(peer1, peer2, ()).is_ok());
assert!(graph.add_edge(peer1, server2, ()).is_ok());
let result = graph.add_edge(server1, server2, ());
assert!(
result.is_err(),
"Server->Server should be rejected by role rules"
);
let client3 = graph.add_node(RoleBasedNode::new(NodeRole::Client, 1));
assert!(graph.add_edge(client3, server1, ()).is_ok());
let result = graph.add_edge(client3, server2, ());
assert!(
result.is_err(),
"Should exceed capacity (max=1 outgoing for client3)"
);
assert_eq!(graph.edge_count(), 7);
println!("Successfully demonstrated relational constraints using other_node parameter!");
}