use crate::seed::SeedRng;
use std::time::Duration;
pub struct JitterModel {
_rng: SeedRng,
base_delay: Duration,
max_jitter: Duration,
}
impl JitterModel {
pub fn new(rng: SeedRng, base_delay: Duration, max_jitter: Duration) -> Self {
Self {
_rng: rng,
base_delay,
max_jitter,
}
}
pub fn next_delay(&mut self) -> Duration {
let jitter_micros = self._rng.range(0, self.max_jitter.as_micros() as u64);
self.base_delay + Duration::from_micros(jitter_micros)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_rng(seed_byte: u8) -> SeedRng {
SeedRng::new([seed_byte; 32])
}
#[test]
fn delay_within_expected_range() {
let base = Duration::from_millis(10);
let max_jitter = Duration::from_millis(5);
let mut model = JitterModel::new(make_rng(42), base, max_jitter);
for _ in 0..100 {
let delay = model.next_delay();
assert!(
delay >= base && delay <= base + max_jitter,
"delay {:?} out of range [{:?}, {:?}]",
delay,
base,
base + max_jitter
);
}
}
#[test]
fn deterministic_from_same_seed() {
let base = Duration::from_millis(10);
let max_jitter = Duration::from_millis(5);
let mut model_a = JitterModel::new(make_rng(42), base, max_jitter);
let mut model_b = JitterModel::new(make_rng(42), base, max_jitter);
for _ in 0..20 {
assert_eq!(model_a.next_delay(), model_b.next_delay());
}
}
#[test]
fn different_seeds_produce_different_sequences() {
let base = Duration::from_millis(10);
let max_jitter = Duration::from_millis(5);
let mut model_a = JitterModel::new(make_rng(1), base, max_jitter);
let mut model_b = JitterModel::new(make_rng(2), base, max_jitter);
let seq_a: Vec<Duration> = (0..20).map(|_| model_a.next_delay()).collect();
let seq_b: Vec<Duration> = (0..20).map(|_| model_b.next_delay()).collect();
assert_ne!(seq_a, seq_b, "sequences from different seeds should differ");
}
#[test]
fn zero_jitter_returns_constant_base_delay() {
let base = Duration::from_millis(10);
let max_jitter = Duration::ZERO;
let mut model = JitterModel::new(make_rng(42), base, max_jitter);
for _ in 0..20 {
assert_eq!(model.next_delay(), base);
}
}
#[test]
fn multiple_calls_return_varying_delays() {
let base = Duration::from_millis(10);
let max_jitter = Duration::from_millis(50);
let mut model = JitterModel::new(make_rng(42), base, max_jitter);
let delays: Vec<Duration> = (0..20).map(|_| model.next_delay()).collect();
let unique_count = delays
.iter()
.collect::<std::collections::HashSet<_>>()
.len();
assert!(
unique_count > 1,
"expected varying delays with non-zero jitter, got {} unique values out of {}",
unique_count,
delays.len()
);
}
}