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