use super::tests_common::rpt;
use super::*;
#[test]
fn merge_cgroups() {
let r1 = assert_not_starved(&[
rpt(1, 1000, 5e9 as u64, 5e8 as u64, &[0, 1], 50),
rpt(2, 1000, 5e9 as u64, 6e8 as u64, &[0, 1], 60),
]);
let r2 = assert_not_starved(&[
rpt(3, 1000, 5e9 as u64, 25e8 as u64, &[2, 3], 50),
rpt(4, 1000, 5e9 as u64, 26e8 as u64, &[2, 3], 50),
]);
let mut m = r1;
m.merge(r2);
assert_eq!(m.stats.cgroups.len(), 2);
assert_eq!(m.stats.total_workers, 4);
assert!(m.is_pass(), "diff cgroups diff off_cpu should pass");
}
#[test]
fn merge_takes_worst_gap() {
let r1 = assert_not_starved(&[rpt(1, 1000, 5e9 as u64, 5e8 as u64, &[0], 100)]);
let r2 = assert_not_starved(&[rpt(2, 1000, 5e9 as u64, 5e8 as u64, &[1], 500)]);
let mut m = r1;
m.merge(r2);
assert_eq!(m.stats.worst_gap_ms, 500);
assert_eq!(m.stats.worst_gap_cpu, 1);
}
#[test]
fn merge_takes_worst_gap_reverse_self_retains() {
let r1 = assert_not_starved(&[rpt(1, 1000, 5e9 as u64, 5e8 as u64, &[0], 700)]);
let r2 = assert_not_starved(&[rpt(2, 1000, 5e9 as u64, 5e8 as u64, &[1], 200)]);
let mut m = r1;
m.merge(r2);
assert_eq!(
m.stats.worst_gap_ms, 700,
"self's larger gap must be retained",
);
assert_eq!(
m.stats.worst_gap_cpu, 0,
"worst_gap_cpu must stay coupled to self's worst_gap_ms — \
a regression overwriting cpu from other would set this to 1",
);
}
#[test]
fn merge_takes_worst_spread() {
let r1 = assert_not_starved(&[
rpt(1, 1000, 5e9 as u64, 1e9 as u64, &[0], 50),
rpt(2, 1000, 5e9 as u64, 12e8 as u64, &[0], 50),
]); let r2 = assert_not_starved(&[
rpt(3, 1000, 5e9 as u64, 1e9 as u64, &[1], 50),
rpt(4, 1000, 5e9 as u64, 15e8 as u64, &[1], 50),
]); let mut m = r1;
m.merge(r2);
assert!((m.stats.worst_spread - 10.0).abs() < 0.1);
}
#[test]
fn merge_skip_plus_explicit_pass_demotes_skip() {
let mut a = AssertResult::skip("optional");
let mut b = AssertResult::pass();
b.record_pass();
a.merge(b);
assert!(
!a.is_skip(),
"explicit Pass in the merged stream means not all-Skip"
);
assert!(a.is_pass(), "explicit Pass + no Fail → is_pass=true");
}
#[test]
fn merge_skip_plus_empty_pass_stays_skip() {
let mut a = AssertResult::skip("optional");
let b = AssertResult::pass();
a.merge(b);
assert!(
a.is_skip(),
"empty pass() merges to a no-op; stream stays all-Skip"
);
assert!(!a.is_pass(), "all-Skip is not pass");
}
#[test]
fn merge_skip_plus_fail_is_fail_not_skip() {
let mut a = AssertResult::skip("topo missing");
let mut b = AssertResult::pass();
b.record_fail(AssertDetail::new(DetailKind::Other, "synthetic fail"));
a.merge(b);
assert!(a.is_fail());
assert!(!a.is_skip());
}
#[test]
fn merge_accumulates_totals() {
let r1 = assert_not_starved(&[rpt(1, 1000, 5e9 as u64, 5e8 as u64, &[0], 50)]);
let r2 = assert_not_starved(&[rpt(2, 1000, 5e9 as u64, 5e8 as u64, &[1], 50)]);
let mut m = r1;
m.merge(r2);
assert_eq!(m.stats.total_workers, 2);
assert_eq!(m.stats.total_cpus, 2);
}
#[test]
fn merge_three_cgroups_worst_wins_and_iterations_sum() {
fn mk(
worst_spread: f64,
worst_mig: f64,
page_locality: f64,
total_iters: u64,
cg_total_iters: u64,
) -> AssertResult {
let cg = CgroupStats {
total_iterations: cg_total_iters,
page_locality,
..CgroupStats::default()
};
AssertResult {
outcomes: vec![],
passes: vec![],
stats: ScenarioStats {
total_iterations: total_iters,
worst_spread,
worst_migration_ratio: worst_mig,
cgroups: vec![cg],
..ScenarioStats::default()
},
measurements: std::collections::BTreeMap::new(),
info_notes: vec![],
}
}
let mut acc = mk(10.0, 0.1, 0.8, 100, 100);
acc.merge(mk(5.0, 0.3, 0.5, 200, 200));
acc.merge(mk(20.0, 0.2, 0.9, 400, 400));
let s = &acc.stats;
assert_eq!(
s.cgroups.len(),
3,
"3 cgroups must accumulate; a missing entry means stats.cgroups.extend dropped a merge",
);
assert_eq!(s.cgroups[0].total_iterations, 100);
assert_eq!(s.cgroups[1].total_iterations, 200);
assert_eq!(s.cgroups[2].total_iterations, 400);
assert_eq!(s.cgroups[0].page_locality, 0.8);
assert_eq!(s.cgroups[1].page_locality, 0.5);
assert_eq!(s.cgroups[2].page_locality, 0.9);
assert_eq!(s.worst_spread, 20.0, "third cgroup's 20.0 is worst");
assert_eq!(s.worst_migration_ratio, 0.3, "second cgroup's 0.3 is worst");
assert_eq!(
s.total_iterations,
100 + 200 + 400,
"total_iterations must sum (not max) across all merged cgroups",
);
}
#[test]
fn iterations_per_worker_distinguishes_no_workers_from_ran_zero() {
let no_workers = CgroupStats {
num_workers: 0,
total_iterations: 0,
..CgroupStats::default()
};
assert_eq!(no_workers.iterations_per_worker(), None);
let ran_zero = CgroupStats {
num_workers: 4,
total_iterations: 0,
..CgroupStats::default()
};
assert_eq!(ran_zero.iterations_per_worker(), Some(0.0));
let ran = CgroupStats {
num_workers: 4,
total_iterations: 400,
..CgroupStats::default()
};
assert_eq!(ran.iterations_per_worker(), Some(100.0));
}
#[test]
fn repool_worst_iterations_per_worker_lets_measured_zero_win() {
fn cg(num_workers: usize, total_iterations: u64) -> AssertResult {
let mut r = AssertResult::pass();
r.stats.cgroups = vec![CgroupStats {
num_workers,
total_iterations,
..CgroupStats::default()
}];
r
}
let mut acc = AssertResult::pass();
acc.merge(cg(1, 100)); acc.merge(cg(4, 0)); acc.merge(cg(1, 250)); populate_run_distribution_metrics(&mut acc.stats);
assert_eq!(
acc.stats
.ext_metrics
.get("worst_iterations_per_worker")
.copied(),
Some(0.0),
"a cgroup that ran zero iterations must win the worst bucket",
);
}
#[test]
fn repool_worst_iterations_per_worker_skips_no_data() {
fn cg(num_workers: usize, total_iterations: u64) -> AssertResult {
let mut r = AssertResult::pass();
r.stats.cgroups = vec![CgroupStats {
num_workers,
total_iterations,
..CgroupStats::default()
}];
r
}
let mut acc = AssertResult::pass();
acc.merge(cg(0, 0));
acc.merge(cg(0, 0));
populate_run_distribution_metrics(&mut acc.stats);
assert_eq!(
acc.stats.ext_metrics.get("worst_iterations_per_worker"),
None,
"all-None cohort must write no key (absence != measured 0.0)",
);
let mut acc2 = AssertResult::pass();
acc2.merge(cg(1, 75)); acc2.merge(cg(0, 0)); populate_run_distribution_metrics(&mut acc2.stats);
assert_eq!(
acc2.stats
.ext_metrics
.get("worst_iterations_per_worker")
.copied(),
Some(75.0),
"a None contributor must not displace a real reading",
);
}
#[test]
fn repool_worst_iterations_per_cpu_sec_lowest_wins_none_aware() {
fn cg(num_workers: usize, total_iterations: u64, cpu_ns: u64) -> AssertResult {
let mut r = AssertResult::pass();
r.stats.cgroups = vec![CgroupStats {
num_workers,
total_iterations,
total_cpu_time_ns: cpu_ns,
..CgroupStats::default()
}];
r
}
let mut acc = AssertResult::pass();
acc.merge(cg(0, 0, 0)); acc.merge(cg(1, 900, 1_000_000_000)); acc.merge(cg(1, 0, 1_000_000_000)); acc.merge(cg(1, 1500, 1_000_000_000)); populate_run_distribution_metrics(&mut acc.stats);
assert_eq!(
acc.stats
.ext_metrics
.get("worst_iterations_per_cpu_sec")
.copied(),
Some(0.0),
"least-efficient cgroup (measured 0.0) wins; None skipped",
);
}
#[test]
fn populate_run_pooled_iterations_per_cpu_sec_repools_across_cgroups() {
let cg1 = CgroupStats {
total_iterations: 1000,
total_cpu_time_ns: 1_000_000_000,
num_workers: 1,
..CgroupStats::default()
};
let cg2 = CgroupStats {
total_iterations: 10,
total_cpu_time_ns: 9_000_000_000,
num_workers: 1,
..CgroupStats::default()
};
let stats_for = |cg: &CgroupStats| ScenarioStats {
total_iterations: cg.total_iterations,
cgroups: vec![cg.clone()],
..ScenarioStats::default()
};
let mk = |cg: &CgroupStats| AssertResult {
outcomes: vec![],
passes: vec![],
stats: stats_for(cg),
measurements: std::collections::BTreeMap::new(),
info_notes: vec![],
};
let mut acc = mk(&cg1);
acc.merge(mk(&cg2));
populate_run_pooled_iterations_per_cpu_sec(&mut acc.stats);
assert_eq!(
acc.stats.ext_metrics.get("iterations_per_cpu_sec").copied(),
Some(101.0),
"pooled rate must be Σiters/Σcpu-s = 101.0, NOT mean-of-ratios \
(~500.6) or the worst cgroup (~1.11); got {:?}",
acc.stats.ext_metrics.get("iterations_per_cpu_sec"),
);
assert_eq!(acc.stats.total_iterations, 1010);
assert_eq!(
acc.stats
.ext_metrics
.get("total_iterations_pooled")
.copied(),
Some(acc.stats.total_iterations as f64),
"total_iterations_pooled must equal the merge-summed typed total_iterations \
when every cgroup is measured",
);
populate_run_distribution_metrics(&mut acc.stats);
let worst = acc
.stats
.ext_metrics
.get("worst_iterations_per_cpu_sec")
.copied()
.expect("worst_iterations_per_cpu_sec present in ext_metrics");
assert!(
(worst - 10.0 / 9.0).abs() < 1e-9,
"worst_iterations_per_cpu_sec stays the lowest-wins selector (~1.11), \
distinct from the pooled rate; got {worst}",
);
}
#[test]
fn populate_run_pooled_iterations_per_cpu_sec_absent_on_zero_cpu_time() {
let cg = CgroupStats {
total_iterations: 500,
total_cpu_time_ns: 0,
num_workers: 1,
..CgroupStats::default()
};
let mut acc = AssertResult {
outcomes: vec![],
passes: vec![],
stats: ScenarioStats {
total_iterations: cg.total_iterations,
cgroups: vec![cg],
..ScenarioStats::default()
},
measurements: std::collections::BTreeMap::new(),
info_notes: vec![],
};
populate_run_pooled_iterations_per_cpu_sec(&mut acc.stats);
assert!(
!acc.stats.ext_metrics.contains_key("iterations_per_cpu_sec"),
"no pooled rate when Σcpu-time is 0",
);
assert!(
!acc.stats.ext_metrics.contains_key("total_cpu_time_sec")
&& !acc
.stats
.ext_metrics
.contains_key("total_iterations_pooled"),
"both-or-neither: neither component inserted when Σcpu-time is 0",
);
}
#[test]
fn populate_run_pooled_iterations_per_cpu_sec_excludes_zero_cpu_cgroup() {
let unmeasured = CgroupStats {
total_iterations: 500,
total_cpu_time_ns: 0,
num_workers: 1,
..CgroupStats::default()
};
let measured = CgroupStats {
total_iterations: 1000,
total_cpu_time_ns: 1_000_000_000,
num_workers: 1,
..CgroupStats::default()
};
let mk = |cg: &CgroupStats| AssertResult {
outcomes: vec![],
passes: vec![],
stats: ScenarioStats {
total_iterations: cg.total_iterations,
cgroups: vec![cg.clone()],
..ScenarioStats::default()
},
measurements: std::collections::BTreeMap::new(),
info_notes: vec![],
};
let mut acc = mk(&unmeasured);
acc.merge(mk(&measured));
populate_run_pooled_iterations_per_cpu_sec(&mut acc.stats);
assert_eq!(
acc.stats.ext_metrics.get("iterations_per_cpu_sec").copied(),
Some(1000.0),
"zero-cpu cgroup's iters must NOT inflate the pooled rate; got {:?}",
acc.stats.ext_metrics.get("iterations_per_cpu_sec"),
);
assert_eq!(
acc.stats
.ext_metrics
.get("total_iterations_pooled")
.copied(),
Some(1000.0),
);
assert_eq!(acc.stats.total_iterations, 1500);
}
#[test]
fn populate_run_pooled_iterations_per_cpu_sec_single_cgroup() {
let cg = CgroupStats {
total_iterations: 750,
total_cpu_time_ns: 3_000_000_000,
num_workers: 1,
..CgroupStats::default()
};
let mut acc = AssertResult {
outcomes: vec![],
passes: vec![],
stats: ScenarioStats {
total_iterations: cg.total_iterations,
cgroups: vec![cg.clone()],
..ScenarioStats::default()
},
measurements: std::collections::BTreeMap::new(),
info_notes: vec![],
};
populate_run_pooled_iterations_per_cpu_sec(&mut acc.stats);
assert_eq!(
acc.stats.ext_metrics.get("iterations_per_cpu_sec").copied(),
cg.iterations_per_cpu_sec(),
);
assert_eq!(
acc.stats.ext_metrics.get("iterations_per_cpu_sec").copied(),
Some(250.0),
);
}
#[test]
fn populate_run_pooled_iterations_per_cpu_sec_empty_cgroups() {
let mut stats = ScenarioStats::default();
populate_run_pooled_iterations_per_cpu_sec(&mut stats);
assert!(
stats.ext_metrics.is_empty(),
"no components inserted for an empty cgroups vec",
);
}
#[test]
fn populate_run_pooled_iterations_per_cpu_sec_includes_costed_idle_cgroup() {
let idle = CgroupStats {
total_iterations: 0,
total_cpu_time_ns: 2_000_000_000,
num_workers: 1,
..CgroupStats::default()
};
let busy = CgroupStats {
total_iterations: 1000,
total_cpu_time_ns: 1_000_000_000,
num_workers: 1,
..CgroupStats::default()
};
let mk = |cg: &CgroupStats| AssertResult {
outcomes: vec![],
passes: vec![],
stats: ScenarioStats {
total_iterations: cg.total_iterations,
cgroups: vec![cg.clone()],
..ScenarioStats::default()
},
measurements: std::collections::BTreeMap::new(),
info_notes: vec![],
};
let mut acc = mk(&idle);
acc.merge(mk(&busy));
populate_run_pooled_iterations_per_cpu_sec(&mut acc.stats);
let rate = acc
.stats
.ext_metrics
.get("iterations_per_cpu_sec")
.copied()
.expect("pooled rate present");
assert!(
(rate - 1000.0 / 3.0).abs() < 1e-9,
"costed-idle cgroup's CPU must dilute the rate to ~333.33, not 1000; got {rate}",
);
assert_eq!(
acc.stats
.ext_metrics
.get("total_iterations_pooled")
.copied(),
Some(1000.0),
);
assert_eq!(
acc.stats.ext_metrics.get("total_cpu_time_sec").copied(),
Some(3.0),
);
}
#[test]
fn populate_run_pooled_iterations_per_cpu_sec_tiny_denominator_stays_finite() {
let cg = CgroupStats {
total_iterations: 1000,
total_cpu_time_ns: 1,
num_workers: 1,
..CgroupStats::default()
};
let mut acc = AssertResult {
outcomes: vec![],
passes: vec![],
stats: ScenarioStats {
total_iterations: cg.total_iterations,
cgroups: vec![cg],
..ScenarioStats::default()
},
measurements: std::collections::BTreeMap::new(),
info_notes: vec![],
};
populate_run_pooled_iterations_per_cpu_sec(&mut acc.stats);
let rate = acc
.stats
.ext_metrics
.get("iterations_per_cpu_sec")
.copied()
.expect("tiny-denom rate present (finite, not dropped)");
assert!(
rate.is_finite() && rate > 0.0,
"tiny-denom rate must be finite-but-enormous (~1e12), not inf/absent; got {rate}",
);
}
#[test]
fn populate_run_pooled_taobench_repools_across_cgroups() {
let tb = |fast: u64, slow: u64, secs: u64| {
Some(crate::workload::taobench::run::TaobenchStats {
get_cmds: fast + slow,
get_misses: slow,
fast_ops: fast,
slow_ops: slow,
elapsed_ns: secs * 1_000_000_000,
})
};
let cg1 = CgroupStats {
taobench_whole: tb(800, 200, 10),
num_workers: 1,
..CgroupStats::default()
};
let cg2 = CgroupStats {
taobench_whole: tb(300, 700, 8),
num_workers: 1,
..CgroupStats::default()
};
let mk = |cg: &CgroupStats| AssertResult {
outcomes: vec![],
passes: vec![],
stats: ScenarioStats {
cgroups: vec![cg.clone()],
..ScenarioStats::default()
},
measurements: std::collections::BTreeMap::new(),
info_notes: vec![],
};
let mut acc = mk(&cg1);
acc.merge(mk(&cg2));
populate_run_pooled_taobench(&mut acc.stats);
let e = &acc.stats.ext_metrics;
assert_eq!(e.get("total_taobench_ops").copied(), Some(2000.0));
assert_eq!(e.get("total_taobench_fast_ops").copied(), Some(1100.0));
assert_eq!(e.get("total_taobench_slow_ops").copied(), Some(900.0));
assert_eq!(
e.get("total_taobench_wall_sec").copied(),
Some(10.0),
"wall is MAX(10,8) = 10, not Σ = 18",
);
assert_eq!(e.get("taobench_total_ops_per_sec").copied(), Some(200.0));
assert_eq!(e.get("taobench_fast_ops_per_sec").copied(), Some(110.0));
assert_eq!(e.get("taobench_slow_ops_per_sec").copied(), Some(90.0));
let hit = e
.get("taobench_hit_fraction")
.copied()
.expect("hit_fraction derived");
assert!(
(hit - 0.55).abs() < 1e-9,
"Σfast/Σops = 1100/2000 = 0.55, got {hit}",
);
}
#[test]
fn populate_run_pooled_taobench_absent_without_taobench_cgroup() {
let cg = CgroupStats {
num_workers: 1,
..CgroupStats::default()
};
let mut stats = ScenarioStats {
cgroups: vec![cg],
..ScenarioStats::default()
};
populate_run_pooled_taobench(&mut stats);
assert!(
stats.ext_metrics.is_empty(),
"no taobench keys when no cgroup ran taobench",
);
let mut empty = ScenarioStats::default();
populate_run_pooled_taobench(&mut empty);
assert!(empty.ext_metrics.is_empty(), "no keys for empty cgroups");
}
#[test]
fn populate_run_pooled_taobench_absent_on_zero_wall() {
let cg = CgroupStats {
taobench_whole: Some(crate::workload::taobench::run::TaobenchStats {
get_cmds: 100,
get_misses: 10,
fast_ops: 90,
slow_ops: 10,
elapsed_ns: 0,
}),
num_workers: 1,
..CgroupStats::default()
};
let mut stats = ScenarioStats {
cgroups: vec![cg],
..ScenarioStats::default()
};
populate_run_pooled_taobench(&mut stats);
assert!(
stats.ext_metrics.is_empty(),
"no keys when the wall window is 0 (qps undefined)",
);
}
#[test]
fn populate_run_pooled_taobench_hit_fraction_absent_when_no_ops() {
let cg = CgroupStats {
taobench_whole: Some(crate::workload::taobench::run::TaobenchStats {
get_cmds: 0,
get_misses: 0,
fast_ops: 0,
slow_ops: 0,
elapsed_ns: 5_000_000_000,
}),
num_workers: 1,
..CgroupStats::default()
};
let mut stats = ScenarioStats {
cgroups: vec![cg],
..ScenarioStats::default()
};
populate_run_pooled_taobench(&mut stats);
let e = &stats.ext_metrics;
assert_eq!(e.get("total_taobench_ops").copied(), Some(0.0));
assert_eq!(e.get("total_taobench_wall_sec").copied(), Some(5.0));
assert_eq!(e.get("taobench_total_ops_per_sec").copied(), Some(0.0));
assert!(
!e.contains_key("taobench_hit_fraction"),
"hit_fraction absent when no ops completed",
);
assert_eq!(e.get("total_taobench_get_cmds").copied(), Some(0.0));
assert_eq!(e.get("total_taobench_get_hits").copied(), Some(0.0));
assert!(
!e.contains_key("taobench_command_hit_rate"),
"command_hit_rate absent when no lookups issued",
);
}
#[test]
fn populate_run_pooled_taobench_command_hit_diverges_from_response() {
let mk = |cmds: u64, misses: u64, fast: u64, slow: u64, secs: u64| {
Some(crate::workload::taobench::run::TaobenchStats {
get_cmds: cmds,
get_misses: misses,
fast_ops: fast,
slow_ops: slow,
elapsed_ns: secs * 1_000_000_000,
})
};
let cg1 = CgroupStats {
taobench_whole: mk(1000, 100, 850, 50, 10),
num_workers: 1,
..CgroupStats::default()
};
let cg2 = CgroupStats {
taobench_whole: mk(2000, 400, 1500, 100, 10),
num_workers: 1,
..CgroupStats::default()
};
let mut stats = ScenarioStats {
cgroups: vec![cg1, cg2],
..ScenarioStats::default()
};
populate_run_pooled_taobench(&mut stats);
let e = &stats.ext_metrics;
assert_eq!(e.get("total_taobench_get_cmds").copied(), Some(3000.0));
assert_eq!(e.get("total_taobench_get_hits").copied(), Some(2500.0));
let cmd_hit = e
.get("taobench_command_hit_rate")
.copied()
.expect("command_hit_rate derived");
assert!(
(cmd_hit - 2500.0 / 3000.0).abs() < 1e-9,
"Σhits/Σcmds = 2500/3000 = 0.8333, got {cmd_hit}",
);
let resp_hit = e
.get("taobench_hit_fraction")
.copied()
.expect("hit_fraction derived");
assert!(
(resp_hit - 2350.0 / 2500.0).abs() < 1e-9,
"Σfast/Σcompleted = 2350/2500 = 0.94, got {resp_hit}",
);
assert!(
(cmd_hit - resp_hit).abs() > 0.05,
"command-time ({cmd_hit}) and response-time ({resp_hit}) hit DIVERGE — \
the reason both are registered whole-run keys",
);
}
fn taobench_serve_pc(lows: u32, low_us: u32, highs: u32, high_us: u32) -> PhaseCgroupStats {
let mut serve = crate::workload::schbench::plat::PlatStats::default();
for _ in 0..lows {
serve.add_lat(low_us);
}
for _ in 0..highs {
serve.add_lat(high_us);
}
PhaseCgroupStats {
taobench: Some(crate::workload::taobench::run::TaobenchPhaseStats {
serve_lat: serve,
..Default::default()
}),
..PhaseCgroupStats::default()
}
}
#[test]
fn populate_run_pooled_taobench_distribution_unions_cross_cgroup_percentile() {
let mut stats = ScenarioStats {
phases: vec![schbench_phase(
1,
vec![
("cg_a", taobench_serve_pc(99, 10, 0, 0)),
("cg_b", taobench_serve_pc(0, 0, 1, 10000)),
],
)],
..ScenarioStats::default()
};
populate_run_pooled_taobench_distribution(&mut stats);
let e = &stats.ext_metrics;
let p99 = e
.get("taobench_serve_p99_us_whole")
.copied()
.expect("union p99 present");
assert!(
p99 < 1000.0,
"union p99 = percentile-of-pooled (~10 µs bucket), got {p99} \
(mean-of-per-cgroup-p99 would be ~5005)",
);
let max = e
.get("taobench_serve_max_us_whole")
.copied()
.expect("union max present");
assert!(
max > 5000.0,
"union max = max-of-max (the high sample's ~10000 µs bucket), got {max}",
);
}
#[test]
fn populate_run_pooled_taobench_distribution_unions_cross_phase_percentile() {
let mut stats = ScenarioStats {
phases: vec![
schbench_phase(1, vec![("cg", taobench_serve_pc(99, 10, 0, 0))]),
schbench_phase(2, vec![("cg", taobench_serve_pc(0, 0, 1, 10000))]),
],
..ScenarioStats::default()
};
populate_run_pooled_taobench_distribution(&mut stats);
let e = &stats.ext_metrics;
let p99 = e
.get("taobench_serve_p99_us_whole")
.copied()
.expect("union p99 present");
assert!(
p99 < 1000.0,
"cross-phase union p99 = percentile-of-pooled (~10 µs); phase-1 samples \
must be in the union, got {p99}",
);
let max = e
.get("taobench_serve_max_us_whole")
.copied()
.expect("union max present");
assert!(
max > 5000.0,
"cross-phase union max = phase-2's high sample (~10000 µs), got {max}",
);
}
#[test]
fn populate_run_pooled_taobench_distribution_absent_without_serve_samples() {
let mut empty = ScenarioStats::default();
populate_run_pooled_taobench_distribution(&mut empty);
assert!(
!empty
.ext_metrics
.contains_key("taobench_serve_p99_us_whole"),
"no serve keys for an empty run",
);
let mut stats = ScenarioStats {
phases: vec![schbench_phase(
1,
vec![("cg", taobench_serve_pc(0, 0, 0, 0))],
)],
..ScenarioStats::default()
};
populate_run_pooled_taobench_distribution(&mut stats);
assert!(
!stats
.ext_metrics
.contains_key("taobench_serve_p99_us_whole"),
"no serve keys when the histogram is empty (closed loop)",
);
assert!(
!stats
.ext_metrics
.contains_key("taobench_serve_max_us_whole"),
"no serve max key when the histogram is empty",
);
}
fn schbench_pc(msg_rd: u64, msg_pc: u64, wkr_rd: u64, wkr_pc: u64, loops: u64) -> PhaseCgroupStats {
PhaseCgroupStats {
schbench: Some(crate::workload::schbench::run::SchbenchPhaseStats {
msg_run_delay_ns: msg_rd,
msg_pcount: msg_pc,
worker_run_delay_ns: wkr_rd,
worker_pcount: wkr_pc,
loop_count: loops,
..Default::default()
}),
..PhaseCgroupStats::default()
}
}
fn schbench_wakeup_pc(lows: u32, low_us: u32, highs: u32, high_us: u32) -> PhaseCgroupStats {
let mut p = crate::workload::schbench::plat::PlatStats::default();
for _ in 0..lows {
p.add_lat(low_us);
}
for _ in 0..highs {
p.add_lat(high_us);
}
PhaseCgroupStats {
schbench: Some(crate::workload::schbench::run::SchbenchPhaseStats {
wakeup: p,
..Default::default()
}),
..PhaseCgroupStats::default()
}
}
fn schbench_phase(step: u16, cgs: Vec<(&str, PhaseCgroupStats)>) -> PhaseBucket {
PhaseBucket {
step_index: step,
label: String::new(),
start_ms: 0,
end_ms: 0,
sample_count: 0,
metrics: std::collections::BTreeMap::new(),
per_cgroup: cgs.into_iter().map(|(n, c)| (n.to_string(), c)).collect(),
}
}
#[test]
fn populate_run_pooled_schbench_repools_across_phases_and_cgroups() {
let mut stats = ScenarioStats {
phases: vec![
schbench_phase(
1,
vec![
("cg_a", schbench_pc(300, 3, 500, 1, 400)),
("cg_b", schbench_pc(0, 0, 300, 1, 600)),
],
),
schbench_phase(2, vec![("cg_a", schbench_pc(100, 97, 200, 8, 0))]),
],
..ScenarioStats::default()
};
populate_run_pooled_schbench(&mut stats);
let e = &stats.ext_metrics;
assert_eq!(
e.get("total_schbench_msg_run_delay_ns").copied(),
Some(400.0),
);
assert_eq!(e.get("total_schbench_msg_pcount").copied(), Some(100.0));
assert_eq!(
e.get("total_schbench_worker_run_delay_ns").copied(),
Some(1000.0),
);
assert_eq!(e.get("total_schbench_worker_pcount").copied(), Some(10.0));
assert_eq!(e.get("total_schbench_loops").copied(), Some(1000.0));
assert_eq!(
e.get("schbench_msg_run_delay_ns_per_sched").copied(),
Some(4.0),
"Σ400/Σ100 = 4 ns/sched",
);
assert_eq!(
e.get("schbench_worker_run_delay_ns_per_sched").copied(),
Some(100.0),
"Σ1000/Σ10 = 100 ns/sched",
);
}
#[test]
fn populate_run_pooled_schbench_role_gating_omits_unscheduled_role() {
let mut stats = ScenarioStats {
phases: vec![schbench_phase(
1,
vec![("cg", schbench_pc(0, 0, 200, 4, 50))],
)],
..ScenarioStats::default()
};
populate_run_pooled_schbench(&mut stats);
let e = &stats.ext_metrics;
assert_eq!(
e.get("schbench_worker_run_delay_ns_per_sched").copied(),
Some(50.0),
"200/4 = 50 ns/sched",
);
assert_eq!(e.get("total_schbench_loops").copied(), Some(50.0));
assert!(
!e.contains_key("total_schbench_msg_pcount"),
"message components absent when that role never scheduled",
);
assert!(
!e.contains_key("schbench_msg_run_delay_ns_per_sched"),
"message Rate absent (no 0/0)",
);
}
#[test]
fn populate_run_pooled_schbench_absent_without_schbench() {
let mut stats = ScenarioStats {
phases: vec![schbench_phase(1, vec![("cg", PhaseCgroupStats::default())])],
..ScenarioStats::default()
};
populate_run_pooled_schbench(&mut stats);
assert!(
stats.ext_metrics.is_empty(),
"no schbench keys when no carrier ran",
);
let mut empty = ScenarioStats::default();
populate_run_pooled_schbench(&mut empty);
assert!(empty.ext_metrics.is_empty(), "no keys for empty phases");
}
#[test]
fn populate_run_pooled_schbench_distribution_unions_cross_cgroup_percentile() {
let mut stats = ScenarioStats {
phases: vec![schbench_phase(
1,
vec![
("cg_a", schbench_wakeup_pc(99, 10, 0, 0)),
("cg_b", schbench_wakeup_pc(0, 0, 1, 10000)),
],
)],
..ScenarioStats::default()
};
populate_run_pooled_schbench_distribution(&mut stats);
let e = &stats.ext_metrics;
let p99 = e
.get("wakeup_p99_latency_us_whole")
.copied()
.expect("union p99 present");
assert!(
p99 < 1000.0,
"union p99 = percentile-of-pooled (~10 µs bucket), got {p99} \
(mean-of-per-cgroup-p99 would be ~5005)",
);
let max = e
.get("wakeup_max_latency_us_whole")
.copied()
.expect("union max present");
assert!(
max > 5000.0,
"union max = max-of-max (the high sample's ~10000 µs bucket), got {max}",
);
assert!(
!e.contains_key("request_p99_latency_us_whole"),
"request_*_whole absent when no request samples",
);
assert!(
!e.contains_key("rps_p50_whole"),
"rps_*_whole absent when no rps samples",
);
}
#[test]
fn populate_run_pooled_schbench_distribution_unions_cross_phase_percentile() {
let mut stats = ScenarioStats {
phases: vec![
schbench_phase(1, vec![("cg", schbench_wakeup_pc(99, 10, 0, 0))]),
schbench_phase(2, vec![("cg", schbench_wakeup_pc(0, 0, 1, 10000))]),
],
..ScenarioStats::default()
};
populate_run_pooled_schbench_distribution(&mut stats);
let e = &stats.ext_metrics;
let p99 = e
.get("wakeup_p99_latency_us_whole")
.copied()
.expect("union p99 present");
assert!(
p99 < 1000.0,
"cross-phase union p99 = percentile-of-pooled (~10 µs); phase-1 samples \
must be in the union, got {p99}",
);
let max = e
.get("wakeup_max_latency_us_whole")
.copied()
.expect("union max present");
assert!(
max > 5000.0,
"cross-phase union max = phase-2's high sample (~10000 µs), got {max}",
);
}
#[test]
fn phase_cgroup_merge_saturates_counter_overflow() {
let a = PhaseCgroupStats {
total_iterations: u64::MAX,
total_cpu_time_ns: u64::MAX,
total_migrations: u64::MAX,
numa_pages_local: u64::MAX,
numa_pages_total: u64::MAX,
wake_sample_total: u64::MAX,
timer_sample_total: u64::MAX,
..PhaseCgroupStats::default()
};
let b = PhaseCgroupStats {
total_iterations: 5,
total_cpu_time_ns: 5,
total_migrations: 5,
numa_pages_local: 5,
numa_pages_total: 5,
wake_sample_total: 5,
timer_sample_total: 5,
..PhaseCgroupStats::default()
};
let m = PhaseCgroupStats::merge(a, b);
assert_eq!(m.total_iterations, u64::MAX, "saturates, not wraps to 4");
assert_eq!(m.total_cpu_time_ns, u64::MAX);
assert_eq!(m.total_migrations, u64::MAX);
assert_eq!(m.numa_pages_local, u64::MAX);
assert_eq!(m.numa_pages_total, u64::MAX);
assert_eq!(m.wake_sample_total, u64::MAX);
assert_eq!(m.timer_sample_total, u64::MAX);
}
#[test]
fn assert_result_merge_saturates_run_level_counters() {
let mut a = AssertResult::pass();
a.stats.total_iterations = u64::MAX;
a.stats.total_migrations = u64::MAX;
let mut b = AssertResult::pass();
b.stats.total_iterations = 7;
b.stats.total_migrations = 7;
a.merge(b);
assert_eq!(a.stats.total_iterations, u64::MAX, "saturates, not wraps");
assert_eq!(a.stats.total_migrations, u64::MAX);
}
#[test]
fn populate_run_pooled_schbench_saturates_counter_overflow() {
let mut stats = ScenarioStats {
phases: vec![
schbench_phase(
1,
vec![(
"cg",
schbench_pc(u64::MAX, u64::MAX, u64::MAX, u64::MAX, u64::MAX),
)],
),
schbench_phase(2, vec![("cg", schbench_pc(5, 5, 5, 5, 5))]),
],
..ScenarioStats::default()
};
populate_run_pooled_schbench(&mut stats);
let e = &stats.ext_metrics;
assert_eq!(
e.get("total_schbench_msg_run_delay_ns").copied(),
Some(u64::MAX as f64),
"msg run-delay pool saturates, not wraps",
);
assert_eq!(
e.get("total_schbench_loops").copied(),
Some(u64::MAX as f64),
"loop Counter pool saturates",
);
}
#[test]
fn populate_run_pooled_iterations_per_cpu_sec_saturates_counter_overflow() {
let mut stats = ScenarioStats {
cgroups: vec![
CgroupStats {
total_cpu_time_ns: u64::MAX,
total_iterations: u64::MAX,
..CgroupStats::default()
},
CgroupStats {
total_cpu_time_ns: u64::MAX,
total_iterations: 10,
..CgroupStats::default()
},
],
..ScenarioStats::default()
};
super::run_metrics::populate_run_pooled_iterations_per_cpu_sec(&mut stats);
let e = &stats.ext_metrics;
assert_eq!(
e.get("total_iterations_pooled").copied(),
Some(u64::MAX as f64),
"summed_iters saturates, not wraps to 9",
);
assert_eq!(
e.get("total_cpu_time_sec").copied(),
Some(u64::MAX as f64 / 1e9),
"summed_ns saturates to a finite huge cpu-time-sec",
);
}
#[test]
fn cgroup_stats_saturates_per_worker_counter_overflow() {
let report = |iters: u64, mig: u64, cpu_ns: u64| crate::workload::WorkerReport {
iterations: iters,
migration_count: mig,
schedstat_cpu_time_ns: cpu_ns,
..crate::workload::WorkerReport::default()
};
let reports = vec![report(u64::MAX, u64::MAX, u64::MAX), report(5, 5, 5)];
let cg = super::reductions::cgroup_stats(&reports);
assert_eq!(
cg.total_iterations,
u64::MAX,
"iterations pool saturates, not wraps to 4",
);
assert_eq!(cg.total_migrations, u64::MAX, "migration pool saturates");
assert_eq!(cg.total_cpu_time_ns, u64::MAX, "cpu-time pool saturates");
}
#[test]
fn numa_agg_cross_node_migrated_pool_saturates_counter_overflow() {
let pc = |migrated: u64| PhaseCgroupStats {
numa_pages_local: 50,
numa_pages_total: 100,
cross_node_migrated: migrated,
..PhaseCgroupStats::default()
};
let mut stats = ScenarioStats {
phases: vec![
schbench_phase(1, vec![("cg", pc(u64::MAX))]),
schbench_phase(2, vec![("cg", pc(5))]),
],
..ScenarioStats::default()
};
populate_run_distribution_metrics(&mut stats);
let ratio = stats
.ext_metrics
.get("worst_cross_node_migration_ratio")
.copied()
.expect("worst_cross_node_migration_ratio present");
assert!(
ratio > 1e10,
"cross-phase migrated pool saturates (huge ratio), got {ratio} \
(a wrapped sum would read ~0.04)",
);
}
#[test]
fn assert_thresholds_max_migrations_gate_saturates_not_wraps() {
let report = |mig: u64| crate::workload::WorkerReport {
migration_count: mig,
iterations: 1,
..crate::workload::WorkerReport::default()
};
let reports = vec![report(u64::MAX), report(5)];
let thresholds = super::reductions::AbsoluteThresholds {
max_migrations: Some(1000),
..Default::default()
};
let r = super::reductions::assert_thresholds(&reports, &thresholds);
assert!(
r.is_fail(),
"saturated total_mig (u64::MAX) > 1000 must FAIL the gate; a wrapped sum \
(u64::MAX + 5 → 4) would falsely pass",
);
}
#[test]
fn run_metric_total_counters_saturate_cross_cgroup() {
let cg = |mig: u64, iters: u64| CgroupStats {
total_migrations: mig,
total_iterations: iters,
..CgroupStats::default()
};
let stats = ScenarioStats {
cgroups: vec![cg(u64::MAX, u64::MAX), cg(5, 5)],
..ScenarioStats::default()
};
assert_eq!(
stats.run_metric(crate::stats::BuiltinMetric::TotalMigrations),
Some(u64::MAX as f64),
"total_migrations cross-cgroup typed read saturates",
);
assert_eq!(
stats.run_metric(crate::stats::BuiltinMetric::TotalIterations),
Some(u64::MAX as f64),
"total_iterations cross-cgroup typed read saturates",
);
}
#[test]
fn cgroup_counter_total_saturates_cross_cgroup() {
let pc = |mig: u64| PhaseCgroupStats {
total_migrations: mig,
..PhaseCgroupStats::default()
};
let bucket = schbench_phase(1, vec![("a", pc(u64::MAX)), ("b", pc(5))]);
assert_eq!(
bucket.cgroup_counter_total("total_migrations"),
Some(u64::MAX as f64),
"cross-cgroup counter pool saturates, not wraps",
);
}
#[test]
fn merge_scenario_stats_worst_wins_and_iterations_sum() {
let mut a = AssertResult::pass();
a.stats.total_iterations = 100;
a.stats.worst_spread = 5.0;
a.stats.worst_migration_ratio = 0.1;
let mut b = AssertResult::pass();
b.stats.total_iterations = 400;
b.stats.worst_spread = 15.0;
b.stats.worst_migration_ratio = 0.4;
a.merge(b);
assert_eq!(a.stats.total_iterations, 500);
assert_eq!(a.stats.worst_spread, 15.0);
assert_eq!(a.stats.worst_migration_ratio, 0.4);
}
#[test]
fn merge_scenario_stats_worst_wins_when_other_is_smaller() {
let mut a = AssertResult::pass();
a.stats.worst_spread = 30.0;
a.stats.worst_gap_ms = 500;
a.stats.worst_gap_cpu = 7;
a.stats.worst_migration_ratio = 0.9;
a.stats.total_iterations = 500;
let mut b = AssertResult::pass();
b.stats.worst_spread = 5.0;
b.stats.worst_gap_ms = 100;
b.stats.worst_gap_cpu = 3;
b.stats.worst_migration_ratio = 0.1;
b.stats.total_iterations = 50;
a.merge(b);
assert_eq!(a.stats.worst_spread, 30.0);
assert_eq!(a.stats.worst_gap_ms, 500);
assert_eq!(a.stats.worst_gap_cpu, 7);
assert_eq!(a.stats.worst_migration_ratio, 0.9);
assert_eq!(a.stats.total_iterations, 550);
}
#[test]
fn merge_ext_metrics_higher_is_worse_takes_max() {
let mut a = AssertResult::pass();
a.stats.ext_metrics.insert("worst_spread".into(), 10.0);
let mut b = AssertResult::pass();
b.stats.ext_metrics.insert("worst_spread".into(), 42.0);
a.merge(b);
assert_eq!(a.stats.ext_metrics["worst_spread"], 42.0);
}
#[test]
fn merge_ext_metrics_higher_is_better_takes_min() {
let mut a = AssertResult::pass();
a.stats.ext_metrics.insert("total_iterations".into(), 10.0);
let mut b = AssertResult::pass();
b.stats.ext_metrics.insert("total_iterations".into(), 42.0);
a.merge(b);
assert_eq!(
a.stats.ext_metrics["total_iterations"], 10.0,
"higher_is_worse=false must take min on merge"
);
}
#[test]
fn merge_ext_metrics_unknown_metric_defaults_to_max() {
let mut a = AssertResult::pass();
a.stats.ext_metrics.insert("unknown_metric".into(), 10.0);
let mut b = AssertResult::pass();
b.stats.ext_metrics.insert("unknown_metric".into(), 42.0);
a.merge(b);
assert_eq!(a.stats.ext_metrics["unknown_metric"], 42.0);
}
#[test]
fn merge_ext_metrics_first_insert_uses_other_value() {
let mut a = AssertResult::pass();
let mut b = AssertResult::pass();
b.stats.ext_metrics.insert("total_iterations".into(), 77.0);
a.merge(b);
assert_eq!(a.stats.ext_metrics["total_iterations"], 77.0);
}
#[test]
fn merge_pass_and_fail() {
let pass = AssertResult::pass();
let mut fail = AssertResult::pass();
fail.record_fail(AssertDetail::new(DetailKind::Other, "something failed"));
let mut merged = pass;
merged.merge(fail);
assert!(merged.is_fail(), "merging pass+fail must produce fail");
assert!(
merged
.failure_details()
.any(|d| d.message.contains("something failed"))
);
}
#[test]
fn merge_fail_and_pass() {
let mut fail = AssertResult::pass();
fail.record_fail(AssertDetail::new(DetailKind::Other, "first failed"));
let pass = AssertResult::pass();
let mut merged = fail;
merged.merge(pass);
assert!(merged.is_fail(), "merging fail+pass must produce fail");
}
#[test]
fn merge_outcomes_extend_and_stats_sum_coexist() {
let mut a = AssertResult::pass();
a.record_fail(AssertDetail::new(DetailKind::Other, "fail_a"));
a.stats.total_iterations = 100;
a.stats.total_workers = 2;
let mut b = AssertResult::pass();
b.record_skip("skip_b");
b.stats.total_iterations = 50;
b.stats.total_workers = 3;
a.merge(b);
assert_eq!(a.outcomes.len(), 2, "Fail + Skip both extend");
assert!(a.is_fail(), "Fail dominates the verdict");
assert_eq!(a.stats.total_iterations, 150, "stats SUM (not max)");
assert_eq!(a.stats.total_workers, 5);
assert_eq!(a.failure_details().count(), 1);
assert_eq!(a.skip_details().count(), 1);
}
#[test]
fn merge_inconclusive_precedence() {
fn merged(lhs: AssertResult, rhs: AssertResult) -> AssertResult {
let mut a = lhs;
a.merge(rhs);
a
}
fn mk_pass() -> AssertResult {
AssertResult::pass()
}
fn mk_skip() -> AssertResult {
let mut r = AssertResult::pass();
r.record_skip("s");
r
}
fn mk_inconc() -> AssertResult {
let mut r = AssertResult::pass();
r.record_inconclusive(AssertDetail::new(DetailKind::Other, "i"));
r
}
fn mk_fail() -> AssertResult {
let mut r = AssertResult::pass();
r.record_fail(AssertDetail::new(DetailKind::Other, "f"));
r
}
let pi = merged(mk_pass(), mk_inconc());
assert!(pi.is_inconclusive() && !pi.is_fail() && !pi.is_pass());
let ip = merged(mk_inconc(), mk_pass());
assert!(ip.is_inconclusive() && !ip.is_fail() && !ip.is_pass());
let si = merged(mk_skip(), mk_inconc());
assert!(si.is_inconclusive() && !si.is_skip() && !si.is_fail());
let is_ = merged(mk_inconc(), mk_skip());
assert!(is_.is_inconclusive() && !is_.is_skip() && !is_.is_fail());
let fi = merged(mk_fail(), mk_inconc());
assert!(fi.is_fail() && !fi.is_inconclusive() && !fi.is_pass());
let if_ = merged(mk_inconc(), mk_fail());
assert!(if_.is_fail() && !if_.is_inconclusive() && !if_.is_pass());
let ii = merged(mk_inconc(), mk_inconc());
assert!(ii.is_inconclusive() && !ii.is_fail() && !ii.is_pass());
assert_eq!(
ii.outcomes.len(),
2,
"both Inconclusive outcomes extend the merged vec"
);
}
#[test]
fn assert_result_merge_combines_stats() {
let mut a = AssertResult {
outcomes: vec![Outcome::Fail(AssertDetail::new(DetailKind::Other, "a"))],
passes: vec![],
stats: ScenarioStats {
cgroups: vec![],
total_workers: 2,
total_cpus: 4,
total_migrations: 10,
worst_spread: 5.0,
worst_gap_ms: 100,
worst_gap_cpu: 0,
..Default::default()
},
measurements: std::collections::BTreeMap::new(),
info_notes: vec![],
};
let b = AssertResult {
outcomes: vec![Outcome::Fail(AssertDetail::new(DetailKind::Other, "b"))],
passes: vec![],
stats: ScenarioStats {
cgroups: vec![],
total_workers: 3,
total_cpus: 6,
total_migrations: 20,
worst_spread: 15.0,
worst_gap_ms: 500,
worst_gap_cpu: 2,
..Default::default()
},
measurements: std::collections::BTreeMap::new(),
info_notes: vec![],
};
a.merge(b);
assert!(a.is_fail());
assert_eq!(
a.failure_details()
.map(|d| d.message.as_str())
.collect::<Vec<_>>(),
vec!["a", "b"]
);
assert_eq!(a.stats.total_workers, 5);
assert_eq!(a.stats.total_cpus, 10);
assert_eq!(a.stats.total_migrations, 30);
assert_eq!(a.stats.worst_spread, 15.0);
assert_eq!(a.stats.worst_gap_ms, 500);
assert_eq!(a.stats.worst_gap_cpu, 2);
}
#[test]
fn assert_result_merge_ext_metrics_max_value() {
let mut a = AssertResult::pass();
a.stats.ext_metrics.insert("latency".into(), 10.0);
a.stats.ext_metrics.insert("throughput".into(), 100.0);
let mut b = AssertResult::pass();
b.stats.ext_metrics.insert("latency".into(), 20.0);
b.stats.ext_metrics.insert("jitter".into(), 5.0);
a.merge(b);
assert_eq!(a.stats.ext_metrics["latency"], 20.0);
assert_eq!(a.stats.ext_metrics["throughput"], 100.0);
assert_eq!(a.stats.ext_metrics["jitter"], 5.0);
}
#[test]
fn assert_result_merge_ext_metrics_keeps_larger() {
let mut a = AssertResult::pass();
a.stats.ext_metrics.insert("x".into(), 50.0);
let mut b = AssertResult::pass();
b.stats.ext_metrics.insert("x".into(), 30.0);
a.merge(b);
assert_eq!(a.stats.ext_metrics["x"], 50.0);
}
fn phase_bucket(
step_index: u16,
label: &str,
start_ms: u64,
end_ms: u64,
sample_count: usize,
metrics: &[(&str, f64)],
) -> PhaseBucket {
PhaseBucket {
per_cgroup: Default::default(),
step_index,
label: label.to_string(),
start_ms,
end_ms,
sample_count,
metrics: metrics
.iter()
.map(|(k, v)| ((*k).to_string(), *v))
.collect(),
}
}
#[test]
fn assert_result_merge_per_phase_counter_sums() {
let mut a = AssertResult::pass();
a.stats.phases = vec![phase_bucket(
1,
"Step[0]",
0,
100,
5,
&[("total_migrations", 25.0)],
)];
let mut b = AssertResult::pass();
b.stats.phases = vec![phase_bucket(
1,
"Step[0]",
0,
100,
5,
&[("total_migrations", 75.0)],
)];
a.merge(b);
assert_eq!(a.stats.phases.len(), 1);
assert_eq!(a.stats.phases[0].metrics["total_migrations"], 100.0);
}
#[test]
fn assert_result_merge_per_phase_peak_takes_max() {
let mut a = AssertResult::pass();
a.stats.phases = vec![phase_bucket(
2,
"Step[1]",
0,
100,
5,
&[("worst_gap_ms", 12.0)],
)];
let mut b = AssertResult::pass();
b.stats.phases = vec![phase_bucket(
2,
"Step[1]",
0,
100,
5,
&[("worst_gap_ms", 7.0)],
)];
a.merge(b);
assert_eq!(a.stats.phases[0].metrics["worst_gap_ms"], 12.0);
}
#[test]
fn assert_result_merge_per_phase_per_cgroup_unions_and_folds() {
use crate::assert::PhaseCgroupStats;
use std::collections::BTreeSet;
let mut a = AssertResult::pass();
let mut a_bucket = phase_bucket(1, "Step[0]", 0, 100, 5, &[]);
a_bucket.per_cgroup.insert(
"cg_0".to_string(),
PhaseCgroupStats {
num_workers: 4,
cpus_used: BTreeSet::from([0, 1]),
wake_latencies_ns: vec![10, 20],
wake_sample_total: 2,
timer_latencies_ns: vec![],
timer_sample_total: 0,
run_delays_ns: vec![1_000],
off_cpu_pcts: vec![5.0, 20.0],
total_migrations: 3,
total_iterations: 100,
total_cpu_time_ns: 1_000,
numa_pages_local: 90,
numa_pages_total: 100,
cross_node_migrated: 100,
max_gap_ms: 7,
max_gap_cpu: 3,
stripped: false,
metrics: std::collections::BTreeMap::new(),
schbench: None,
taobench: None,
},
);
a_bucket.per_cgroup.insert(
"cg_x".to_string(),
PhaseCgroupStats {
off_cpu_pcts: vec![], ..Default::default()
},
);
a_bucket.per_cgroup.insert(
"cg_a".to_string(),
PhaseCgroupStats {
num_workers: 1,
..Default::default()
},
);
a.stats.phases = vec![a_bucket];
let mut b = AssertResult::pass();
let mut b_bucket = phase_bucket(1, "Step[0]", 0, 100, 5, &[]);
b_bucket.per_cgroup.insert(
"cg_0".to_string(),
PhaseCgroupStats {
num_workers: 4,
cpus_used: BTreeSet::from([1, 2]),
wake_latencies_ns: vec![30],
wake_sample_total: 1,
timer_latencies_ns: vec![],
timer_sample_total: 0,
run_delays_ns: vec![2_000, 3_000],
off_cpu_pcts: vec![3.0, 15.0],
total_migrations: 2,
total_iterations: 50,
total_cpu_time_ns: 500,
numa_pages_local: 40,
numa_pages_total: 50,
cross_node_migrated: 50,
max_gap_ms: 9,
max_gap_cpu: 5,
stripped: false,
metrics: std::collections::BTreeMap::new(),
schbench: None,
taobench: None,
},
);
b_bucket.per_cgroup.insert(
"cg_x".to_string(),
PhaseCgroupStats {
off_cpu_pcts: vec![7.0], ..Default::default()
},
);
b_bucket.per_cgroup.insert(
"cg_b".to_string(),
PhaseCgroupStats {
num_workers: 2,
..Default::default()
},
);
b.stats.phases = vec![b_bucket];
a.merge(b);
let pc = &a.stats.phases[0].per_cgroup;
assert_eq!(pc["cg_a"].num_workers, 1, "cg_a (a-only) carried by value");
assert_eq!(pc["cg_b"].num_workers, 2, "cg_b (b-only) carried by value");
let cg0 = &pc["cg_0"];
assert_eq!(cg0.wake_latencies_ns, vec![10, 20, 30], "latencies concat");
assert_eq!(cg0.wake_sample_total, 3, "wake_sample_total sums");
assert_eq!(
cg0.run_delays_ns,
vec![1_000, 2_000, 3_000],
"run_delays concat (raw ns)"
);
assert_eq!(
cg0.off_cpu_pcts,
vec![5.0, 20.0, 3.0, 15.0],
"off_cpu_pcts concat (mean + spread re-pool from these raw samples)",
);
assert_eq!(cg0.cpus_used, BTreeSet::from([0, 1, 2]), "cpus_used union");
assert_eq!(cg0.total_migrations, 5, "migrations sum (3+2)");
assert_eq!(cg0.total_iterations, 150, "iterations sum (100+50)");
assert_eq!(cg0.total_cpu_time_ns, 1_500, "cpu time sum (1000+500)");
assert_eq!(cg0.numa_pages_local, 130, "numa_pages_local sum (90+40)");
assert_eq!(cg0.numa_pages_total, 150, "numa_pages_total sum (100+50)");
assert_eq!(
cg0.cross_node_migrated, 100,
"cross_node_migrated MAX(100,50)=100 NOT 150 — system-wide vmstat delta",
);
assert_eq!(
cg0.max_gap_ms, 9,
"gap ms = argmax-by-ms(7@cpu3, 9@cpu5) = 9"
);
assert_eq!(
cg0.max_gap_cpu, 5,
"gap cpu coupled to the winning gap (b's 5, NOT a's 3)",
);
assert_eq!(
cg0.num_workers, 8,
"num_workers SUMs (4+4) — a Counter over disjoint per-handle worker \
subsets, not a Peak",
);
assert_eq!(
pc["cg_x"].off_cpu_pcts,
vec![7.0],
"empty off_cpu_pcts (not measured) ∪ measured = measured",
);
}
#[test]
fn assert_result_merge_keeps_per_cgroup_across_distinct_steps() {
use crate::assert::{PhaseBucket, PhaseCgroupStats};
let bucket = |idx: u16, name: &str, iters: u64| {
let mut pc = std::collections::BTreeMap::new();
pc.insert(
name.to_string(),
PhaseCgroupStats {
total_iterations: iters,
..Default::default()
},
);
PhaseBucket {
step_index: idx,
label: format!("Step[{}]", idx - 1),
start_ms: 0,
end_ms: 100,
sample_count: 0,
metrics: std::collections::BTreeMap::new(),
per_cgroup: pc,
}
};
let mut a = AssertResult::pass();
a.stats.phases = vec![bucket(1, "cgA", 11)];
let mut b = AssertResult::pass();
b.stats.phases = vec![bucket(2, "cgB", 22)];
a.merge(b);
assert_eq!(
a.stats.phases.len(),
2,
"both distinct-step buckets survive"
);
let s1 = a
.stats
.phases
.iter()
.find(|p| p.step_index == 1)
.expect("step 1 survives");
let s2 = a
.stats
.phases
.iter()
.find(|p| p.step_index == 2)
.expect("step 2 survives");
assert_eq!(
s1.per_cgroup["cgA"].total_iterations, 11,
"step 1 per_cgroup carried"
);
assert_eq!(
s2.per_cgroup["cgB"].total_iterations, 22,
"step 2 per_cgroup carried"
);
}
#[test]
fn assert_result_merge_per_phase_gauge_last_takes_later_end_ms() {
let mut a = AssertResult::pass();
a.stats.phases = vec![phase_bucket(
1,
"Step[0]",
0,
200,
5,
&[("worst_spread", 0.42)],
)];
let mut b = AssertResult::pass();
b.stats.phases = vec![phase_bucket(
1,
"Step[0]",
0,
100,
5,
&[("worst_spread", 0.11)],
)];
a.merge(b);
assert_eq!(a.stats.phases[0].metrics["worst_spread"], 0.42);
assert_eq!(a.stats.phases[0].start_ms, 0);
assert_eq!(a.stats.phases[0].end_ms, 200);
}
#[test]
fn assert_result_merge_per_phase_gauge_last_reverse_picks_later_end_ms() {
let mut a = AssertResult::pass();
a.stats.phases = vec![phase_bucket(
1,
"Step[0]",
0,
100,
5,
&[("worst_spread", 0.42)],
)];
let mut b = AssertResult::pass();
b.stats.phases = vec![phase_bucket(
1,
"Step[0]",
0,
200,
5,
&[("worst_spread", 0.11)],
)];
a.merge(b);
assert_eq!(a.stats.phases[0].metrics["worst_spread"], 0.11);
assert_eq!(a.stats.phases[0].end_ms, 200);
}
#[test]
fn assert_result_merge_per_phase_unpaired_step_indices_keep_both() {
let mut a = AssertResult::pass();
a.stats.phases = vec![phase_bucket(
1,
"Step[0]",
0,
100,
3,
&[("total_migrations", 5.0)],
)];
let mut b = AssertResult::pass();
b.stats.phases = vec![phase_bucket(
2,
"Step[1]",
100,
200,
3,
&[("total_migrations", 8.0)],
)];
a.merge(b);
assert_eq!(a.stats.phases.len(), 2);
assert_eq!(a.stats.phases[0].step_index, 1);
assert_eq!(a.stats.phases[1].step_index, 2);
assert_eq!(a.stats.phases[0].metrics["total_migrations"], 5.0);
assert_eq!(a.stats.phases[1].metrics["total_migrations"], 8.0);
}
#[test]
fn assert_result_merge_per_phase_unknown_metric_takes_mean() {
let mut a = AssertResult::pass();
a.stats.phases = vec![phase_bucket(
0,
"BASELINE",
0,
100,
5,
&[("custom.metric", 10.0)],
)];
let mut b = AssertResult::pass();
b.stats.phases = vec![phase_bucket(
0,
"BASELINE",
0,
100,
5,
&[("custom.metric", 30.0)],
)];
a.merge(b);
assert_eq!(a.stats.phases[0].metrics["custom.metric"], 20.0);
}
#[test]
fn assert_result_merge_per_phase_one_side_only_keeps_value() {
let mut a = AssertResult::pass();
a.stats.phases = vec![phase_bucket(
1,
"Step[0]",
0,
100,
5,
&[("total_migrations", 7.0), ("worst_gap_ms", 12.0)],
)];
let mut b = AssertResult::pass();
b.stats.phases = vec![phase_bucket(
1,
"Step[0]",
0,
100,
5,
&[("total_migrations", 3.0)],
)];
a.merge(b);
assert_eq!(a.stats.phases[0].metrics["total_migrations"], 10.0);
assert_eq!(a.stats.phases[0].metrics["worst_gap_ms"], 12.0);
}
#[test]
fn assert_result_merge_per_phase_window_invariants() {
let mut a = AssertResult::pass();
a.stats.phases = vec![phase_bucket(1, "Step[0]", 50, 150, 4, &[])];
let mut b = AssertResult::pass();
b.stats.phases = vec![phase_bucket(1, "Step[0]", 10, 200, 6, &[])];
a.merge(b);
assert_eq!(a.stats.phases[0].start_ms, 10);
assert_eq!(a.stats.phases[0].end_ms, 200);
assert_eq!(a.stats.phases[0].sample_count, 10);
}
#[test]
fn merge_kind_enum_exhaustively_covers_metric_kind_variants() {
use crate::stats::{GaugeAgg, MergeKind, MetricKind};
assert_eq!(MetricKind::Counter.merge_kind(), MergeKind::Commutative);
assert_eq!(MetricKind::Peak.merge_kind(), MergeKind::Commutative);
assert_eq!(
MetricKind::Gauge(GaugeAgg::Avg).merge_kind(),
MergeKind::Commutative,
);
assert_eq!(
MetricKind::Gauge(GaugeAgg::Max).merge_kind(),
MergeKind::Commutative,
);
assert_eq!(
MetricKind::Gauge(GaugeAgg::Last).merge_kind(),
MergeKind::NonCommutative,
);
assert_eq!(
MetricKind::Timestamp.merge_kind(),
MergeKind::NonCommutative,
);
assert_eq!(MetricKind::DeltaSum.merge_kind(), MergeKind::Commutative);
assert_eq!(
MetricKind::PerPhaseDeltaSum.merge_kind(),
MergeKind::Commutative,
);
assert_eq!(
MetricKind::Rate {
numerator: "n",
denominator: "d",
}
.merge_kind(),
MergeKind::Recompute,
);
assert_eq!(
MetricKind::Distribution {
source: crate::stats::SampleSource::WakeLatencyNs,
reduction: crate::stats::SampleReduction::P99,
}
.merge_kind(),
MergeKind::Recompute,
);
assert_eq!(
MetricKind::WorstLowest {
numerator: crate::stats::WorstLowestNumerator::NumaLocal,
denominator: crate::stats::WorstLowestDenominator::NumaTotal,
}
.merge_kind(),
MergeKind::Recompute,
);
assert_eq!(
MetricKind::WakeLatencyTailRatio.merge_kind(),
MergeKind::Recompute,
);
assert_eq!(
MetricKind::WorstCrossNodeRatio.merge_kind(),
MergeKind::Recompute,
);
assert_eq!(MetricKind::PerPhase.merge_kind(), MergeKind::Recompute);
assert_eq!(
MetricKind::PerRunDistribution.merge_kind(),
MergeKind::Recompute,
);
}
#[test]
fn merge_matched_phase_buckets_repools_synthesized_zero_count() {
use std::collections::BTreeMap;
let synth = PhaseBucket {
per_cgroup: Default::default(),
step_index: 2,
label: "Step[1]".to_string(),
start_ms: 2000,
end_ms: 3000,
sample_count: 0, metrics: BTreeMap::from([
("total_phase_iterations".to_string(), 600.0),
("total_phase_duration_sec".to_string(), 1.0),
]),
};
let captured = PhaseBucket {
per_cgroup: Default::default(),
step_index: 2,
label: "Step[1]".to_string(),
start_ms: 2000,
end_ms: 3000,
sample_count: 5, metrics: BTreeMap::from([
("total_phase_iterations".to_string(), 1200.0),
("total_phase_duration_sec".to_string(), 3.0),
]),
};
let merged = merge_matched_phase_buckets(synth, captured);
let r = merged
.metrics
.get("iteration_rate")
.copied()
.expect("merged bucket carries the re-derived iteration_rate");
assert!(
(r - 450.0).abs() < f64::EPSILON,
"synthesized rate's iterations must pool into Σiters/Σseconds = 450, \
not be dropped (400) or averaged as ratios (500); got {r}",
);
assert_eq!(
merged.metrics.get("total_phase_iterations").copied(),
Some(1800.0),
"iteration components sum across the merged buckets",
);
}