#[derive(Debug, Clone)]
pub(crate) struct XorShift64 {
state: u64,
}
impl XorShift64 {
pub(crate) fn new(seed: u64) -> Self {
Self {
state: if seed == 0 {
0xDEAD_BEEF_CAFE_BABE
} else {
seed
},
}
}
pub(crate) fn next_u64(&mut self) -> u64 {
let mut x = self.state;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
self.state = x;
x
}
pub(crate) fn next_f32(&mut self) -> f32 {
(self.next_u64() >> 40) as f32 / (1u32 << 24) as f32
}
pub(crate) fn jitter_i64(&mut self, range: u64) -> i64 {
if range == 0 {
return 0;
}
let range = range.min(i64::MAX as u64);
let span = range.saturating_mul(2).saturating_add(1);
let r = self.next_u64() % span;
((r as i128) - (range as i128)) as i64
}
}
pub(crate) fn derive_seed(explicit: Option<u64>, counter: u64) -> u64 {
explicit.unwrap_or_else(|| {
counter.wrapping_mul(0x9E37_79B9_7F4A_7C15).wrapping_add(1)
})
}
#[derive(Debug, Clone)]
pub(crate) struct ChaosPlan {
pub(crate) frame_delays_ms: Option<Vec<u64>>,
pub(crate) duplicate: bool,
pub(crate) active: bool,
}
impl ChaosPlan {
pub(crate) const PASSTHROUGH: Self = Self {
frame_delays_ms: None,
duplicate: false,
active: false,
};
pub(crate) fn from_failure(
failure: Option<&crate::fixture::FailureConfig>,
base_latency_ms: u64,
frame_count: usize,
chaos_counter: u64,
) -> Self {
let Some(f) = failure else {
return Self::PASSTHROUGH;
};
if !f.has_chaos() {
return Self::PASSTHROUGH;
}
let seed = derive_seed(f.chaos_seed, chaos_counter);
let mut rng = XorShift64::new(seed);
let p = f.probability.unwrap_or(1.0);
if rng.next_f32() >= p {
return Self::PASSTHROUGH;
}
let duplicate = f.duplicate_frames.unwrap_or(false);
let effective_count = if duplicate {
frame_count.saturating_mul(2)
} else {
frame_count
};
let frame_delays_ms = f.latency_jitter_ms.and_then(|range| {
if range == 0 {
None
} else {
Some(
(0..effective_count)
.map(|_| {
let delta = rng.jitter_i64(range);
(base_latency_ms as i64 + delta).max(0) as u64
})
.collect(),
)
}
});
Self {
frame_delays_ms,
duplicate,
active: true,
}
}
pub(crate) fn apply_frame_duplication(&self, frames: Vec<String>) -> Vec<String> {
if !self.duplicate {
return frames;
}
let mut out = Vec::with_capacity(frames.len().saturating_mul(2));
for frame in frames {
out.push(frame.clone());
out.push(frame);
}
out
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn xorshift_zero_seed_is_rewritten_to_fixed_constant() {
let mut r1 = XorShift64::new(0);
let mut r2 = XorShift64::new(0);
assert_eq!(r1.next_u64(), r2.next_u64());
assert_ne!(r1.state, 0);
}
#[test]
fn xorshift_nonzero_seed_is_deterministic() {
let mut a = XorShift64::new(42);
let mut b = XorShift64::new(42);
for _ in 0..10 {
assert_eq!(a.next_u64(), b.next_u64());
}
}
#[test]
fn next_f32_is_in_unit_interval() {
let mut r = XorShift64::new(1);
for _ in 0..1_000 {
let v = r.next_f32();
assert!((0.0..1.0).contains(&v));
}
}
#[test]
fn jitter_range_zero_returns_zero() {
let mut r = XorShift64::new(7);
for _ in 0..20 {
assert_eq!(r.jitter_i64(0), 0);
}
}
#[test]
fn jitter_range_is_symmetric_and_bounded() {
let mut r = XorShift64::new(123);
let mut saw_neg = false;
let mut saw_pos = false;
for _ in 0..1_000 {
let j = r.jitter_i64(10);
assert!((-10..=10).contains(&j));
if j < 0 {
saw_neg = true;
}
if j > 0 {
saw_pos = true;
}
}
assert!(saw_neg, "expected at least one negative jitter");
assert!(saw_pos, "expected at least one positive jitter");
}
#[test]
fn derive_seed_honors_explicit() {
assert_eq!(derive_seed(Some(1234), 999), 1234);
}
#[test]
fn derive_seed_without_override_scatters_counters() {
let a = derive_seed(None, 0);
let b = derive_seed(None, 1);
let c = derive_seed(None, 2);
assert_ne!(a, b);
assert_ne!(b, c);
assert_ne!(a, c);
}
#[test]
fn plan_with_no_failure_is_passthrough() {
let plan = ChaosPlan::from_failure(None, 25, 4, 0);
assert!(!plan.active);
assert!(plan.frame_delays_ms.is_none());
}
#[test]
fn plan_with_no_chaos_fields_is_passthrough() {
let f = crate::fixture::FailureConfig {
latency_ms: Some(1000),
corrupt_body: Some(true),
truncate_after_frames: Some(5),
disconnect_after_ms: Some(200),
..Default::default()
};
let plan = ChaosPlan::from_failure(Some(&f), 25, 4, 0);
assert!(!plan.active);
assert!(plan.frame_delays_ms.is_none());
}
#[test]
fn plan_with_probability_zero_is_passthrough() {
let f = crate::fixture::FailureConfig {
probability: Some(0.0),
latency_jitter_ms: Some(10),
duplicate_frames: Some(true),
chaos_seed: Some(1),
..Default::default()
};
let plan = ChaosPlan::from_failure(Some(&f), 20, 3, 0);
assert!(!plan.active);
assert!(plan.frame_delays_ms.is_none());
}
#[test]
fn plan_with_probability_one_always_activates_and_duplicates() {
let f = crate::fixture::FailureConfig {
probability: Some(1.0),
duplicate_frames: Some(true),
chaos_seed: Some(1),
..Default::default()
};
let plan = ChaosPlan::from_failure(Some(&f), 20, 3, 0);
assert!(plan.active);
assert!(plan.frame_delays_ms.is_none());
let dup = plan.apply_frame_duplication(vec!["a".into(), "b".into(), "c".into()]);
assert_eq!(dup, vec!["a", "a", "b", "b", "c", "c"]);
}
#[test]
fn plan_with_jitter_adjusts_delays_deterministically() {
let f = crate::fixture::FailureConfig {
latency_jitter_ms: Some(5),
chaos_seed: Some(42),
..Default::default()
};
let plan_a = ChaosPlan::from_failure(Some(&f), 20, 4, 0);
let plan_b = ChaosPlan::from_failure(Some(&f), 20, 4, 0);
assert_eq!(plan_a.frame_delays_ms, plan_b.frame_delays_ms);
let delays = plan_a.frame_delays_ms.as_ref().expect("delays present");
assert_eq!(delays.len(), 4);
for d in delays {
assert!((15..=25).contains(d));
}
}
#[test]
fn plan_jitter_clamps_negative_to_zero() {
let f = crate::fixture::FailureConfig {
latency_jitter_ms: Some(100),
chaos_seed: Some(7),
..Default::default()
};
let plan = ChaosPlan::from_failure(Some(&f), 5, 20, 0);
let delays = plan.frame_delays_ms.as_ref().expect("delays present");
assert!(delays.contains(&0));
assert!(delays.iter().all(|d| *d <= 105));
}
#[test]
fn apply_frame_duplication_is_noop_when_not_configured() {
let plan = ChaosPlan::PASSTHROUGH;
let frames = vec!["a".to_string(), "b".to_string()];
assert_eq!(plan.apply_frame_duplication(frames.clone()), frames);
}
#[test]
fn jitter_i64_caps_extreme_range_without_panicking() {
let mut r = XorShift64::new(1);
let _ = r.jitter_i64(u64::MAX);
let _ = r.jitter_i64(u64::MAX - 1);
}
#[test]
fn plan_zero_jitter_is_passthrough() {
let f = crate::fixture::FailureConfig {
latency_jitter_ms: Some(0),
chaos_seed: Some(1),
..Default::default()
};
let plan = ChaosPlan::from_failure(Some(&f), 20, 3, 0);
assert!(plan.active);
assert!(plan.frame_delays_ms.is_none());
}
}