/ crates / tor-rtmock / src / time_core.rs
time_core.rs
  1  //! [`MockTimeCore`] and [`MockCoarseTimeProvider`]
  2  
  3  use derive_deftly::{define_derive_deftly, Deftly};
  4  use std::time::{Duration, Instant, SystemTime};
  5  use tor_rtcompat::{CoarseDuration, CoarseInstant};
  6  use tor_rtcompat::{CoarseTimeProvider, RealCoarseTimeProvider};
  7  
  8  define_derive_deftly! {
  9      /// Derive getters for struct fields.
 10      ///
 11      /// Like `amplify::Getters` but `pub(crate)`.
 12      ///
 13      /// TODO add this feature to `amplify`.
 14      CrateGetters:
 15      ${define REF ${if not(fmeta(getter_copy)) { & }}}
 16      $(
 17          impl $ttype {
 18              ${fattrs doc}
 19              pub(crate) fn $fname(&self) -> $REF $ftype {
 20                  $REF self.$fname
 21              }
 22          }
 23      )
 24  }
 25  
 26  /// Mock time, as a value
 27  ///
 28  /// Contains an `Instant`, `SystemTime` and `CoarseInstant`.
 29  ///
 30  /// Arranges that they are all moved in step,
 31  /// unless explicitly requested otherwise.
 32  #[derive(Clone, Debug, Deftly)]
 33  #[derive_deftly(CrateGetters)]
 34  pub(crate) struct MockTimeCore {
 35      /// Current time (monotonic clock)
 36      #[deftly(getter_copy)]
 37      instant: Instant,
 38  
 39      /// Current wallclock time
 40      #[deftly(getter_copy)]
 41      wallclock: SystemTime,
 42  
 43      /// Coarse time tracking
 44      coarse: MockCoarseTimeProvider,
 45  }
 46  
 47  impl MockTimeCore {
 48      /// Create a new `MockTimeCore`
 49      pub(crate) fn new(instant: Instant, wallclock: SystemTime) -> Self {
 50          MockTimeCore {
 51              instant,
 52              coarse: MockCoarseTimeProvider::new(),
 53              wallclock,
 54          }
 55      }
 56  
 57      /// Advance by a duration
 58      ///
 59      /// All three time values are advanced in step.
 60      pub(crate) fn advance(&mut self, d: Duration) {
 61          self.instant += d;
 62          self.wallclock += d;
 63          self.coarse.advance(d);
 64      }
 65  
 66      /// Warp the wallclock (only)
 67      //
 68      // We *could* just expose the field for mutable access,
 69      // but this way seems more regular.
 70      pub(crate) fn jump_wallclock(&mut self, new_wallclock: SystemTime) {
 71          self.wallclock = new_wallclock;
 72      }
 73  }
 74  
 75  /// A mockable [`CoarseTimeProvider`]
 76  #[derive(Clone, Debug)]
 77  pub(crate) struct MockCoarseTimeProvider {
 78      /// Starting point
 79      started: CoarseInstant,
 80  
 81      /// How much we have advanced
 82      ///
 83      /// We track this as a `Duration`, not a [`CoarseDuration`] (or [`CoarseInstant`])
 84      /// to avoid accumulating rounding errors,
 85      /// which might otherwise cause the mocked `Instant` and `CoarseInstant`
 86      /// clocks to run at noticeably different *rates*.
 87      elapsed: Duration,
 88  }
 89  
 90  impl MockCoarseTimeProvider {
 91      /// Start a new [`MockCoarseTimeProvider`]
 92      pub(crate) fn new() -> Self {
 93          MockCoarseTimeProvider {
 94              started: RealCoarseTimeProvider::new().now_coarse(),
 95              elapsed: Duration::ZERO,
 96          }
 97      }
 98  
 99      /// Advance the mocked coarse time by `dur`
100      pub(crate) fn advance(&mut self, dur: Duration) {
101          self.elapsed += dur;
102      }
103  }
104  
105  impl CoarseTimeProvider for MockCoarseTimeProvider {
106      fn now_coarse(&self) -> CoarseInstant {
107          self.started + CoarseDuration::from(self.elapsed)
108      }
109  }