diff --git a/common/Cargo.toml b/common/Cargo.toml index 095d169e7f..e0c023cb93 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -88,6 +88,7 @@ criterion = "0.3" #test tracing-subscriber = { version = "0.3.2", default-features = false, features = ["fmt", "time", "ansi", "smallvec", "env-filter"] } +petgraph = "0.5.1" [[bench]] name = "chonk_benchmark" diff --git a/common/src/comp/skillset/mod.rs b/common/src/comp/skillset/mod.rs index 5ef2e4b325..aa7861620e 100644 --- a/common/src/comp/skillset/mod.rs +++ b/common/src/comp/skillset/mod.rs @@ -16,6 +16,8 @@ use tracing::{trace, warn}; pub mod skills; +#[cfg(test)] mod test; + /// BTreeSet is used here to ensure that skills are ordered. This is important /// to ensure that the hash created from it is consistent so that we don't /// needlessly force a respec when loading skills from persistence. diff --git a/common/src/comp/skillset/test.rs b/common/src/comp/skillset/test.rs new file mode 100644 index 0000000000..bda4849ad6 --- /dev/null +++ b/common/src/comp/skillset/test.rs @@ -0,0 +1,28 @@ +use super::*; +use crate::comp::{skillset::SkillPrerequisitesMap, Skill}; +use hashbrown::HashMap; + +// Unneeded cfg(test) here keeps rust-analyzer happy +#[cfg(test)] +use petgraph::{algo::is_cyclic_undirected, graph::UnGraph}; + +#[test] +fn check_cyclic_skill_deps() { + let skill_prereqs = + SkillPrerequisitesMap::load_expect_cloned("common.skill_trees.skill_prerequisites").0; + let mut graph = UnGraph::new_undirected(); + let mut nodes = HashMap::::new(); + let mut add_node = |graph: &mut UnGraph, node: Skill| { + *nodes.entry(node).or_insert_with(|| graph.add_node(node)) + }; + + for (skill, prereqs) in skill_prereqs.iter() { + let skill_node = add_node(&mut graph, *skill); + for (prereq, _) in prereqs.iter() { + let prereq_node = add_node(&mut graph, *prereq); + graph.add_edge(prereq_node, skill_node, ()); + } + } + + assert!(!is_cyclic_undirected(&graph)); +}