#![cfg(test)]
use super::test_util::balanced_sample;
use super::*;
fn sample_with_events(
elapsed_ms: u64,
clock_base: u64,
fallback: i64,
keep_last: i64,
) -> MonitorSample {
MonitorSample {
prog_stats: None,
elapsed_ms,
cpus: vec![
CpuSnapshot {
nr_running: 2,
rq_clock: clock_base,
event_counters: Some(ScxEventCounters {
select_cpu_fallback: fallback,
dispatch_keep_last: keep_last,
..Default::default()
}),
..Default::default()
},
CpuSnapshot {
nr_running: 2,
rq_clock: clock_base + 100,
event_counters: Some(ScxEventCounters {
select_cpu_fallback: fallback,
dispatch_keep_last: keep_last,
..Default::default()
}),
..Default::default()
},
],
}
}
#[test]
fn thresholds_fallback_rate_sustained_fails() {
let t = MonitorThresholds {
sustained_samples: 3,
max_fallback_rate: 10.0,
fail_on_stall: false,
enforce: true,
..Default::default()
};
let samples: Vec<_> = (0..4)
.map(|i| sample_with_events(i * 100, 1000 + i * 500, i as i64 * 10, 0))
.collect();
let summary = MonitorSummary::from_samples(&samples);
let report = MonitorReport {
samples,
summary,
..Default::default()
};
let v = t.evaluate(&report);
assert!(v.is_fail());
assert!(v.details.iter().any(|d| d.contains("fallback rate")));
}
#[test]
fn thresholds_fallback_rate_below_sustained_passes() {
let t = MonitorThresholds {
sustained_samples: 3,
max_fallback_rate: 10.0,
fail_on_stall: false,
..Default::default()
};
let mut samples: Vec<_> = (0..3)
.map(|i| sample_with_events(i * 100, 1000 + i * 500, i as i64 * 10, 0))
.collect();
samples.push(sample_with_events(300, 2500, 20, 0));
let summary = MonitorSummary::from_samples(&samples);
let report = MonitorReport {
samples,
summary,
..Default::default()
};
let v = t.evaluate(&report);
assert!(v.passed, "2 violations < sustained=3: {:?}", v.details);
}
#[test]
fn thresholds_keep_last_rate_sustained_fails() {
let t = MonitorThresholds {
sustained_samples: 3,
max_keep_last_rate: 10.0,
fail_on_stall: false,
enforce: true,
..Default::default()
};
let samples: Vec<_> = (0..4)
.map(|i| sample_with_events(i * 100, 1000 + i * 500, 0, i as i64 * 10))
.collect();
let summary = MonitorSummary::from_samples(&samples);
let report = MonitorReport {
samples,
summary,
..Default::default()
};
let v = t.evaluate(&report);
assert!(v.is_fail());
assert!(v.details.iter().any(|d| d.contains("keep_last rate")));
}
#[test]
fn thresholds_keep_last_rate_below_sustained_passes() {
let t = MonitorThresholds {
sustained_samples: 3,
max_keep_last_rate: 10.0,
fail_on_stall: false,
..Default::default()
};
let mut samples: Vec<_> = (0..3)
.map(|i| sample_with_events(i * 100, 1000 + i * 500, 0, i as i64 * 10))
.collect();
samples.push(sample_with_events(300, 2500, 0, 20));
let summary = MonitorSummary::from_samples(&samples);
let report = MonitorReport {
samples,
summary,
..Default::default()
};
let v = t.evaluate(&report);
assert!(v.passed, "2 violations < sustained=3: {:?}", v.details);
}
#[test]
fn thresholds_event_rate_interrupted_resets() {
let t = MonitorThresholds {
sustained_samples: 3,
max_fallback_rate: 10.0,
fail_on_stall: false,
..Default::default()
};
let mut samples = Vec::new();
for i in 0..3u64 {
samples.push(sample_with_events(
i * 100,
1000 + i * 500,
i as i64 * 10,
0,
));
}
samples.push(sample_with_events(300, 2500, 20, 0));
for i in 0..2u64 {
samples.push(sample_with_events(
400 + i * 100,
3000 + i * 500,
30 + (i + 1) as i64 * 10,
0,
));
}
let summary = MonitorSummary::from_samples(&samples);
let report = MonitorReport {
samples,
summary,
..Default::default()
};
let v = t.evaluate(&report);
assert!(
v.is_pass(),
"interrupted rate violations should pass: {:?}",
v.details
);
}
#[test]
fn thresholds_no_event_counters_skips_rate_check() {
let t = MonitorThresholds {
sustained_samples: 1,
max_fallback_rate: 0.0, max_keep_last_rate: 0.0,
fail_on_stall: false,
..Default::default()
};
let samples: Vec<_> = (0..5)
.map(|i| balanced_sample(i * 100, 1000 + i * 500))
.collect();
let summary = MonitorSummary::from_samples(&samples);
let report = MonitorReport {
samples,
summary,
..Default::default()
};
let v = t.evaluate(&report);
assert!(
v.is_pass(),
"no event counters should skip rate check: {:?}",
v.details
);
}
#[test]
fn thresholds_default_event_rate_values() {
let t = MonitorThresholds::default();
assert!((t.max_fallback_rate - 200.0).abs() < f64::EPSILON);
assert!((t.max_keep_last_rate - 100.0).abs() < f64::EPSILON);
}
#[test]
fn summary_keep_last_rate_computed() {
let samples = vec![
sample_with_events(0, 1000, 0, 0),
sample_with_events(100, 1500, 0, 5),
sample_with_events(200, 2000, 0, 10),
];
let summary = MonitorSummary::from_samples(&samples);
let deltas = summary.event_deltas.unwrap();
assert!((deltas.keep_last_rate - 100.0).abs() < f64::EPSILON);
}
#[test]
fn event_deltas_none_without_counters() {
let samples = vec![balanced_sample(100, 1000), balanced_sample(200, 1500)];
let summary = MonitorSummary::from_samples(&samples);
assert!(summary.event_deltas.is_none());
}
#[test]
fn event_deltas_single_sample() {
let samples = vec![sample_with_events(100, 1000, 50, 25)];
let summary = MonitorSummary::from_samples(&samples);
let deltas = summary.event_deltas.unwrap();
assert_eq!(deltas.fallback_rate, 0.0);
assert_eq!(deltas.keep_last_rate, 0.0);
}
#[test]
fn event_deltas_max_fallback_burst() {
let samples = vec![
sample_with_events(0, 1000, 0, 0),
sample_with_events(100, 1500, 5, 0),
sample_with_events(200, 2000, 100, 0),
];
let summary = MonitorSummary::from_samples(&samples);
let deltas = summary.event_deltas.unwrap();
assert!(deltas.max_fallback_burst > 0);
}
#[test]
fn event_deltas_counter_reset_clamps_to_zero() {
let samples = vec![
sample_with_events(0, 1000, 1000, 500),
sample_with_events(1000, 2000, 5, 2),
];
let summary = MonitorSummary::from_samples(&samples);
let deltas = summary.event_deltas.unwrap();
assert!(
deltas.total_fallback >= 0,
"reset must not produce negative total_fallback, got {}",
deltas.total_fallback
);
assert!(
deltas.fallback_rate >= 0.0,
"reset must not produce negative fallback_rate, got {}",
deltas.fallback_rate
);
assert!(
deltas.total_dispatch_keep_last >= 0,
"reset must not produce negative keep_last total, got {}",
deltas.total_dispatch_keep_last
);
assert!(
deltas.keep_last_rate >= 0.0,
"reset must not produce negative keep_last_rate, got {}",
deltas.keep_last_rate
);
}
#[test]
fn event_deltas_all_counters_computed() {
let make = |elapsed_ms, fb, kl, dsq_off, exit, migdis| MonitorSample {
prog_stats: None,
elapsed_ms,
cpus: vec![CpuSnapshot {
nr_running: 1,
rq_clock: elapsed_ms * 10,
event_counters: Some(ScxEventCounters {
select_cpu_fallback: fb,
dispatch_local_dsq_offline: dsq_off,
dispatch_keep_last: kl,
enq_skip_exiting: exit,
enq_skip_migration_disabled: migdis,
..Default::default()
}),
..Default::default()
}],
};
let samples = vec![
make(100, 10, 20, 30, 40, 50),
make(200, 110, 120, 130, 140, 150),
];
let summary = MonitorSummary::from_samples(&samples);
let d = summary.event_deltas.unwrap();
assert_eq!(d.total_fallback, 100);
assert_eq!(d.total_dispatch_keep_last, 100);
assert_eq!(d.total_dispatch_offline, 100);
assert_eq!(d.total_enq_skip_exiting, 100);
assert_eq!(d.total_enq_skip_migration_disabled, 100);
}
#[test]
fn neg_fallback_rate_threshold_fires() {
let t = MonitorThresholds {
sustained_samples: 2,
max_fallback_rate: 5.0,
fail_on_stall: false,
enforce: true,
..Default::default()
};
let samples: Vec<_> = (0..3u64)
.map(|i| sample_with_events(i * 100, 1000 + i * 500, i as i64 * 10, 0))
.collect();
let summary = MonitorSummary::from_samples(&samples);
assert!(
summary.event_deltas.is_some(),
"event deltas must be computed"
);
let report = MonitorReport {
samples,
summary,
..Default::default()
};
let v = t.evaluate(&report);
assert!(!v.passed, "fallback rate must be caught");
let detail = v
.failure_details()
.find(|d| d.contains("fallback rate"))
.unwrap();
assert!(detail.contains("/s"), "must include rate unit: {detail}");
assert!(
detail.contains("exceeded threshold"),
"must state threshold: {detail}"
);
assert!(
detail.contains("5.0/s"),
"must show threshold value: {detail}"
);
assert!(
detail.contains("consecutive intervals"),
"must show sustained count: {detail}"
);
}
#[test]
fn neg_keep_last_rate_threshold_fires() {
let t = MonitorThresholds {
sustained_samples: 2,
max_keep_last_rate: 5.0,
fail_on_stall: false,
enforce: true,
..Default::default()
};
let samples: Vec<_> = (0..3u64)
.map(|i| sample_with_events(i * 100, 1000 + i * 500, 0, i as i64 * 10))
.collect();
let summary = MonitorSummary::from_samples(&samples);
assert!(summary.event_deltas.is_some());
let report = MonitorReport {
samples,
summary,
..Default::default()
};
let v = t.evaluate(&report);
assert!(!v.passed, "keep_last rate must be caught");
let detail = v
.failure_details()
.find(|d| d.contains("keep_last rate"))
.unwrap();
assert!(detail.contains("/s"), "must include rate unit: {detail}");
assert!(
detail.contains("exceeded threshold"),
"must state threshold: {detail}"
);
assert!(
detail.contains("5.0/s"),
"must show threshold value: {detail}"
);
}