use std::sync::Arc;
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
pub trait Clock: Send + Sync {
fn now(&self) -> Instant;
fn unix_timestamp(&self) -> u64;
fn unix_timestamp_nanos(&self) -> u64;
}
#[derive(Debug, Clone, Copy)]
pub struct SystemClock;
impl Clock for SystemClock {
fn now(&self) -> Instant {
Instant::now()
}
fn unix_timestamp(&self) -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0)
}
fn unix_timestamp_nanos(&self) -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_nanos() as u64)
.unwrap_or(0)
}
}
pub struct MockClock {
current_time: parking_lot::RwLock<Instant>,
unix_timestamp: parking_lot::RwLock<u64>,
}
impl Clone for MockClock {
fn clone(&self) -> Self {
Self {
current_time: parking_lot::RwLock::new(*self.current_time.read()),
unix_timestamp: parking_lot::RwLock::new(*self.unix_timestamp.read()),
}
}
}
impl MockClock {
pub fn new() -> Self {
Self {
current_time: parking_lot::RwLock::new(Instant::now()),
unix_timestamp: parking_lot::RwLock::new(
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0),
),
}
}
pub fn with_instant(instant: Instant, unix_ts: u64) -> Self {
Self {
current_time: parking_lot::RwLock::new(instant),
unix_timestamp: parking_lot::RwLock::new(unix_ts),
}
}
pub fn advance(&self, duration: Duration) {
let mut time = self.current_time.write();
*time = time.checked_add(duration).unwrap_or(*time);
let mut unix_ts = self.unix_timestamp.write();
*unix_ts = unix_ts.saturating_add(duration.as_secs());
}
pub fn set_time(&self, instant: Instant, unix_ts: u64) {
let mut time = self.current_time.write();
*time = instant;
let mut unix_ts_lock = self.unix_timestamp.write();
*unix_ts_lock = unix_ts;
}
pub fn as_arc(self) -> Arc<dyn Clock> {
Arc::new(self)
}
}
impl Default for MockClock {
fn default() -> Self {
Self::new()
}
}
impl Clock for MockClock {
fn now(&self) -> Instant {
*self.current_time.read()
}
fn unix_timestamp(&self) -> u64 {
*self.unix_timestamp.read()
}
fn unix_timestamp_nanos(&self) -> u64 {
*self.unix_timestamp.read() * 1_000_000_000
}
}
#[cfg(test)]
pub(crate) fn system_clock() -> Arc<dyn Clock> {
Arc::new(SystemClock)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_system_clock_now() {
let clock = SystemClock;
let now = clock.now();
let later = clock.now();
assert!(later >= now);
}
#[test]
fn test_system_clock_unix_timestamp() {
let clock = SystemClock;
let ts = clock.unix_timestamp();
assert!(ts > 1_700_000_000);
assert!(ts < 2_000_000_000);
}
#[test]
fn test_system_clock_unix_timestamp_nanos() {
let clock = SystemClock;
let ts_nanos = clock.unix_timestamp_nanos();
let ts_secs = clock.unix_timestamp();
let expected_nanos = ts_secs * 1_000_000_000;
assert!(ts_nanos >= expected_nanos);
assert!(ts_nanos < expected_nanos + 2_000_000_000);
}
#[test]
fn test_mock_clock_advance() {
let clock = MockClock::new();
let start = clock.now();
clock.advance(Duration::from_secs(10));
let elapsed = clock.now().duration_since(start);
assert_eq!(elapsed, Duration::from_secs(10));
}
#[test]
fn test_mock_clock_set_time() {
let clock = MockClock::new();
let custom_instant = Instant::now() + Duration::from_secs(100);
let custom_unix_ts = 1_700_000_000;
clock.set_time(custom_instant, custom_unix_ts);
assert_eq!(clock.now(), custom_instant);
assert_eq!(clock.unix_timestamp(), custom_unix_ts);
}
#[test]
fn test_mock_clock_unix_timestamp_nanos() {
let clock = MockClock::new();
clock.advance(Duration::from_secs(5));
let ts_secs = clock.unix_timestamp();
let ts_nanos = clock.unix_timestamp_nanos();
assert_eq!(ts_nanos, ts_secs * 1_000_000_000);
}
#[test]
fn test_mock_clock_thread_safety() {
let clock = Arc::new(MockClock::new());
let mut handles = vec![];
for _ in 0..10 {
let clock_clone = Arc::clone(&clock);
handles.push(std::thread::spawn(move || {
clock_clone.advance(Duration::from_secs(1));
clock_clone.now()
}));
}
for handle in handles {
handle.join().unwrap();
}
let elapsed = clock.now().duration_since(Instant::now());
let _ = elapsed;
}
#[test]
fn test_system_clock_arc() {
let clock = system_clock();
let ts = clock.unix_timestamp();
assert!(ts > 1_700_000_000);
}
#[test]
fn test_mock_clock_with_instant() {
let instant = Instant::now();
let unix_ts = 1_700_000_000;
let clock = MockClock::with_instant(instant, unix_ts);
assert_eq!(clock.now(), instant);
assert_eq!(clock.unix_timestamp(), unix_ts);
}
#[test]
fn test_mock_clock_default() {
let clock = MockClock::default();
let ts = clock.unix_timestamp();
assert!(ts > 1_700_000_000);
}
#[test]
fn test_mock_clock_clone() {
let clock = MockClock::new();
clock.advance(Duration::from_secs(42));
let cloned = clock.clone();
assert_eq!(cloned.unix_timestamp(), clock.unix_timestamp());
assert_eq!(clock.now(), cloned.now());
}
#[test]
fn test_mock_clock_as_arc() {
let clock = MockClock::new();
let arc_clock: Arc<dyn Clock> = clock.as_arc();
let ts = arc_clock.unix_timestamp();
assert!(ts > 1_700_000_000);
let now = arc_clock.now();
assert!(now.elapsed() < Duration::from_secs(5));
}
#[test]
fn test_mock_clock_clone_independent() {
let clock = MockClock::new();
clock.advance(Duration::from_secs(10));
let cloned = clock.clone();
clock.advance(Duration::from_secs(20));
assert_eq!(clock.unix_timestamp(), cloned.unix_timestamp() + 20);
}
#[test]
fn test_system_clock_copy_and_clone() {
let clock1 = SystemClock;
let clock2 = clock1;
let ts1 = clock1.unix_timestamp();
let ts2 = clock2.unix_timestamp();
assert!(ts1 <= ts2);
}
#[test]
fn test_mock_clock_advance_then_nanos() {
let clock = MockClock::with_instant(Instant::now(), 100);
clock.advance(Duration::from_secs(5));
let nanos = clock.unix_timestamp_nanos();
assert_eq!(nanos, 105 * 1_000_000_000);
}
}