pub trait Clock: Send + Sync {
fn now(&self) -> u64;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct SystemClock;
impl Clock for SystemClock {
fn now(&self) -> u64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("system time before Unix epoch")
.as_secs()
}
}
impl SystemClock {
pub fn now_system_time(&self) -> std::time::SystemTime {
std::time::SystemTime::now()
}
}
#[derive(Debug, Clone)]
pub struct MockClock {
current_time: u64,
}
impl MockClock {
pub fn new(current_time: u64) -> Self {
Self { current_time }
}
pub fn with_system_time() -> Self {
Self::new(
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("system time before Unix epoch")
.as_secs(),
)
}
pub fn advance_by(&mut self, seconds: u64) {
self.current_time = self.current_time.saturating_add(seconds);
}
pub fn set(&mut self, time: u64) {
self.current_time = time;
}
pub fn now_system_time(&self) -> std::time::SystemTime {
std::time::SystemTime::UNIX_EPOCH
.checked_add(std::time::Duration::from_secs(self.current_time))
.expect("time overflow")
}
}
impl Default for MockClock {
fn default() -> Self {
Self::with_system_time()
}
}
impl Clock for MockClock {
fn now(&self) -> u64 {
self.current_time
}
}
pub type SharedClock = std::sync::Arc<dyn Clock>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn system_clock_returns_monotonically_increasing_time() {
let clock = SystemClock;
let time1 = clock.now();
let time2 = clock.now();
assert!(time2 >= time1);
}
#[test]
fn mock_clock_can_be_initialized() {
let clock = MockClock::new(1000);
assert_eq!(clock.now(), 1000);
}
#[test]
fn mock_clock_can_be_advanced() {
let mut clock = MockClock::new(1000);
clock.advance_by(100);
assert_eq!(clock.now(), 1100);
}
#[test]
fn mock_clock_can_be_set() {
let mut clock = MockClock::new(1000);
clock.set(2000);
assert_eq!(clock.now(), 2000);
}
#[test]
fn mock_clock_system_time_conversion() {
let clock = MockClock::new(1000);
let system_time = clock.now_system_time();
let duration = system_time.duration_since(std::time::UNIX_EPOCH).expect("time overflow");
assert_eq!(duration.as_secs(), 1000);
}
}