#![cfg(test)]
use super::*;
#[test]
fn empty_samples_default_summary() {
let summary = MonitorSummary::from_samples(&[]);
assert_eq!(summary.total_samples, 0);
assert_eq!(summary.max_imbalance_ratio, 0.0);
assert_eq!(summary.max_local_dsq_depth, 0);
assert!(!summary.stuck_detected);
assert_eq!(summary.avg_imbalance_ratio, 0.0);
assert_eq!(summary.avg_nr_running, 0.0);
assert_eq!(summary.avg_local_dsq_depth, 0.0);
}
#[test]
fn single_sample_imbalanced_cpus() {
let sample = MonitorSample {
prog_stats: None,
elapsed_ms: 100,
cpus: vec![
CpuSnapshot {
nr_running: 1,
local_dsq_depth: 3,
rq_clock: 1000,
..Default::default()
},
CpuSnapshot {
nr_running: 4,
local_dsq_depth: 1,
rq_clock: 2000,
..Default::default()
},
],
};
let summary = MonitorSummary::from_samples(&[sample]);
assert_eq!(summary.total_samples, 1);
assert!((summary.max_imbalance_ratio - 4.0).abs() < f64::EPSILON);
assert_eq!(summary.max_local_dsq_depth, 3);
assert!(!summary.stuck_detected);
assert!((summary.avg_imbalance_ratio - 4.0).abs() < f64::EPSILON);
assert!((summary.avg_nr_running - 2.5).abs() < f64::EPSILON);
assert!((summary.avg_local_dsq_depth - 2.0).abs() < f64::EPSILON);
}
#[test]
fn stuck_detected_when_clock_stuck() {
let s1 = MonitorSample {
prog_stats: None,
elapsed_ms: 100,
cpus: vec![
CpuSnapshot {
nr_running: 1,
rq_clock: 5000,
..Default::default()
},
CpuSnapshot {
nr_running: 1,
rq_clock: 6000,
..Default::default()
},
],
};
let s2 = MonitorSample {
prog_stats: None,
elapsed_ms: 200,
cpus: vec![
CpuSnapshot {
nr_running: 1,
rq_clock: 5000, ..Default::default()
},
CpuSnapshot {
nr_running: 1,
rq_clock: 7000,
..Default::default()
},
],
};
let summary = MonitorSummary::from_samples(&[s1, s2]);
assert!(summary.stuck_detected);
}
#[test]
fn balanced_cpus_ratio_one() {
let sample = MonitorSample {
prog_stats: None,
elapsed_ms: 50,
cpus: vec![
CpuSnapshot {
nr_running: 3,
rq_clock: 100,
..Default::default()
},
CpuSnapshot {
nr_running: 3,
rq_clock: 200,
..Default::default()
},
],
};
let summary = MonitorSummary::from_samples(&[sample]);
assert!((summary.max_imbalance_ratio - 1.0).abs() < f64::EPSILON);
assert!(!summary.stuck_detected);
assert!((summary.avg_imbalance_ratio - 1.0).abs() < f64::EPSILON);
assert!((summary.avg_nr_running - 3.0).abs() < f64::EPSILON);
assert!((summary.avg_local_dsq_depth - 0.0).abs() < f64::EPSILON);
}
#[test]
fn single_cpu_no_division_by_zero() {
let sample = MonitorSample {
prog_stats: None,
elapsed_ms: 10,
cpus: vec![CpuSnapshot {
nr_running: 5,
local_dsq_depth: 2,
rq_clock: 1000,
..Default::default()
}],
};
let summary = MonitorSummary::from_samples(&[sample]);
assert_eq!(summary.total_samples, 1);
assert!((summary.max_imbalance_ratio - 1.0).abs() < f64::EPSILON);
assert_eq!(summary.max_local_dsq_depth, 2);
assert!(!summary.stuck_detected);
}
#[test]
fn all_zero_snapshots() {
let sample = MonitorSample {
prog_stats: None,
elapsed_ms: 0,
cpus: vec![CpuSnapshot::default(), CpuSnapshot::default()],
};
let summary = MonitorSummary::from_samples(&[sample]);
assert_eq!(summary.total_samples, 1);
assert!((summary.max_imbalance_ratio - 1.0).abs() < f64::EPSILON);
assert_eq!(summary.max_local_dsq_depth, 0);
assert!(!summary.stuck_detected);
assert_eq!(summary.avg_imbalance_ratio, 0.0);
assert_eq!(summary.avg_nr_running, 0.0);
assert_eq!(summary.avg_local_dsq_depth, 0.0);
}
#[test]
fn empty_cpus_in_sample() {
let sample = MonitorSample {
prog_stats: None,
elapsed_ms: 10,
cpus: vec![],
};
let summary = MonitorSummary::from_samples(&[sample]);
assert_eq!(summary.total_samples, 1);
assert!((summary.max_imbalance_ratio - 1.0).abs() < f64::EPSILON);
assert_eq!(summary.avg_imbalance_ratio, 0.0);
assert_eq!(summary.avg_nr_running, 0.0);
assert_eq!(summary.avg_local_dsq_depth, 0.0);
}
#[test]
fn min_nr_zero_division_guard() {
let sample = MonitorSample {
prog_stats: None,
elapsed_ms: 10,
cpus: vec![
CpuSnapshot {
nr_running: 0,
rq_clock: 100,
..Default::default()
},
CpuSnapshot {
nr_running: 0,
rq_clock: 200,
..Default::default()
},
],
};
let summary = MonitorSummary::from_samples(&[sample]);
assert!((summary.max_imbalance_ratio - 1.0).abs() < f64::EPSILON);
}
#[test]
fn min_nr_zero_max_nr_nonzero() {
let sample = MonitorSample {
prog_stats: None,
elapsed_ms: 10,
cpus: vec![
CpuSnapshot {
nr_running: 0,
rq_clock: 100,
..Default::default()
},
CpuSnapshot {
nr_running: 5,
rq_clock: 200,
..Default::default()
},
],
};
let summary = MonitorSummary::from_samples(&[sample]);
assert!((summary.max_imbalance_ratio - 5.0).abs() < f64::EPSILON);
}
#[test]
fn advancing_clocks_no_stuck() {
let s1 = MonitorSample {
prog_stats: None,
elapsed_ms: 100,
cpus: vec![
CpuSnapshot {
nr_running: 1,
rq_clock: 1000,
..Default::default()
},
CpuSnapshot {
nr_running: 1,
rq_clock: 2000,
..Default::default()
},
],
};
let s2 = MonitorSample {
prog_stats: None,
elapsed_ms: 200,
cpus: vec![
CpuSnapshot {
nr_running: 1,
rq_clock: 1500,
..Default::default()
},
CpuSnapshot {
nr_running: 1,
rq_clock: 2500,
..Default::default()
},
],
};
let s3 = MonitorSample {
prog_stats: None,
elapsed_ms: 300,
cpus: vec![
CpuSnapshot {
nr_running: 1,
rq_clock: 2000,
..Default::default()
},
CpuSnapshot {
nr_running: 1,
rq_clock: 3000,
..Default::default()
},
],
};
let summary = MonitorSummary::from_samples(&[s1, s2, s3]);
assert!(!summary.stuck_detected);
assert_eq!(summary.total_samples, 3);
}
#[test]
fn different_length_cpu_vecs() {
let s1 = MonitorSample {
prog_stats: None,
elapsed_ms: 100,
cpus: vec![
CpuSnapshot {
nr_running: 1,
rq_clock: 1000,
..Default::default()
},
CpuSnapshot {
nr_running: 1,
rq_clock: 2000,
..Default::default()
},
],
};
let s2 = MonitorSample {
prog_stats: None,
elapsed_ms: 200,
cpus: vec![
CpuSnapshot {
nr_running: 1,
rq_clock: 1500,
..Default::default()
},
CpuSnapshot {
nr_running: 1,
rq_clock: 2500,
..Default::default()
},
CpuSnapshot {
nr_running: 1,
rq_clock: 3000,
..Default::default()
},
],
};
let summary = MonitorSummary::from_samples(&[s1, s2]);
assert!(!summary.stuck_detected);
assert_eq!(summary.total_samples, 2);
assert_eq!(summary.max_local_dsq_depth, 0);
}
#[test]
fn from_samples_fields_sane_values() {
let samples: Vec<_> = (0..5u64)
.map(|i| MonitorSample {
prog_stats: None,
elapsed_ms: i * 100,
cpus: vec![
CpuSnapshot {
nr_running: (i as u32 + 1),
scx_nr_running: i as u32,
local_dsq_depth: (i as u32) % 3,
rq_clock: 1000 + i * 500,
scx_flags: 0,
event_counters: Some(ScxEventCounters {
select_cpu_fallback: i as i64 * 2,
dispatch_keep_last: i as i64,
..Default::default()
}),
schedstat: None,
vcpu_cpu_time_ns: None,
vcpu_perf: None,
sched_domains: None,
},
CpuSnapshot {
nr_running: (i as u32 + 2),
scx_nr_running: i as u32 + 1,
local_dsq_depth: 0,
rq_clock: 1100 + i * 600,
scx_flags: 0,
event_counters: Some(ScxEventCounters {
select_cpu_fallback: i as i64 * 3,
dispatch_keep_last: i as i64 * 2,
..Default::default()
}),
schedstat: None,
vcpu_cpu_time_ns: None,
vcpu_perf: None,
sched_domains: None,
},
],
})
.collect();
let summary = MonitorSummary::from_samples(&samples);
assert_eq!(summary.total_samples, 5);
assert!(
summary.max_imbalance_ratio >= 1.0,
"ratio must be >= 1.0: {}",
summary.max_imbalance_ratio
);
assert!(
summary.max_imbalance_ratio <= 10.0,
"ratio must be reasonable: {}",
summary.max_imbalance_ratio
);
assert!(
summary.max_local_dsq_depth <= DSQ_PLAUSIBILITY_CEILING,
"dsq depth must be below plausibility ceiling: {}",
summary.max_local_dsq_depth
);
assert!(
summary.max_local_dsq_depth <= 10,
"dsq depth must be small in this controlled test: {}",
summary.max_local_dsq_depth
);
assert!(
!summary.stuck_detected,
"no stuck expected with advancing rq_clock"
);
let deltas = summary
.event_deltas
.as_ref()
.expect("event deltas must be present");
assert!(
deltas.total_fallback >= 0,
"fallback count must be non-negative"
);
assert!(
deltas.total_dispatch_keep_last >= 0,
"keep_last count must be non-negative"
);
assert!(
deltas.fallback_rate >= 0.0,
"fallback rate must be non-negative"
);
assert!(
deltas.keep_last_rate >= 0.0,
"keep_last rate must be non-negative"
);
assert!(
summary.avg_imbalance_ratio >= 1.0,
"avg imbalance must be >= 1.0: {}",
summary.avg_imbalance_ratio,
);
assert!(
summary.avg_nr_running > 0.0,
"avg nr_running must be positive: {}",
summary.avg_nr_running,
);
assert!(
summary.avg_local_dsq_depth >= 0.0,
"avg dsq_depth must be non-negative: {}",
summary.avg_local_dsq_depth,
);
}
#[test]
fn from_samples_empty_all_defaults() {
let summary = MonitorSummary::from_samples(&[]);
assert_eq!(summary.total_samples, 0);
assert_eq!(summary.max_imbalance_ratio, 0.0);
assert_eq!(summary.max_local_dsq_depth, 0);
assert!(!summary.stuck_detected);
assert_eq!(summary.avg_imbalance_ratio, 0.0);
assert_eq!(summary.avg_nr_running, 0.0);
assert_eq!(summary.avg_local_dsq_depth, 0.0);
assert!(
summary.event_deltas.is_none(),
"empty input must not produce event deltas"
);
}