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