use super::*;
#[test]
fn cadence_gate_fires_on_batch_counts_even_with_pathological_slow_ms() {
let world_size = 2;
let mut coord = ClusterCoordinator::for_test(
ClusterCoordinatorConfig::new(
ApplyPolicy::Cadence,
AverageBackend::Nccl,
world_size,
ElChe::new(world_size, 2),
)
.no_divergence_guard(),
);
coord
.el_che_mut_for_test()
.report_timing(&[10_000.0, 10_000.0], &[1, 1], 0.0);
let counts = coord.el_che_for_test().batch_counts().to_vec();
assert!(
counts.iter().all(|&c| c > 0),
"batch_counts populated after first calibration",
);
for (r, &c) in counts.iter().enumerate() {
coord.set_steps_since_avg_for_test(r, c);
coord.set_wall_ms_accum_for_test(r, 1.0);
}
assert!(
coord.should_average(),
"count-based gate must fire on completed batch_counts \
regardless of `smoothed_slow_ms` in the trust window — \
this is the structural invariant that prevents wall-gate \
deadlock",
);
}
#[test]
fn cadence_gate_does_not_fire_below_batch_counts() {
let world_size = 2;
let mut coord = ClusterCoordinator::for_test(
ClusterCoordinatorConfig::new(
ApplyPolicy::Cadence,
AverageBackend::Nccl,
world_size,
ElChe::new(world_size, 4),
)
.no_divergence_guard(),
);
coord
.el_che_mut_for_test()
.report_timing(&[10.0, 20.0], &[2, 2], 0.0);
let counts = coord.el_che_for_test().batch_counts().to_vec();
assert!(counts[0] >= 2 && counts[1] >= 2);
for (r, &c) in counts.iter().enumerate() {
coord.set_steps_since_avg_for_test(r, c.saturating_sub(1));
coord.set_wall_ms_accum_for_test(r, 1e9);
}
assert!(
!coord.should_average(),
"gate must NOT fire when ranks have not completed batch_counts, \
regardless of accumulated wall time",
);
}
#[test]
fn cpu_rearm_is_independent_of_nccl_ack() {
let world_size = 2;
let mut coord = ClusterCoordinator::for_test(
ClusterCoordinatorConfig::new(
ApplyPolicy::Cadence,
AverageBackend::Cpu,
world_size,
ElChe::new(world_size, 2),
)
.no_divergence_guard(),
);
coord
.el_che_mut_for_test()
.report_timing(&[10.0, 20.0], &[2, 2], 0.0);
let counts = coord.el_che_for_test().batch_counts().to_vec();
for (r, &c) in counts.iter().enumerate() {
coord.set_steps_since_avg_for_test(r, c);
}
coord.set_all_nccl_ack_for_test(false);
assert!(
coord.should_average(),
"CPU re-arm must NOT depend on the ack slots — the cycle is Idle \
and every rank completed its batch_counts, so the gate must \
fire even with the acks all-false",
);
}
#[test]
fn cpu_gate_blocks_while_cycle_in_flight() {
let world_size = 2;
let mut coord = ClusterCoordinator::for_test(
ClusterCoordinatorConfig::new(
ApplyPolicy::Cadence,
AverageBackend::Cpu,
world_size,
ElChe::new(world_size, 2),
)
.no_divergence_guard(),
);
coord
.el_che_mut_for_test()
.report_timing(&[10.0, 20.0], &[2, 2], 0.0);
let counts = coord.el_che_for_test().batch_counts().to_vec();
for (r, &c) in counts.iter().enumerate() {
coord.set_steps_since_avg_for_test(r, c);
}
coord.set_cpu_avg_pending_for_test();
assert!(
!coord.should_average(),
"CPU gate must not re-fire while a cycle is Pending in flight",
);
}
#[test]
fn final_consensus_reduce_needed_only_with_trailing_steps() {
let world_size = 2;
let mut coord = ClusterCoordinator::for_test(
ClusterCoordinatorConfig::new(
ApplyPolicy::Cadence,
AverageBackend::Cpu,
world_size,
ElChe::new(world_size, 2),
)
.no_divergence_guard(),
);
coord.set_steps_since_avg_for_test(0, 0);
coord.set_steps_since_avg_for_test(1, 0);
assert!(
!coord.needs_final_consensus_reduce_for_test(),
"no trailing steps -> no final reduce",
);
coord.set_steps_since_avg_for_test(1, 3);
assert!(
coord.needs_final_consensus_reduce_for_test(),
"a rank with trailing steps -> final reduce before shutdown",
);
}
#[test]
fn quiesced_zero_step_tail_rank_does_not_block_reduce() {
let world_size = 3;
let mut coord = ClusterCoordinator::for_test(
ClusterCoordinatorConfig::new(
ApplyPolicy::Cadence,
AverageBackend::Cpu,
world_size,
ElChe::new(world_size, 4),
)
.no_divergence_guard(),
);
coord
.el_che_mut_for_test()
.report_timing(&[200.0, 1000.0, 1000.0], &[4, 4, 4], 10.0);
let counts = coord.el_che_for_test().batch_counts().to_vec();
coord.install_chunk_pool_for_test(0, 0);
for r in 0..world_size {
coord.set_rank_epoch_for_test(r, 0);
}
coord.set_steps_since_avg_for_test(0, counts[0]);
coord.set_steps_since_avg_for_test(1, counts[1]);
coord.set_steps_since_avg_for_test(2, 0);
assert!(
coord.should_average(),
"quiesced 0-step tail rank must not block the reduce (counts={counts:?})",
);
coord.install_chunk_pool_for_test(0, 1000);
assert!(
!coord.should_average(),
"a 0-step rank that can still get work must block the gate",
);
}
#[test]
fn slot_zero_final_window_rank_does_not_block_the_gate() {
let world_size = 2;
let mut coord = ClusterCoordinator::for_test(
ClusterCoordinatorConfig::new(
ApplyPolicy::Cadence,
AverageBackend::Cpu,
world_size,
ElChe::new(world_size, 2),
)
.no_divergence_guard(),
);
coord.install_chunk_pool_for_test(0, 3);
for r in 0..world_size {
coord.set_rank_epoch_for_test(r, 0);
}
coord.refresh_final_window_plan_for_test(0);
let sizes: Vec<usize> = (0..world_size)
.map(|r| coord.compute_chunk_batches_for_test(r, 0))
.collect();
let zero = sizes.iter().position(|&n| n == 0).expect(
"precondition: the plan consolidates a lone-1 into a 0-slot sit-out",
);
let mover = 1 - zero;
let counts = coord.el_che_for_test().batch_counts().to_vec();
coord.set_steps_since_avg_for_test(mover, counts[mover]);
coord.set_steps_since_avg_for_test(zero, 0);
assert!(
coord.should_average(),
"a slot-0 final-window rank must not block the gate \
(sizes={sizes:?}, counts={counts:?})",
);
coord.set_steps_since_avg_for_test(mover, 0);
assert!(
!coord.should_average(),
"a 0-step rank holding a NONZERO plan slot must still block \
(sizes={sizes:?})",
);
}
#[test]
fn stale_final_window_plan_repins_when_counts_change() {
let world_size = 2;
let mut coord = ClusterCoordinator::for_test(
ClusterCoordinatorConfig::new(
ApplyPolicy::Cadence,
AverageBackend::Cpu,
world_size,
ElChe::new(world_size, 2),
)
.no_divergence_guard(),
);
coord.install_chunk_pool_for_test(0, 4);
for r in 0..world_size {
coord.set_rank_epoch_for_test(r, 0);
}
coord.refresh_final_window_plan_for_test(0);
let before: Vec<usize> = (0..world_size)
.map(|r| coord.compute_chunk_batches_for_test(r, 0))
.collect();
assert_eq!(before.iter().sum::<usize>(), 4, "coverage exact: {before:?}");
coord
.el_che_mut_for_test()
.report_timing(&[100.0, 1000.0], &[2, 2], 10.0);
let counts = coord.el_che_for_test().batch_counts().to_vec();
assert_ne!(counts, vec![2, 2], "precondition: counts changed");
coord.refresh_final_window_plan_for_test(0);
let after: Vec<usize> = (0..world_size)
.map(|r| coord.compute_chunk_batches_for_test(r, 0))
.collect();
assert_eq!(
after.iter().sum::<usize>(),
4,
"re-pin still covers the whole remainder: {after:?}",
);
assert_ne!(
after, before,
"the plan must be re-derived from the live counts \
(counts={counts:?}), not kept from the stale pin",
);
}