cu29_clock/
lib.rs

1#[cfg(test)]
2#[macro_use]
3extern crate approx;
4use bincode::de::BorrowDecoder;
5use bincode::de::Decoder;
6use bincode::enc::Encoder;
7use bincode::error::{DecodeError, EncodeError};
8use bincode::BorrowDecode;
9use bincode::{Decode, Encode};
10use core::ops::{Add, Sub};
11pub use quanta::Instant;
12use quanta::{Clock, Mock};
13use serde::{Deserialize, Serialize};
14use std::convert::Into;
15use std::fmt::{Display, Formatter};
16use std::ops::{AddAssign, Div, Mul, SubAssign};
17use std::sync::Arc;
18use std::time::Duration;
19
20/// For Robot times, the underlying type is a u64 representing nanoseconds.
21/// It is always positive to simplify the reasoning on the user side.
22#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default)]
23pub struct CuDuration(pub u64);
24
25impl CuDuration {
26    // Lowest value a CuDuration can have.
27    pub const MIN: CuDuration = CuDuration(0u64);
28    // Highest value a CuDuration can have reserving the max value for None.
29    pub const MAX: CuDuration = CuDuration(NONE_VALUE - 1);
30    pub fn max(self, other: CuDuration) -> CuDuration {
31        let Self(lhs) = self;
32        let Self(rhs) = other;
33        CuDuration(lhs.max(rhs))
34    }
35
36    pub fn min(self, other: CuDuration) -> CuDuration {
37        let Self(lhs) = self;
38        let Self(rhs) = other;
39        CuDuration(lhs.min(rhs))
40    }
41
42    pub fn as_nanos(&self) -> u64 {
43        let Self(nanos) = self;
44        *nanos
45    }
46}
47
48/// bridge the API with standard Durations.
49impl From<Duration> for CuDuration {
50    fn from(duration: Duration) -> Self {
51        CuDuration(duration.as_nanos() as u64)
52    }
53}
54
55impl From<CuDuration> for Duration {
56    fn from(val: CuDuration) -> Self {
57        let CuDuration(nanos) = val;
58        Duration::from_nanos(nanos)
59    }
60}
61
62impl From<u64> for CuDuration {
63    fn from(duration: u64) -> Self {
64        CuDuration(duration)
65    }
66}
67
68impl From<CuDuration> for u64 {
69    fn from(val: CuDuration) -> Self {
70        let CuDuration(nanos) = val;
71        nanos
72    }
73}
74
75impl Sub for CuDuration {
76    type Output = Self;
77
78    fn sub(self, rhs: Self) -> Self::Output {
79        let CuDuration(lhs) = self;
80        let CuDuration(rhs) = rhs;
81        CuDuration(lhs - rhs)
82    }
83}
84
85impl Add for CuDuration {
86    type Output = Self;
87
88    fn add(self, rhs: Self) -> Self::Output {
89        let CuDuration(lhs) = self;
90        let CuDuration(rhs) = rhs;
91        CuDuration(lhs + rhs)
92    }
93}
94
95impl AddAssign for CuDuration {
96    fn add_assign(&mut self, rhs: Self) {
97        let CuDuration(lhs) = self;
98        let CuDuration(rhs) = rhs;
99        *lhs += rhs;
100    }
101}
102
103impl SubAssign for CuDuration {
104    fn sub_assign(&mut self, rhs: Self) {
105        let CuDuration(lhs) = self;
106        let CuDuration(rhs) = rhs;
107        *lhs -= rhs;
108    }
109}
110
111// a way to divide a duration by a scalar.
112// useful to compute averages for example.
113impl<T> Div<T> for CuDuration
114where
115    T: Into<u64>,
116{
117    type Output = Self;
118    fn div(self, rhs: T) -> Self {
119        let CuDuration(lhs) = self;
120        CuDuration(lhs / rhs.into())
121    }
122}
123//
124// a way to multiply a duration by a scalar.
125// useful to compute offsets for example.
126// CuDuration * scalar
127impl<T> Mul<T> for CuDuration
128where
129    T: Into<u64>,
130{
131    type Output = CuDuration;
132
133    fn mul(self, rhs: T) -> CuDuration {
134        let CuDuration(lhs) = self;
135        CuDuration(lhs * rhs.into())
136    }
137}
138
139// u64 * CuDuration
140impl Mul<CuDuration> for u64 {
141    type Output = CuDuration;
142
143    fn mul(self, rhs: CuDuration) -> CuDuration {
144        let CuDuration(nanos) = rhs;
145        CuDuration(self * nanos)
146    }
147}
148
149// u32 * CuDuration
150impl Mul<CuDuration> for u32 {
151    type Output = CuDuration;
152
153    fn mul(self, rhs: CuDuration) -> CuDuration {
154        let CuDuration(nanos) = rhs;
155        CuDuration(self as u64 * nanos)
156    }
157}
158
159// i32 * CuDuration
160impl Mul<CuDuration> for i32 {
161    type Output = CuDuration;
162
163    fn mul(self, rhs: CuDuration) -> CuDuration {
164        let CuDuration(nanos) = rhs;
165        CuDuration(self as u64 * nanos)
166    }
167}
168
169impl Encode for CuDuration {
170    fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
171        let CuDuration(nanos) = self;
172        nanos.encode(encoder)
173    }
174}
175
176impl<Context> Decode<Context> for CuDuration {
177    fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
178        Ok(CuDuration(u64::decode(decoder)?))
179    }
180}
181
182impl<'de, Context> BorrowDecode<'de, Context> for CuDuration {
183    fn borrow_decode<D: BorrowDecoder<'de>>(decoder: &mut D) -> Result<Self, DecodeError> {
184        Ok(CuDuration(u64::decode(decoder)?))
185    }
186}
187
188impl Display for CuDuration {
189    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
190        let Self(nanos) = *self;
191        if nanos >= 86_400_000_000_000 {
192            write!(f, "{:.3} d", nanos as f64 / 86_400_000_000_000.0)
193        } else if nanos >= 3_600_000_000_000 {
194            write!(f, "{:.3} h", nanos as f64 / 3_600_000_000_000.0)
195        } else if nanos >= 60_000_000_000 {
196            write!(f, "{:.3} m", nanos as f64 / 60_000_000_000.0)
197        } else if nanos >= 1_000_000_000 {
198            write!(f, "{:.3} s", nanos as f64 / 1_000_000_000.0)
199        } else if nanos >= 1_000_000 {
200            write!(f, "{:.3} ms", nanos as f64 / 1_000_000.0)
201        } else if nanos >= 1_000 {
202            write!(f, "{:.3} µs", nanos as f64 / 1_000.0)
203        } else {
204            write!(f, "{nanos} ns")
205        }
206    }
207}
208
209/// A robot time is just a duration from a fixed point in time.
210pub type CuTime = CuDuration;
211
212/// Homebrewed `Option<CuDuration>` to avoid using 128bits just to represent an Option.
213#[derive(Copy, Clone, Debug, PartialEq, Encode, Decode, Serialize, Deserialize)]
214pub struct OptionCuTime(CuTime);
215
216const NONE_VALUE: u64 = 0xFFFFFFFFFFFFFFFF;
217
218impl OptionCuTime {
219    #[inline]
220    pub fn is_none(&self) -> bool {
221        let Self(CuDuration(nanos)) = self;
222        *nanos == NONE_VALUE
223    }
224
225    #[inline]
226    pub fn none() -> Self {
227        OptionCuTime(CuDuration(NONE_VALUE))
228    }
229
230    #[inline]
231    pub fn unwrap(self) -> CuTime {
232        if self.is_none() {
233            panic!("called `OptionCuTime::unwrap()` on a `None` value");
234        }
235        self.0
236    }
237}
238
239impl Display for OptionCuTime {
240    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
241        if self.is_none() {
242            write!(f, "None")
243        } else {
244            write!(f, "{}", self.0)
245        }
246    }
247}
248
249impl Default for OptionCuTime {
250    fn default() -> Self {
251        Self::none()
252    }
253}
254
255impl From<Option<CuTime>> for OptionCuTime {
256    #[inline]
257    fn from(duration: Option<CuTime>) -> Self {
258        match duration {
259            Some(duration) => OptionCuTime(duration),
260            None => OptionCuTime(CuDuration(NONE_VALUE)),
261        }
262    }
263}
264
265impl From<OptionCuTime> for Option<CuTime> {
266    #[inline]
267    fn from(val: OptionCuTime) -> Self {
268        let OptionCuTime(CuDuration(nanos)) = val;
269        if nanos == NONE_VALUE {
270            None
271        } else {
272            Some(CuDuration(nanos))
273        }
274    }
275}
276
277impl From<CuTime> for OptionCuTime {
278    #[inline]
279    fn from(val: CuTime) -> Self {
280        Some(val).into()
281    }
282}
283
284/// Represents a time range.
285#[derive(Copy, Clone, Debug, Encode, Decode, Serialize, Deserialize, PartialEq)]
286pub struct CuTimeRange {
287    pub start: CuTime,
288    pub end: CuTime,
289}
290
291/// Builds a time range from a slice of CuTime.
292/// This is an O(n) operation.
293impl From<&[CuTime]> for CuTimeRange {
294    fn from(slice: &[CuTime]) -> Self {
295        CuTimeRange {
296            start: *slice.iter().min().expect("Empty slice"),
297            end: *slice.iter().max().expect("Empty slice"),
298        }
299    }
300}
301
302/// Represents a time range with possible undefined start or end or both.
303#[derive(Default, Copy, Clone, Debug, Encode, Decode, Serialize, Deserialize)]
304pub struct PartialCuTimeRange {
305    pub start: OptionCuTime,
306    pub end: OptionCuTime,
307}
308
309/// The time of validity of a message can be more than one time but can be a time range of Tovs.
310/// For example a sub scan for a lidar, a set of images etc... can have a range of validity.
311#[derive(Default, Clone, Debug, PartialEq, Encode, Decode, Serialize, Deserialize, Copy)]
312pub enum Tov {
313    #[default]
314    None,
315    Time(CuTime),
316    Range(CuTimeRange),
317}
318
319impl From<Option<CuDuration>> for Tov {
320    fn from(duration: Option<CuDuration>) -> Self {
321        match duration {
322            Some(duration) => Tov::Time(duration),
323            None => Tov::None,
324        }
325    }
326}
327
328impl From<CuDuration> for Tov {
329    fn from(duration: CuDuration) -> Self {
330        Tov::Time(duration)
331    }
332}
333
334/// A running Robot clock.
335/// The clock is a monotonic clock that starts at an arbitrary reference time.
336/// It is clone resilient, ie a clone will be the same clock, even when mocked.
337#[derive(Clone, Debug)]
338pub struct RobotClock {
339    inner: Clock,      // This is a wrapper on quanta::Clock today.
340    ref_time: Instant, // The reference instant on which this clock is based.
341}
342
343/// A mock clock that can be controlled by the user.
344#[derive(Debug, Clone)]
345pub struct RobotClockMock(Arc<Mock>); // wraps the Mock from quanta today.
346
347impl RobotClockMock {
348    pub fn increment(&self, amount: Duration) {
349        let Self(mock) = self;
350        mock.increment(amount);
351    }
352
353    /// Decrements the time by the given amount.
354    /// Be careful this brakes the monotonicity of the clock.
355    pub fn decrement(&self, amount: Duration) {
356        let Self(mock) = self;
357        mock.decrement(amount);
358    }
359
360    /// Gets the current value of time.
361    pub fn value(&self) -> u64 {
362        let Self(mock) = self;
363        mock.value()
364    }
365
366    /// A convenient way to get the current time from the mocking side.
367    pub fn now(&self) -> CuTime {
368        let Self(mock) = self;
369        mock.value().into()
370    }
371
372    /// Sets the absolute value of the time.
373    pub fn set_value(&self, value: u64) {
374        let Self(mock) = self;
375        let v = mock.value();
376        // had to work around the quata API here.
377        if v < value {
378            self.increment(Duration::from_nanos(value) - Duration::from_nanos(v));
379        } else {
380            self.decrement(Duration::from_nanos(v) - Duration::from_nanos(value));
381        }
382    }
383}
384
385impl RobotClock {
386    /// Creates a RobotClock using now as its reference time.
387    /// It will start a 0ns incrementing monotonically.
388    pub fn new() -> Self {
389        let clock = Clock::new();
390        let ref_time = clock.now();
391        RobotClock {
392            inner: clock,
393            ref_time,
394        }
395    }
396
397    /// Builds a monotonic clock starting at the given reference time.
398    pub fn from_ref_time(ref_time_ns: u64) -> Self {
399        let clock = Clock::new();
400        let ref_time = clock.now() - Duration::from_nanos(ref_time_ns);
401        RobotClock {
402            inner: Clock::new(),
403            ref_time,
404        }
405    }
406
407    /// Build a fake clock with a reference time of 0.
408    /// The RobotMock interface enables you to control all the clones of the clock given.
409    pub fn mock() -> (Self, RobotClockMock) {
410        let (clock, mock) = Clock::mock();
411        let ref_time = clock.now();
412        (
413            RobotClock {
414                inner: clock,
415                ref_time,
416            },
417            RobotClockMock(mock),
418        )
419    }
420
421    // Now returns the time that passed since the reference time, usually the start time.
422    // It is a monotonically increasing value.
423    #[inline]
424    pub fn now(&self) -> CuTime {
425        // TODO: this could be further optimized to avoid this constant conversion from 2 fields to one under the hood.
426        // Let's say this is the default implementation.
427        (self.inner.now() - self.ref_time).into()
428    }
429
430    // A less precise but quicker time
431    #[inline]
432    pub fn recent(&self) -> CuTime {
433        (self.inner.recent() - self.ref_time).into()
434    }
435}
436
437impl Default for RobotClock {
438    fn default() -> Self {
439        Self::new()
440    }
441}
442
443/// A trait to provide a clock to the runtime.
444pub trait ClockProvider {
445    fn get_clock(&self) -> RobotClock;
446}
447
448#[cfg(test)]
449mod tests {
450    use super::*;
451    #[test]
452    fn test_cuduration_comparison_operators() {
453        let a = CuDuration(100);
454        let b = CuDuration(200);
455
456        assert!(a < b);
457        assert!(b > a);
458        assert_ne!(a, b);
459        assert_eq!(a, CuDuration(100));
460    }
461
462    #[test]
463    fn test_cuduration_arithmetic_operations() {
464        let a = CuDuration(100);
465        let b = CuDuration(50);
466
467        assert_eq!(a + b, CuDuration(150));
468        assert_eq!(a - b, CuDuration(50));
469        assert_eq!(a * 2u32, CuDuration(200));
470        assert_eq!(a / 2u32, CuDuration(50));
471    }
472
473    #[test]
474    fn test_option_cutime_handling() {
475        let some_time = OptionCuTime::from(Some(CuTime::from(100u64)));
476        let none_time = OptionCuTime::none();
477
478        assert!(!some_time.is_none());
479        assert!(none_time.is_none());
480        assert_eq!(some_time.unwrap(), CuTime::from(100u64));
481        assert_eq!(
482            Option::<CuTime>::from(some_time),
483            Some(CuTime::from(100u64))
484        );
485        assert_eq!(Option::<CuTime>::from(none_time), None);
486    }
487
488    #[test]
489    fn test_mock() {
490        let (clock, mock) = RobotClock::mock();
491        assert_eq!(clock.now(), Duration::from_secs(0).into());
492        mock.increment(Duration::from_secs(1));
493        assert_eq!(clock.now(), Duration::from_secs(1).into());
494    }
495
496    #[test]
497    fn test_mock_clone() {
498        let (clock, mock) = RobotClock::mock();
499        assert_eq!(clock.now(), Duration::from_secs(0).into());
500        let clock_clone = clock.clone();
501        mock.increment(Duration::from_secs(1));
502        assert_eq!(clock_clone.now(), Duration::from_secs(1).into());
503    }
504
505    #[test]
506    fn test_robot_clock_synchronization() {
507        // Test that multiple clocks created from the same mock stay synchronized
508        let (clock1, mock) = RobotClock::mock();
509        let clock2 = clock1.clone();
510
511        assert_eq!(clock1.now(), CuDuration(0));
512        assert_eq!(clock2.now(), CuDuration(0));
513
514        mock.increment(Duration::from_secs(5));
515
516        assert_eq!(clock1.now(), Duration::from_secs(5).into());
517        assert_eq!(clock2.now(), Duration::from_secs(5).into());
518    }
519
520    #[test]
521    fn test_from_ref_time() {
522        let tolerance_ms = 10;
523        let clock = RobotClock::from_ref_time(1_000_000_000);
524        assert_relative_eq!(
525            <CuDuration as Into<Duration>>::into(clock.now()).as_millis() as f64,
526            Duration::from_secs(1).as_millis() as f64,
527            epsilon = tolerance_ms as f64
528        );
529    }
530
531    #[test]
532    fn longest_duration() {
533        let maxcu = CuDuration(u64::MAX);
534        let maxd: Duration = maxcu.into();
535        assert_eq!(maxd.as_nanos(), u64::MAX as u128);
536        let s = maxd.as_secs();
537        let y = s / 60 / 60 / 24 / 365;
538        assert!(y >= 584); // 584 years of robot uptime, we should be good.
539    }
540
541    #[test]
542    fn test_some_time_arithmetics() {
543        let a: CuDuration = 10.into();
544        let b: CuDuration = 20.into();
545        let c = a + b;
546        assert_eq!(c.0, 30);
547        let d = b - a;
548        assert_eq!(d.0, 10);
549    }
550
551    #[test]
552    fn test_build_range_from_slice() {
553        let range = CuTimeRange::from(&[20.into(), 10.into(), 30.into()][..]);
554        assert_eq!(range.start, 10.into());
555        assert_eq!(range.end, 30.into());
556    }
557
558    #[test]
559    fn test_time_range_operations() {
560        // Test creating a time range and checking its properties
561        let start = CuTime::from(100u64);
562        let end = CuTime::from(200u64);
563        let range = CuTimeRange { start, end };
564
565        assert_eq!(range.start, start);
566        assert_eq!(range.end, end);
567
568        // Test creating from a slice
569        let times = [
570            CuTime::from(150u64),
571            CuTime::from(120u64),
572            CuTime::from(180u64),
573        ];
574        let range_from_slice = CuTimeRange::from(&times[..]);
575
576        // Range should capture min and max values
577        assert_eq!(range_from_slice.start, CuTime::from(120u64));
578        assert_eq!(range_from_slice.end, CuTime::from(180u64));
579    }
580
581    #[test]
582    fn test_partial_time_range() {
583        // Test creating a partial time range with defined start/end
584        let start = CuTime::from(100u64);
585        let end = CuTime::from(200u64);
586
587        let partial_range = PartialCuTimeRange {
588            start: OptionCuTime::from(start),
589            end: OptionCuTime::from(end),
590        };
591
592        // Test converting to Option
593        let opt_start: Option<CuTime> = partial_range.start.into();
594        let opt_end: Option<CuTime> = partial_range.end.into();
595
596        assert_eq!(opt_start, Some(start));
597        assert_eq!(opt_end, Some(end));
598
599        // Test partial range with undefined values
600        let partial_undefined = PartialCuTimeRange::default();
601        assert!(partial_undefined.start.is_none());
602        assert!(partial_undefined.end.is_none());
603    }
604
605    #[test]
606    fn test_tov_conversions() {
607        // Test different Time of Validity (Tov) variants
608        let time = CuTime::from(100u64);
609
610        // Test conversion from CuTime
611        let tov_time: Tov = time.into();
612        assert!(matches!(tov_time, Tov::Time(_)));
613
614        if let Tov::Time(t) = tov_time {
615            assert_eq!(t, time);
616        }
617
618        // Test conversion from Option<CuTime>
619        let some_time = Some(time);
620        let tov_some: Tov = some_time.into();
621        assert!(matches!(tov_some, Tov::Time(_)));
622
623        let none_time: Option<CuDuration> = None;
624        let tov_none: Tov = none_time.into();
625        assert!(matches!(tov_none, Tov::None));
626
627        // Test range
628        let start = CuTime::from(100u64);
629        let end = CuTime::from(200u64);
630        let range = CuTimeRange { start, end };
631        let tov_range = Tov::Range(range);
632
633        assert!(matches!(tov_range, Tov::Range(_)));
634    }
635
636    #[test]
637    fn test_cuduration_display() {
638        // Test the display implementation for different magnitudes
639        let nano = CuDuration(42);
640        assert_eq!(nano.to_string(), "42 ns");
641
642        let micro = CuDuration(42_000);
643        assert_eq!(micro.to_string(), "42.000 µs");
644
645        let milli = CuDuration(42_000_000);
646        assert_eq!(milli.to_string(), "42.000 ms");
647
648        let sec = CuDuration(1_500_000_000);
649        assert_eq!(sec.to_string(), "1.500 s");
650
651        let min = CuDuration(90_000_000_000);
652        assert_eq!(min.to_string(), "1.500 m");
653
654        let hour = CuDuration(3_600_000_000_000);
655        assert_eq!(hour.to_string(), "1.000 h");
656
657        let day = CuDuration(86_400_000_000_000);
658        assert_eq!(day.to_string(), "1.000 d");
659    }
660
661    #[test]
662    fn test_robot_clock_precision() {
663        // Test that RobotClock::now() and RobotClock::recent() return different values
664        // and that recent() is always <= now()
665        let clock = RobotClock::new();
666
667        // We can't guarantee the exact values, but we can check relationships
668        let recent = clock.recent();
669        let now = clock.now();
670
671        // recent() should be less than or equal to now()
672        assert!(recent <= now);
673
674        // Test precision of from_ref_time
675        let ref_time_ns = 1_000_000_000; // 1 second
676        let clock = RobotClock::from_ref_time(ref_time_ns);
677
678        // Clock should start at approximately ref_time_ns
679        let now = clock.now();
680        let now_ns: u64 = now.into();
681
682        // Allow reasonable tolerance for clock initialization time
683        let tolerance_ns = 50_000_000; // 50ms tolerance
684        assert!(now_ns >= ref_time_ns);
685        assert!(now_ns < ref_time_ns + tolerance_ns);
686    }
687
688    #[test]
689    fn test_mock_clock_advanced_operations() {
690        // Test more complex operations with the mock clock
691        let (clock, mock) = RobotClock::mock();
692
693        // Test initial state
694        assert_eq!(clock.now(), CuDuration(0));
695
696        // Test increment
697        mock.increment(Duration::from_secs(10));
698        assert_eq!(clock.now(), Duration::from_secs(10).into());
699
700        // Test decrement (unusual but supported)
701        mock.decrement(Duration::from_secs(5));
702        assert_eq!(clock.now(), Duration::from_secs(5).into());
703
704        // Test setting absolute value
705        mock.set_value(30_000_000_000); // 30 seconds in ns
706        assert_eq!(clock.now(), Duration::from_secs(30).into());
707
708        // Test that getting the time from the mock directly works
709        assert_eq!(mock.now(), Duration::from_secs(30).into());
710        assert_eq!(mock.value(), 30_000_000_000);
711    }
712
713    #[test]
714    fn test_cuduration_min_max() {
715        // Test MIN and MAX constants
716        assert_eq!(CuDuration::MIN, CuDuration(0));
717
718        // Test min/max methods
719        let a = CuDuration(100);
720        let b = CuDuration(200);
721
722        assert_eq!(a.min(b), a);
723        assert_eq!(a.max(b), b);
724        assert_eq!(b.min(a), a);
725        assert_eq!(b.max(a), b);
726
727        // Edge cases
728        assert_eq!(a.min(a), a);
729        assert_eq!(a.max(a), a);
730
731        // Test with MIN/MAX constants
732        assert_eq!(a.min(CuDuration::MIN), CuDuration::MIN);
733        assert_eq!(a.max(CuDuration::MAX), CuDuration::MAX);
734    }
735
736    #[test]
737    fn test_clock_provider_trait() {
738        // Test implementing the ClockProvider trait
739        struct TestClockProvider {
740            clock: RobotClock,
741        }
742
743        impl ClockProvider for TestClockProvider {
744            fn get_clock(&self) -> RobotClock {
745                self.clock.clone()
746            }
747        }
748
749        // Create a provider with a mock clock
750        let (clock, mock) = RobotClock::mock();
751        let provider = TestClockProvider { clock };
752
753        // Test that provider returns a clock synchronized with the original
754        let provider_clock = provider.get_clock();
755        assert_eq!(provider_clock.now(), CuDuration(0));
756
757        // Advance the mock clock and check that provider's clock also advances
758        mock.increment(Duration::from_secs(5));
759        assert_eq!(provider_clock.now(), Duration::from_secs(5).into());
760    }
761}