use crate::{Clock, ControllableClock, MonotonicClock};
use chrono::{DateTime, Duration, Utc};
use parking_lot::Mutex;
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct MockClock {
inner: Arc<Mutex<MockClockInner>>,
}
#[derive(Debug)]
struct MockClockInner {
monotonic_clock: MonotonicClock,
create_time: i64,
epoch: i64,
millis_to_add: i64,
millis_to_add_each_time: i64,
add_every_time: bool,
}
impl MockClock {
pub fn new() -> Self {
let monotonic_clock = MonotonicClock::new();
let create_time = monotonic_clock.millis();
MockClock {
inner: Arc::new(Mutex::new(MockClockInner {
monotonic_clock,
create_time,
epoch: create_time,
millis_to_add: 0,
millis_to_add_each_time: 0,
add_every_time: false,
})),
}
}
pub fn add_millis(&self, millis: i64, add_every_time: bool) {
if add_every_time {
self.set_auto_advance_millis(millis);
} else {
self.advance_millis(millis);
}
}
pub fn advance_millis(&self, millis: i64) {
let mut inner = self.inner.lock();
inner.millis_to_add = inner.millis_to_add.saturating_add(millis);
}
pub fn set_auto_advance_millis(&self, millis: i64) {
let mut inner = self.inner.lock();
inner.millis_to_add_each_time = millis;
inner.add_every_time = true;
}
pub fn clear_auto_advance(&self) {
let mut inner = self.inner.lock();
inner.millis_to_add_each_time = 0;
inner.add_every_time = false;
}
}
impl Default for MockClock {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl Clock for MockClock {
fn millis(&self) -> i64 {
let mut inner = self.inner.lock();
let elapsed = inner
.monotonic_clock
.millis()
.saturating_sub(inner.create_time);
let result = inner
.epoch
.saturating_add(elapsed)
.saturating_add(inner.millis_to_add);
if inner.add_every_time {
inner.millis_to_add = inner
.millis_to_add
.saturating_add(inner.millis_to_add_each_time);
}
result
}
}
impl ControllableClock for MockClock {
fn set_time(&self, instant: DateTime<Utc>) {
let mut inner = self.inner.lock();
let current_monotonic = inner.monotonic_clock.millis();
let elapsed = current_monotonic.saturating_sub(inner.create_time);
inner.epoch = instant.timestamp_millis().saturating_sub(elapsed);
inner.millis_to_add = 0;
inner.millis_to_add_each_time = 0;
inner.add_every_time = false;
}
#[inline]
fn add_duration(&self, duration: Duration) {
let millis = duration.num_milliseconds();
self.advance_millis(millis);
}
fn reset(&self) {
let mut inner = self.inner.lock();
inner.epoch = inner.create_time;
inner.millis_to_add = 0;
inner.millis_to_add_each_time = 0;
inner.add_every_time = false;
}
}