use crate::runtime::clock::{Clock, CycleTime};
use std::cell::RefCell;
use std::rc::Rc;
use std::time::{Duration, Instant};
use time::OffsetDateTime;
pub struct TestClock(Rc<RefCell<TestClockInner>>);
impl TestClock {
pub fn new() -> Self {
TestClock(Rc::new(RefCell::new(TestClockInner::new())))
}
pub fn starting_at(wall_time: OffsetDateTime) -> Self {
TestClock(Rc::new(RefCell::new(TestClockInner::starting_at(
wall_time,
))))
}
pub fn advance(&self, duration: Duration) {
self.0.borrow_mut().advance(duration);
}
pub fn set_elapsed(&self, elapsed: Duration) {
self.0.borrow_mut().set_elapsed(elapsed);
}
pub fn elapsed(&self) -> Duration {
self.0.borrow().elapsed
}
pub fn current_times(&self) -> (Instant, OffsetDateTime) {
let inner = self.0.borrow();
(inner.current_instant(), inner.current_wall_time())
}
pub fn reset(&self) {
self.0.borrow_mut().reset();
}
}
impl Clock for TestClock {
fn cycle_time(&mut self) -> CycleTime {
let inner = self.0.borrow();
CycleTime {
instant: inner.current_instant(),
unix_time: inner.current_wall_time(),
}
}
}
impl Default for TestClock {
fn default() -> Self {
Self::new()
}
}
impl Clone for TestClock {
fn clone(&self) -> Self {
TestClock(self.0.clone())
}
}
struct TestClockInner {
baseline_wall_time: OffsetDateTime,
baseline_instant: Instant,
elapsed: Duration,
}
impl TestClockInner {
fn new() -> Self {
let baseline_wall_time = OffsetDateTime::UNIX_EPOCH;
let duration_since_epoch = OffsetDateTime::now_utc() - baseline_wall_time;
let baseline_instant = Instant::now() - duration_since_epoch;
Self {
baseline_wall_time,
baseline_instant,
elapsed: Duration::ZERO,
}
}
fn starting_at(baseline_wall_time: OffsetDateTime) -> Self {
let duration_since_epoch = baseline_wall_time - OffsetDateTime::UNIX_EPOCH;
let baseline_instant = Instant::now() - duration_since_epoch;
Self {
baseline_wall_time,
baseline_instant,
elapsed: Duration::ZERO,
}
}
fn advance(&mut self, duration: Duration) {
self.elapsed += duration;
}
fn set_elapsed(&mut self, elapsed: Duration) {
self.elapsed = elapsed;
}
fn reset(&mut self) {
self.elapsed = Duration::ZERO;
}
fn current_instant(&self) -> Instant {
self.baseline_instant + self.elapsed
}
fn current_wall_time(&self) -> OffsetDateTime {
self.baseline_wall_time + self.elapsed
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_clock_starts_at_epoch() {
let mut clock = TestClock::new();
let time = clock.cycle_time();
assert_eq!(time.unix_time, OffsetDateTime::UNIX_EPOCH);
assert_eq!(clock.elapsed(), Duration::ZERO);
}
#[test]
fn test_clock_advance() {
let mut clock = TestClock::new();
clock.advance(Duration::from_secs(3600));
let time = clock.cycle_time();
assert_eq!(
time.unix_time,
OffsetDateTime::UNIX_EPOCH + Duration::from_secs(3600)
);
assert_eq!(clock.elapsed(), Duration::from_secs(3600));
}
#[test]
fn test_predictable_instants() {
let mut clock = TestClock::new();
let time1 = clock.cycle_time();
clock.advance(Duration::from_millis(500));
let time2 = clock.cycle_time();
assert_eq!(
time2.instant.duration_since(time1.instant),
Duration::from_millis(500)
);
}
#[test]
fn test_starting_at_custom_time() {
let start_time = OffsetDateTime::from_unix_timestamp(1000000000).unwrap(); let mut clock = TestClock::starting_at(start_time);
let time = clock.cycle_time();
assert_eq!(time.unix_time, start_time);
assert_eq!(clock.elapsed(), Duration::ZERO);
clock.advance(Duration::from_secs(60));
let time2 = clock.cycle_time();
assert_eq!(time2.unix_time, start_time + Duration::from_secs(60));
}
#[test]
fn test_set_elapsed() {
let mut clock = TestClock::new();
clock.set_elapsed(Duration::from_secs(300));
let time = clock.cycle_time();
assert_eq!(
time.unix_time,
OffsetDateTime::UNIX_EPOCH + Duration::from_secs(300)
);
assert_eq!(clock.elapsed(), Duration::from_secs(300));
}
#[test]
fn test_reset() {
let mut clock = TestClock::new();
clock.advance(Duration::from_secs(1000));
assert_eq!(clock.elapsed(), Duration::from_secs(1000));
clock.reset();
let time = clock.cycle_time();
assert_eq!(time.unix_time, OffsetDateTime::UNIX_EPOCH);
assert_eq!(clock.elapsed(), Duration::ZERO);
}
#[test]
fn test_readable_test_assertions() {
let mut clock = TestClock::new();
clock.set_elapsed(Duration::from_secs(3600)); let time = clock.cycle_time();
assert_eq!(time.unix_time.unix_timestamp(), 3600);
clock.advance(Duration::from_secs(1800)); let time2 = clock.cycle_time();
assert_eq!(time2.unix_time.unix_timestamp(), 5400); }
#[test]
fn test_clone_behavior() {
let clock1 = TestClock::new();
let clock2 = clock1.clone();
clock1.advance(Duration::from_secs(100));
assert_eq!(clock1.elapsed(), Duration::from_secs(100));
assert_eq!(clock2.elapsed(), Duration::from_secs(100));
}
#[test]
fn test_instant_consistency() {
let mut clock = TestClock::new();
let time1 = clock.cycle_time();
let time2 = clock.cycle_time();
assert_eq!(time1.instant, time2.instant);
assert_eq!(time1.unix_time, time2.unix_time);
}
}