use super::*;
#[test]
fn empty_contains_nothing() {
let set = MetricSet::empty();
assert!(!set.contains(Metric::Loc));
assert!(!set.contains(Metric::Halstead));
assert!(!set.contains(Metric::Mi));
}
#[test]
fn all_contains_every_variant() {
let set = MetricSet::all();
for m in [
Metric::Cognitive,
Metric::Cyclomatic,
Metric::Halstead,
Metric::Loc,
Metric::Nom,
Metric::Tokens,
Metric::NArgs,
Metric::Exit,
Metric::Abc,
Metric::Npm,
Metric::Npa,
Metric::Mi,
Metric::Wmc,
] {
assert!(set.contains(m), "MetricSet::all() must contain {m}");
}
}
#[test]
fn with_dependencies_pulls_in_mi_inputs() {
let set = MetricSet::from_slice_with_deps(&[Metric::Mi]);
assert!(set.contains(Metric::Mi));
assert!(set.contains(Metric::Loc), "Mi depends on Loc");
assert!(set.contains(Metric::Cyclomatic), "Mi depends on Cyclomatic");
assert!(set.contains(Metric::Halstead), "Mi depends on Halstead");
assert!(!set.contains(Metric::Abc));
assert!(!set.contains(Metric::Tokens));
}
#[test]
fn with_dependencies_pulls_in_wmc_inputs() {
let set = MetricSet::from_slice_with_deps(&[Metric::Wmc]);
assert!(set.contains(Metric::Wmc));
assert!(
set.contains(Metric::Cyclomatic),
"Wmc depends on Cyclomatic"
);
assert!(set.contains(Metric::Nom), "Wmc depends on Nom");
}
#[test]
fn closure_is_idempotent_for_mixed_input() {
let a = MetricSet::from_slice_with_deps(&[Metric::Mi, Metric::Loc]);
let b = MetricSet::from_slice_with_deps(&[Metric::Mi]);
assert_eq!(a, b);
}
#[test]
fn closure_handles_duplicate_input() {
let set = MetricSet::from_slice_with_deps(&[Metric::Mi, Metric::Mi, Metric::Mi]);
assert_eq!(set, MetricSet::from_slice_with_deps(&[Metric::Mi]));
}
#[test]
fn empty_slice_yields_empty_set() {
assert_eq!(MetricSet::from_slice_with_deps(&[]), MetricSet::empty());
}
const ALL_VARIANTS: &[Metric] = &[
Metric::Cognitive,
Metric::Cyclomatic,
Metric::Halstead,
Metric::Loc,
Metric::Nom,
Metric::Tokens,
Metric::NArgs,
Metric::Exit,
Metric::Abc,
Metric::Npm,
Metric::Npa,
Metric::Mi,
Metric::Wmc,
];
#[allow(dead_code)]
fn _all_variants_exhaustive_guard(m: Metric) {
match m {
Metric::Cognitive
| Metric::Cyclomatic
| Metric::Halstead
| Metric::Loc
| Metric::Nom
| Metric::Tokens
| Metric::NArgs
| Metric::Exit
| Metric::Abc
| Metric::Npm
| Metric::Npa
| Metric::Mi
| Metric::Wmc => (),
}
}
#[test]
fn from_str_round_trips_every_variant_display_name() {
for &m in ALL_VARIANTS {
let parsed: Metric = m
.to_string()
.parse()
.unwrap_or_else(|e| panic!("Display->FromStr round-trip failed for {m}: {e}"));
assert_eq!(parsed, m, "round-trip mismatch for {m}");
}
}
#[test]
fn from_str_accepts_nexits_alias_for_exit() {
assert_eq!("exit".parse::<Metric>().unwrap(), Metric::Exit);
assert_eq!("nexits".parse::<Metric>().unwrap(), Metric::Exit);
}
#[test]
fn from_str_rejects_uppercase() {
let err = "Loc".parse::<Metric>().unwrap_err();
assert_eq!(err.to_string(), "unknown metric: Loc");
}
#[test]
fn names_table_parses_to_every_variant() {
use std::collections::HashSet;
let mut seen: HashSet<Metric> = HashSet::new();
for name in Metric::NAMES {
let parsed = name
.parse::<Metric>()
.unwrap_or_else(|_| panic!("Metric::NAMES contains {name:?} but FromStr rejects it"));
seen.insert(parsed);
}
for &m in ALL_VARIANTS {
assert!(
seen.contains(&m),
"Metric::{m:?} is not represented in Metric::NAMES; \
add the canonical spelling to the table",
);
}
}
#[test]
fn names_table_is_alphabetised() {
let mut sorted: Vec<&str> = Metric::NAMES.to_vec();
sorted.sort_unstable();
assert_eq!(
Metric::NAMES,
sorted.as_slice(),
"Metric::NAMES must stay alphabetised",
);
}
#[test]
fn with_metric_set_does_not_resolve_dependencies() {
let resolved = MetricSet::from_slice_with_deps(&[Metric::Mi]);
assert!(resolved.contains(Metric::Mi));
assert!(resolved.contains(Metric::Loc));
assert!(resolved.contains(Metric::Cyclomatic));
assert!(resolved.contains(Metric::Halstead));
let bare = MetricSet::empty().with(Metric::Mi);
assert!(bare.contains(Metric::Mi));
assert!(!bare.contains(Metric::Loc), "with(Mi) must NOT pull Loc");
assert!(
!bare.contains(Metric::Cyclomatic),
"with(Mi) must NOT pull Cyclomatic",
);
assert!(
!bare.contains(Metric::Halstead),
"with(Mi) must NOT pull Halstead",
);
}
#[test]
fn from_str_rejects_unknown_name() {
let err = "bogus".parse::<Metric>().unwrap_err();
assert_eq!(err.to_string(), "unknown metric: bogus");
}
#[test]
fn distinct_bits_per_variant() {
let mut seen: u32 = 0;
for &m in ALL_VARIANTS {
let bit = m.bit();
assert_ne!(bit, 0, "bit() must be non-zero for {m}");
assert_eq!(seen & bit, 0, "duplicate bit for {m}: {bit:#b}");
seen |= bit;
}
assert_eq!(seen, MetricSet::ALL_BITS);
}
#[test]
fn all_variants_round_trip_through_all_contains() {
let set = MetricSet::all();
for &m in ALL_VARIANTS {
assert!(
set.contains(m),
"MetricSet::all() must contain {m}; \
did a new variant land without updating ALL_BITS?",
);
}
}
#[test]
fn storage_width_covers_every_variant() {
const STORAGE_BITS: usize = u32::BITS as usize;
assert!(
ALL_VARIANTS.len() <= STORAGE_BITS,
"MetricSet storage exhausted: {} variants > {STORAGE_BITS}-bit storage; widen MetricSet",
ALL_VARIANTS.len(),
);
}