use super::alg;
use super::*;
#[test]
fn has_no_nodes_by_default() {
let g: Graph<i32, i32> = Graph::new();
assert_eq!(g.node_count(), 0);
}
#[test]
fn has_no_edges_by_default() {
let g: Graph<i32, i32> = Graph::new();
assert_eq!(g.edge_count(), 0);
}
#[test]
fn defaults_to_directed() {
let g: Graph<i32, i32> = Graph::new();
assert!(g.is_directed());
}
#[test]
fn defaults_to_non_multigraph() {
let g: Graph<i32, i32> = Graph::new();
assert!(!g.is_multigraph());
}
#[test]
fn defaults_to_non_compound() {
let g: Graph<i32, i32> = Graph::new();
assert!(!g.is_compound());
}
#[test]
fn can_create_undirected_graph() {
let g: Graph<i32, i32> = Graph::with_options(GraphOptions {
directed: false,
..Default::default()
});
assert!(!g.is_directed());
}
#[test]
fn can_create_multigraph() {
let g: Graph<i32, i32> = Graph::with_options(GraphOptions {
multigraph: true,
..Default::default()
});
assert!(g.is_multigraph());
}
#[test]
fn can_create_compound_graph() {
let g: Graph<i32, i32> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
assert!(g.is_compound());
}
#[test]
fn set_node_creates_node() {
let mut g: Graph<&str, ()> = Graph::new();
g.set_node("a", None);
assert!(g.has_node("a"));
assert_eq!(g.node_count(), 1);
}
#[test]
fn set_node_with_label() {
let mut g: Graph<i32, ()> = Graph::new();
g.set_node("a", Some(42));
assert_eq!(g.node("a"), Some(&42));
}
#[test]
fn set_node_overwrites_label() {
let mut g: Graph<i32, ()> = Graph::new();
g.set_node("a", Some(1));
g.set_node("a", Some(2));
assert_eq!(g.node("a"), Some(&2));
assert_eq!(g.node_count(), 1);
}
#[test]
fn set_node_does_not_overwrite_with_none() {
let mut g: Graph<i32, ()> = Graph::new();
g.set_node("a", Some(42));
g.set_node("a", None);
assert_eq!(g.node("a"), Some(&42));
}
#[test]
fn set_node_with_default_label() {
let mut g: Graph<String, ()> = Graph::new();
g.set_default_node_label(|v| format!("label-{}", v));
g.set_node("a", None);
assert_eq!(g.node("a"), Some(&"label-a".to_string()));
}
#[test]
fn node_returns_none_for_nonexistent() {
let g: Graph<i32, ()> = Graph::new();
assert_eq!(g.node("a"), None);
}
#[test]
fn remove_node_removes_node() {
let mut g: Graph<i32, ()> = Graph::new();
g.set_node("a", Some(1));
g.remove_node("a");
assert!(!g.has_node("a"));
assert_eq!(g.node_count(), 0);
}
#[test]
fn remove_node_returns_label() {
let mut g: Graph<i32, ()> = Graph::new();
g.set_node("a", Some(42));
assert_eq!(g.remove_node("a"), Some(42));
}
#[test]
fn remove_node_removes_incident_edges() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("b", "c", None, None);
g.remove_node("b");
assert_eq!(g.edge_count(), 0);
}
#[test]
fn remove_nonexistent_node_returns_none() {
let mut g: Graph<i32, ()> = Graph::new();
assert_eq!(g.remove_node("a"), None);
}
#[test]
fn nodes_returns_all_nodes() {
let mut g: Graph<(), ()> = Graph::new();
g.set_node("a", None);
g.set_node("b", None);
let mut nodes = g.nodes();
nodes.sort();
assert_eq!(nodes, vec!["a", "b"]);
}
#[test]
fn sources_returns_nodes_with_no_in_edges() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("b", "c", None, None);
let mut sources = g.sources();
sources.sort();
assert_eq!(sources, vec!["a"]);
}
#[test]
fn sinks_returns_nodes_with_no_out_edges() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("b", "c", None, None);
let mut sinks = g.sinks();
sinks.sort();
assert_eq!(sinks, vec!["c"]);
}
#[test]
fn set_edge_creates_edge() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
assert!(g.has_edge("a", "b", None));
assert_eq!(g.edge_count(), 1);
}
#[test]
fn set_edge_creates_nodes_if_needed() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
assert!(g.has_node("a"));
assert!(g.has_node("b"));
}
#[test]
fn set_edge_with_label() {
let mut g: Graph<(), i32> = Graph::new();
g.set_edge("a", "b", Some(42), None);
assert_eq!(g.edge("a", "b", None), Some(&42));
}
#[test]
fn set_edge_overwrites_label() {
let mut g: Graph<(), i32> = Graph::new();
g.set_edge("a", "b", Some(1), None);
g.set_edge("a", "b", Some(2), None);
assert_eq!(g.edge("a", "b", None), Some(&2));
assert_eq!(g.edge_count(), 1);
}
#[test]
fn set_edge_does_not_overwrite_with_none() {
let mut g: Graph<(), i32> = Graph::new();
g.set_edge("a", "b", Some(42), None);
g.set_edge("a", "b", None, None);
assert_eq!(g.edge("a", "b", None), Some(&42));
}
#[test]
fn set_edge_with_default_label() {
let mut g: Graph<(), String> = Graph::new();
g.set_default_edge_label(|_| "default".to_string());
g.set_edge("a", "b", None, None);
assert_eq!(g.edge("a", "b", None), Some(&"default".to_string()));
}
#[test]
fn edge_returns_none_for_nonexistent() {
let g: Graph<(), i32> = Graph::new();
assert_eq!(g.edge("a", "b", None), None);
}
#[test]
fn remove_edge_removes_edge() {
let mut g: Graph<(), i32> = Graph::new();
g.set_edge("a", "b", Some(42), None);
g.remove_edge("a", "b", None);
assert!(!g.has_edge("a", "b", None));
assert_eq!(g.edge_count(), 0);
}
#[test]
fn remove_edge_returns_label() {
let mut g: Graph<(), i32> = Graph::new();
g.set_edge("a", "b", Some(42), None);
assert_eq!(g.remove_edge("a", "b", None), Some(42));
}
#[test]
fn remove_edge_keeps_nodes() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.remove_edge("a", "b", None);
assert!(g.has_node("a"));
assert!(g.has_node("b"));
}
#[test]
fn edges_returns_all_edges() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("b", "c", None, None);
assert_eq!(g.edge_count(), 2);
assert_eq!(g.edges().len(), 2);
}
#[test]
fn directed_edge_has_direction() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
assert!(g.has_edge("a", "b", None));
assert!(!g.has_edge("b", "a", None));
}
#[test]
fn in_edges_returns_incoming() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("c", "b", None, None);
let in_e = g.in_edges("b", None).unwrap();
assert_eq!(in_e.len(), 2);
}
#[test]
fn in_edges_filtered_by_source() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("c", "b", None, None);
let in_e = g.in_edges("b", Some("a")).unwrap();
assert_eq!(in_e.len(), 1);
assert_eq!(in_e[0].v, "a");
}
#[test]
fn out_edges_returns_outgoing() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("a", "c", None, None);
let out_e = g.out_edges("a", None).unwrap();
assert_eq!(out_e.len(), 2);
}
#[test]
fn out_edges_filtered_by_target() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("a", "c", None, None);
let out_e = g.out_edges("a", Some("b")).unwrap();
assert_eq!(out_e.len(), 1);
assert_eq!(out_e[0].w, "b");
}
#[test]
fn node_edges_returns_all_incident() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("c", "b", None, None);
let edges = g.node_edges("b", None).unwrap();
assert_eq!(edges.len(), 2);
}
#[test]
fn predecessors_returns_incoming_nodes() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("c", "b", None, None);
let mut preds = g.predecessors("b").unwrap();
preds.sort();
assert_eq!(preds, vec!["a", "c"]);
}
#[test]
fn successors_returns_outgoing_nodes() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("a", "c", None, None);
let mut succs = g.successors("a").unwrap();
succs.sort();
assert_eq!(succs, vec!["b", "c"]);
}
#[test]
fn neighbors_returns_all_adjacent() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("c", "a", None, None);
let mut neighbors = g.neighbors("a").unwrap();
neighbors.sort();
assert_eq!(neighbors, vec!["b", "c"]);
}
#[test]
fn undirected_edge_both_directions() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
directed: false,
..Default::default()
});
g.set_edge("a", "b", None, None);
assert!(g.has_edge("a", "b", None));
assert!(g.has_edge("b", "a", None));
}
#[test]
fn undirected_predecessors_and_successors_are_same() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
directed: false,
..Default::default()
});
g.set_edge("a", "b", None, None);
let preds = g.predecessors("b").unwrap();
let succs = g.successors("b").unwrap();
assert_eq!(preds, succs);
}
#[test]
fn multigraph_allows_named_edges() {
let mut g: Graph<(), i32> = Graph::with_options(GraphOptions {
multigraph: true,
..Default::default()
});
g.set_edge("a", "b", Some(1), Some("x"));
g.set_edge("a", "b", Some(2), Some("y"));
assert_eq!(g.edge("a", "b", Some("x")), Some(&1));
assert_eq!(g.edge("a", "b", Some("y")), Some(&2));
assert_eq!(g.edge_count(), 2);
}
#[test]
fn multigraph_remove_named_edge() {
let mut g: Graph<(), i32> = Graph::with_options(GraphOptions {
multigraph: true,
..Default::default()
});
g.set_edge("a", "b", Some(1), Some("x"));
g.set_edge("a", "b", Some(2), Some("y"));
g.remove_edge("a", "b", Some("x"));
assert!(!g.has_edge("a", "b", Some("x")));
assert!(g.has_edge("a", "b", Some("y")));
assert_eq!(g.edge_count(), 1);
}
#[test]
fn compound_set_parent() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
g.set_node("a", None);
g.set_node("b", None);
g.set_parent("b", Some("a"));
assert_eq!(g.parent("b"), Some("a"));
}
#[test]
fn compound_children_of_parent() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
g.set_node("a", None);
g.set_node("b", None);
g.set_node("c", None);
g.set_parent("b", Some("a"));
g.set_parent("c", Some("a"));
let mut children = g.children(Some("a"));
children.sort();
assert_eq!(children, vec!["b", "c"]);
}
#[test]
fn compound_top_level_nodes() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
g.set_node("a", None);
g.set_node("b", None);
g.set_parent("b", Some("a"));
let mut top = g.children(None);
top.sort();
assert_eq!(top, vec!["a"]);
}
#[test]
fn compound_parent_defaults_to_none() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
g.set_node("a", None);
assert_eq!(g.parent("a"), None);
}
#[test]
fn compound_reset_parent_to_root() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
g.set_node("a", None);
g.set_node("b", None);
g.set_parent("b", Some("a"));
g.set_parent("b", None);
assert_eq!(g.parent("b"), None);
assert!(g.children(Some("a")).is_empty());
}
#[test]
fn compound_remove_node_reparents_children() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
g.set_node("a", None);
g.set_node("b", None);
g.set_node("c", None);
g.set_parent("b", Some("a"));
g.set_parent("c", Some("b"));
g.remove_node("b");
assert_eq!(g.parent("c"), Some("a"));
}
#[test]
fn compound_set_parent_creates_nodes() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
g.set_parent("b", Some("a"));
assert!(g.has_node("a"));
assert!(g.has_node("b"));
assert_eq!(g.parent("b"), Some("a"));
}
#[test]
fn set_path_creates_chain() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["a", "b", "c"], None);
assert!(g.has_edge("a", "b", None));
assert!(g.has_edge("b", "c", None));
assert!(!g.has_edge("a", "c", None));
}
#[test]
fn is_leaf_for_sink_node() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
assert!(g.is_leaf("b"));
assert!(!g.is_leaf("a"));
}
#[test]
fn is_leaf_for_isolated_node() {
let mut g: Graph<(), ()> = Graph::new();
g.set_node("a", None);
assert!(g.is_leaf("a"));
}
#[test]
fn filter_nodes_copies_matching_nodes() {
let mut g: Graph<i32, ()> = Graph::new();
g.set_node("a", Some(1));
g.set_node("b", Some(2));
let g2 = g.filter_nodes(|v| v == "a");
assert!(g2.has_node("a"));
assert!(!g2.has_node("b"));
assert_eq!(g2.node("a"), Some(&1));
}
#[test]
fn filter_nodes_copies_edges_for_matching_nodes() {
let mut g: Graph<(), i32> = Graph::new();
g.set_edge("a", "b", Some(1), None);
g.set_edge("b", "c", Some(2), None);
let g2 = g.filter_nodes(|v| v != "c");
assert!(g2.has_edge("a", "b", None));
assert!(!g2.has_edge("b", "c", None));
}
#[test]
fn filter_nodes_preserves_compound_parents() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
g.set_node("a", None);
g.set_node("b", None);
g.set_node("c", None);
g.set_parent("b", Some("a"));
g.set_parent("c", Some("b"));
let g2 = g.filter_nodes(|v| v != "b");
assert_eq!(g2.parent("c"), Some("a"));
}
#[test]
fn node_mut_allows_modification() {
let mut g: Graph<i32, ()> = Graph::new();
g.set_node("a", Some(1));
*g.node_mut("a").unwrap() = 42;
assert_eq!(g.node("a"), Some(&42));
}
#[test]
fn topsort_empty_graph() {
let g: Graph<(), ()> = Graph::new();
assert_eq!(alg::topsort(&g).unwrap(), Vec::<String>::new());
}
#[test]
fn topsort_simple_chain() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["a", "b", "c"], None);
assert_eq!(alg::topsort(&g).unwrap(), vec!["a", "b", "c"]);
}
#[test]
fn topsort_diamond() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["a", "b", "d"], None);
g.set_path(&["a", "c", "d"], None);
let result = alg::topsort(&g).unwrap();
let pos = |v: &str| result.iter().position(|x| x == v).unwrap();
assert!(pos("a") < pos("b"));
assert!(pos("a") < pos("c"));
assert!(pos("b") < pos("d"));
assert!(pos("c") < pos("d"));
}
#[test]
fn topsort_detects_cycle() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["a", "b", "c", "a"], None);
assert!(alg::topsort(&g).is_err());
}
#[test]
fn topsort_detects_self_loop() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "a", None, None);
assert!(alg::topsort(&g).is_err());
}
#[test]
fn is_acyclic_true_for_dag() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["a", "b", "c"], None);
assert!(alg::is_acyclic(&g));
}
#[test]
fn is_acyclic_false_for_cyclic() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["a", "b", "c", "a"], None);
assert!(!alg::is_acyclic(&g));
}
#[test]
fn is_acyclic_false_for_self_loop() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "a", None, None);
assert!(!alg::is_acyclic(&g));
}
#[test]
fn tarjan_empty_graph() {
let g: Graph<(), ()> = Graph::new();
assert!(alg::tarjan(&g).is_empty());
}
#[test]
fn tarjan_single_node() {
let mut g: Graph<(), ()> = Graph::new();
g.set_node("a", None);
let sccs = alg::tarjan(&g);
assert_eq!(sccs.len(), 1);
assert_eq!(sccs[0], vec!["a"]);
}
#[test]
fn tarjan_cycle() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["a", "b", "c", "a"], None);
let sccs = alg::tarjan(&g);
let big_scc: Vec<&Vec<String>> = sccs.iter().filter(|s| s.len() > 1).collect();
assert_eq!(big_scc.len(), 1);
let mut scc = big_scc[0].clone();
scc.sort();
assert_eq!(scc, vec!["a", "b", "c"]);
}
#[test]
fn tarjan_two_cycles() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["a", "b", "a"], None);
g.set_path(&["c", "d", "c"], None);
let sccs = alg::tarjan(&g);
let mut big_sccs: Vec<Vec<String>> = sccs
.into_iter()
.filter(|s| s.len() > 1)
.map(|mut s| {
s.sort();
s
})
.collect();
big_sccs.sort();
assert_eq!(big_sccs.len(), 2);
assert_eq!(big_sccs[0], vec!["a", "b"]);
assert_eq!(big_sccs[1], vec!["c", "d"]);
}
#[test]
fn find_cycles_empty() {
let g: Graph<(), ()> = Graph::new();
assert!(alg::find_cycles(&g).is_empty());
}
#[test]
fn find_cycles_acyclic() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["a", "b", "c"], None);
assert!(alg::find_cycles(&g).is_empty());
}
#[test]
fn find_cycles_self_loop() {
let mut g: Graph<(), ()> = Graph::new();
g.set_node("a", None);
g.set_edge("a", "a", None, None);
let cycles = alg::find_cycles(&g);
assert_eq!(cycles.len(), 1);
}
#[test]
fn components_empty() {
let g: Graph<(), ()> = Graph::new();
assert!(alg::components(&g).is_empty());
}
#[test]
fn components_unconnected_nodes() {
let mut g: Graph<(), ()> = Graph::new();
g.set_node("a", None);
g.set_node("b", None);
let comps = alg::components(&g);
assert_eq!(comps.len(), 2);
}
#[test]
fn components_connected() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("b", "c", None, None);
let comps = alg::components(&g);
assert_eq!(comps.len(), 1);
assert_eq!(comps[0].len(), 3);
}
#[test]
fn components_two_components() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("c", "d", None, None);
let comps = alg::components(&g);
assert_eq!(comps.len(), 2);
}
#[test]
fn preorder_singleton() {
let mut g: Graph<(), ()> = Graph::new();
g.set_node("a", None);
assert_eq!(alg::preorder(&g, &["a"]), vec!["a"]);
}
#[test]
fn preorder_tree() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("a", "c", None, None);
let result = alg::preorder(&g, &["a"]);
assert_eq!(result[0], "a");
assert_eq!(result.len(), 3);
}
#[test]
fn postorder_singleton() {
let mut g: Graph<(), ()> = Graph::new();
g.set_node("a", None);
assert_eq!(alg::postorder(&g, &["a"]), vec!["a"]);
}
#[test]
fn postorder_tree() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("a", "c", None, None);
let result = alg::postorder(&g, &["a"]);
assert_eq!(result.last().unwrap(), "a");
assert_eq!(result.len(), 3);
}
#[test]
fn dfs_visits_each_node_once() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["a", "b", "d"], None);
g.set_path(&["a", "c", "d"], None);
let result = alg::preorder(&g, &["a"]);
assert_eq!(result.len(), 4);
let unique: std::collections::HashSet<_> = result.iter().collect();
assert_eq!(unique.len(), 4);
}
#[test]
fn dijkstra_simple_path() {
let mut g: Graph<(), f64> = Graph::new();
g.set_edge("a", "b", Some(1.0), None);
g.set_edge("b", "c", Some(2.0), None);
g.set_edge("a", "c", Some(10.0), None);
let result = alg::dijkstra(&g, "a", |w| *w);
assert_eq!(result["a"].0, 0.0);
assert_eq!(result["b"].0, 1.0);
assert_eq!(result["c"].0, 3.0); }
#[test]
fn prim_simple_graph() {
let mut g: Graph<(), f64> = Graph::with_options(GraphOptions {
directed: false,
..Default::default()
});
g.set_edge("a", "b", Some(1.0), None);
g.set_edge("b", "c", Some(2.0), None);
g.set_edge("a", "c", Some(5.0), None);
let mst = alg::prim(&g, |w| *w);
assert_eq!(mst.node_count(), 3);
assert_eq!(mst.edge_count(), 2);
assert!(mst.has_edge("a", "b", None));
assert!(mst.has_edge("b", "c", None));
}
#[test]
fn set_node_is_idempotent() {
let mut g: Graph<&str, ()> = Graph::new();
g.set_node("a", Some("foo"));
g.set_node("a", Some("foo"));
assert_eq!(g.node("a"), Some(&"foo"));
assert_eq!(g.node_count(), 1);
}
#[test]
fn nodes_empty_graph() {
let g: Graph<(), ()> = Graph::new();
assert_eq!(g.nodes(), Vec::<String>::new());
}
#[test]
fn remove_node_is_idempotent() {
let mut g: Graph<(), ()> = Graph::new();
g.set_node("a", None);
g.remove_node("a");
let result = g.remove_node("a");
assert_eq!(result, None);
assert!(!g.has_node("a"));
assert_eq!(g.node_count(), 0);
}
#[test]
fn sources_includes_isolated_nodes() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["a", "b", "c"], None);
g.set_node("d", None);
let mut sources = g.sources();
sources.sort();
assert_eq!(sources, vec!["a", "d"]);
}
#[test]
fn sinks_includes_isolated_nodes() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["a", "b", "c"], None);
g.set_node("d", None);
let mut sinks = g.sinks();
sinks.sort();
assert_eq!(sinks, vec!["c", "d"]);
}
#[test]
fn filter_nodes_returns_identical_graph_for_all_pass() {
let mut g: Graph<i32, i32> = Graph::new();
g.set_node("a", Some(123));
g.set_path(&["a", "b", "c"], None);
g.set_edge("a", "c", Some(456), None);
let g2 = g.filter_nodes(|_| true);
let mut nodes = g2.nodes();
nodes.sort();
assert_eq!(nodes, vec!["a", "b", "c"]);
assert!(g2.has_edge("a", "b", None));
assert!(g2.has_edge("b", "c", None));
assert!(g2.has_edge("a", "c", None));
assert_eq!(g2.node("a"), Some(&123));
assert_eq!(g2.edge("a", "c", None), Some(&456));
}
#[test]
fn filter_nodes_returns_empty_graph_for_none_pass() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["a", "b", "c"], None);
let g2 = g.filter_nodes(|_| false);
assert_eq!(g2.nodes().len(), 0);
assert_eq!(g2.edges().len(), 0);
}
#[test]
fn filter_nodes_removes_edges_to_removed_nodes() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
let g2 = g.filter_nodes(|v| v == "a");
let mut nodes = g2.nodes();
nodes.sort();
assert_eq!(nodes, vec!["a"]);
assert_eq!(g2.edges().len(), 0);
}
#[test]
fn filter_nodes_preserves_directed_option() {
let g: Graph<(), ()> = Graph::new();
assert!(g.filter_nodes(|_| true).is_directed());
let g: Graph<(), ()> = Graph::with_options(GraphOptions {
directed: false,
..Default::default()
});
assert!(!g.filter_nodes(|_| true).is_directed());
}
#[test]
fn filter_nodes_preserves_multigraph_option() {
let g: Graph<(), ()> = Graph::with_options(GraphOptions {
multigraph: true,
..Default::default()
});
assert!(g.filter_nodes(|_| true).is_multigraph());
let g: Graph<(), ()> = Graph::new();
assert!(!g.filter_nodes(|_| true).is_multigraph());
}
#[test]
fn filter_nodes_preserves_compound_option() {
let g: Graph<(), ()> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
assert!(g.filter_nodes(|_| true).is_compound());
let g: Graph<(), ()> = Graph::new();
assert!(!g.filter_nodes(|_| true).is_compound());
}
#[test]
fn filter_nodes_includes_subgraphs() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
g.set_parent("a", Some("parent"));
let g2 = g.filter_nodes(|_| true);
assert_eq!(g2.parent("a"), Some("parent"));
}
#[test]
fn filter_nodes_includes_multi_level_subgraphs() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
g.set_parent("a", Some("parent"));
g.set_parent("parent", Some("root"));
let g2 = g.filter_nodes(|_| true);
assert_eq!(g2.parent("a"), Some("parent"));
assert_eq!(g2.parent("parent"), Some("root"));
}
#[test]
fn filter_nodes_promotes_child_when_parent_filtered_out() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
g.set_parent("a", Some("parent"));
g.set_parent("parent", Some("root"));
let g2 = g.filter_nodes(|v| v != "parent");
assert_eq!(g2.parent("a"), Some("root"));
}
#[test]
#[should_panic]
fn set_parent_throws_on_non_compound() {
let mut g: Graph<(), ()> = Graph::new();
g.set_parent("a", Some("b"));
}
#[test]
fn set_parent_moves_node_to_new_parent() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
g.set_parent("a", Some("parent"));
g.set_parent("a", Some("parent2"));
assert_eq!(g.parent("a"), Some("parent2"));
assert!(g.children(Some("parent")).is_empty());
assert_eq!(g.children(Some("parent2")), vec!["a"]);
}
#[test]
fn set_parent_idempotent_remove() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
g.set_parent("a", Some("parent"));
g.set_parent("a", None);
g.set_parent("a", None);
assert_eq!(g.parent("a"), None);
let mut top = g.children(None);
top.sort();
assert_eq!(top, vec!["a", "parent"]);
}
#[test]
fn parent_returns_none_for_non_compound() {
let g: Graph<(), ()> = Graph::new();
assert_eq!(g.parent("a"), None);
}
#[test]
fn parent_returns_none_for_unknown_node() {
let g: Graph<(), ()> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
assert_eq!(g.parent("a"), None);
}
#[test]
fn parent_returns_current_assignment() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
g.set_node("a", None);
g.set_node("parent", None);
g.set_parent("a", Some("parent"));
assert_eq!(g.parent("a"), Some("parent"));
}
#[test]
fn children_defaults_to_empty_for_new_compound_node() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
g.set_node("a", None);
assert!(g.children(Some("a")).is_empty());
}
#[test]
fn children_returns_empty_for_non_compound_leaf() {
let mut g: Graph<(), ()> = Graph::new();
g.set_node("a", None);
assert!(g.children(Some("a")).is_empty());
}
#[test]
fn children_returns_all_nodes_for_non_compound_root() {
let mut g: Graph<(), ()> = Graph::new();
g.set_node("a", None);
g.set_node("b", None);
let mut children = g.children(None);
children.sort();
assert_eq!(children, vec!["a", "b"]);
}
#[test]
fn children_of_parent_compound() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
g.set_parent("a", Some("parent"));
g.set_parent("b", Some("parent"));
let mut children = g.children(Some("parent"));
children.sort();
assert_eq!(children, vec!["a", "b"]);
}
#[test]
fn children_without_parent_returns_top_level() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
g.set_node("a", None);
g.set_node("b", None);
g.set_node("c", None);
g.set_node("parent", None);
g.set_parent("a", Some("parent"));
let mut top = g.children(None);
top.sort();
assert_eq!(top, vec!["b", "c", "parent"]);
}
#[test]
fn remove_node_removes_compound_relationships() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
g.set_parent("c", Some("b"));
g.set_parent("b", Some("a"));
g.remove_node("b");
assert_eq!(g.parent("b"), None);
assert!(g.children(Some("b")).is_empty());
let a_children = g.children(Some("a"));
assert!(!a_children.contains(&"b".to_string()));
assert_eq!(g.parent("c"), Some("a"));
}
#[test]
fn predecessors_returns_none_for_unknown_node() {
let g: Graph<(), ()> = Graph::new();
assert_eq!(g.predecessors("a"), None);
}
#[test]
fn predecessors_includes_self_loops() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("b", "c", None, None);
g.set_edge("a", "a", None, None);
let mut preds = g.predecessors("a").unwrap();
preds.sort();
assert_eq!(preds, vec!["a"]);
}
#[test]
fn successors_returns_none_for_unknown_node() {
let g: Graph<(), ()> = Graph::new();
assert_eq!(g.successors("a"), None);
}
#[test]
fn successors_includes_self_loops() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("b", "c", None, None);
g.set_edge("a", "a", None, None);
let mut succs = g.successors("a").unwrap();
succs.sort();
assert_eq!(succs, vec!["a", "b"]);
}
#[test]
fn successors_returns_empty_for_leaf() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("b", "c", None, None);
let succs = g.successors("c").unwrap();
assert!(succs.is_empty());
}
#[test]
fn neighbors_returns_none_for_unknown_node() {
let g: Graph<(), ()> = Graph::new();
assert_eq!(g.neighbors("a"), None);
}
#[test]
fn neighbors_includes_self_loop() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("b", "c", None, None);
g.set_edge("a", "a", None, None);
let mut neighbors = g.neighbors("a").unwrap();
neighbors.sort();
assert_eq!(neighbors, vec!["a", "b"]);
}
#[test]
fn is_leaf_false_for_connected_undirected() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
directed: false,
..Default::default()
});
g.set_node("a", None);
g.set_node("b", None);
g.set_edge("a", "b", None, None);
assert!(!g.is_leaf("b"));
}
#[test]
fn is_leaf_true_for_unconnected_undirected() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
directed: false,
..Default::default()
});
g.set_node("a", None);
assert!(g.is_leaf("a"));
}
#[test]
fn is_leaf_true_for_unconnected_directed() {
let mut g: Graph<(), ()> = Graph::new();
g.set_node("a", None);
assert!(g.is_leaf("a"));
}
#[test]
fn is_leaf_false_for_predecessor_directed() {
let mut g: Graph<(), ()> = Graph::new();
g.set_node("a", None);
g.set_node("b", None);
g.set_edge("a", "b", None, None);
assert!(!g.is_leaf("a"));
}
#[test]
fn is_leaf_true_for_successor_directed() {
let mut g: Graph<(), ()> = Graph::new();
g.set_node("a", None);
g.set_node("b", None);
g.set_edge("a", "b", None, None);
assert!(g.is_leaf("b"));
}
#[test]
fn edges_empty_graph() {
let g: Graph<(), ()> = Graph::new();
assert!(g.edges().is_empty());
}
#[test]
fn set_path_with_label() {
let mut g: Graph<(), i32> = Graph::new();
g.set_path(&["a", "b", "c"], Some(42));
assert_eq!(g.edge("a", "b", None), Some(&42));
assert_eq!(g.edge("b", "c", None), Some(&42));
}
#[test]
fn set_edge_creates_multi_edge_only_in_multigraph() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
multigraph: true,
..Default::default()
});
g.set_edge("a", "b", None, Some("name"));
assert!(!g.has_edge("a", "b", None));
assert!(g.has_edge("a", "b", Some("name")));
}
#[test]
#[should_panic]
fn set_edge_panics_for_named_edge_on_non_multigraph() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, Some("name"));
}
#[test]
fn set_edge_directed_treats_opposite_as_distinct() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
assert!(g.has_edge("a", "b", None));
assert!(!g.has_edge("b", "a", None));
}
#[test]
fn set_edge_undirected_handles_both_directions() {
let mut g: Graph<(), i32> = Graph::with_options(GraphOptions {
directed: false,
..Default::default()
});
g.set_edge("a", "b", Some(42), None);
assert_eq!(g.edge("a", "b", None), Some(&42));
assert_eq!(g.edge("b", "a", None), Some(&42));
}
#[test]
fn remove_edge_no_effect_when_absent() {
let mut g: Graph<(), ()> = Graph::new();
g.remove_edge("a", "b", None);
assert!(!g.has_edge("a", "b", None));
assert_eq!(g.edge_count(), 0);
}
#[test]
fn remove_edge_correctly_removes_neighbors() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.remove_edge("a", "b", None);
assert_eq!(g.successors("a").unwrap(), Vec::<String>::new());
assert_eq!(g.neighbors("a").unwrap(), Vec::<String>::new());
assert_eq!(g.predecessors("b").unwrap(), Vec::<String>::new());
assert_eq!(g.neighbors("b").unwrap(), Vec::<String>::new());
}
#[test]
fn remove_edge_correctly_decrements_neighbor_count_multigraph() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
multigraph: true,
..Default::default()
});
g.set_edge("a", "b", None, None);
g.set_edge("a", "b", None, Some("foo"));
g.remove_edge("a", "b", None);
assert!(g.has_edge("a", "b", Some("foo")));
assert_eq!(g.successors("a").unwrap(), vec!["b"]);
assert_eq!(g.neighbors("a").unwrap(), vec!["b"]);
assert_eq!(g.predecessors("b").unwrap(), vec!["a"]);
assert_eq!(g.neighbors("b").unwrap(), vec!["a"]);
}
#[test]
fn remove_edge_works_with_undirected() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
directed: false,
..Default::default()
});
g.set_edge("h", "g", None, None);
g.remove_edge("g", "h", None);
assert_eq!(g.neighbors("g").unwrap(), Vec::<String>::new());
assert_eq!(g.neighbors("h").unwrap(), Vec::<String>::new());
}
#[test]
fn in_edges_returns_none_for_unknown_node() {
let g: Graph<(), ()> = Graph::new();
assert_eq!(g.in_edges("a", None), None);
}
#[test]
fn in_edges_returns_empty_for_source_node() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("b", "c", None, None);
let in_e = g.in_edges("a", None).unwrap();
assert!(in_e.is_empty());
}
#[test]
fn in_edges_works_for_multigraph() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
multigraph: true,
..Default::default()
});
g.set_edge("a", "b", None, None);
g.set_edge("a", "b", None, Some("bar"));
g.set_edge("a", "b", None, Some("foo"));
let in_e = g.in_edges("a", None).unwrap();
assert!(in_e.is_empty());
let in_b = g.in_edges("b", None).unwrap();
assert_eq!(in_b.len(), 3);
}
#[test]
fn out_edges_returns_none_for_unknown_node() {
let g: Graph<(), ()> = Graph::new();
assert_eq!(g.out_edges("a", None), None);
}
#[test]
fn out_edges_returns_all_outgoing() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("b", "c", None, None);
let out_a = g.out_edges("a", None).unwrap();
assert_eq!(out_a.len(), 1);
assert_eq!(out_a[0].v, "a");
assert_eq!(out_a[0].w, "b");
let out_b = g.out_edges("b", None).unwrap();
assert_eq!(out_b.len(), 1);
let out_c = g.out_edges("c", None).unwrap();
assert!(out_c.is_empty());
}
#[test]
fn out_edges_works_for_multigraph() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
multigraph: true,
..Default::default()
});
g.set_edge("a", "b", None, None);
g.set_edge("a", "b", None, Some("bar"));
g.set_edge("a", "b", None, Some("foo"));
let out_a = g.out_edges("a", None).unwrap();
assert_eq!(out_a.len(), 3);
let out_b = g.out_edges("b", None).unwrap();
assert!(out_b.is_empty());
}
#[test]
fn node_edges_returns_none_for_unknown_node() {
let g: Graph<(), ()> = Graph::new();
assert_eq!(g.node_edges("a", None), None);
}
#[test]
fn node_edges_returns_all_incident_edges() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("b", "c", None, None);
let ne_a = g.node_edges("a", None).unwrap();
assert_eq!(ne_a.len(), 1);
let ne_b = g.node_edges("b", None).unwrap();
assert_eq!(ne_b.len(), 2);
let ne_c = g.node_edges("c", None).unwrap();
assert_eq!(ne_c.len(), 1);
}
#[test]
fn node_edges_works_for_multigraph() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
multigraph: true,
..Default::default()
});
g.set_edge("a", "b", None, None);
g.set_edge("a", "b", None, Some("bar"));
g.set_edge("a", "b", None, Some("foo"));
let ne_a = g.node_edges("a", None).unwrap();
assert_eq!(ne_a.len(), 3);
let ne_b = g.node_edges("b", None).unwrap();
assert_eq!(ne_b.len(), 3);
}
#[test]
fn undirected_in_edges_and_out_edges_are_same() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
directed: false,
..Default::default()
});
g.set_edge("a", "b", None, None);
let in_b = g.in_edges("b", None).unwrap();
let out_b = g.out_edges("b", None).unwrap();
assert_eq!(in_b.len(), out_b.len());
}
#[test]
fn undirected_edge_retrieval_either_direction() {
let mut g: Graph<(), i32> = Graph::with_options(GraphOptions {
directed: false,
..Default::default()
});
g.set_edge("a", "b", Some(42), None);
assert_eq!(g.edge("a", "b", None), Some(&42));
assert_eq!(g.edge("b", "a", None), Some(&42));
}
#[test]
fn undirected_neighbors_returns_all() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
directed: false,
..Default::default()
});
g.set_edge("a", "b", None, None);
g.set_edge("b", "c", None, None);
let mut neighbors = g.neighbors("b").unwrap();
neighbors.sort();
assert_eq!(neighbors, vec!["a", "c"]);
}
#[test]
fn set_default_edge_label_does_not_change_existing() {
let mut g: Graph<(), String> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_default_edge_label(|_| "foo".to_string());
assert_eq!(g.edge("a", "b", None), None);
}
#[test]
fn set_default_edge_label_not_used_with_explicit_value() {
let mut g: Graph<(), String> = Graph::new();
g.set_default_edge_label(|_| "foo".to_string());
g.set_edge("a", "b", Some("bar".to_string()), None);
assert_eq!(g.edge("a", "b", None), Some(&"bar".to_string()));
}
#[test]
fn set_default_node_label_does_not_change_existing() {
let mut g: Graph<String, ()> = Graph::new();
g.set_node("a", None);
g.set_default_node_label(|_| "foo".to_string());
assert_eq!(g.node("a"), None);
}
#[test]
fn set_default_node_label_not_used_with_explicit_value() {
let mut g: Graph<String, ()> = Graph::new();
g.set_default_node_label(|_| "foo".to_string());
g.set_node("a", Some("bar".to_string()));
assert_eq!(g.node("a"), Some(&"bar".to_string()));
}
#[test]
fn dijkstra_distance_zero_for_source() {
let mut g: Graph<(), f64> = Graph::new();
g.set_node("source", None);
let result = alg::dijkstra(&g, "source", |w| *w);
assert_eq!(result["source"].0, 0.0);
}
#[test]
fn dijkstra_infinity_for_unconnected() {
let mut g: Graph<(), f64> = Graph::new();
g.set_node("a", None);
g.set_node("b", None);
let result = alg::dijkstra(&g, "a", |w| *w);
assert_eq!(result["a"].0, 0.0);
assert!(result["b"].0.is_infinite());
}
#[test]
fn dijkstra_distance_and_path() {
let mut g: Graph<(), f64> = Graph::new();
g.set_edge("a", "b", Some(1.0), None);
g.set_edge("b", "c", Some(1.0), None);
g.set_edge("b", "d", Some(1.0), None);
let result = alg::dijkstra(&g, "a", |w| *w);
assert_eq!(result["a"].0, 0.0);
assert_eq!(result["b"].0, 1.0);
assert_eq!(result["c"].0, 2.0);
assert_eq!(result["d"].0, 2.0);
assert_eq!(result["b"].1, Some("a".to_string()));
assert_eq!(result["c"].1, Some("b".to_string()));
}
#[test]
fn dijkstra_works_for_undirected() {
let mut g: Graph<(), f64> = Graph::with_options(GraphOptions {
directed: false,
..Default::default()
});
g.set_edge("a", "b", Some(1.0), None);
g.set_edge("b", "c", Some(2.0), None);
g.set_edge("b", "d", Some(3.0), None);
let result = alg::dijkstra(&g, "a", |w| *w);
assert_eq!(result["a"].0, 0.0);
assert_eq!(result["b"].0, 1.0);
assert_eq!(result["c"].0, 3.0);
assert_eq!(result["d"].0, 4.0);
}
#[test]
fn prim_empty_graph() {
let g: Graph<(), f64> = Graph::with_options(GraphOptions {
directed: false,
..Default::default()
});
let mst = alg::prim(&g, |w| *w);
assert_eq!(mst.node_count(), 0);
assert_eq!(mst.edge_count(), 0);
}
#[test]
fn prim_single_node() {
let mut g: Graph<(), f64> = Graph::with_options(GraphOptions {
directed: false,
..Default::default()
});
g.set_node("a", None);
let mst = alg::prim(&g, |w| *w);
assert_eq!(mst.node_count(), 1);
assert_eq!(mst.edge_count(), 0);
}
#[test]
fn components_single_node() {
let mut g: Graph<(), ()> = Graph::new();
g.set_node("a", None);
let comps = alg::components(&g);
assert_eq!(comps.len(), 1);
assert_eq!(comps[0].len(), 1);
}
#[test]
fn find_cycles_finds_single_cycle() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["a", "b", "c", "a"], None);
let cycles = alg::find_cycles(&g);
assert_eq!(cycles.len(), 1);
let mut cycle = cycles[0].clone();
cycle.sort();
assert_eq!(cycle, vec!["a", "b", "c"]);
}
#[test]
fn find_cycles_finds_two_separate_cycles() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["a", "b", "a"], None);
g.set_path(&["c", "d", "c"], None);
let mut cycles = alg::find_cycles(&g);
assert_eq!(cycles.len(), 2);
for cycle in &mut cycles {
cycle.sort();
}
cycles.sort();
assert_eq!(cycles[0], vec!["a", "b"]);
assert_eq!(cycles[1], vec!["c", "d"]);
}
#[test]
fn tarjan_chain_all_singletons() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["a", "b", "c"], None);
let sccs = alg::tarjan(&g);
assert_eq!(sccs.len(), 3);
for scc in &sccs {
assert_eq!(scc.len(), 1);
}
}
#[test]
fn preorder_multiple_roots() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("c", "d", None, None);
let result = alg::preorder(&g, &["a", "c"]);
assert_eq!(result.len(), 4);
let pos = |v: &str| result.iter().position(|x| x == v).unwrap();
assert!(pos("a") < pos("b"));
assert!(pos("c") < pos("d"));
}
#[test]
fn postorder_multiple_roots() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("c", "d", None, None);
let result = alg::postorder(&g, &["a", "c"]);
assert_eq!(result.len(), 4);
let pos = |v: &str| result.iter().position(|x| x == v).unwrap();
assert!(pos("b") < pos("a"));
assert!(pos("d") < pos("c"));
}
#[test]
fn set_graph_label_and_get() {
let mut g: Graph<(), ()> = Graph::new();
g.set_graph_label("foo".to_string());
assert_eq!(g.graph_label::<String>(), Some(&"foo".to_string()));
}
#[test]
fn graph_label_defaults_to_none() {
let g: Graph<(), ()> = Graph::new();
assert_eq!(g.graph_label::<String>(), None);
}
#[test]
fn set_node_creates_with_no_label() {
let mut g: Graph<&str, ()> = Graph::new();
g.set_node("a", None);
assert!(g.has_node("a"));
assert_eq!(g.node("a"), None);
assert_eq!(g.node_count(), 1);
}
#[test]
fn set_node_uses_stringified_id() {
let mut g: Graph<(), ()> = Graph::new();
g.set_node("1", None);
assert!(g.has_node("1"));
assert_eq!(g.nodes(), vec!["1"]);
}
#[test]
fn set_default_node_label_with_constant_fn() {
let mut g: Graph<String, ()> = Graph::new();
g.set_default_node_label(|_| "foo".to_string());
g.set_node("a", None);
assert_eq!(g.node("a"), Some(&"foo".to_string()));
}
#[test]
fn set_default_node_label_with_name_fn() {
let mut g: Graph<String, ()> = Graph::new();
g.set_default_node_label(|v| format!("{}-foo", v));
g.set_node("a", None);
assert_eq!(g.node("a"), Some(&"a-foo".to_string()));
}
#[test]
fn set_parent_creates_child_if_not_exist() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
g.set_node("parent", None);
g.set_parent("a", Some("parent"));
assert!(g.has_node("a"));
assert_eq!(g.parent("a"), Some("parent"));
}
#[test]
fn set_parent_has_parent_none_if_never_invoked() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
g.set_node("a", None);
assert_eq!(g.parent("a"), None);
}
#[test]
fn set_parent_removes_parent_on_none() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
g.set_parent("a", Some("parent"));
g.set_parent("a", None);
assert_eq!(g.parent("a"), None);
let mut top = g.children(None);
top.sort();
assert_eq!(top, vec!["a", "parent"]);
}
#[test]
fn set_parent_stringified_id() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
g.set_parent("2", Some("1"));
g.set_parent("3", Some("2"));
assert_eq!(g.parent("2"), Some("1"));
assert_eq!(g.parent("3"), Some("2"));
}
#[test]
#[should_panic]
fn set_parent_preserves_tree_invariant() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
g.set_parent("c", Some("b"));
g.set_parent("b", Some("a"));
g.set_parent("a", Some("c"));
}
#[test]
fn children_returns_empty_for_unknown_compound_node() {
let g: Graph<(), ()> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
assert!(g.children(Some("a")).is_empty());
}
#[test]
fn children_returns_empty_for_non_compound_without_node() {
let g: Graph<(), ()> = Graph::new();
assert!(g.children(Some("a")).is_empty());
}
#[test]
fn set_edge_changes_value_for_multi_edge() {
let mut g: Graph<(), &str> = Graph::with_options(GraphOptions {
multigraph: true,
..Default::default()
});
g.set_edge("a", "b", Some("value"), Some("name"));
g.set_edge("a", "b", None, Some("name"));
assert_eq!(g.edge("a", "b", Some("name")), Some(&"value"));
assert!(g.has_edge("a", "b", Some("name")));
}
#[test]
fn set_edge_undirected_id_order() {
let mut g: Graph<(), &str> = Graph::with_options(GraphOptions {
directed: false,
..Default::default()
});
g.set_edge("9", "10", Some("foo"), None);
assert!(g.has_edge("9", "10", None));
assert!(g.has_edge("10", "9", None));
assert_eq!(g.edge("9", "10", None), Some(&"foo"));
}
#[test]
fn set_edge_stringified_ids() {
let mut g: Graph<(), &str> = Graph::new();
g.set_edge("1", "2", Some("foo"), None);
let edges = g.edges();
assert_eq!(edges.len(), 1);
assert_eq!(edges[0].v, "1");
assert_eq!(edges[0].w, "2");
assert_eq!(g.edge("1", "2", None), Some(&"foo"));
}
#[test]
fn set_edge_stringified_ids_multigraph() {
let mut g: Graph<(), &str> = Graph::with_options(GraphOptions {
multigraph: true,
..Default::default()
});
g.set_edge("1", "2", Some("foo"), Some("3"));
assert_eq!(g.edge("1", "2", Some("3")), Some(&"foo"));
let edges = g.edges();
assert_eq!(edges.len(), 1);
assert_eq!(edges[0].v, "1");
assert_eq!(edges[0].w, "2");
assert_eq!(edges[0].name, Some("3".to_string()));
}
#[test]
fn set_default_edge_label_does_not_override_existing_multi_edge() {
let mut g: Graph<(), String> = Graph::with_options(GraphOptions {
multigraph: true,
..Default::default()
});
g.set_edge("a", "b", Some("old".to_string()), Some("name"));
g.set_default_edge_label(|_| "should not set this".to_string());
g.set_edge("a", "b", None, Some("name"));
assert_eq!(g.edge("a", "b", Some("name")), Some(&"old".to_string()));
}
#[test]
fn edge_returns_none_for_nonexistent_edge() {
let g: Graph<(), i32> = Graph::new();
assert_eq!(g.edge("a", "b", None), None);
assert_eq!(g.edge("a", "b", Some("foo")), None);
}
#[test]
fn edge_returns_value_of_multi_edge() {
let mut g: Graph<(), i32> = Graph::with_options(GraphOptions {
multigraph: true,
..Default::default()
});
g.set_edge("a", "b", Some(42), Some("foo"));
assert_eq!(g.edge("a", "b", Some("foo")), Some(&42));
assert_eq!(g.edge("a", "b", None), None);
}
#[test]
fn edge_undirected_returns_either_direction() {
let mut g: Graph<(), i32> = Graph::with_options(GraphOptions {
directed: false,
..Default::default()
});
g.set_edge("a", "b", Some(42), None);
assert_eq!(g.edge("a", "b", None), Some(&42));
assert_eq!(g.edge("b", "a", None), Some(&42));
}
#[test]
fn edge_by_obj_simple() {
let mut g: Graph<(), &str> = Graph::new();
g.set_edge("a", "b", Some("value"), None);
let e = Edge::new("a", "b");
assert_eq!(g.edge_by_obj(&e), Some(&"value"));
}
#[test]
fn edge_by_obj_multigraph() {
let mut g: Graph<(), &str> = Graph::with_options(GraphOptions {
multigraph: true,
..Default::default()
});
g.set_edge("a", "b", Some("value"), Some("name"));
let e = Edge::with_name("a", "b", "name");
assert_eq!(g.edge_by_obj(&e), Some(&"value"));
}
#[test]
fn remove_edge_by_name_multigraph() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
multigraph: true,
..Default::default()
});
g.set_edge("a", "b", None, Some("foo"));
g.remove_edge("a", "b", Some("foo"));
assert!(!g.has_edge("a", "b", Some("foo")));
assert_eq!(g.edge_count(), 0);
}
#[test]
fn in_edges_returns_edges_that_point_at_node() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("b", "c", None, None);
assert_eq!(g.in_edges("a", None).unwrap().len(), 0);
let in_b = g.in_edges("b", None).unwrap();
assert_eq!(in_b.len(), 1);
assert_eq!(in_b[0].v, "a");
assert_eq!(in_b[0].w, "b");
let in_c = g.in_edges("c", None).unwrap();
assert_eq!(in_c.len(), 1);
assert_eq!(in_c[0].v, "b");
assert_eq!(in_c[0].w, "c");
}
#[test]
fn in_edges_filtered_by_source_multigraph() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
multigraph: true,
..Default::default()
});
g.set_edge("a", "b", None, None);
g.set_edge("a", "b", None, Some("foo"));
g.set_edge("a", "c", None, None);
g.set_edge("b", "c", None, None);
g.set_edge("z", "a", None, None);
g.set_edge("z", "b", None, None);
assert_eq!(g.in_edges("a", Some("b")).unwrap().len(), 0);
let in_b_from_a = g.in_edges("b", Some("a")).unwrap();
assert_eq!(in_b_from_a.len(), 2);
}
#[test]
fn out_edges_returns_edges_from_node() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("b", "c", None, None);
let out_a = g.out_edges("a", None).unwrap();
assert_eq!(out_a.len(), 1);
assert_eq!(out_a[0].v, "a");
assert_eq!(out_a[0].w, "b");
let out_b = g.out_edges("b", None).unwrap();
assert_eq!(out_b.len(), 1);
assert_eq!(out_b[0].v, "b");
assert_eq!(out_b[0].w, "c");
let out_c = g.out_edges("c", None).unwrap();
assert!(out_c.is_empty());
}
#[test]
fn out_edges_filtered_by_target_multigraph() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
multigraph: true,
..Default::default()
});
g.set_edge("a", "b", None, None);
g.set_edge("a", "b", None, Some("foo"));
g.set_edge("a", "c", None, None);
g.set_edge("b", "c", None, None);
g.set_edge("z", "a", None, None);
g.set_edge("z", "b", None, None);
let out_a_to_b = g.out_edges("a", Some("b")).unwrap();
assert_eq!(out_a_to_b.len(), 2);
let out_b_to_a = g.out_edges("b", Some("a")).unwrap();
assert!(out_b_to_a.is_empty());
}
#[test]
fn node_edges_between_specific_nodes_multigraph() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
multigraph: true,
..Default::default()
});
g.set_edge("a", "b", None, None);
g.set_edge("a", "b", None, Some("foo"));
g.set_edge("a", "c", None, None);
g.set_edge("b", "c", None, None);
g.set_edge("z", "a", None, None);
g.set_edge("z", "b", None, None);
let ne_a_b = g.node_edges("a", Some("b")).unwrap();
assert_eq!(ne_a_b.len(), 2);
let ne_b_a = g.node_edges("b", Some("a")).unwrap();
assert_eq!(ne_b_a.len(), 2);
}
#[test]
fn topsort_sorts_earlier_nodes_before_later() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["b", "c", "a"], None);
let result = alg::topsort(&g).unwrap();
assert_eq!(result, vec!["b", "c", "a"]);
}
#[test]
fn topsort_cycle_with_extra_edge() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["b", "c", "a", "b"], None);
g.set_edge("b", "d", None, None);
assert!(alg::topsort(&g).is_err());
}
#[test]
fn topsort_cycle_with_isolated_node() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["b", "c", "a", "b"], None);
g.set_node("d", None);
assert!(alg::topsort(&g).is_err());
}
#[test]
fn is_acyclic_false_for_single_node_cycle() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["a", "a"], None);
assert!(!alg::is_acyclic(&g));
}
#[test]
fn find_cycles_single_node_self_loop() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["a", "a"], None);
let cycles = alg::find_cycles(&g);
assert_eq!(cycles.len(), 1);
assert_eq!(cycles[0], vec!["a"]);
}
#[test]
fn find_cycles_two_node_cycle() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["a", "b", "a"], None);
let mut cycles = alg::find_cycles(&g);
assert_eq!(cycles.len(), 1);
cycles[0].sort();
assert_eq!(cycles[0], vec!["a", "b"]);
}
#[test]
fn find_cycles_triangle() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["a", "b", "c", "a"], None);
let mut cycles = alg::find_cycles(&g);
assert_eq!(cycles.len(), 1);
cycles[0].sort();
assert_eq!(cycles[0], vec!["a", "b", "c"]);
}
#[test]
fn find_cycles_multiple_mixed() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["a", "b", "a"], None);
g.set_path(&["c", "d", "e", "c"], None);
g.set_path(&["f", "g", "g"], None);
g.set_node("h", None);
let mut cycles = alg::find_cycles(&g);
for c in &mut cycles {
c.sort();
}
cycles.sort();
assert_eq!(cycles.len(), 3);
assert_eq!(cycles[0], vec!["a", "b"]);
assert_eq!(cycles[1], vec!["c", "d", "e"]);
assert_eq!(cycles[2], vec!["g"]);
}
#[test]
fn tarjan_singletons_for_non_scc_nodes() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["a", "b", "c"], None);
g.set_edge("d", "c", None, None);
let mut sccs: Vec<Vec<String>> = alg::tarjan(&g)
.into_iter()
.map(|mut s| {
s.sort();
s
})
.collect();
sccs.sort();
assert_eq!(sccs, vec![vec!["a"], vec!["b"], vec!["c"], vec!["d"]]);
}
#[test]
fn tarjan_single_component_for_cycle_of_1_edge() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["a", "b", "a"], None);
let mut sccs: Vec<Vec<String>> = alg::tarjan(&g)
.into_iter()
.map(|mut s| {
s.sort();
s
})
.collect();
sccs.sort();
assert_eq!(sccs, vec![vec!["a", "b"]]);
}
#[test]
fn tarjan_single_component_for_triangle() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["a", "b", "c", "a"], None);
let mut sccs: Vec<Vec<String>> = alg::tarjan(&g)
.into_iter()
.map(|mut s| {
s.sort();
s
})
.collect();
sccs.sort();
assert_eq!(sccs, vec![vec!["a", "b", "c"]]);
}
#[test]
fn tarjan_multiple_components_with_singleton() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["a", "b", "a"], None);
g.set_path(&["c", "d", "e", "c"], None);
g.set_node("f", None);
let mut sccs: Vec<Vec<String>> = alg::tarjan(&g)
.into_iter()
.map(|mut s| {
s.sort();
s
})
.collect();
sccs.sort();
assert_eq!(sccs, vec![vec!["a", "b"], vec!["c", "d", "e"], vec!["f"]]);
}
#[test]
fn components_returns_nodes_connected_by_neighbor_in_digraph() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["a", "b", "c", "a"], None);
g.set_edge("d", "c", None, None);
g.set_edge("e", "f", None, None);
let mut comps: Vec<Vec<String>> = alg::components(&g)
.into_iter()
.map(|mut c| {
c.sort();
c
})
.collect();
comps.sort();
assert_eq!(comps.len(), 2);
assert_eq!(comps[0], vec!["a", "b", "c", "d"]);
assert_eq!(comps[1], vec!["e", "f"]);
}
#[test]
fn preorder_visits_each_node_once() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["a", "b", "d", "e"], None);
g.set_path(&["a", "c", "d", "e"], None);
let mut nodes = alg::preorder(&g, &["a"]);
nodes.sort();
assert_eq!(nodes, vec!["a", "b", "c", "d", "e"]);
}
#[test]
fn preorder_works_for_tree() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_path(&["a", "c", "d"], None);
g.set_edge("c", "e", None, None);
let nodes = alg::preorder(&g, &["a"]);
let mut sorted = nodes.clone();
sorted.sort();
assert_eq!(sorted, vec!["a", "b", "c", "d", "e"]);
let pos = |v: &str| nodes.iter().position(|x| x == v).unwrap();
assert!(pos("b") > pos("a"));
assert!(pos("c") > pos("a"));
assert!(pos("d") > pos("c"));
assert!(pos("e") > pos("c"));
}
#[test]
fn preorder_works_for_array_of_roots() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("c", "d", None, None);
g.set_node("e", None);
g.set_node("f", None);
let nodes = alg::preorder(&g, &["a", "c", "e"]);
let pos = |v: &str| nodes.iter().position(|x| x == v).unwrap();
assert!(pos("b") > pos("a"));
assert!(pos("d") > pos("c"));
let mut sorted = nodes.clone();
sorted.sort();
assert_eq!(sorted, vec!["a", "b", "c", "d", "e"]);
}
#[test]
fn postorder_visits_each_node_once() {
let mut g: Graph<(), ()> = Graph::new();
g.set_path(&["a", "b", "d", "e"], None);
g.set_path(&["a", "c", "d", "e"], None);
let mut nodes = alg::postorder(&g, &["a"]);
nodes.sort();
assert_eq!(nodes, vec!["a", "b", "c", "d", "e"]);
}
#[test]
fn postorder_works_for_tree() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_path(&["a", "c", "d"], None);
g.set_edge("c", "e", None, None);
let nodes = alg::postorder(&g, &["a"]);
let pos = |v: &str| nodes.iter().position(|x| x == v).unwrap();
assert!(pos("b") < pos("a"));
assert!(pos("c") < pos("a"));
assert!(pos("d") < pos("c"));
assert!(pos("e") < pos("c"));
let mut sorted = nodes.clone();
sorted.sort();
assert_eq!(sorted, vec!["a", "b", "c", "d", "e"]);
}
#[test]
fn postorder_works_for_array_of_roots() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("c", "d", None, None);
g.set_node("e", None);
g.set_node("f", None);
let nodes = alg::postorder(&g, &["a", "b", "c", "e"]);
let pos = |v: &str| nodes.iter().position(|x| x == v).unwrap();
assert!(pos("b") < pos("a"));
assert!(pos("d") < pos("c"));
let mut sorted = nodes.clone();
sorted.sort();
assert_eq!(sorted, vec!["a", "b", "c", "d", "e"]);
}
#[test]
fn postorder_works_for_multiple_connected_roots() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("a", "c", None, None);
g.set_edge("d", "c", None, None);
let nodes = alg::postorder(&g, &["a", "d"]);
let pos = |v: &str| nodes.iter().position(|x| x == v).unwrap();
assert!(pos("b") < pos("a"));
assert!(pos("c") < pos("a"));
assert!(pos("c") < pos("d"));
let mut sorted = nodes.clone();
sorted.sort();
assert_eq!(sorted, vec!["a", "b", "c", "d"]);
}
#[test]
fn dijkstra_returns_distance_and_path_to_other_nodes() {
let mut g: Graph<(), f64> = Graph::new();
g.set_path(&["a", "b", "c"], Some(1.0));
g.set_edge("b", "d", Some(1.0), None);
let result = alg::dijkstra(&g, "a", |w| *w);
assert_eq!(result["a"].0, 0.0);
assert_eq!(result["b"].0, 1.0);
assert_eq!(result["c"].0, 2.0);
assert_eq!(result["d"].0, 2.0);
assert_eq!(result["b"].1, Some("a".to_string()));
assert_eq!(result["c"].1, Some("b".to_string()));
assert_eq!(result["d"].1, Some("b".to_string()));
}
#[test]
fn dijkstra_uses_weight_function() {
let mut g: Graph<(), f64> = Graph::new();
g.set_edge("a", "b", Some(1.0), None);
g.set_edge("a", "c", Some(2.0), None);
g.set_edge("b", "d", Some(3.0), None);
g.set_edge("c", "d", Some(3.0), None);
let result = alg::dijkstra(&g, "a", |w| *w);
assert_eq!(result["a"].0, 0.0);
assert_eq!(result["b"].0, 1.0);
assert_eq!(result["c"].0, 2.0);
assert_eq!(result["d"].0, 4.0);
assert_eq!(result["d"].1, Some("b".to_string()));
}
#[test]
fn dijkstra_works_for_undirected_reverse_start() {
let mut g: Graph<(), f64> = Graph::with_options(GraphOptions {
directed: false,
..Default::default()
});
g.set_path(&["a", "b", "c"], Some(1.0));
g.set_edge("b", "d", Some(1.0), None);
let result = alg::dijkstra(&g, "d", |w| *w);
assert_eq!(result["d"].0, 0.0);
assert_eq!(result["b"].0, 1.0);
assert_eq!(result["a"].0, 2.0);
assert_eq!(result["c"].0, 2.0);
}
#[test]
fn prim_deterministic_result() {
let mut g: Graph<(), f64> = Graph::with_options(GraphOptions {
directed: false,
..Default::default()
});
g.set_edge("a", "b", Some(1.0), None);
g.set_edge("b", "c", Some(2.0), None);
g.set_edge("b", "d", Some(3.0), None);
g.set_edge("c", "d", Some(20.0), None);
g.set_edge("c", "e", Some(60.0), None);
g.set_edge("d", "e", Some(1.0), None);
let mst = alg::prim(&g, |w| *w);
assert_eq!(mst.node_count(), 5);
let mut na = mst.neighbors("a").unwrap();
na.sort();
assert_eq!(na, vec!["b"]);
let mut nb = mst.neighbors("b").unwrap();
nb.sort();
assert_eq!(nb, vec!["a", "c", "d"]);
let mut nc = mst.neighbors("c").unwrap();
nc.sort();
assert_eq!(nc, vec!["b"]);
let mut nd = mst.neighbors("d").unwrap();
nd.sort();
assert_eq!(nd, vec!["b", "e"]);
let mut ne = mst.neighbors("e").unwrap();
ne.sort();
assert_eq!(ne, vec!["d"]);
}
#[test]
#[should_panic]
fn prim_throws_for_unconnected_graph() {
let mut g: Graph<(), f64> = Graph::with_options(GraphOptions {
directed: false,
..Default::default()
});
g.set_node("a", None);
g.set_node("b", None);
let _mst = alg::prim(&g, |w| *w);
}
#[test]
fn dijkstra_all_returns_0_for_node_itself() {
let mut g: Graph<(), f64> = Graph::new();
g.set_node("a", None);
let result = alg::dijkstra_all(&g, |_e| 1.0, None);
assert_eq!(result["a"]["a"].0, 0.0);
assert_eq!(result["a"]["a"].1, None);
}
#[test]
fn dijkstra_all_returns_distance_and_path_from_all_nodes() {
let mut g: Graph<(), f64> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("b", "c", None, None);
let result = alg::dijkstra_all(&g, |_e| 1.0, None);
assert_eq!(result["a"]["a"].0, 0.0);
assert_eq!(result["a"]["b"].0, 1.0);
assert_eq!(result["a"]["b"].1, Some("a".to_string()));
assert_eq!(result["a"]["c"].0, 2.0);
assert_eq!(result["a"]["c"].1, Some("b".to_string()));
assert!(result["b"]["a"].0.is_infinite());
assert_eq!(result["b"]["b"].0, 0.0);
assert_eq!(result["b"]["c"].0, 1.0);
assert!(result["c"]["a"].0.is_infinite());
assert!(result["c"]["b"].0.is_infinite());
assert_eq!(result["c"]["c"].0, 0.0);
}
#[test]
fn dijkstra_all_uses_weight_function() {
let mut g: Graph<(), f64> = Graph::new();
g.set_edge("a", "b", Some(2.0), None);
g.set_edge("b", "c", Some(3.0), None);
let weight_fn = |e: &Edge| *g.edge(&e.v, &e.w, e.name.as_deref()).unwrap_or(&1.0);
let result = alg::dijkstra_all(&g, weight_fn, None);
assert_eq!(result["a"]["a"].0, 0.0);
assert_eq!(result["a"]["b"].0, 2.0);
assert_eq!(result["a"]["c"].0, 5.0);
assert!(result["b"]["a"].0.is_infinite());
assert_eq!(result["b"]["b"].0, 0.0);
assert_eq!(result["b"]["c"].0, 3.0);
}
#[test]
#[should_panic]
fn dijkstra_all_throws_for_negative_weights() {
let mut g: Graph<(), f64> = Graph::new();
g.set_edge("a", "b", Some(1.0), None);
g.set_edge("a", "c", Some(-2.0), None);
g.set_edge("b", "d", Some(3.0), None);
g.set_edge("c", "d", Some(3.0), None);
let weight_fn = |e: &Edge| *g.edge(&e.v, &e.w, e.name.as_deref()).unwrap_or(&1.0);
let _result = alg::dijkstra_all(&g, weight_fn, None);
}
#[test]
fn floyd_warshall_returns_0_for_node_itself() {
let mut g: Graph<(), f64> = Graph::new();
g.set_node("a", None);
let result = alg::floyd_warshall(&g, |_e| 1.0, None);
assert_eq!(result["a"]["a"].0, 0.0);
assert_eq!(result["a"]["a"].1, None);
}
#[test]
fn floyd_warshall_returns_all_distances() {
let mut g: Graph<(), f64> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("b", "c", None, None);
let result = alg::floyd_warshall(&g, |_e| 1.0, None);
assert_eq!(result["a"]["a"].0, 0.0);
assert_eq!(result["a"]["b"].0, 1.0);
assert_eq!(result["a"]["b"].1, Some("a".to_string()));
assert_eq!(result["a"]["c"].0, 2.0);
assert_eq!(result["a"]["c"].1, Some("b".to_string()));
assert!(result["b"]["a"].0.is_infinite());
assert_eq!(result["b"]["b"].0, 0.0);
assert_eq!(result["b"]["c"].0, 1.0);
assert!(result["c"]["a"].0.is_infinite());
assert!(result["c"]["b"].0.is_infinite());
assert_eq!(result["c"]["c"].0, 0.0);
}
#[test]
fn floyd_warshall_uses_weight_function() {
let mut g: Graph<(), f64> = Graph::new();
g.set_edge("a", "b", Some(2.0), None);
g.set_edge("b", "c", Some(3.0), None);
let weight_fn = |e: &Edge| *g.edge(&e.v, &e.w, e.name.as_deref()).unwrap_or(&1.0);
let result = alg::floyd_warshall(&g, weight_fn, None);
assert_eq!(result["a"]["a"].0, 0.0);
assert_eq!(result["a"]["b"].0, 2.0);
assert_eq!(result["a"]["c"].0, 5.0);
assert!(result["b"]["a"].0.is_infinite());
assert_eq!(result["b"]["b"].0, 0.0);
assert_eq!(result["b"]["c"].0, 3.0);
}
#[test]
fn floyd_warshall_handles_negative_weights() {
let mut g: Graph<(), f64> = Graph::new();
g.set_edge("a", "b", Some(1.0), None);
g.set_edge("a", "c", Some(-2.0), None);
g.set_edge("b", "d", Some(3.0), None);
g.set_edge("c", "d", Some(3.0), None);
let weight_fn = |e: &Edge| *g.edge(&e.v, &e.w, e.name.as_deref()).unwrap_or(&1.0);
let result = alg::floyd_warshall(&g, weight_fn, None);
assert_eq!(result["a"]["a"].0, 0.0);
assert_eq!(result["a"]["b"].0, 1.0);
assert_eq!(result["a"]["b"].1, Some("a".to_string()));
assert_eq!(result["a"]["c"].0, -2.0);
assert_eq!(result["a"]["c"].1, Some("a".to_string()));
assert_eq!(result["a"]["d"].0, 1.0);
assert_eq!(result["a"]["d"].1, Some("c".to_string()));
assert!(result["b"]["a"].0.is_infinite());
assert_eq!(result["b"]["b"].0, 0.0);
assert_eq!(result["b"]["d"].0, 3.0);
assert!(result["c"]["a"].0.is_infinite());
assert_eq!(result["c"]["c"].0, 0.0);
assert_eq!(result["c"]["d"].0, 3.0);
assert_eq!(result["d"]["d"].0, 0.0);
}
#[test]
fn floyd_warshall_includes_negative_self_edges() {
let mut g: Graph<(), f64> = Graph::new();
g.set_edge("a", "a", Some(-1.0), None);
let weight_fn = |e: &Edge| *g.edge(&e.v, &e.w, e.name.as_deref()).unwrap_or(&1.0);
let result = alg::floyd_warshall(&g, weight_fn, None);
assert_eq!(result["a"]["a"].0, -2.0);
assert_eq!(result["a"]["a"].1, Some("a".to_string()));
}
#[test]
fn bellman_ford_distance_0_for_source() {
let mut g: Graph<(), f64> = Graph::new();
g.set_node("source", None);
let weight_fn = |e: &Edge| *g.edge(&e.v, &e.w, e.name.as_deref()).unwrap_or(&1.0);
let result = alg::bellman_ford(&g, "source", weight_fn, None);
assert_eq!(result["source"].0, 0.0);
assert_eq!(result["source"].1, None);
}
#[test]
fn bellman_ford_infinity_for_unconnected() {
let mut g: Graph<(), f64> = Graph::new();
g.set_node("a", None);
g.set_node("b", None);
let weight_fn = |e: &Edge| *g.edge(&e.v, &e.w, e.name.as_deref()).unwrap_or(&1.0);
let result = alg::bellman_ford(&g, "a", weight_fn, None);
assert_eq!(result["a"].0, 0.0);
assert!(result["b"].0.is_infinite());
}
#[test]
fn bellman_ford_returns_distance_and_path() {
let mut g: Graph<(), f64> = Graph::new();
g.set_path(&["a", "b", "c"], None);
g.set_edge("b", "d", None, None);
let result = alg::bellman_ford(&g, "a", |_e| 1.0, None);
assert_eq!(result["a"].0, 0.0);
assert_eq!(result["b"].0, 1.0);
assert_eq!(result["b"].1, Some("a".to_string()));
assert_eq!(result["c"].0, 2.0);
assert_eq!(result["c"].1, Some("b".to_string()));
assert_eq!(result["d"].0, 2.0);
assert_eq!(result["d"].1, Some("b".to_string()));
}
#[test]
fn bellman_ford_works_for_undirected() {
let mut g: Graph<(), f64> = Graph::with_options(GraphOptions {
directed: false,
..Default::default()
});
g.set_path(&["a", "b", "c"], None);
g.set_edge("b", "d", None, None);
let edge_fn = |v: &str| -> Vec<Edge> { g.node_edges(v, None).unwrap_or_default() };
let result = alg::bellman_ford(&g, "a", |_e| 1.0, Some(&edge_fn));
assert_eq!(result["a"].0, 0.0);
assert_eq!(result["b"].0, 1.0);
assert_eq!(result["b"].1, Some("a".to_string()));
assert_eq!(result["c"].0, 2.0);
assert_eq!(result["c"].1, Some("b".to_string()));
assert_eq!(result["d"].0, 2.0);
assert_eq!(result["d"].1, Some("b".to_string()));
}
#[test]
fn bellman_ford_uses_weight_function() {
let mut g: Graph<(), f64> = Graph::new();
g.set_edge("a", "b", Some(1.0), None);
g.set_edge("a", "c", Some(2.0), None);
g.set_edge("b", "d", Some(3.0), None);
g.set_edge("c", "d", Some(3.0), None);
let weight_fn = |e: &Edge| *g.edge(&e.v, &e.w, e.name.as_deref()).unwrap_or(&1.0);
let result = alg::bellman_ford(&g, "a", weight_fn, None);
assert_eq!(result["a"].0, 0.0);
assert_eq!(result["b"].0, 1.0);
assert_eq!(result["b"].1, Some("a".to_string()));
assert_eq!(result["c"].0, 2.0);
assert_eq!(result["c"].1, Some("a".to_string()));
assert_eq!(result["d"].0, 4.0);
assert_eq!(result["d"].1, Some("b".to_string()));
}
#[test]
fn bellman_ford_works_with_negative_edges() {
let mut g: Graph<(), f64> = Graph::new();
g.set_edge("a", "b", Some(-1.0), None);
g.set_edge("a", "c", Some(4.0), None);
g.set_edge("b", "c", Some(3.0), None);
g.set_edge("b", "d", Some(2.0), None);
g.set_edge("b", "e", Some(2.0), None);
g.set_edge("d", "c", Some(5.0), None);
g.set_edge("d", "b", Some(1.0), None);
g.set_edge("e", "d", Some(-3.0), None);
let weight_fn = |e: &Edge| *g.edge(&e.v, &e.w, e.name.as_deref()).unwrap_or(&1.0);
let result = alg::bellman_ford(&g, "a", weight_fn, None);
assert_eq!(result["a"].0, 0.0);
assert_eq!(result["b"].0, -1.0);
assert_eq!(result["b"].1, Some("a".to_string()));
assert_eq!(result["c"].0, 2.0);
assert_eq!(result["c"].1, Some("b".to_string()));
assert_eq!(result["d"].0, -2.0);
assert_eq!(result["d"].1, Some("e".to_string()));
assert_eq!(result["e"].0, 1.0);
assert_eq!(result["e"].1, Some("b".to_string()));
}
#[test]
#[should_panic]
fn bellman_ford_throws_for_negative_cycle() {
let mut g: Graph<(), f64> = Graph::new();
g.set_edge("a", "b", Some(1.0), None);
g.set_edge("b", "c", Some(3.0), None);
g.set_edge("c", "d", Some(-5.0), None);
g.set_edge("d", "e", Some(4.0), None);
g.set_edge("d", "b", Some(1.0), None);
g.set_edge("c", "f", Some(8.0), None);
let weight_fn = |e: &Edge| *g.edge(&e.v, &e.w, e.name.as_deref()).unwrap_or(&1.0);
let _result = alg::bellman_ford(&g, "a", weight_fn, None);
}
#[test]
fn json_preserves_graph_options() {
use super::json;
use serde_json;
{
let g: Graph<i32, i32> = Graph::with_options(GraphOptions {
directed: true,
..Default::default()
});
let j = json::graph_to_json::<i32, i32, ()>(&g, None);
let json_str = serde_json::to_string(&j).unwrap();
let j2: json::JsonGraph<i32, i32, ()> = serde_json::from_str(&json_str).unwrap();
let (g2, _): (Graph<i32, i32>, _) = json::graph_from_json(j2);
assert!(g2.is_directed());
}
{
let g: Graph<i32, i32> = Graph::with_options(GraphOptions {
directed: false,
..Default::default()
});
let j = json::graph_to_json::<i32, i32, ()>(&g, None);
let json_str = serde_json::to_string(&j).unwrap();
let j2: json::JsonGraph<i32, i32, ()> = serde_json::from_str(&json_str).unwrap();
let (g2, _): (Graph<i32, i32>, _) = json::graph_from_json(j2);
assert!(!g2.is_directed());
}
{
let g: Graph<i32, i32> = Graph::with_options(GraphOptions {
multigraph: true,
..Default::default()
});
let j = json::graph_to_json::<i32, i32, ()>(&g, None);
let json_str = serde_json::to_string(&j).unwrap();
let j2: json::JsonGraph<i32, i32, ()> = serde_json::from_str(&json_str).unwrap();
let (g2, _): (Graph<i32, i32>, _) = json::graph_from_json(j2);
assert!(g2.is_multigraph());
}
{
let g: Graph<i32, i32> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
let j = json::graph_to_json::<i32, i32, ()>(&g, None);
let json_str = serde_json::to_string(&j).unwrap();
let j2: json::JsonGraph<i32, i32, ()> = serde_json::from_str(&json_str).unwrap();
let (g2, _): (Graph<i32, i32>, _) = json::graph_from_json(j2);
assert!(g2.is_compound());
}
}
#[test]
fn json_preserves_graph_value() {
use super::json;
use serde_json;
{
let g: Graph<i32, i32> = Graph::new();
let j = json::graph_to_json(&g, Some(&42i32));
let json_str = serde_json::to_string(&j).unwrap();
let j2: json::JsonGraph<i32, i32, i32> = serde_json::from_str(&json_str).unwrap();
let (_, label) = json::graph_from_json(j2);
assert_eq!(label, Some(42));
}
{
let g: Graph<i32, i32> = Graph::new();
let j = json::graph_to_json::<i32, i32, i32>(&g, None);
let json_str = serde_json::to_string(&j).unwrap();
let j2: json::JsonGraph<i32, i32, i32> = serde_json::from_str(&json_str).unwrap();
let (_, label) = json::graph_from_json(j2);
assert_eq!(label, None);
}
}
#[test]
fn json_preserves_nodes() {
use super::json;
use serde_json;
{
let mut g: Graph<i32, i32> = Graph::new();
g.set_node("a", None);
let j = json::graph_to_json::<i32, i32, ()>(&g, None);
let json_str = serde_json::to_string(&j).unwrap();
let j2: json::JsonGraph<i32, i32, ()> = serde_json::from_str(&json_str).unwrap();
let (g2, _) = json::graph_from_json(j2);
assert!(g2.has_node("a"));
assert_eq!(g2.node("a"), None);
}
{
let mut g: Graph<i32, i32> = Graph::new();
g.set_node("a", Some(1));
let j = json::graph_to_json::<i32, i32, ()>(&g, None);
let json_str = serde_json::to_string(&j).unwrap();
let j2: json::JsonGraph<i32, i32, ()> = serde_json::from_str(&json_str).unwrap();
let (g2, _) = json::graph_from_json(j2);
assert_eq!(g2.node("a"), Some(&1));
}
}
#[test]
fn json_preserves_simple_edges() {
use super::json;
use serde_json;
{
let mut g: Graph<i32, i32> = Graph::new();
g.set_edge("a", "b", None, None);
let j = json::graph_to_json::<i32, i32, ()>(&g, None);
let json_str = serde_json::to_string(&j).unwrap();
let j2: json::JsonGraph<i32, i32, ()> = serde_json::from_str(&json_str).unwrap();
let (g2, _) = json::graph_from_json(j2);
assert!(g2.has_edge("a", "b", None));
assert_eq!(g2.edge("a", "b", None), None);
}
{
let mut g: Graph<i32, i32> = Graph::new();
g.set_edge("a", "b", Some(1), None);
let j = json::graph_to_json::<i32, i32, ()>(&g, None);
let json_str = serde_json::to_string(&j).unwrap();
let j2: json::JsonGraph<i32, i32, ()> = serde_json::from_str(&json_str).unwrap();
let (g2, _) = json::graph_from_json(j2);
assert_eq!(g2.edge("a", "b", None), Some(&1));
}
}
#[test]
fn json_preserves_multi_edges() {
use super::json;
use serde_json;
let mut g: Graph<i32, i32> = Graph::with_options(GraphOptions {
multigraph: true,
..Default::default()
});
g.set_edge("a", "b", None, Some("foo"));
let j = json::graph_to_json::<i32, i32, ()>(&g, None);
let json_str = serde_json::to_string(&j).unwrap();
let j2: json::JsonGraph<i32, i32, ()> = serde_json::from_str(&json_str).unwrap();
let (g2, _) = json::graph_from_json(j2);
assert!(g2.has_edge("a", "b", Some("foo")));
let mut g: Graph<i32, i32> = Graph::with_options(GraphOptions {
multigraph: true,
..Default::default()
});
g.set_edge("a", "b", Some(1), Some("foo"));
let j = json::graph_to_json::<i32, i32, ()>(&g, None);
let json_str = serde_json::to_string(&j).unwrap();
let j2: json::JsonGraph<i32, i32, ()> = serde_json::from_str(&json_str).unwrap();
let (g2, _) = json::graph_from_json(j2);
assert_eq!(g2.edge("a", "b", Some("foo")), Some(&1));
}
#[test]
fn json_preserves_parent_child_relationships() {
use super::json;
use serde_json;
{
let mut g: Graph<i32, i32> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
g.set_node("a", None);
let j = json::graph_to_json::<i32, i32, ()>(&g, None);
let json_str = serde_json::to_string(&j).unwrap();
let j2: json::JsonGraph<i32, i32, ()> = serde_json::from_str(&json_str).unwrap();
let (g2, _) = json::graph_from_json(j2);
assert_eq!(g2.parent("a"), None);
}
{
let mut g: Graph<i32, i32> = Graph::with_options(GraphOptions {
compound: true,
..Default::default()
});
g.set_parent("a", Some("parent"));
let j = json::graph_to_json::<i32, i32, ()>(&g, None);
let json_str = serde_json::to_string(&j).unwrap();
let j2: json::JsonGraph<i32, i32, ()> = serde_json::from_str(&json_str).unwrap();
let (g2, _) = json::graph_from_json(j2);
assert_eq!(g2.parent("a"), Some("parent"));
}
}
#[test]
fn priority_queue_new_is_empty() {
let pq = alg_internal::PriorityQueue::new();
assert!(pq.is_empty());
}
#[test]
fn priority_queue_insert_and_extract_min() {
let mut pq = alg_internal::PriorityQueue::new();
pq.insert("b".to_string(), 2.0);
pq.insert("a".to_string(), 1.0);
let (key, pri) = pq.extract_min().unwrap();
assert_eq!(key, "a");
assert_eq!(pri, 1.0);
}
#[test]
fn priority_queue_extract_min_in_order() {
let mut pq = alg_internal::PriorityQueue::new();
pq.insert("b".to_string(), 2.0);
pq.insert("a".to_string(), 1.0);
pq.insert("c".to_string(), 3.0);
pq.insert("e".to_string(), 5.0);
pq.insert("d".to_string(), 4.0);
assert_eq!(pq.extract_min().unwrap().0, "a");
assert_eq!(pq.extract_min().unwrap().0, "b");
assert_eq!(pq.extract_min().unwrap().0, "c");
assert_eq!(pq.extract_min().unwrap().0, "d");
assert_eq!(pq.extract_min().unwrap().0, "e");
}
#[test]
fn priority_queue_extract_min_empty_returns_none() {
let mut pq = alg_internal::PriorityQueue::new();
assert!(pq.extract_min().is_none());
}
#[test]
fn priority_queue_decrease() {
let mut pq = alg_internal::PriorityQueue::new();
pq.insert("a".to_string(), 10.0);
pq.insert("b".to_string(), 5.0);
pq.decrease("a", 1.0);
let (key, _) = pq.extract_min().unwrap();
assert_eq!(key, "a");
}
#[test]
fn priority_queue_decrease_does_not_increase() {
let mut pq = alg_internal::PriorityQueue::new();
pq.insert("a".to_string(), 1.0);
pq.decrease("a", 5.0); let (key, pri) = pq.extract_min().unwrap();
assert_eq!(key, "a");
assert_eq!(pri, 1.0);
}
#[test]
fn priority_queue_insert_multiple() {
let mut pq = alg_internal::PriorityQueue::new();
pq.insert("a".to_string(), 1.0);
assert!(!pq.is_empty());
pq.insert("b".to_string(), 2.0);
assert!(!pq.is_empty());
}
mod alg_internal {
pub struct PriorityQueue {
entries: std::collections::HashMap<String, f64>,
}
impl PriorityQueue {
pub fn new() -> Self {
Self {
entries: std::collections::HashMap::new(),
}
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn insert(&mut self, key: String, priority: f64) {
self.entries.insert(key, priority);
}
pub fn decrease(&mut self, key: &str, priority: f64) {
if let Some(p) = self.entries.get_mut(key)
&& priority < *p
{
*p = priority;
}
}
pub fn extract_min(&mut self) -> Option<(String, f64)> {
if self.entries.is_empty() {
return None;
}
let (key, priority) = self
.entries
.iter()
.min_by(|a, b| a.1.partial_cmp(b.1).unwrap())
.map(|(k, v)| (k.clone(), *v))?;
self.entries.remove(&key);
Some((key, priority))
}
}
}
#[test]
fn extract_path_returns_source_to_source() {
use std::collections::HashMap;
let mut sp: HashMap<String, alg::PathEntry> = HashMap::new();
sp.insert("a".to_string(), (0.0, None));
sp.insert("b".to_string(), (73.0, Some("a".to_string())));
let result = alg::extract_path(&sp, "a", "a");
assert_eq!(result.weight, 0.0);
assert_eq!(result.path, vec!["a"]);
}
#[test]
fn extract_path_returns_weight_and_path_from_source_to_dest() {
use std::collections::HashMap;
let mut sp: HashMap<String, alg::PathEntry> = HashMap::new();
sp.insert("a".to_string(), (0.0, None));
sp.insert("b".to_string(), (25.0, Some("a".to_string())));
sp.insert("c".to_string(), (55.0, Some("b".to_string())));
sp.insert("d".to_string(), (44.0, Some("b".to_string())));
sp.insert("e".to_string(), (73.0, Some("c".to_string())));
sp.insert("f".to_string(), (65.0, Some("d".to_string())));
sp.insert("g".to_string(), (67.0, Some("b".to_string())));
let result = alg::extract_path(&sp, "a", "e");
assert_eq!(result.weight, 73.0);
assert_eq!(result.path, vec!["a", "b", "c", "e"]);
}
#[test]
#[should_panic]
fn extract_path_throws_for_invalid_source() {
use std::collections::HashMap;
let mut sp: HashMap<String, alg::PathEntry> = HashMap::new();
sp.insert("a".to_string(), (0.0, None));
sp.insert("b".to_string(), (17.0, Some("c".to_string())));
sp.insert("c".to_string(), (42.0, Some("a".to_string())));
alg::extract_path(&sp, "b", "c");
}
#[test]
#[should_panic]
fn extract_path_throws_for_invalid_destination() {
use std::collections::HashMap;
let mut sp: HashMap<String, alg::PathEntry> = HashMap::new();
sp.insert("a".to_string(), (0.0, None));
sp.insert("b".to_string(), (99.0, Some("a".to_string())));
sp.insert("c".to_string(), (100.0, None));
alg::extract_path(&sp, "a", "c");
}
#[test]
fn reduce_returns_initial_accumulator_for_empty_graph() {
let g: Graph<(), ()> = Graph::new();
let result = alg::reduce(&g, &[], alg::DfsOrder::Pre, |a: i32, _| a, 0);
assert_eq!(result, 0);
}
#[test]
fn reduce_applies_accumulator_to_all_nodes() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
directed: false,
..Default::default()
});
g.set_path(&["1", "2", "3", "5", "7"], None);
g.set_path(&["2", "5", "11", "13"], None);
let result = alg::reduce(
&g,
&["2"],
alg::DfsOrder::Pre,
|a: i32, b: &str| a + b.parse::<i32>().unwrap(),
0,
);
assert_eq!(result, 42);
}
#[test]
fn reduce_traverses_in_pre_order() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
directed: false,
..Default::default()
});
g.set_path(&["1", "2", "3", "5", "7"], None);
g.set_path(&["2", "5", "11", "13"], None);
let result = alg::reduce(
&g,
&["2"],
alg::DfsOrder::Pre,
|a: String, b: &str| format!("{}{}-", a, b),
String::new(),
);
assert_eq!(result, "2-1-3-5-7-11-13-");
}
#[test]
fn reduce_traverses_in_post_order() {
let mut g: Graph<(), ()> = Graph::with_options(GraphOptions {
directed: false,
..Default::default()
});
g.set_path(&["1", "2", "3", "5", "7"], None);
g.set_path(&["2", "5", "11", "13"], None);
let result = alg::reduce(
&g,
&["2"],
alg::DfsOrder::Post,
|a: String, b: &str| format!("{}{}-", a, b),
String::new(),
);
assert_eq!(result, "1-7-13-11-5-3-2-");
}
#[test]
fn dijkstra_uses_optionally_supplied_edge_function() {
}
#[test]
fn all_shortest_paths_returns_0_for_node_itself() {
}
#[test]
fn all_shortest_paths_returns_distance_and_path_from_all_nodes() {
}
#[test]
fn all_shortest_paths_uses_weight_function() {
}
#[test]
fn all_shortest_paths_uses_incident_function() {
}
#[test]
fn all_shortest_paths_works_with_undirected() {
}
#[test]
fn graph_label_mut_allows_modification() {
let mut g: Graph<(), ()> = Graph::new();
g.set_graph_label(42i32);
*g.graph_label_mut::<i32>().unwrap() = 99;
assert_eq!(g.graph_label::<i32>(), Some(&99));
}
#[test]
fn edge_mut_allows_modification() {
let mut g: Graph<(), i32> = Graph::new();
g.set_edge("a", "b", Some(1), None);
*g.edge_mut("a", "b", None).unwrap() = 42;
assert_eq!(g.edge("a", "b", None), Some(&42));
}
#[test]
fn set_edge_with_none_keeps_existing_value() {
let mut g: Graph<(), &str> = Graph::new();
g.set_edge("a", "b", Some("foo"), None);
g.set_edge("a", "b", None, None);
assert_eq!(g.edge("a", "b", None), Some(&"foo"));
assert!(g.has_edge("a", "b", None));
}
#[test]
fn edges_returns_correct_edge_descriptors() {
let mut g: Graph<(), ()> = Graph::new();
g.set_edge("a", "b", None, None);
g.set_edge("b", "c", None, None);
let mut edges = g.edges();
edges.sort_by(|a, b| a.v.cmp(&b.v).then(a.w.cmp(&b.w)));
assert_eq!(edges.len(), 2);
assert_eq!(edges[0].v, "a");
assert_eq!(edges[0].w, "b");
assert_eq!(edges[1].v, "b");
assert_eq!(edges[1].w, "c");
}
#[test]
fn edge_display_format() {
let e = Edge::new("a", "b");
assert_eq!(format!("{}", e), "a->b");
let e2 = Edge::with_name("a", "b", "foo");
assert_eq!(format!("{}", e2), "a->b:foo");
}
#[test]
fn graph_default_same_as_new() {
let g1: Graph<(), ()> = Graph::new();
let g2: Graph<(), ()> = Graph::default();
assert_eq!(g1.node_count(), g2.node_count());
assert_eq!(g1.edge_count(), g2.edge_count());
assert_eq!(g1.is_directed(), g2.is_directed());
assert_eq!(g1.is_multigraph(), g2.is_multigraph());
assert_eq!(g1.is_compound(), g2.is_compound());
}
#[test]
fn graph_debug_format() {
let g: Graph<i32, i32> = Graph::new();
let debug = format!("{:?}", g);
assert!(debug.contains("Graph"));
assert!(debug.contains("directed"));
}