use sendspin::sync::{Clock, ClockSync, DefaultClock};
use std::sync::atomic::{AtomicI64, Ordering};
use std::sync::Arc;
use std::time::{Duration, Instant};
struct MockClock {
micros: AtomicI64,
}
impl MockClock {
fn new(initial: i64) -> Self {
Self {
micros: AtomicI64::new(initial),
}
}
fn set(&self, micros: i64) {
self.micros.store(micros, Ordering::SeqCst);
}
fn advance(&self, delta_micros: i64) {
self.micros.fetch_add(delta_micros, Ordering::SeqCst);
}
}
impl Clock for MockClock {
fn now_micros(&self) -> i64 {
self.micros.load(Ordering::SeqCst)
}
}
#[test]
fn test_default_clock_monotonic() {
let clock = DefaultClock::new();
let t1 = clock.now_micros();
std::thread::yield_now();
let t2 = clock.now_micros();
assert!(t2 >= t1, "clock must be monotonically non-decreasing");
}
#[test]
fn test_default_clock_micros_to_instant_roundtrip() {
let clock = DefaultClock::new();
let now_micros = clock.now_micros();
let instant = clock
.micros_to_instant(now_micros)
.expect("conversion should succeed for current time");
let diff = Instant::now().duration_since(instant);
assert!(
diff < Duration::from_millis(5),
"round-trip drift too large: {:?}",
diff
);
}
#[test]
fn test_default_clock_instant_to_micros_roundtrip() {
let clock = DefaultClock::new();
let now = Instant::now();
let micros = clock.instant_to_micros(now);
let back = clock
.micros_to_instant(micros)
.expect("conversion should succeed");
let diff = if back >= now {
back.duration_since(now)
} else {
now.duration_since(back)
};
assert!(
diff < Duration::from_millis(1),
"roundtrip error too large: {:?}",
diff
);
}
#[test]
fn test_default_clock_future_micros_to_instant() {
let clock = DefaultClock::new();
let future = clock.now_micros() + 1_000_000; let instant = clock
.micros_to_instant(future)
.expect("future time should convert");
assert!(
instant > Instant::now(),
"future micros should map to a future Instant"
);
}
#[test]
fn test_default_clock_past_micros_to_instant() {
let clock = DefaultClock::new();
let past = clock.now_micros() - 1_000_000; let instant = clock
.micros_to_instant(past)
.expect("past time should convert");
assert!(
instant < Instant::now(),
"past micros should map to a past Instant"
);
}
#[test]
fn test_mock_clock_deterministic() {
let clock = MockClock::new(1_000_000);
assert_eq!(clock.now_micros(), 1_000_000);
clock.advance(500);
assert_eq!(clock.now_micros(), 1_000_500);
}
#[test]
fn test_clock_sync_with_mock_clock() {
let clock = Arc::new(MockClock::new(100_000));
let mut sync = ClockSync::new(clock.clone() as Arc<dyn Clock>);
clock.set(100_000);
let t1 = clock.now_micros();
let t2 = 105_100; let t3 = 105_110; clock.set(100_200); let t4 = clock.now_micros();
sync.update(t1, t2, t3, t4);
clock.set(1_100_000);
let t1_2 = clock.now_micros();
let t2_2 = 1_105_100;
let t3_2 = 1_105_110;
clock.set(1_100_200);
let t4_2 = clock.now_micros();
sync.update(t1_2, t2_2, t3_2, t4_2);
assert!(
sync.is_synchronized(),
"should be synchronized after 2 samples"
);
let client_micros = sync.server_to_client_micros(1_105_000);
let client = client_micros.expect("should have a value");
let diff = (client - 1_100_000).abs();
assert!(
diff < 50,
"server→client conversion off by {}µs (expected ~0)",
diff
);
}
#[test]
fn test_clock_sync_accessor_returns_injected_clock() {
let clock = Arc::new(MockClock::new(42_000));
let sync = ClockSync::new(clock.clone() as Arc<dyn Clock>);
assert_eq!(sync.clock().now_micros(), 42_000);
}
#[test]
fn test_clock_sync_instant_to_client_micros_is_infallible() {
let clock = Arc::new(MockClock::new(500_000));
let sync = ClockSync::new(clock as Arc<dyn Clock>);
let result: i64 = sync.instant_to_client_micros(Instant::now());
assert!(result > 0, "should return a positive value, got {}", result);
}
#[test]
fn test_micros_to_instant_far_past_returns_none() {
let clock = DefaultClock::new();
let very_far_past = clock.now_micros() - 1_000_000_000_000; let _result = clock.micros_to_instant(very_far_past);
}
#[test]
fn test_mock_clock_micros_to_instant_returns_none_for_unreachable_past() {
let clock = MockClock::new(1_000_000_000); let result = clock.micros_to_instant(0);
let _ = result;
}
#[test]
fn test_mock_clock_bridge_round_trip() {
let clock = MockClock::new(500_000);
let original = 500_000i64;
if let Some(instant) = clock.micros_to_instant(original) {
let back = clock.instant_to_micros(instant);
let diff = (back - original).abs();
assert!(
diff < 1_000,
"MockClock bridge round-trip error too large: {}µs",
diff
);
}
}