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