#![cfg(test)]
use crate::combinator::adaptive_hedge::PeakEwmaHedgeController;
use crate::util::det_rng::DetRng;
use std::time::Duration;
fn generate_observations(seed: u64, count: usize, max_ms: u64) -> Vec<Duration> {
let mut rng = DetRng::new(seed);
(0..count)
.map(|_| {
if max_ms == 0 {
Duration::ZERO
} else {
Duration::from_millis(rng.next_u64() % max_ms)
}
})
.collect()
}
#[test]
fn metamorphic_pointwise_monotonicity() {
let min_delay = Duration::from_millis(5);
let max_delay = Duration::from_millis(1000);
let initial = Duration::from_millis(50);
let decay = 0.95;
let base_seq = generate_observations(0x1234, 100, 200);
let offset = Duration::from_millis(20);
let dominating_seq: Vec<Duration> = base_seq.iter().map(|d| *d + offset).collect();
let controller_a = PeakEwmaHedgeController::new(initial, min_delay, max_delay, decay);
let controller_b = PeakEwmaHedgeController::new(initial, min_delay, max_delay, decay);
for i in 0..base_seq.len() {
controller_a.observe(dominating_seq[i]);
controller_b.observe(base_seq[i]);
let delay_a = controller_a.current_config().hedge_delay;
let delay_b = controller_b.current_config().hedge_delay;
assert!(
delay_a >= delay_b,
"Monotonicity violated at step {}: delay_a={:?} < delay_b={:?}",
i,
delay_a,
delay_b
);
}
}
#[test]
fn metamorphic_geometric_decay() {
let min_delay = Duration::from_millis(1);
let max_delay = Duration::from_millis(10000);
let initial = Duration::from_millis(10);
let decay = 0.90;
let controller = PeakEwmaHedgeController::new(initial, min_delay, max_delay, decay);
let peak_ms = 1000;
controller.observe(Duration::from_millis(peak_ms));
let mut expected_ms = peak_ms as f64;
for step in 1..=50 {
controller.observe(Duration::from_nanos(1));
expected_ms *= decay;
let expected_clamped = expected_ms.max(min_delay.as_millis() as f64);
let actual_ms = controller.current_config().hedge_delay.as_millis() as f64;
let diff = (actual_ms - expected_clamped).abs();
assert!(
diff <= 1.0,
"Decay violated at step {}: expected roughly {}, got {} (diff: {})",
step,
expected_clamped,
actual_ms,
diff
);
}
}
#[test]
fn metamorphic_scale_invariance() {
let base_min = 10;
let base_max = 500;
let base_initial = 50;
let decay = 0.99;
let scale_factor = 3;
let controller_base = PeakEwmaHedgeController::new(
Duration::from_millis(base_initial),
Duration::from_millis(base_min),
Duration::from_millis(base_max),
decay,
);
let controller_scaled = PeakEwmaHedgeController::new(
Duration::from_millis(base_initial * scale_factor),
Duration::from_millis(base_min * scale_factor),
Duration::from_millis(base_max * scale_factor),
decay,
);
let seq = generate_observations(0x5678, 100, 200);
for obs in seq {
controller_base.observe(obs);
controller_scaled.observe(obs * (scale_factor as u32));
let delay_base = controller_base.current_config().hedge_delay.as_nanos();
let delay_scaled = controller_scaled.current_config().hedge_delay.as_nanos();
let expected_scaled = delay_base * u128::from(scale_factor);
let diff = delay_scaled.abs_diff(expected_scaled);
let truncation_tolerance = 1_000;
assert!(
diff <= truncation_tolerance,
"Scale invariance violated: base_nanos={}, scaled_nanos={}, expected_nanos={}, diff_nanos={}",
delay_base,
delay_scaled,
expected_scaled,
diff
);
}
}
#[test]
fn generate_observations_zero_max_yields_zero_latencies() {
let samples = generate_observations(0x9abc, 16, 0);
assert_eq!(samples.len(), 16);
assert!(samples.iter().all(|sample| *sample == Duration::ZERO));
}