1#![cfg_attr(not(feature = "std"), no_std)]
2
3extern crate alloc;
4#[cfg(test)]
5extern crate approx;
6
7mod calibration;
8#[cfg_attr(target_arch = "aarch64", path = "aarch64.rs")]
9#[cfg_attr(all(target_os = "none", target_arch = "arm"), path = "cortexm.rs")]
10#[cfg_attr(target_arch = "riscv64", path = "riscv64.rs")]
11#[cfg_attr(target_arch = "x86_64", path = "x86_64.rs")]
12#[cfg_attr(
13 not(any(
14 target_arch = "x86_64",
15 target_arch = "aarch64",
16 all(target_os = "none", target_arch = "arm"),
17 target_arch = "riscv64"
18 )),
19 path = "fallback.rs"
20)]
21mod raw_counter;
22
23pub use raw_counter::*;
24
25use bincode::BorrowDecode;
26use bincode::de::BorrowDecoder;
27use bincode::de::Decoder;
28use bincode::enc::Encoder;
29use bincode::error::{DecodeError, EncodeError};
30use bincode::{Decode, Encode};
31use core::ops::{Add, Sub};
32use serde::{Deserialize, Serialize};
33
34use portable_atomic::{AtomicU64, Ordering};
36
37use alloc::format;
38use alloc::sync::Arc;
39use core::fmt::{Display, Formatter};
40use core::ops::{AddAssign, Div, Mul, SubAssign};
41
42#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
44pub struct CuInstant(u64);
45
46pub type Instant = CuInstant; impl CuInstant {
49 pub fn now() -> Self {
50 CuInstant(calibration::counter_to_nanos(read_raw_counter))
51 }
52
53 pub fn as_nanos(&self) -> u64 {
54 self.0
55 }
56}
57
58impl Sub for CuInstant {
59 type Output = CuDuration;
60
61 fn sub(self, other: CuInstant) -> CuDuration {
62 CuDuration(self.0.saturating_sub(other.0))
63 }
64}
65
66impl Sub<CuDuration> for CuInstant {
67 type Output = CuInstant;
68
69 fn sub(self, duration: CuDuration) -> CuInstant {
70 CuInstant(self.0.saturating_sub(duration.as_nanos()))
71 }
72}
73
74impl Add<CuDuration> for CuInstant {
75 type Output = CuInstant;
76
77 fn add(self, duration: CuDuration) -> CuInstant {
78 CuInstant(self.0.saturating_add(duration.as_nanos()))
79 }
80}
81
82#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default)]
85pub struct CuDuration(pub u64);
86
87impl CuDuration {
88 pub const MIN: CuDuration = CuDuration(0u64);
90 pub const MAX: CuDuration = CuDuration(NONE_VALUE - 1);
92
93 pub fn max(self, other: CuDuration) -> CuDuration {
94 let Self(lhs) = self;
95 let Self(rhs) = other;
96 CuDuration(lhs.max(rhs))
97 }
98
99 pub fn min(self, other: CuDuration) -> CuDuration {
100 let Self(lhs) = self;
101 let Self(rhs) = other;
102 CuDuration(lhs.min(rhs))
103 }
104
105 pub fn as_nanos(&self) -> u64 {
106 let Self(nanos) = self;
107 *nanos
108 }
109
110 pub fn as_micros(&self) -> u64 {
111 let Self(nanos) = self;
112 nanos / 1_000
113 }
114
115 pub fn as_millis(&self) -> u64 {
116 let Self(nanos) = self;
117 nanos / 1_000_000
118 }
119
120 pub fn as_secs(&self) -> u64 {
121 let Self(nanos) = self;
122 nanos / 1_000_000_000
123 }
124
125 pub fn from_nanos(nanos: u64) -> Self {
126 CuDuration(nanos)
127 }
128
129 pub fn from_micros(micros: u64) -> Self {
130 CuDuration(micros * 1_000)
131 }
132
133 pub fn from_millis(millis: u64) -> Self {
134 CuDuration(millis * 1_000_000)
135 }
136
137 pub fn from_secs(secs: u64) -> Self {
138 CuDuration(secs * 1_000_000_000)
139 }
140}
141
142pub trait SaturatingSub {
144 fn saturating_sub(self, other: Self) -> Self;
145}
146
147impl SaturatingSub for CuDuration {
148 fn saturating_sub(self, other: Self) -> Self {
149 let Self(lhs) = self;
150 let Self(rhs) = other;
151 CuDuration(lhs.saturating_sub(rhs))
152 }
153}
154
155#[cfg(feature = "std")]
157impl From<std::time::Duration> for CuDuration {
158 fn from(duration: std::time::Duration) -> Self {
159 CuDuration(duration.as_nanos() as u64)
160 }
161}
162
163#[cfg(not(feature = "std"))]
164impl From<core::time::Duration> for CuDuration {
165 fn from(duration: core::time::Duration) -> Self {
166 CuDuration(duration.as_nanos() as u64)
167 }
168}
169
170#[cfg(feature = "std")]
171impl From<CuDuration> for std::time::Duration {
172 fn from(val: CuDuration) -> Self {
173 let CuDuration(nanos) = val;
174 std::time::Duration::from_nanos(nanos)
175 }
176}
177
178impl From<u64> for CuDuration {
179 fn from(duration: u64) -> Self {
180 CuDuration(duration)
181 }
182}
183
184impl From<CuDuration> for u64 {
185 fn from(val: CuDuration) -> Self {
186 let CuDuration(nanos) = val;
187 nanos
188 }
189}
190
191impl Sub for CuDuration {
192 type Output = Self;
193
194 fn sub(self, rhs: Self) -> Self::Output {
195 let CuDuration(lhs) = self;
196 let CuDuration(rhs) = rhs;
197 CuDuration(lhs - rhs)
198 }
199}
200
201impl Add for CuDuration {
202 type Output = Self;
203
204 fn add(self, rhs: Self) -> Self::Output {
205 let CuDuration(lhs) = self;
206 let CuDuration(rhs) = rhs;
207 CuDuration(lhs + rhs)
208 }
209}
210
211impl AddAssign for CuDuration {
212 fn add_assign(&mut self, rhs: Self) {
213 let CuDuration(lhs) = self;
214 let CuDuration(rhs) = rhs;
215 *lhs += rhs;
216 }
217}
218
219impl SubAssign for CuDuration {
220 fn sub_assign(&mut self, rhs: Self) {
221 let CuDuration(lhs) = self;
222 let CuDuration(rhs) = rhs;
223 *lhs -= rhs;
224 }
225}
226
227impl<T> Div<T> for CuDuration
230where
231 T: Into<u64>,
232{
233 type Output = Self;
234 fn div(self, rhs: T) -> Self {
235 let CuDuration(lhs) = self;
236 CuDuration(lhs / rhs.into())
237 }
238}
239
240impl<T> Mul<T> for CuDuration
244where
245 T: Into<u64>,
246{
247 type Output = CuDuration;
248
249 fn mul(self, rhs: T) -> CuDuration {
250 let CuDuration(lhs) = self;
251 CuDuration(lhs * rhs.into())
252 }
253}
254
255impl Mul<CuDuration> for u64 {
257 type Output = CuDuration;
258
259 fn mul(self, rhs: CuDuration) -> CuDuration {
260 let CuDuration(nanos) = rhs;
261 CuDuration(self * nanos)
262 }
263}
264
265impl Mul<CuDuration> for u32 {
267 type Output = CuDuration;
268
269 fn mul(self, rhs: CuDuration) -> CuDuration {
270 let CuDuration(nanos) = rhs;
271 CuDuration(self as u64 * nanos)
272 }
273}
274
275impl Mul<CuDuration> for i32 {
277 type Output = CuDuration;
278
279 fn mul(self, rhs: CuDuration) -> CuDuration {
280 let CuDuration(nanos) = rhs;
281 CuDuration(self as u64 * nanos)
282 }
283}
284
285impl Encode for CuDuration {
286 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
287 let CuDuration(nanos) = self;
288 nanos.encode(encoder)
289 }
290}
291
292impl<Context> Decode<Context> for CuDuration {
293 fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
294 Ok(CuDuration(u64::decode(decoder)?))
295 }
296}
297
298impl<'de, Context> BorrowDecode<'de, Context> for CuDuration {
299 fn borrow_decode<D: BorrowDecoder<'de>>(decoder: &mut D) -> Result<Self, DecodeError> {
300 Ok(CuDuration(u64::decode(decoder)?))
301 }
302}
303
304impl Display for CuDuration {
305 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
306 let Self(nanos) = *self;
307 if nanos >= 86_400_000_000_000 {
308 write!(f, "{:.3} d", nanos as f64 / 86_400_000_000_000.0)
309 } else if nanos >= 3_600_000_000_000 {
310 write!(f, "{:.3} h", nanos as f64 / 3_600_000_000_000.0)
311 } else if nanos >= 60_000_000_000 {
312 write!(f, "{:.3} m", nanos as f64 / 60_000_000_000.0)
313 } else if nanos >= 1_000_000_000 {
314 write!(f, "{:.3} s", nanos as f64 / 1_000_000_000.0)
315 } else if nanos >= 1_000_000 {
316 write!(f, "{:.3} ms", nanos as f64 / 1_000_000.0)
317 } else if nanos >= 1_000 {
318 write!(f, "{:.3} µs", nanos as f64 / 1_000.0)
319 } else {
320 write!(f, "{nanos} ns")
321 }
322 }
323}
324
325pub type CuTime = CuDuration;
327
328#[inline(always)]
331pub fn busy_wait_for(duration: CuDuration) {
332 busy_wait_until(CuInstant::now() + duration);
333}
334
335#[inline(always)]
338pub fn busy_wait_until(time: CuInstant) {
339 while CuInstant::now() < time {
340 core::hint::spin_loop();
341 }
342}
343
344#[derive(Copy, Clone, Debug, PartialEq, Encode, Decode, Serialize, Deserialize)]
346pub struct OptionCuTime(CuTime);
347
348const NONE_VALUE: u64 = 0xFFFFFFFFFFFFFFFF;
349
350impl OptionCuTime {
351 #[inline]
352 pub fn is_none(&self) -> bool {
353 let Self(CuDuration(nanos)) = self;
354 *nanos == NONE_VALUE
355 }
356
357 #[inline]
358 pub const fn none() -> Self {
359 OptionCuTime(CuDuration(NONE_VALUE))
360 }
361
362 #[inline]
363 pub fn unwrap(self) -> CuTime {
364 if self.is_none() {
365 panic!("called `OptionCuTime::unwrap()` on a `None` value");
366 }
367 self.0
368 }
369}
370
371impl Display for OptionCuTime {
372 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
373 if self.is_none() {
374 write!(f, "None")
375 } else {
376 write!(f, "{}", self.0)
377 }
378 }
379}
380
381impl Default for OptionCuTime {
382 fn default() -> Self {
383 Self::none()
384 }
385}
386
387impl From<Option<CuTime>> for OptionCuTime {
388 #[inline]
389 fn from(duration: Option<CuTime>) -> Self {
390 match duration {
391 Some(duration) => OptionCuTime(duration),
392 None => OptionCuTime(CuDuration(NONE_VALUE)),
393 }
394 }
395}
396
397impl From<OptionCuTime> for Option<CuTime> {
398 #[inline]
399 fn from(val: OptionCuTime) -> Self {
400 let OptionCuTime(CuDuration(nanos)) = val;
401 if nanos == NONE_VALUE {
402 None
403 } else {
404 Some(CuDuration(nanos))
405 }
406 }
407}
408
409impl From<CuTime> for OptionCuTime {
410 #[inline]
411 fn from(val: CuTime) -> Self {
412 Some(val).into()
413 }
414}
415
416#[derive(Copy, Clone, Debug, Encode, Decode, Serialize, Deserialize, PartialEq)]
418pub struct CuTimeRange {
419 pub start: CuTime,
420 pub end: CuTime,
421}
422
423impl From<&[CuTime]> for CuTimeRange {
426 fn from(slice: &[CuTime]) -> Self {
427 CuTimeRange {
428 start: *slice.iter().min().expect("Empty slice"),
429 end: *slice.iter().max().expect("Empty slice"),
430 }
431 }
432}
433
434#[derive(Default, Copy, Clone, Debug, Encode, Decode, Serialize, Deserialize)]
436pub struct PartialCuTimeRange {
437 pub start: OptionCuTime,
438 pub end: OptionCuTime,
439}
440
441impl Display for PartialCuTimeRange {
442 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
443 let start = if self.start.is_none() {
444 "…"
445 } else {
446 &format!("{}", self.start)
447 };
448 let end = if self.end.is_none() {
449 "…"
450 } else {
451 &format!("{}", self.end)
452 };
453 write!(f, "[{start} – {end}]")
454 }
455}
456
457#[derive(Default, Clone, Debug, PartialEq, Encode, Decode, Serialize, Deserialize, Copy)]
460pub enum Tov {
461 #[default]
462 None,
463 Time(CuTime),
464 Range(CuTimeRange),
465}
466
467impl Display for Tov {
468 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
469 match self {
470 Tov::None => write!(f, "None"),
471 Tov::Time(t) => write!(f, "{t}"),
472 Tov::Range(r) => write!(f, "[{} – {}]", r.start, r.end),
473 }
474 }
475}
476
477impl From<Option<CuDuration>> for Tov {
478 fn from(duration: Option<CuDuration>) -> Self {
479 match duration {
480 Some(duration) => Tov::Time(duration),
481 None => Tov::None,
482 }
483 }
484}
485
486impl From<CuDuration> for Tov {
487 fn from(duration: CuDuration) -> Self {
488 Tov::Time(duration)
489 }
490}
491
492#[derive(Clone, Debug)]
494struct InternalClock {
495 mock_state: Option<Arc<AtomicU64>>,
498}
499
500#[cfg(feature = "std")]
502#[inline(always)]
503fn read_rtc_ns() -> u64 {
504 std::time::SystemTime::now()
505 .duration_since(std::time::UNIX_EPOCH)
506 .unwrap()
507 .as_nanos() as u64
508}
509
510#[cfg(feature = "std")]
511#[inline(always)]
512fn sleep_ns(ns: u64) {
513 std::thread::sleep(std::time::Duration::from_nanos(ns));
514}
515
516impl InternalClock {
517 fn new(
518 read_rtc_ns: impl Fn() -> u64 + Send + Sync + 'static,
519 sleep_ns: impl Fn(u64) + Send + Sync + 'static,
520 ) -> Self {
521 initialize();
522
523 calibration::calibrate(read_raw_counter, read_rtc_ns, sleep_ns);
525 InternalClock { mock_state: None }
526 }
527
528 fn mock() -> (Self, Arc<AtomicU64>) {
529 let mock_state = Arc::new(AtomicU64::new(0));
530 let clock = InternalClock {
531 mock_state: Some(Arc::clone(&mock_state)),
532 };
533 (clock, mock_state)
534 }
535
536 fn now(&self) -> CuInstant {
537 if let Some(ref mock_state) = self.mock_state {
538 CuInstant(mock_state.load(Ordering::Relaxed))
539 } else {
540 CuInstant::now()
541 }
542 }
543
544 fn recent(&self) -> CuInstant {
545 self.now()
548 }
549}
550
551#[derive(Clone, Debug)]
555pub struct RobotClock {
556 inner: InternalClock,
557 ref_time: CuInstant,
558}
559
560#[derive(Debug, Clone)]
562pub struct RobotClockMock(Arc<AtomicU64>);
563
564impl RobotClockMock {
565 pub fn increment(&self, amount: CuDuration) {
566 let Self(mock_state) = self;
567 mock_state.fetch_add(amount.as_nanos(), Ordering::Relaxed);
568 }
569
570 pub fn decrement(&self, amount: CuDuration) {
573 let Self(mock_state) = self;
574 mock_state.fetch_sub(amount.as_nanos(), Ordering::Relaxed);
575 }
576
577 pub fn value(&self) -> u64 {
579 let Self(mock_state) = self;
580 mock_state.load(Ordering::Relaxed)
581 }
582
583 pub fn now(&self) -> CuTime {
585 let Self(mock_state) = self;
586 CuDuration(mock_state.load(Ordering::Relaxed))
587 }
588
589 pub fn set_value(&self, value: u64) {
591 let Self(mock_state) = self;
592 mock_state.store(value, Ordering::Relaxed);
593 }
594}
595
596impl RobotClock {
597 #[cfg(feature = "std")]
601 pub fn new() -> Self {
602 let clock = InternalClock::new(read_rtc_ns, sleep_ns);
603 let ref_time = clock.now();
604 RobotClock {
605 inner: clock,
606 ref_time,
607 }
608 }
609
610 pub fn new_with_rtc(
613 read_rtc_ns: impl Fn() -> u64 + Send + Sync + 'static,
614 sleep_ns: impl Fn(u64) + Send + Sync + 'static,
615 ) -> Self {
616 let clock = InternalClock::new(read_rtc_ns, sleep_ns);
617 let ref_time = clock.now();
618 RobotClock {
619 inner: clock,
620 ref_time,
621 }
622 }
623
624 #[cfg(feature = "std")]
626 pub fn from_ref_time(ref_time_ns: u64) -> Self {
627 let clock = InternalClock::new(read_rtc_ns, sleep_ns);
628 let ref_time = clock.now() - CuDuration(ref_time_ns);
629 RobotClock {
630 inner: clock,
631 ref_time,
632 }
633 }
634
635 pub fn from_ref_time_with_rtc(
637 read_rtc_ns: fn() -> u64,
638 sleep_ns: fn(u64),
639 ref_time_ns: u64,
640 ) -> Self {
641 let clock = InternalClock::new(read_rtc_ns, sleep_ns);
642 let ref_time = clock.now() - CuDuration(ref_time_ns);
643 RobotClock {
644 inner: clock,
645 ref_time,
646 }
647 }
648
649 pub fn mock() -> (Self, RobotClockMock) {
652 let (clock, mock_state) = InternalClock::mock();
653 let ref_time = clock.now();
654 (
655 RobotClock {
656 inner: clock,
657 ref_time,
658 },
659 RobotClockMock(mock_state),
660 )
661 }
662
663 #[inline]
666 pub fn now(&self) -> CuTime {
667 self.inner.now() - self.ref_time
668 }
669
670 #[inline]
672 pub fn recent(&self) -> CuTime {
673 self.inner.recent() - self.ref_time
674 }
675}
676
677#[cfg(feature = "std")]
680impl Default for RobotClock {
681 fn default() -> Self {
682 Self::new()
683 }
684}
685
686pub trait ClockProvider {
688 fn get_clock(&self) -> RobotClock;
689}
690
691#[cfg(test)]
692mod tests {
693 use super::*;
694 use approx::assert_relative_eq;
695
696 #[test]
697 fn test_cuduration_comparison_operators() {
698 let a = CuDuration(100);
699 let b = CuDuration(200);
700
701 assert!(a < b);
702 assert!(b > a);
703 assert_ne!(a, b);
704 assert_eq!(a, CuDuration(100));
705 }
706
707 #[test]
708 fn test_cuduration_arithmetic_operations() {
709 let a = CuDuration(100);
710 let b = CuDuration(50);
711
712 assert_eq!(a + b, CuDuration(150));
713 assert_eq!(a - b, CuDuration(50));
714 assert_eq!(a * 2u32, CuDuration(200));
715 assert_eq!(a / 2u32, CuDuration(50));
716 }
717
718 #[test]
719 fn test_robot_clock_monotonic() {
720 let clock = RobotClock::new();
721 let t1 = clock.now();
722 let t2 = clock.now();
723 assert!(t2 >= t1);
724 }
725
726 #[test]
727 fn test_robot_clock_mock() {
728 let (clock, mock) = RobotClock::mock();
729 let t1 = clock.now();
730 mock.increment(CuDuration::from_millis(100));
731 let t2 = clock.now();
732 assert!(t2 > t1);
733 assert_eq!(t2 - t1, CuDuration(100_000_000)); }
735
736 #[test]
737 fn test_robot_clock_clone_consistency() {
738 let (clock1, mock) = RobotClock::mock();
739 let clock2 = clock1.clone();
740
741 mock.set_value(1_000_000_000); assert_eq!(clock1.now(), clock2.now());
743 }
744
745 #[test]
746 fn test_from_ref_time() {
747 let tolerance_ms = 10f64;
748 let clock = RobotClock::from_ref_time(1_000_000_000);
749 assert_relative_eq!(
750 clock.now().as_millis() as f64,
751 CuDuration::from_secs(1).as_millis() as f64,
752 epsilon = tolerance_ms
753 );
754 }
755
756 #[test]
757 fn longest_duration() {
758 let maxcu = CuDuration(u64::MAX);
759 assert_eq!(maxcu.as_nanos(), u64::MAX);
760 let s = maxcu.as_secs();
761 let y = s / 60 / 60 / 24 / 365;
762 assert!(y >= 584); }
764
765 #[test]
766 fn test_some_time_arithmetics() {
767 let a: CuDuration = 10.into();
768 let b: CuDuration = 20.into();
769 let c = a + b;
770 assert_eq!(c.0, 30);
771 let d = b - a;
772 assert_eq!(d.0, 10);
773 }
774
775 #[test]
776 fn test_build_range_from_slice() {
777 let range = CuTimeRange::from(&[20.into(), 10.into(), 30.into()][..]);
778 assert_eq!(range.start, 10.into());
779 assert_eq!(range.end, 30.into());
780 }
781
782 #[test]
783 fn test_time_range_operations() {
784 let start = CuTime::from(100u64);
786 let end = CuTime::from(200u64);
787 let range = CuTimeRange { start, end };
788
789 assert_eq!(range.start, start);
790 assert_eq!(range.end, end);
791
792 let times = [
794 CuTime::from(150u64),
795 CuTime::from(120u64),
796 CuTime::from(180u64),
797 ];
798 let range_from_slice = CuTimeRange::from(×[..]);
799
800 assert_eq!(range_from_slice.start, CuTime::from(120u64));
802 assert_eq!(range_from_slice.end, CuTime::from(180u64));
803 }
804
805 #[test]
806 fn test_partial_time_range() {
807 let start = CuTime::from(100u64);
809 let end = CuTime::from(200u64);
810
811 let partial_range = PartialCuTimeRange {
812 start: OptionCuTime::from(start),
813 end: OptionCuTime::from(end),
814 };
815
816 let opt_start: Option<CuTime> = partial_range.start.into();
818 let opt_end: Option<CuTime> = partial_range.end.into();
819
820 assert_eq!(opt_start, Some(start));
821 assert_eq!(opt_end, Some(end));
822
823 let partial_undefined = PartialCuTimeRange::default();
825 assert!(partial_undefined.start.is_none());
826 assert!(partial_undefined.end.is_none());
827 }
828
829 #[test]
830 fn test_tov_conversions() {
831 let time = CuTime::from(100u64);
833
834 let tov_time: Tov = time.into();
836 assert!(matches!(tov_time, Tov::Time(_)));
837
838 if let Tov::Time(t) = tov_time {
839 assert_eq!(t, time);
840 }
841
842 let some_time = Some(time);
844 let tov_some: Tov = some_time.into();
845 assert!(matches!(tov_some, Tov::Time(_)));
846
847 let none_time: Option<CuDuration> = None;
848 let tov_none: Tov = none_time.into();
849 assert!(matches!(tov_none, Tov::None));
850
851 let start = CuTime::from(100u64);
853 let end = CuTime::from(200u64);
854 let range = CuTimeRange { start, end };
855 let tov_range = Tov::Range(range);
856
857 assert!(matches!(tov_range, Tov::Range(_)));
858 }
859
860 #[cfg(feature = "std")]
861 #[test]
862 fn test_cuduration_display() {
863 let nano = CuDuration(42);
865 assert_eq!(nano.to_string(), "42 ns");
866
867 let micro = CuDuration(42_000);
868 assert_eq!(micro.to_string(), "42.000 µs");
869
870 let milli = CuDuration(42_000_000);
871 assert_eq!(milli.to_string(), "42.000 ms");
872
873 let sec = CuDuration(1_500_000_000);
874 assert_eq!(sec.to_string(), "1.500 s");
875
876 let min = CuDuration(90_000_000_000);
877 assert_eq!(min.to_string(), "1.500 m");
878
879 let hour = CuDuration(3_600_000_000_000);
880 assert_eq!(hour.to_string(), "1.000 h");
881
882 let day = CuDuration(86_400_000_000_000);
883 assert_eq!(day.to_string(), "1.000 d");
884 }
885
886 #[test]
887 fn test_robot_clock_precision() {
888 let clock = RobotClock::new();
891
892 let recent = clock.recent();
894 let now = clock.now();
895
896 assert!(recent <= now);
898
899 let ref_time_ns = 1_000_000_000; let clock = RobotClock::from_ref_time(ref_time_ns);
902
903 let now = clock.now();
905 let now_ns: u64 = now.into();
906
907 let tolerance_ns = 50_000_000; assert!(now_ns >= ref_time_ns);
910 assert!(now_ns < ref_time_ns + tolerance_ns);
911 }
912
913 #[test]
914 fn test_mock_clock_advanced_operations() {
915 let (clock, mock) = RobotClock::mock();
917
918 assert_eq!(clock.now(), CuDuration(0));
920
921 mock.increment(CuDuration::from_secs(10));
923 assert_eq!(clock.now(), CuDuration::from_secs(10));
924
925 mock.decrement(CuDuration::from_secs(5));
927 assert_eq!(clock.now(), CuDuration::from_secs(5));
928
929 mock.set_value(30_000_000_000); assert_eq!(clock.now(), CuDuration::from_secs(30));
932
933 assert_eq!(mock.now(), CuDuration::from_secs(30));
935 assert_eq!(mock.value(), 30_000_000_000);
936 }
937
938 #[test]
939 fn test_cuduration_min_max() {
940 assert_eq!(CuDuration::MIN, CuDuration(0));
942
943 let a = CuDuration(100);
945 let b = CuDuration(200);
946
947 assert_eq!(a.min(b), a);
948 assert_eq!(a.max(b), b);
949 assert_eq!(b.min(a), a);
950 assert_eq!(b.max(a), b);
951
952 assert_eq!(a.min(a), a);
954 assert_eq!(a.max(a), a);
955
956 assert_eq!(a.min(CuDuration::MIN), CuDuration::MIN);
958 assert_eq!(a.max(CuDuration::MAX), CuDuration::MAX);
959 }
960
961 #[test]
962 fn test_clock_provider_trait() {
963 struct TestClockProvider {
965 clock: RobotClock,
966 }
967
968 impl ClockProvider for TestClockProvider {
969 fn get_clock(&self) -> RobotClock {
970 self.clock.clone()
971 }
972 }
973
974 let (clock, mock) = RobotClock::mock();
976 let provider = TestClockProvider { clock };
977
978 let provider_clock = provider.get_clock();
980 assert_eq!(provider_clock.now(), CuDuration(0));
981
982 mock.increment(CuDuration::from_secs(5));
984 assert_eq!(provider_clock.now(), CuDuration::from_secs(5));
985 }
986}