use super::tests_common::rpt;
use super::*;
#[test]
fn plan_default_empty() {
let plan = AssertPlan::new();
assert!(!plan.not_starved);
assert!(!plan.isolation);
assert!(plan.max_gap_ms.is_none());
assert!(plan.max_spread_pct.is_none());
}
#[test]
fn plan_check_not_starved() {
let plan = AssertPlan::new().check_not_starved();
let reports = [rpt(1, 1000, 5e9 as u64, 5e8 as u64, &[0], 50)];
let r = plan.assert_cgroup(&reports, None, None);
assert!(r.passed);
assert_eq!(r.stats.total_workers, 1);
}
#[test]
fn plan_check_isolation_with_cpuset() {
let plan = AssertPlan::new().check_not_starved().check_isolation();
let expected: BTreeSet<usize> = [0, 1].into_iter().collect();
let reports = [rpt(1, 1000, 5e9 as u64, 5e8 as u64, &[0, 1, 4], 50)];
let r = plan.assert_cgroup(&reports, Some(&expected), None);
assert!(!r.passed);
assert!(r.details.iter().any(|d| d.contains("unexpected")));
}
#[test]
fn plan_isolation_skipped_without_cpuset() {
let plan = AssertPlan::new().check_isolation();
let reports = [rpt(1, 1000, 5e9 as u64, 5e8 as u64, &[0, 1, 4], 50)];
let r = plan.assert_cgroup(&reports, None, None);
assert!(r.passed);
}
#[test]
fn plan_custom_gap_threshold_pass() {
let plan = AssertPlan::new().check_not_starved().max_gap_ms(3000);
let reports = [rpt(1, 1000, 5e9 as u64, 5e8 as u64, &[0], 2500)];
let r = plan.assert_cgroup(&reports, None, None);
assert!(r.passed, "2500ms < 3000ms threshold: {:?}", r.details);
}
#[test]
fn plan_custom_gap_threshold_fail() {
let plan = AssertPlan::new().check_not_starved().max_gap_ms(1500);
let reports = [rpt(1, 1000, 5e9 as u64, 5e8 as u64, &[0], 2000)];
let r = plan.assert_cgroup(&reports, None, None);
assert!(!r.passed);
assert!(r.details.iter().any(|d| d.contains("stuck")));
assert!(r.details.iter().any(|d| d.contains("threshold 1500ms")));
}
#[test]
fn plan_custom_gap_threshold_produces_stuck_kind() {
let plan = AssertPlan::new().check_not_starved().max_gap_ms(1500);
let reports = [rpt(1, 1000, 5e9 as u64, 5e8 as u64, &[0], 2000)];
let r = plan.assert_cgroup(&reports, None, None);
assert!(!r.passed);
assert!(
r.details.iter().any(|d| d.kind == DetailKind::Stuck),
"custom gap override must produce a Stuck-kind detail: {:?}",
r.details
);
}
#[test]
fn plan_permissive_overrides_clear_unfair_and_stuck_preserve_starved() {
let reports = [
rpt(1, 1000, 5e9 as u64, 5e8 as u64, &[0], 500),
rpt(2, 0, 5e9 as u64, 0, &[0], 500),
rpt(3, 500, 5e9 as u64, 4e9 as u64, &[0], 500),
rpt(4, 1000, 5e9 as u64, 5e8 as u64, &[0], 4000),
];
let mut plan = AssertPlan::new();
plan.not_starved = true;
plan.max_spread_pct = Some(100.0);
plan.max_gap_ms = Some(5000);
let r = plan.assert_cgroup(&reports, None, None);
assert!(
r.details.iter().any(|d| d.kind == DetailKind::Starved),
"starved detail must survive permissive overrides: {:?}",
r.details
);
assert!(
!r.details.iter().any(|d| d.kind == DetailKind::Unfair),
"unfair detail must be cleared by permissive spread: {:?}",
r.details
);
assert!(
!r.details.iter().any(|d| d.kind == DetailKind::Stuck),
"stuck detail must be cleared by permissive gap: {:?}",
r.details
);
assert!(!r.passed, "starved alone is still a failure");
}
#[test]
fn plan_no_checks_always_passes() {
let plan = AssertPlan::new();
let reports = [rpt(1, 0, 0, 0, &[], 5000)]; let r = plan.assert_cgroup(&reports, None, None);
assert!(r.passed, "no checks enabled should pass");
}
#[test]
fn plan_default_all_checks_disabled() {
let plan = AssertPlan::default();
assert!(!plan.not_starved, "default must not enable not_starved");
assert!(!plan.isolation, "default must not enable isolation");
assert!(
plan.max_gap_ms.is_none(),
"default must not set gap override"
);
assert!(
plan.max_spread_pct.is_none(),
"default must not set spread override"
);
let reports = [rpt(1, 0, 0, 0, &[], 99999)];
let r = plan.assert_cgroup(&reports, None, None);
assert!(r.passed, "all-disabled plan must pass any input");
}
#[test]
fn assert_plan_default_equals_new() {
let d = AssertPlan::default();
let n = AssertPlan::new();
assert_eq!(d.not_starved, n.not_starved);
assert_eq!(d.isolation, n.isolation);
assert_eq!(d.max_gap_ms, n.max_gap_ms);
assert_eq!(d.max_spread_pct, n.max_spread_pct);
let reports = [rpt(1, 1000, 5e9 as u64, 5e8 as u64, &[0], 50)];
let rd = d.assert_cgroup(&reports, None, None);
let rn = n.assert_cgroup(&reports, None, None);
assert_eq!(rd.passed, rn.passed);
}
#[test]
fn plan_starved_still_fails_with_custom_gap() {
let plan = AssertPlan::new().check_not_starved().max_gap_ms(5000);
let reports = [
rpt(1, 1000, 5e9 as u64, 5e8 as u64, &[0], 100), rpt(2, 0, 5e9 as u64, 0, &[1], 1500), ];
let r = plan.assert_cgroup(&reports, None, None);
assert!(
!r.passed,
"starved worker must fail even with relaxed gap threshold"
);
assert!(r.details.iter().any(|d| d.contains("starved")));
assert!(!r.details.iter().any(|d| d.contains("stuck")));
}