1use std::{
23 any::Any,
24 cell::RefCell,
25 collections::{BTreeMap, BinaryHeap},
26 fmt::Debug,
27 ops::Deref,
28 time::Duration,
29};
30
31use ahash::AHashMap;
32use jiff::Timestamp;
33use nautilus_core::{
34 AtomicTime, UUID4, UnixNanos,
35 correctness::{check_positive_u64, check_predicate_true, check_valid_string_utf8},
36 datetime::{NANOSECONDS_IN_SECOND, try_datetime_to_unix_nanos},
37 string::formatting::Separable,
38};
39use ustr::Ustr;
40
41use crate::timer::{
42 ScheduledTimeEvent, TestTimer, TimeEvent, TimeEventCallback, TimeEventHandler, Timer,
43 create_valid_interval,
44};
45
46pub trait Clock: Debug + Any {
50 fn utc_now(&self) -> Timestamp {
52 self.timestamp_ns().to_datetime_utc()
53 }
54
55 fn timestamp_ns(&self) -> UnixNanos;
57
58 fn timestamp_us(&self) -> u64;
60
61 fn timestamp_ms(&self) -> u64;
63
64 fn timestamp(&self) -> f64;
66
67 fn timer_names(&self) -> Vec<&str>;
69
70 fn timer_count(&self) -> usize;
72
73 fn timer_exists(&self, name: &Ustr) -> bool;
75
76 fn register_default_handler(&mut self, callback: TimeEventCallback);
78
79 fn cancel_default_handler(&mut self);
86
87 fn cancel_callbacks(&mut self);
94
95 fn set_time_alert(
112 &mut self,
113 name: &str,
114 alert_time: Timestamp,
115 callback: Option<TimeEventCallback>,
116 allow_past: Option<bool>,
117 ) -> anyhow::Result<()> {
118 self.set_time_alert_ns(
119 name,
120 try_datetime_to_unix_nanos(alert_time)?,
121 callback,
122 allow_past,
123 )
124 }
125
126 fn set_time_alert_ns(
150 &mut self,
151 name: &str,
152 alert_time_ns: UnixNanos,
153 callback: Option<TimeEventCallback>,
154 allow_past: Option<bool>,
155 ) -> anyhow::Result<()>;
156
157 #[expect(clippy::too_many_arguments)]
181 fn set_timer(
182 &mut self,
183 name: &str,
184 interval: Duration,
185 start_time: Option<Timestamp>,
186 stop_time: Option<Timestamp>,
187 callback: Option<TimeEventCallback>,
188 allow_past: Option<bool>,
189 fire_immediately: Option<bool>,
190 ) -> anyhow::Result<()> {
191 self.set_timer_ns(
192 name,
193 duration_to_nanos(interval)?,
194 start_time.map(try_datetime_to_unix_nanos).transpose()?,
195 stop_time.map(try_datetime_to_unix_nanos).transpose()?,
196 callback,
197 allow_past,
198 fire_immediately,
199 )
200 }
201
202 #[expect(clippy::too_many_arguments)]
238 fn set_timer_ns(
239 &mut self,
240 name: &str,
241 interval_ns: u64,
242 start_time_ns: Option<UnixNanos>,
243 stop_time_ns: Option<UnixNanos>,
244 callback: Option<TimeEventCallback>,
245 allow_past: Option<bool>,
246 fire_immediately: Option<bool>,
247 ) -> anyhow::Result<()>;
248
249 fn next_time_ns(&self, name: &str) -> Option<UnixNanos>;
253
254 fn cancel_timer(&mut self, name: &str);
256
257 fn cancel_timers(&mut self);
259
260 fn reset(&mut self);
264}
265
266impl dyn Clock {
267 pub fn as_any(&self) -> &dyn std::any::Any {
269 self
270 }
271
272 pub fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
274 self
275 }
276}
277
278#[derive(Debug)]
283pub struct ClockApi<'a> {
284 backing: ClockApiBacking<'a>,
285}
286
287impl<'a> ClockApi<'a> {
288 pub(crate) fn new(clock: &'a RefCell<dyn Clock>) -> Self {
289 Self {
290 backing: ClockApiBacking::Native(clock),
291 }
292 }
293
294 #[doc(hidden)]
300 #[must_use]
301 #[expect(
302 clippy::too_many_arguments,
303 reason = "clock API backing mirrors the full ClockApi surface"
304 )]
305 pub fn from_handlers<
306 TimestampNs,
307 SetTimeAlertNs,
308 SetTimerNs,
309 TimerNames,
310 TimerCount,
311 TimerExists,
312 NextTimeNs,
313 CancelTimer,
314 CancelTimers,
315 >(
316 timestamp_ns: TimestampNs,
317 set_time_alert_ns: SetTimeAlertNs,
318 set_timer_ns: SetTimerNs,
319 timer_names: TimerNames,
320 timer_count: TimerCount,
321 timer_exists: TimerExists,
322 next_time_ns: NextTimeNs,
323 cancel_timer: CancelTimer,
324 cancel_timers: CancelTimers,
325 ) -> Self
326 where
327 TimestampNs: Fn() -> UnixNanos + 'a,
328 SetTimeAlertNs:
329 Fn(&str, UnixNanos, Option<TimeEventCallback>, Option<bool>) -> anyhow::Result<()> + 'a,
330 SetTimerNs: Fn(
331 &str,
332 u64,
333 Option<UnixNanos>,
334 Option<UnixNanos>,
335 Option<TimeEventCallback>,
336 Option<bool>,
337 Option<bool>,
338 ) -> anyhow::Result<()>
339 + 'a,
340 TimerNames: Fn() -> Vec<String> + 'a,
341 TimerCount: Fn() -> usize + 'a,
342 TimerExists: Fn(&str) -> bool + 'a,
343 NextTimeNs: Fn(&str) -> Option<UnixNanos> + 'a,
344 CancelTimer: Fn(&str) + 'a,
345 CancelTimers: Fn() + 'a,
346 {
347 Self {
348 backing: ClockApiBacking::Handlers(ClockApiHandlers {
349 timestamp_ns: Box::new(timestamp_ns),
350 set_time_alert_ns: Box::new(set_time_alert_ns),
351 set_timer_ns: Box::new(set_timer_ns),
352 timer_names: Box::new(timer_names),
353 timer_count: Box::new(timer_count),
354 timer_exists: Box::new(timer_exists),
355 next_time_ns: Box::new(next_time_ns),
356 cancel_timer: Box::new(cancel_timer),
357 cancel_timers: Box::new(cancel_timers),
358 }),
359 }
360 }
361
362 #[must_use]
368 pub fn timestamp_ns(&self) -> UnixNanos {
369 match &self.backing {
370 ClockApiBacking::Native(clock) => clock.borrow().timestamp_ns(),
371 ClockApiBacking::Handlers(handlers) => (handlers.timestamp_ns)(),
372 }
373 }
374
375 #[must_use]
381 pub fn timestamp_us(&self) -> u64 {
382 match &self.backing {
383 ClockApiBacking::Native(clock) => clock.borrow().timestamp_us(),
384 ClockApiBacking::Handlers(handlers) => (handlers.timestamp_ns)().as_micros(),
385 }
386 }
387
388 #[must_use]
394 pub fn timestamp_ms(&self) -> u64 {
395 match &self.backing {
396 ClockApiBacking::Native(clock) => clock.borrow().timestamp_ms(),
397 ClockApiBacking::Handlers(handlers) => (handlers.timestamp_ns)().as_millis(),
398 }
399 }
400
401 #[must_use]
407 pub fn timestamp(&self) -> f64 {
408 match &self.backing {
409 ClockApiBacking::Native(clock) => clock.borrow().timestamp(),
410 ClockApiBacking::Handlers(handlers) => {
411 (handlers.timestamp_ns)().as_f64() / (NANOSECONDS_IN_SECOND as f64)
412 }
413 }
414 }
415
416 #[must_use]
422 pub fn utc_now(&self) -> Timestamp {
423 match &self.backing {
424 ClockApiBacking::Native(clock) => clock.borrow().utc_now(),
425 ClockApiBacking::Handlers(handlers) => (handlers.timestamp_ns)().to_datetime_utc(),
426 }
427 }
428
429 pub fn set_time_alert(
443 &self,
444 name: &str,
445 alert_time: Timestamp,
446 callback: Option<TimeEventCallback>,
447 allow_past: Option<bool>,
448 ) -> anyhow::Result<()> {
449 match &self.backing {
450 ClockApiBacking::Native(clock) => clock
451 .borrow_mut()
452 .set_time_alert(name, alert_time, callback, allow_past),
453 ClockApiBacking::Handlers(handlers) => (handlers.set_time_alert_ns)(
454 name,
455 try_datetime_to_unix_nanos(alert_time)?,
456 callback,
457 allow_past,
458 ),
459 }
460 }
461
462 pub fn set_time_alert_ns(
475 &self,
476 name: &str,
477 alert_time_ns: UnixNanos,
478 callback: Option<TimeEventCallback>,
479 allow_past: Option<bool>,
480 ) -> anyhow::Result<()> {
481 match &self.backing {
482 ClockApiBacking::Native(clock) => {
483 clock
484 .borrow_mut()
485 .set_time_alert_ns(name, alert_time_ns, callback, allow_past)
486 }
487 ClockApiBacking::Handlers(handlers) => {
488 (handlers.set_time_alert_ns)(name, alert_time_ns, callback, allow_past)
489 }
490 }
491 }
492
493 #[expect(clippy::too_many_arguments, reason = "timer scheduling mirrors Clock")]
507 pub fn set_timer(
508 &self,
509 name: &str,
510 interval: Duration,
511 start_time: Option<Timestamp>,
512 stop_time: Option<Timestamp>,
513 callback: Option<TimeEventCallback>,
514 allow_past: Option<bool>,
515 fire_immediately: Option<bool>,
516 ) -> anyhow::Result<()> {
517 match &self.backing {
518 ClockApiBacking::Native(clock) => clock.borrow_mut().set_timer(
519 name,
520 interval,
521 start_time,
522 stop_time,
523 callback,
524 allow_past,
525 fire_immediately,
526 ),
527 ClockApiBacking::Handlers(handlers) => (handlers.set_timer_ns)(
528 name,
529 duration_to_nanos(interval)?,
530 start_time.map(try_datetime_to_unix_nanos).transpose()?,
531 stop_time.map(try_datetime_to_unix_nanos).transpose()?,
532 callback,
533 allow_past,
534 fire_immediately,
535 ),
536 }
537 }
538
539 #[expect(clippy::too_many_arguments, reason = "timer scheduling mirrors Clock")]
552 pub fn set_timer_ns(
553 &self,
554 name: &str,
555 interval_ns: u64,
556 start_time_ns: Option<UnixNanos>,
557 stop_time_ns: Option<UnixNanos>,
558 callback: Option<TimeEventCallback>,
559 allow_past: Option<bool>,
560 fire_immediately: Option<bool>,
561 ) -> anyhow::Result<()> {
562 match &self.backing {
563 ClockApiBacking::Native(clock) => clock.borrow_mut().set_timer_ns(
564 name,
565 interval_ns,
566 start_time_ns,
567 stop_time_ns,
568 callback,
569 allow_past,
570 fire_immediately,
571 ),
572 ClockApiBacking::Handlers(handlers) => (handlers.set_timer_ns)(
573 name,
574 interval_ns,
575 start_time_ns,
576 stop_time_ns,
577 callback,
578 allow_past,
579 fire_immediately,
580 ),
581 }
582 }
583
584 #[must_use]
590 pub fn timer_names(&self) -> Vec<String> {
591 match &self.backing {
592 ClockApiBacking::Native(clock) => clock
593 .borrow()
594 .timer_names()
595 .into_iter()
596 .map(str::to_string)
597 .collect(),
598 ClockApiBacking::Handlers(handlers) => (handlers.timer_names)(),
599 }
600 }
601
602 #[must_use]
608 pub fn timer_count(&self) -> usize {
609 match &self.backing {
610 ClockApiBacking::Native(clock) => clock.borrow().timer_count(),
611 ClockApiBacking::Handlers(handlers) => (handlers.timer_count)(),
612 }
613 }
614
615 #[must_use]
621 pub fn timer_exists(&self, name: &str) -> bool {
622 match &self.backing {
623 ClockApiBacking::Native(clock) => clock.borrow().timer_exists(&Ustr::from(name)),
624 ClockApiBacking::Handlers(handlers) => (handlers.timer_exists)(name),
625 }
626 }
627
628 #[must_use]
636 pub fn next_time_ns(&self, name: &str) -> Option<UnixNanos> {
637 match &self.backing {
638 ClockApiBacking::Native(clock) => clock.borrow().next_time_ns(name),
639 ClockApiBacking::Handlers(handlers) => (handlers.next_time_ns)(name),
640 }
641 }
642
643 pub fn cancel_timer(&self, name: &str) {
649 match &self.backing {
650 ClockApiBacking::Native(clock) => clock.borrow_mut().cancel_timer(name),
651 ClockApiBacking::Handlers(handlers) => (handlers.cancel_timer)(name),
652 }
653 }
654
655 pub fn cancel_timers(&self) {
661 match &self.backing {
662 ClockApiBacking::Native(clock) => clock.borrow_mut().cancel_timers(),
663 ClockApiBacking::Handlers(handlers) => (handlers.cancel_timers)(),
664 }
665 }
666}
667
668enum ClockApiBacking<'a> {
669 Native(&'a RefCell<dyn Clock>),
670 Handlers(ClockApiHandlers<'a>),
671}
672
673struct ClockApiHandlers<'a> {
674 timestamp_ns: Box<dyn Fn() -> UnixNanos + 'a>,
675 set_time_alert_ns: Box<SetTimeAlertNsHandler<'a>>,
676 set_timer_ns: Box<SetTimerNsHandler<'a>>,
677 timer_names: Box<dyn Fn() -> Vec<String> + 'a>,
678 timer_count: Box<dyn Fn() -> usize + 'a>,
679 timer_exists: Box<dyn Fn(&str) -> bool + 'a>,
680 next_time_ns: Box<NextTimeNsHandler<'a>>,
681 cancel_timer: Box<dyn Fn(&str) + 'a>,
682 cancel_timers: Box<dyn Fn() + 'a>,
683}
684
685impl Debug for ClockApiBacking<'_> {
686 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
687 match self {
688 Self::Native(_) => f.write_str("Native"),
689 Self::Handlers(_) => f.write_str("Handlers"),
690 }
691 }
692}
693
694type SetTimeAlertNsHandler<'a> =
695 dyn Fn(&str, UnixNanos, Option<TimeEventCallback>, Option<bool>) -> anyhow::Result<()> + 'a;
696type NextTimeNsHandler<'a> = dyn Fn(&str) -> Option<UnixNanos> + 'a;
697type SetTimerNsHandler<'a> = dyn Fn(
698 &str,
699 u64,
700 Option<UnixNanos>,
701 Option<UnixNanos>,
702 Option<TimeEventCallback>,
703 Option<bool>,
704 Option<bool>,
705 ) -> anyhow::Result<()>
706 + 'a;
707
708fn duration_to_nanos(duration: Duration) -> anyhow::Result<u64> {
709 u64::try_from(duration.as_nanos())
710 .map_err(|_| anyhow::anyhow!("Interval exceeds u64 nanoseconds"))
711}
712
713#[derive(Debug, Default)]
718pub struct CallbackRegistry {
719 default_callback: Option<TimeEventCallback>,
720 callbacks: AHashMap<Ustr, TimeEventCallback>,
721}
722
723impl CallbackRegistry {
724 #[must_use]
726 pub fn new() -> Self {
727 Self::default()
728 }
729
730 pub fn register_default_handler(&mut self, callback: TimeEventCallback) {
732 self.default_callback = Some(callback);
733 }
734
735 pub fn cancel_default_handler(&mut self) {
737 self.default_callback = None;
738 }
739
740 pub fn register_callback(&mut self, name: Ustr, callback: TimeEventCallback) {
742 self.callbacks.insert(name, callback);
743 }
744
745 #[must_use]
747 pub fn has_any_callback(&self, name: &Ustr) -> bool {
748 self.callbacks.contains_key(name) || self.default_callback.is_some()
749 }
750
751 #[must_use]
753 pub fn get_callback(&self, name: &Ustr) -> Option<TimeEventCallback> {
754 self.callbacks
755 .get(name)
756 .cloned()
757 .or_else(|| self.default_callback.clone())
758 }
759
760 #[must_use]
766 pub fn get_handler(&self, event: TimeEvent) -> TimeEventHandler {
767 let callback = self
768 .get_callback(&event.name)
769 .unwrap_or_else(|| panic!("Event '{}' should have associated handler", event.name));
770
771 TimeEventHandler::new(event, callback)
772 }
773
774 pub fn clear(&mut self) {
776 self.callbacks.clear();
777 }
778}
779
780pub fn validate_and_prepare_time_alert(
790 name: &str,
791 mut alert_time_ns: UnixNanos,
792 allow_past: Option<bool>,
793 ts_now: UnixNanos,
794) -> anyhow::Result<(Ustr, UnixNanos)> {
795 check_valid_string_utf8(name, stringify!(name))?;
796
797 let name = Ustr::from(name);
798 let allow_past = allow_past.unwrap_or(true);
799
800 if alert_time_ns < ts_now {
801 if allow_past {
802 log::warn!(
803 "Timer '{name}' alert time {} was in the past, adjusted to current time for immediate firing",
804 alert_time_ns.to_rfc3339(),
805 );
806 alert_time_ns = ts_now;
807 } else {
808 anyhow::bail!(
809 "Timer '{name}' alert time {} was in the past (current time is {ts_now})",
810 alert_time_ns.to_rfc3339(),
811 );
812 }
813 }
814
815 Ok((name, alert_time_ns))
816}
817
818pub fn validate_and_prepare_timer(
834 name: &str,
835 interval_ns: u64,
836 start_time_ns: Option<UnixNanos>,
837 stop_time_ns: Option<UnixNanos>,
838 allow_past: Option<bool>,
839 fire_immediately: Option<bool>,
840 ts_now: UnixNanos,
841) -> anyhow::Result<(Ustr, UnixNanos, Option<UnixNanos>, bool, bool)> {
842 check_valid_string_utf8(name, stringify!(name))?;
843 check_positive_u64(interval_ns, stringify!(interval_ns))?;
844
845 let name = Ustr::from(name);
846 let allow_past = allow_past.unwrap_or(true);
847 let fire_immediately = fire_immediately.unwrap_or(false);
848
849 let start_time_ns = start_time_ns
850 .filter(|start_time_ns| *start_time_ns != 0)
851 .unwrap_or(ts_now);
852
853 let next_event_time = if fire_immediately {
854 start_time_ns
855 } else {
856 start_time_ns.checked_add(interval_ns).ok_or_else(|| {
857 anyhow::anyhow!("Timer '{name}' first event time exceeds UnixNanos range")
858 })?
859 };
860
861 if !allow_past && next_event_time < ts_now {
862 anyhow::bail!(
863 "Timer '{name}' next event time {} would be in the past (current time is {ts_now})",
864 next_event_time.to_rfc3339(),
865 );
866 }
867
868 if let Some(stop_time) = stop_time_ns {
869 if stop_time <= start_time_ns {
870 anyhow::bail!(
871 "Timer '{name}' stop time {} must be after start time {}",
872 stop_time.to_rfc3339(),
873 start_time_ns.to_rfc3339(),
874 );
875 }
876
877 if !allow_past && stop_time <= ts_now {
878 anyhow::bail!(
879 "Timer '{name}' stop time {} is in the past (current time is {ts_now})",
880 stop_time.to_rfc3339(),
881 );
882 }
883 }
884
885 Ok((
886 name,
887 start_time_ns,
888 stop_time_ns,
889 allow_past,
890 fire_immediately,
891 ))
892}
893
894#[derive(Debug)]
903pub struct TestClock {
904 time: AtomicTime,
905 timers: BTreeMap<Ustr, TestTimer>,
906 timer_queue: BinaryHeap<ScheduledTimeEvent>,
907 callbacks: CallbackRegistry,
908}
909
910impl TestClock {
911 #[must_use]
913 pub fn new() -> Self {
914 Self {
915 time: AtomicTime::new(false, UnixNanos::default()),
916 timers: BTreeMap::new(),
917 timer_queue: BinaryHeap::new(),
918 callbacks: CallbackRegistry::new(),
919 }
920 }
921
922 pub fn advance_time(&mut self, to_time_ns: UnixNanos, set_time: bool) -> Vec<TimeEvent> {
936 const WARN_TIME_EVENTS_THRESHOLD: usize = 1_000_000;
937
938 let from_time_ns = self.time.get_time_ns();
939
940 assert!(
941 to_time_ns >= from_time_ns,
942 "Invariant: time must be non-decreasing, `to_time_ns` {to_time_ns} < `from_time_ns` {from_time_ns}"
943 );
944
945 if set_time {
946 self.time.set_time(to_time_ns);
947 }
948
949 let mut events: Vec<TimeEvent> = Vec::new();
950
951 while self
952 .timer_queue
953 .peek()
954 .is_some_and(|entry| entry.0.ts_event <= to_time_ns)
955 {
956 let entry = self
957 .timer_queue
958 .pop()
959 .expect("timer queue peeked Some but pop returned None");
960
961 let Some((event, next_event)) = self.advance_timer_from_entry(&entry.0) else {
962 continue;
963 };
964
965 events.push(event);
966 if let Some(next_event) = next_event {
967 self.timer_queue.push(next_event);
968 }
969 }
970
971 self.compact_timer_queue_if_needed();
972
973 if events.len() >= WARN_TIME_EVENTS_THRESHOLD {
974 log::warn!(
975 "Allocated {} time events during clock advancement from {} to {}, \
976 consider stopping the timer between large time ranges with no data points",
977 events.len().separate_with_commas(),
978 from_time_ns,
979 to_time_ns
980 );
981 }
982
983 events.sort_by(|a, b| {
984 a.ts_event
985 .cmp(&b.ts_event)
986 .then_with(|| a.name.cmp(&b.name))
987 });
988 events
989 }
990
991 #[must_use]
999 pub fn match_handlers(&self, events: Vec<TimeEvent>) -> Vec<TimeEventHandler> {
1000 events
1001 .into_iter()
1002 .map(|event| self.callbacks.get_handler(event))
1003 .collect()
1004 }
1005
1006 fn replace_existing_timer_if_needed(&mut self, name: &Ustr) {
1007 replace_existing_timer(&mut self.timers, name);
1008 self.compact_timer_queue_if_needed();
1009 }
1010
1011 fn insert_timer(&mut self, timer: TestTimer) {
1012 self.timer_queue.push(Self::scheduled_event(&timer));
1013 self.timers.insert(timer.name, timer);
1014 self.compact_timer_queue_if_needed();
1015 }
1016
1017 fn advance_timer_from_entry(
1018 &mut self,
1019 entry: &TimeEvent,
1020 ) -> Option<(TimeEvent, Option<ScheduledTimeEvent>)> {
1021 let timer = self.timers.get_mut(&entry.name)?;
1022 if timer.next_time_ns() != entry.ts_event {
1023 return None;
1024 }
1025
1026 let Some((event, _)) = timer.next() else {
1027 self.timers.remove(&entry.name);
1028 return None;
1029 };
1030
1031 let next_entry = if timer.is_expired() {
1032 self.timers.remove(&entry.name);
1033 None
1034 } else {
1035 Some(Self::scheduled_event(timer))
1036 };
1037
1038 Some((event, next_entry))
1039 }
1040
1041 fn compact_timer_queue_if_needed(&mut self) {
1042 if self.timer_queue.len() > self.timers.len().saturating_mul(2) {
1043 self.compact_timer_queue();
1044 }
1045 }
1046
1047 fn compact_timer_queue(&mut self) {
1048 self.timer_queue = self.timers.values().map(Self::scheduled_event).collect();
1049 }
1050
1051 fn scheduled_event(timer: &TestTimer) -> ScheduledTimeEvent {
1052 ScheduledTimeEvent::new(TimeEvent::new(
1053 timer.name,
1054 UUID4::new(),
1055 timer.next_time_ns(),
1056 timer.next_time_ns(),
1057 ))
1058 }
1059}
1060
1061impl Default for TestClock {
1062 fn default() -> Self {
1064 Self::new()
1065 }
1066}
1067
1068impl Deref for TestClock {
1069 type Target = AtomicTime;
1070
1071 fn deref(&self) -> &Self::Target {
1072 &self.time
1073 }
1074}
1075
1076impl Clock for TestClock {
1077 fn timestamp_ns(&self) -> UnixNanos {
1078 self.time.get_time_ns()
1079 }
1080
1081 fn timestamp_us(&self) -> u64 {
1082 self.time.get_time_us()
1083 }
1084
1085 fn timestamp_ms(&self) -> u64 {
1086 self.time.get_time_ms()
1087 }
1088
1089 fn timestamp(&self) -> f64 {
1090 self.time.get_time()
1091 }
1092
1093 fn timer_names(&self) -> Vec<&str> {
1094 self.timers
1095 .iter()
1096 .filter(|(_, timer)| !timer.is_expired())
1097 .map(|(k, _)| k.as_str())
1098 .collect()
1099 }
1100
1101 fn timer_count(&self) -> usize {
1102 self.timers
1103 .iter()
1104 .filter(|(_, timer)| !timer.is_expired())
1105 .count()
1106 }
1107
1108 fn timer_exists(&self, name: &Ustr) -> bool {
1109 self.timers
1110 .get(name)
1111 .is_some_and(|timer| !timer.is_expired())
1112 }
1113
1114 fn register_default_handler(&mut self, callback: TimeEventCallback) {
1115 self.callbacks.register_default_handler(callback);
1116 }
1117
1118 fn cancel_default_handler(&mut self) {
1119 self.callbacks.cancel_default_handler();
1120 }
1121
1122 fn cancel_callbacks(&mut self) {
1123 self.callbacks.clear();
1124 }
1125
1126 fn set_time_alert_ns(
1127 &mut self,
1128 name: &str,
1129 alert_time_ns: UnixNanos,
1130 callback: Option<TimeEventCallback>,
1131 allow_past: Option<bool>,
1132 ) -> anyhow::Result<()> {
1133 let ts_now = self.get_time_ns();
1134 let (name, alert_time_ns) =
1135 validate_and_prepare_time_alert(name, alert_time_ns, allow_past, ts_now)?;
1136
1137 check_predicate_true(
1138 callback.is_some() | self.callbacks.has_any_callback(&name),
1139 "No callbacks provided",
1140 )?;
1141
1142 self.replace_existing_timer_if_needed(&name);
1143
1144 if let Some(callback) = callback {
1145 self.callbacks.register_callback(name, callback);
1146 }
1147
1148 let interval_ns = create_valid_interval((alert_time_ns - ts_now).into());
1150 let fire_immediately = alert_time_ns == ts_now;
1151
1152 let timer = TestTimer::new(
1153 name,
1154 interval_ns,
1155 ts_now,
1156 Some(alert_time_ns),
1157 fire_immediately,
1158 );
1159 self.insert_timer(timer);
1160
1161 Ok(())
1162 }
1163
1164 fn set_timer_ns(
1165 &mut self,
1166 name: &str,
1167 interval_ns: u64,
1168 start_time_ns: Option<UnixNanos>,
1169 stop_time_ns: Option<UnixNanos>,
1170 callback: Option<TimeEventCallback>,
1171 allow_past: Option<bool>,
1172 fire_immediately: Option<bool>,
1173 ) -> anyhow::Result<()> {
1174 let ts_now = self.get_time_ns();
1175 let (name, start_time_ns, stop_time_ns, _allow_past, fire_immediately) =
1176 validate_and_prepare_timer(
1177 name,
1178 interval_ns,
1179 start_time_ns,
1180 stop_time_ns,
1181 allow_past,
1182 fire_immediately,
1183 ts_now,
1184 )?;
1185
1186 check_predicate_true(
1187 callback.is_some() | self.callbacks.has_any_callback(&name),
1188 "No callbacks provided",
1189 )?;
1190
1191 self.replace_existing_timer_if_needed(&name);
1192
1193 if let Some(callback) = callback {
1194 self.callbacks.register_callback(name, callback);
1195 }
1196
1197 let interval_ns = create_valid_interval(interval_ns);
1198
1199 let timer = TestTimer::new(
1200 name,
1201 interval_ns,
1202 start_time_ns,
1203 stop_time_ns,
1204 fire_immediately,
1205 );
1206 self.insert_timer(timer);
1207
1208 Ok(())
1209 }
1210
1211 fn next_time_ns(&self, name: &str) -> Option<UnixNanos> {
1212 self.timers
1213 .get(&Ustr::from(name))
1214 .filter(|timer| !timer.is_expired())
1215 .map(TestTimer::next_time_ns)
1216 }
1217
1218 fn cancel_timer(&mut self, name: &str) {
1219 let timer = self.timers.remove(&Ustr::from(name));
1220 if let Some(mut timer) = timer {
1221 timer.cancel();
1222 }
1223 self.compact_timer_queue_if_needed();
1224 }
1225
1226 fn cancel_timers(&mut self) {
1227 for timer in &mut self.timers.values_mut() {
1228 timer.cancel();
1229 }
1230
1231 self.timers.clear();
1232 self.timer_queue.clear();
1233 }
1234
1235 fn reset(&mut self) {
1236 self.time = AtomicTime::new(false, UnixNanos::default());
1237 self.timers = BTreeMap::new();
1238 self.timer_queue = BinaryHeap::new();
1239 self.callbacks.clear();
1240 }
1241}
1242
1243pub(crate) fn replace_existing_timer<T: Timer>(timers: &mut BTreeMap<Ustr, T>, name: &Ustr) {
1244 let Some(mut timer) = timers.remove(name) else {
1245 return;
1246 };
1247
1248 if timer.is_expired() {
1249 return;
1250 }
1251
1252 timer.cancel();
1253 log::warn!("Timer '{name}' replaced");
1254}
1255
1256#[cfg(test)]
1257mod tests {
1258 use std::{cell::RefCell, collections::BTreeMap, sync::Arc, time::Duration};
1259
1260 use nautilus_core::UnixNanos;
1261 use parking_lot::Mutex;
1262 use proptest::{prelude::*, test_runner::TestCaseResult};
1263 use rstest::{fixture, rstest};
1264 use ustr::Ustr;
1265
1266 use super::*;
1267 use crate::timer::{TimeEvent, TimeEventCallback};
1268
1269 #[derive(Debug, Default)]
1270 struct TestCallback {
1271 called: Arc<Mutex<bool>>,
1273 }
1274
1275 impl TestCallback {
1276 fn new(called: Arc<Mutex<bool>>) -> Self {
1277 Self { called }
1278 }
1279 }
1280
1281 impl From<TestCallback> for TimeEventCallback {
1282 fn from(callback: TestCallback) -> Self {
1283 Self::from(move |_event: TimeEvent| {
1284 *callback.called.lock() = true;
1285 })
1286 }
1287 }
1288
1289 #[fixture]
1290 pub fn test_clock() -> TestClock {
1291 let mut clock = TestClock::new();
1292 clock.register_default_handler(TestCallback::default().into());
1293 clock
1294 }
1295
1296 #[rstest]
1297 fn test_time_monotonicity(mut test_clock: TestClock) {
1298 let initial_time = test_clock.timestamp_ns();
1299 test_clock.advance_time(UnixNanos::from(*initial_time + 1000), true);
1300 assert!(test_clock.timestamp_ns() > initial_time);
1301 }
1302
1303 #[rstest]
1304 fn test_timer_registration(mut test_clock: TestClock) {
1305 test_clock
1306 .set_time_alert_ns(
1307 "test_timer",
1308 (*test_clock.timestamp_ns() + 1000).into(),
1309 None,
1310 None,
1311 )
1312 .unwrap();
1313 assert_eq!(test_clock.timer_count(), 1);
1314 assert_eq!(test_clock.timer_names(), vec!["test_timer"]);
1315 }
1316
1317 #[rstest]
1318 fn test_timer_expiration(mut test_clock: TestClock) {
1319 let alert_time = (*test_clock.timestamp_ns() + 1000).into();
1320 test_clock
1321 .set_time_alert_ns("test_timer", alert_time, None, None)
1322 .unwrap();
1323 let events = test_clock.advance_time(alert_time, true);
1324 assert_eq!(events.len(), 1);
1325 assert_eq!(events[0].name.as_str(), "test_timer");
1326 }
1327
1328 #[rstest]
1329 fn test_timer_cancellation(mut test_clock: TestClock) {
1330 test_clock
1331 .set_time_alert_ns(
1332 "test_timer",
1333 (*test_clock.timestamp_ns() + 1000).into(),
1334 None,
1335 None,
1336 )
1337 .unwrap();
1338 assert_eq!(test_clock.timer_count(), 1);
1339 test_clock.cancel_timer("test_timer");
1340 assert_eq!(test_clock.timer_count(), 0);
1341 }
1342
1343 #[rstest]
1344 fn test_time_advancement(mut test_clock: TestClock) {
1345 let start_time = test_clock.timestamp_ns();
1346 test_clock
1347 .set_timer_ns("test_timer", 1000, Some(start_time), None, None, None, None)
1348 .unwrap();
1349 let events = test_clock.advance_time(UnixNanos::from(*start_time + 2500), true);
1350 assert_eq!(events.len(), 2);
1351 assert_eq!(*events[0].ts_event, *start_time + 1000);
1352 assert_eq!(*events[1].ts_event, *start_time + 2000);
1353 }
1354
1355 #[rstest]
1356 fn test_default_and_custom_callbacks() {
1357 let mut clock = TestClock::new();
1358 let default_called = Arc::new(Mutex::new(false));
1359 let custom_called = Arc::new(Mutex::new(false));
1360
1361 let default_callback = TestCallback::new(Arc::clone(&default_called));
1362 let custom_callback = TestCallback::new(Arc::clone(&custom_called));
1363
1364 clock.register_default_handler(TimeEventCallback::from(default_callback));
1365 clock
1366 .set_time_alert_ns(
1367 "default_timer",
1368 (*clock.timestamp_ns() + 1000).into(),
1369 None,
1370 None,
1371 )
1372 .unwrap();
1373 clock
1374 .set_time_alert_ns(
1375 "custom_timer",
1376 (*clock.timestamp_ns() + 1000).into(),
1377 Some(TimeEventCallback::from(custom_callback)),
1378 None,
1379 )
1380 .unwrap();
1381
1382 let events = clock.advance_time(UnixNanos::from(*clock.timestamp_ns() + 1000), true);
1383 let handlers = clock.match_handlers(events);
1384
1385 for handler in handlers {
1386 handler.callback.call(handler.event);
1387 }
1388
1389 assert!(*default_called.lock());
1390 assert!(*custom_called.lock());
1391 }
1392
1393 #[rstest]
1394 fn test_timer_with_rust_local_callback() {
1395 use std::{cell::RefCell, rc::Rc};
1396
1397 let mut clock = TestClock::new();
1398 let call_count = Rc::new(RefCell::new(0_u32));
1399 let call_count_clone = Rc::clone(&call_count);
1400
1401 let callback: Rc<dyn Fn(TimeEvent)> = Rc::new(move |_event: TimeEvent| {
1403 *call_count_clone.borrow_mut() += 1;
1404 });
1405
1406 clock
1407 .set_time_alert_ns(
1408 "local_timer",
1409 (*clock.timestamp_ns() + 1000).into(),
1410 Some(TimeEventCallback::from(callback)),
1411 None,
1412 )
1413 .unwrap();
1414
1415 let events = clock.advance_time(UnixNanos::from(*clock.timestamp_ns() + 1000), true);
1416 let handlers = clock.match_handlers(events);
1417
1418 for handler in handlers {
1419 handler.callback.call(handler.event);
1420 }
1421
1422 assert_eq!(*call_count.borrow(), 1);
1423 }
1424
1425 #[rstest]
1426 fn test_multiple_timers(mut test_clock: TestClock) {
1427 let start_time = test_clock.timestamp_ns();
1428 test_clock
1429 .set_timer_ns("timer1", 1000, Some(start_time), None, None, None, None)
1430 .unwrap();
1431 test_clock
1432 .set_timer_ns("timer2", 2000, Some(start_time), None, None, None, None)
1433 .unwrap();
1434 let events = test_clock.advance_time(UnixNanos::from(*start_time + 2000), true);
1435 assert_eq!(events.len(), 3);
1436 assert_eq!(events[0].name.as_str(), "timer1");
1437 assert_eq!(events[1].name.as_str(), "timer1");
1438 assert_eq!(events[2].name.as_str(), "timer2");
1439 }
1440
1441 #[rstest]
1442 fn test_allow_past_parameter_true(mut test_clock: TestClock) {
1443 test_clock.set_time(UnixNanos::from(2000));
1444 let current_time = test_clock.timestamp_ns();
1445 let past_time = UnixNanos::from(current_time.as_u64() - 1000);
1446
1447 test_clock
1449 .set_time_alert_ns("past_timer", past_time, None, Some(true))
1450 .unwrap();
1451
1452 assert_eq!(test_clock.timer_count(), 1);
1454 assert_eq!(test_clock.timer_names(), vec!["past_timer"]);
1455
1456 let next_time = test_clock.next_time_ns("past_timer").unwrap();
1458 assert!(next_time >= current_time);
1459 }
1460
1461 #[rstest]
1462 fn test_allow_past_parameter_false(mut test_clock: TestClock) {
1463 test_clock.set_time(UnixNanos::from(2000));
1464 let current_time = test_clock.timestamp_ns();
1465 let past_time = current_time - 1000;
1466
1467 let result = test_clock.set_time_alert_ns("past_timer", past_time, None, Some(false));
1469
1470 assert!(result.is_err());
1472 assert!(format!("{}", result.unwrap_err()).contains("was in the past"));
1473
1474 assert_eq!(test_clock.timer_count(), 0);
1476 assert!(test_clock.timer_names().is_empty());
1477 }
1478
1479 #[rstest]
1480 fn test_invalid_stop_time_validation(mut test_clock: TestClock) {
1481 test_clock.set_time(UnixNanos::from(2000));
1482 let current_time = test_clock.timestamp_ns();
1483 let start_time = current_time + 1000;
1484 let stop_time = current_time + 500; let result = test_clock.set_timer_ns(
1488 "invalid_timer",
1489 100,
1490 Some(start_time),
1491 Some(stop_time),
1492 None,
1493 None,
1494 None,
1495 );
1496
1497 assert!(result.is_err());
1499 assert!(format!("{}", result.unwrap_err()).contains("must be after start time"));
1500
1501 assert_eq!(test_clock.timer_count(), 0);
1503 }
1504
1505 #[rstest]
1506 fn test_set_timer_ns_fire_immediately_true(mut test_clock: TestClock) {
1507 let start_time = test_clock.timestamp_ns();
1508 let interval_ns = 1000;
1509
1510 test_clock
1511 .set_timer_ns(
1512 "fire_immediately_timer",
1513 interval_ns,
1514 Some(start_time),
1515 None,
1516 None,
1517 None,
1518 Some(true),
1519 )
1520 .unwrap();
1521
1522 let events = test_clock.advance_time(start_time + 2500, true);
1524
1525 assert_eq!(events.len(), 3);
1527 assert_eq!(*events[0].ts_event, *start_time); assert_eq!(*events[1].ts_event, *start_time + 1000); assert_eq!(*events[2].ts_event, *start_time + 2000); }
1531
1532 #[rstest]
1533 fn test_set_timer_ns_fire_immediately_false(mut test_clock: TestClock) {
1534 let start_time = test_clock.timestamp_ns();
1535 let interval_ns = 1000;
1536
1537 test_clock
1538 .set_timer_ns(
1539 "normal_timer",
1540 interval_ns,
1541 Some(start_time),
1542 None,
1543 None,
1544 None,
1545 Some(false),
1546 )
1547 .unwrap();
1548
1549 let events = test_clock.advance_time(start_time + 2500, true);
1551
1552 assert_eq!(events.len(), 2);
1554 assert_eq!(*events[0].ts_event, *start_time + 1000); assert_eq!(*events[1].ts_event, *start_time + 2000); }
1557
1558 #[rstest]
1559 fn test_set_timer_ns_fire_immediately_default_is_false(mut test_clock: TestClock) {
1560 let start_time = test_clock.timestamp_ns();
1561 let interval_ns = 1000;
1562
1563 test_clock
1565 .set_timer_ns(
1566 "default_timer",
1567 interval_ns,
1568 Some(start_time),
1569 None,
1570 None,
1571 None,
1572 None,
1573 )
1574 .unwrap();
1575
1576 let events = test_clock.advance_time(start_time + 1500, true);
1577
1578 assert_eq!(events.len(), 1);
1580 assert_eq!(*events[0].ts_event, *start_time + 1000); }
1582
1583 #[rstest]
1584 fn test_set_timer_ns_fire_immediately_with_zero_start_time(mut test_clock: TestClock) {
1585 test_clock.set_time(5000.into());
1586 let interval_ns = 1000;
1587
1588 test_clock
1589 .set_timer_ns(
1590 "zero_start_timer",
1591 interval_ns,
1592 None,
1593 None,
1594 None,
1595 None,
1596 Some(true),
1597 )
1598 .unwrap();
1599
1600 let events = test_clock.advance_time(UnixNanos::from(7000), true);
1601
1602 assert_eq!(events.len(), 3);
1605 assert_eq!(*events[0].ts_event, 5000); assert_eq!(*events[1].ts_event, 6000);
1607 assert_eq!(*events[2].ts_event, 7000);
1608 }
1609
1610 #[rstest]
1611 fn test_multiple_timers_different_fire_immediately_settings(mut test_clock: TestClock) {
1612 let start_time = test_clock.timestamp_ns();
1613 let interval_ns = 1000;
1614
1615 test_clock
1617 .set_timer_ns(
1618 "immediate_timer",
1619 interval_ns,
1620 Some(start_time),
1621 None,
1622 None,
1623 None,
1624 Some(true),
1625 )
1626 .unwrap();
1627
1628 test_clock
1630 .set_timer_ns(
1631 "normal_timer",
1632 interval_ns,
1633 Some(start_time),
1634 None,
1635 None,
1636 None,
1637 Some(false),
1638 )
1639 .unwrap();
1640
1641 let events = test_clock.advance_time(start_time + 1500, true);
1642
1643 assert_eq!(events.len(), 3);
1645
1646 let mut event_times: Vec<u64> = events.iter().map(|e| e.ts_event.as_u64()).collect();
1648 event_times.sort_unstable();
1649
1650 assert_eq!(event_times[0], start_time.as_u64()); assert_eq!(event_times[1], start_time.as_u64() + 1000); assert_eq!(event_times[2], start_time.as_u64() + 1000); }
1654
1655 #[rstest]
1656 fn test_timer_name_collision_overwrites(mut test_clock: TestClock) {
1657 let start_time = test_clock.timestamp_ns();
1658
1659 test_clock
1661 .set_timer_ns(
1662 "collision_timer",
1663 1000,
1664 Some(start_time),
1665 None,
1666 None,
1667 None,
1668 None,
1669 )
1670 .unwrap();
1671
1672 let result = test_clock.set_timer_ns(
1674 "collision_timer",
1675 2000,
1676 Some(start_time),
1677 None,
1678 None,
1679 None,
1680 None,
1681 );
1682
1683 assert!(result.is_ok());
1684 assert_eq!(test_clock.timer_count(), 1);
1686
1687 let next_time = test_clock.next_time_ns("collision_timer").unwrap();
1689 assert_eq!(next_time, start_time + 2000);
1691 }
1692
1693 #[rstest]
1694 fn test_timer_zero_interval_error(mut test_clock: TestClock) {
1695 let start_time = test_clock.timestamp_ns();
1696
1697 let result =
1699 test_clock.set_timer_ns("zero_interval", 0, Some(start_time), None, None, None, None);
1700
1701 assert!(result.is_err());
1702 assert_eq!(test_clock.timer_count(), 0);
1703 }
1704
1705 #[rstest]
1706 fn test_timer_empty_name_error(mut test_clock: TestClock) {
1707 let start_time = test_clock.timestamp_ns();
1708
1709 let result = test_clock.set_timer_ns("", 1000, Some(start_time), None, None, None, None);
1711
1712 assert!(result.is_err());
1713 assert_eq!(test_clock.timer_count(), 0);
1714 }
1715
1716 #[rstest]
1717 fn test_timer_exists(mut test_clock: TestClock) {
1718 let name = Ustr::from("exists_timer");
1719 assert!(!test_clock.timer_exists(&name));
1720
1721 test_clock
1722 .set_time_alert_ns(
1723 name.as_str(),
1724 (*test_clock.timestamp_ns() + 1_000).into(),
1725 None,
1726 None,
1727 )
1728 .unwrap();
1729
1730 assert!(test_clock.timer_exists(&name));
1731 }
1732
1733 #[rstest]
1734 fn test_timer_exists_consistent_with_names_and_count_after_expiry(mut test_clock: TestClock) {
1735 let name = Ustr::from("expiring_timer");
1736 let start_time = test_clock.timestamp_ns();
1737
1738 test_clock
1739 .set_timer_ns(
1740 name.as_str(),
1741 1_000,
1742 Some(start_time),
1743 Some(start_time + 2_500),
1744 None,
1745 None,
1746 None,
1747 )
1748 .unwrap();
1749
1750 assert!(test_clock.timer_exists(&name));
1751 assert_eq!(test_clock.timer_count(), 1);
1752
1753 test_clock.advance_time(start_time + 10_000, true);
1754
1755 assert!(!test_clock.timer_exists(&name));
1757 assert_eq!(test_clock.timer_count(), 0);
1758 assert!(test_clock.timer_names().is_empty());
1759 }
1760
1761 #[rstest]
1762 fn test_timer_rejects_past_stop_time_when_not_allowed(mut test_clock: TestClock) {
1763 test_clock.set_time(UnixNanos::from(10_000));
1764 let current = test_clock.timestamp_ns();
1765
1766 let result = test_clock.set_timer_ns(
1767 "past_stop",
1768 10_000,
1769 Some(current - 500),
1770 Some(current - 100),
1771 None,
1772 Some(false),
1773 None,
1774 );
1775
1776 let err = result.expect_err("expected stop time validation error");
1777 let err_msg = err.to_string();
1778 assert!(err_msg.contains("stop time"));
1779 assert!(err_msg.contains("in the past"));
1780 }
1781
1782 #[rstest]
1783 fn test_timer_accepts_future_stop_time(mut test_clock: TestClock) {
1784 let current = test_clock.timestamp_ns();
1785
1786 let result = test_clock.set_timer_ns(
1787 "future_stop",
1788 1_000,
1789 Some(current),
1790 Some(current + 10_000),
1791 None,
1792 Some(false),
1793 None,
1794 );
1795
1796 assert!(result.is_ok());
1797 }
1798
1799 #[rstest]
1800 fn test_timer_fire_immediately_at_exact_stop_time(mut test_clock: TestClock) {
1801 let start_time = test_clock.timestamp_ns();
1802 let interval_ns = 1000;
1803 let stop_time = start_time + interval_ns; test_clock
1806 .set_timer_ns(
1807 "exact_stop",
1808 interval_ns,
1809 Some(start_time),
1810 Some(stop_time),
1811 None,
1812 None,
1813 Some(true),
1814 )
1815 .unwrap();
1816
1817 let events = test_clock.advance_time(stop_time, true);
1818
1819 assert_eq!(events.len(), 2);
1821 assert_eq!(*events[0].ts_event, *start_time); assert_eq!(*events[1].ts_event, *stop_time); }
1824
1825 #[rstest]
1826 fn test_timer_advance_to_exact_next_time(mut test_clock: TestClock) {
1827 let start_time = test_clock.timestamp_ns();
1828 let interval_ns = 1000;
1829
1830 test_clock
1831 .set_timer_ns(
1832 "exact_advance",
1833 interval_ns,
1834 Some(start_time),
1835 None,
1836 None,
1837 None,
1838 Some(false),
1839 )
1840 .unwrap();
1841
1842 let next_time = test_clock.next_time_ns("exact_advance").unwrap();
1844 let events = test_clock.advance_time(next_time, true);
1845
1846 assert_eq!(events.len(), 1);
1847 assert_eq!(*events[0].ts_event, *next_time);
1848 }
1849
1850 #[rstest]
1851 fn test_allow_past_bar_aggregation_use_case(mut test_clock: TestClock) {
1852 test_clock.set_time(UnixNanos::from(100_500)); let bar_start_time = UnixNanos::from(100_000); let interval_ns = 1000; let result = test_clock.set_timer_ns(
1862 "bar_timer",
1863 interval_ns,
1864 Some(bar_start_time),
1865 None,
1866 None,
1867 Some(false), Some(false), );
1870
1871 assert!(result.is_ok());
1873 assert_eq!(test_clock.timer_count(), 1);
1874
1875 let next_time = test_clock.next_time_ns("bar_timer").unwrap();
1877 assert_eq!(*next_time, 101_000);
1878 }
1879
1880 #[rstest]
1881 fn test_allow_past_false_rejects_when_next_event_in_past(mut test_clock: TestClock) {
1882 test_clock.set_time(UnixNanos::from(102_000)); let past_start_time = UnixNanos::from(100_000); let interval_ns = 1000; let result = test_clock.set_timer_ns(
1891 "past_event_timer",
1892 interval_ns,
1893 Some(past_start_time),
1894 None,
1895 None,
1896 Some(false), Some(false), );
1899
1900 assert!(result.is_err());
1902 assert!(
1903 result
1904 .unwrap_err()
1905 .to_string()
1906 .contains("would be in the past")
1907 );
1908 }
1909
1910 #[rstest]
1911 fn test_allow_past_false_with_fire_immediately_true(mut test_clock: TestClock) {
1912 test_clock.set_time(UnixNanos::from(100_500)); let past_start_time = UnixNanos::from(100_000); let interval_ns = 1000;
1916
1917 let result = test_clock.set_timer_ns(
1920 "immediate_past_timer",
1921 interval_ns,
1922 Some(past_start_time),
1923 None,
1924 None,
1925 Some(false), Some(true), );
1928
1929 assert!(result.is_err());
1931 assert!(
1932 result
1933 .unwrap_err()
1934 .to_string()
1935 .contains("would be in the past")
1936 );
1937 }
1938
1939 #[rstest]
1940 fn test_cancel_timer_during_execution(mut test_clock: TestClock) {
1941 let start_time = test_clock.timestamp_ns();
1942
1943 test_clock
1944 .set_timer_ns(
1945 "cancel_test",
1946 1000,
1947 Some(start_time),
1948 None,
1949 None,
1950 None,
1951 None,
1952 )
1953 .unwrap();
1954
1955 assert_eq!(test_clock.timer_count(), 1);
1956
1957 test_clock.cancel_timer("cancel_test");
1959
1960 assert_eq!(test_clock.timer_count(), 0);
1961
1962 let events = test_clock.advance_time(start_time + 2000, true);
1964 assert_eq!(events.len(), 0);
1965 }
1966
1967 #[rstest]
1968 fn test_cancelled_timer_queue_entry_is_skipped(mut test_clock: TestClock) {
1969 let start_time = test_clock.timestamp_ns();
1970 test_clock
1971 .set_time_alert_ns("cancelled", start_time + 1000, None, None)
1972 .unwrap();
1973 test_clock
1974 .set_time_alert_ns("active", start_time + 2000, None, None)
1975 .unwrap();
1976
1977 test_clock.cancel_timer("cancelled");
1978 assert_eq!(test_clock.timer_count(), 1);
1979 assert_eq!(test_clock.timer_queue.len(), 2);
1980
1981 let events = test_clock.advance_time(start_time + 1000, true);
1982 assert!(events.is_empty());
1983 assert_eq!(test_clock.timer_names(), vec!["active"]);
1984
1985 let events = test_clock.advance_time(start_time + 2000, true);
1986 assert_eq!(events.len(), 1);
1987 assert_eq!(events[0].name.as_str(), "active");
1988 }
1989
1990 #[rstest]
1991 fn test_timer_queue_compacts_stale_entries(mut test_clock: TestClock) {
1992 let start_time = test_clock.timestamp_ns();
1993 test_clock
1994 .set_time_alert_ns("active", start_time + 1000, None, None)
1995 .unwrap();
1996 test_clock
1997 .set_time_alert_ns("cancelled-1", start_time + 2000, None, None)
1998 .unwrap();
1999 test_clock
2000 .set_time_alert_ns("cancelled-2", start_time + 3000, None, None)
2001 .unwrap();
2002
2003 test_clock.cancel_timer("cancelled-1");
2004 assert_eq!(test_clock.timer_queue.len(), 3);
2005
2006 test_clock.cancel_timer("cancelled-2");
2007 assert_eq!(test_clock.timer_count(), 1);
2008 assert_eq!(test_clock.timer_queue.len(), 1);
2009 }
2010
2011 #[rstest]
2012 fn test_cancel_all_timers(mut test_clock: TestClock) {
2013 test_clock
2015 .set_timer_ns("timer1", 1000, None, None, None, None, None)
2016 .unwrap();
2017 test_clock
2018 .set_timer_ns("timer2", 1500, None, None, None, None, None)
2019 .unwrap();
2020 test_clock
2021 .set_timer_ns("timer3", 2000, None, None, None, None, None)
2022 .unwrap();
2023
2024 assert_eq!(test_clock.timer_count(), 3);
2025
2026 test_clock.cancel_timers();
2028
2029 assert_eq!(test_clock.timer_count(), 0);
2030
2031 let events = test_clock.advance_time(UnixNanos::from(5000), true);
2033 assert_eq!(events.len(), 0);
2034 }
2035
2036 #[rstest]
2037 fn test_clock_reset_clears_timers(mut test_clock: TestClock) {
2038 test_clock
2039 .set_timer_ns("reset_test", 1000, None, None, None, None, None)
2040 .unwrap();
2041
2042 assert_eq!(test_clock.timer_count(), 1);
2043
2044 test_clock.reset();
2046
2047 assert_eq!(test_clock.timer_count(), 0);
2048 assert_eq!(test_clock.timestamp_ns(), UnixNanos::default()); }
2050
2051 #[rstest]
2052 fn test_cancel_default_handler_clears_default(mut test_clock: TestClock) {
2053 test_clock.cancel_default_handler();
2055
2056 let alert_time: UnixNanos = (*test_clock.timestamp_ns() + 1000).into();
2058 let err = test_clock
2059 .set_time_alert_ns("alert", alert_time, None, None)
2060 .unwrap_err();
2061 assert!(
2062 err.to_string().contains("No callbacks provided"),
2063 "unexpected error: {err}"
2064 );
2065 }
2066
2067 #[rstest]
2068 fn test_cancel_default_handler_is_idempotent_on_empty_registry() {
2069 let mut clock = TestClock::new();
2071 clock.cancel_default_handler();
2072 clock.cancel_default_handler();
2073 }
2074
2075 #[rstest]
2076 fn test_cancel_callbacks_clears_named(mut test_clock: TestClock) {
2077 let alert_time: UnixNanos = (*test_clock.timestamp_ns() + 1000).into();
2078 let callback = TimeEventCallback::from(TestCallback::default());
2079 test_clock
2080 .set_time_alert_ns("named_alert", alert_time, Some(callback), None)
2081 .unwrap();
2082 test_clock.cancel_timer("named_alert");
2083
2084 test_clock.cancel_default_handler();
2086 test_clock.cancel_callbacks();
2087
2088 let err = test_clock
2089 .set_time_alert_ns("named_alert", alert_time, None, None)
2090 .unwrap_err();
2091 assert!(
2092 err.to_string().contains("No callbacks provided"),
2093 "unexpected error: {err}"
2094 );
2095 }
2096
2097 #[rstest]
2098 fn test_failed_set_time_alert_ns_preserves_existing_timer() {
2099 let mut clock = TestClock::new();
2101 let alert_time: UnixNanos = (*clock.timestamp_ns() + 1000).into();
2102 let callback = TimeEventCallback::from(TestCallback::default());
2103 clock
2104 .set_time_alert_ns("alert", alert_time, Some(callback), None)
2105 .unwrap();
2106 assert_eq!(clock.next_time_ns("alert"), Some(alert_time));
2107
2108 clock.cancel_callbacks();
2110
2111 let err = clock
2114 .set_time_alert_ns("alert", (*alert_time + 1000).into(), None, None)
2115 .unwrap_err();
2116 assert!(
2117 err.to_string().contains("No callbacks provided"),
2118 "unexpected error: {err}"
2119 );
2120 assert_eq!(clock.timer_count(), 1);
2121 assert_eq!(clock.next_time_ns("alert"), Some(alert_time));
2122 }
2123
2124 #[rstest]
2125 fn test_cancel_default_handler_preserves_named_callbacks(mut test_clock: TestClock) {
2126 let alert_time: UnixNanos = (*test_clock.timestamp_ns() + 1000).into();
2127 let callback = TimeEventCallback::from(TestCallback::default());
2128 test_clock
2129 .set_time_alert_ns("alert", alert_time, Some(callback), None)
2130 .unwrap();
2131 test_clock.cancel_timer("alert");
2132
2133 test_clock.cancel_default_handler();
2134
2135 test_clock
2137 .set_time_alert_ns("alert", alert_time, None, None)
2138 .unwrap();
2139 }
2140
2141 #[rstest]
2142 fn test_cancel_callbacks_preserves_default_handler(mut test_clock: TestClock) {
2143 test_clock.cancel_callbacks();
2145
2146 let alert_time: UnixNanos = (*test_clock.timestamp_ns() + 1000).into();
2147 test_clock
2148 .set_time_alert_ns("alert", alert_time, None, None)
2149 .unwrap();
2150 }
2151
2152 #[rstest]
2153 fn test_set_time_alert_default_impl(mut test_clock: TestClock) {
2154 let current_time = test_clock.utc_now();
2155 let alert_time = current_time + jiff::SignedDuration::from_secs(1);
2156
2157 test_clock
2159 .set_time_alert("alert_test", alert_time, None, None)
2160 .unwrap();
2161
2162 assert_eq!(test_clock.timer_count(), 1);
2163 assert_eq!(test_clock.timer_names(), vec!["alert_test"]);
2164
2165 let expected_ns = UnixNanos::from(alert_time);
2167 let next_time = test_clock.next_time_ns("alert_test").unwrap();
2168
2169 let diff = if next_time >= expected_ns {
2171 next_time.as_u64() - expected_ns.as_u64()
2172 } else {
2173 expected_ns.as_u64() - next_time.as_u64()
2174 };
2175 assert!(
2176 diff < 1000,
2177 "Timer should be set within 1 microsecond of expected time"
2178 );
2179 }
2180
2181 #[rstest]
2182 fn test_set_timer_default_impl(mut test_clock: TestClock) {
2183 let current_time = test_clock.utc_now();
2184 let start_time = current_time + jiff::SignedDuration::from_secs(1);
2185 let interval = Duration::from_millis(500);
2186
2187 test_clock
2189 .set_timer(
2190 "timer_test",
2191 interval,
2192 Some(start_time),
2193 None,
2194 None,
2195 None,
2196 None,
2197 )
2198 .unwrap();
2199
2200 assert_eq!(test_clock.timer_count(), 1);
2201 assert_eq!(test_clock.timer_names(), vec!["timer_test"]);
2202
2203 let start_ns = UnixNanos::from(start_time);
2205 let interval_ns = interval.as_nanos() as u64;
2206
2207 let events = test_clock.advance_time(start_ns + interval_ns * 3, true);
2208 assert_eq!(events.len(), 3); assert_eq!(*events[0].ts_event, *start_ns + interval_ns);
2212 assert_eq!(*events[1].ts_event, *start_ns + interval_ns * 2);
2213 assert_eq!(*events[2].ts_event, *start_ns + interval_ns * 3);
2214 }
2215
2216 #[rstest]
2217 fn test_set_timer_with_stop_time_default_impl(mut test_clock: TestClock) {
2218 let current_time = test_clock.utc_now();
2219 let start_time = current_time + jiff::SignedDuration::from_secs(1);
2220 let stop_time = current_time + jiff::SignedDuration::from_secs(3);
2221 let interval = Duration::from_secs(1);
2222
2223 test_clock
2225 .set_timer(
2226 "timer_with_stop",
2227 interval,
2228 Some(start_time),
2229 Some(stop_time),
2230 None,
2231 None,
2232 None,
2233 )
2234 .unwrap();
2235
2236 assert_eq!(test_clock.timer_count(), 1);
2237
2238 let stop_ns = UnixNanos::from(stop_time);
2240 let events = test_clock.advance_time(stop_ns + 1000, true);
2241
2242 assert_eq!(events.len(), 2);
2244
2245 let start_ns = UnixNanos::from(start_time);
2246 let interval_ns = interval.as_nanos() as u64;
2247 assert_eq!(*events[0].ts_event, *start_ns + interval_ns);
2248 assert_eq!(*events[1].ts_event, *start_ns + interval_ns * 2);
2249 }
2250
2251 #[rstest]
2252 fn test_set_timer_fire_immediately_default_impl(mut test_clock: TestClock) {
2253 let current_time = test_clock.utc_now();
2254 let start_time = current_time + jiff::SignedDuration::from_secs(1);
2255 let interval = Duration::from_millis(500);
2256
2257 test_clock
2259 .set_timer(
2260 "immediate_timer",
2261 interval,
2262 Some(start_time),
2263 None,
2264 None,
2265 None,
2266 Some(true),
2267 )
2268 .unwrap();
2269
2270 let start_ns = UnixNanos::from(start_time);
2271 let interval_ns = interval.as_nanos() as u64;
2272
2273 let events = test_clock.advance_time(start_ns + interval_ns, true);
2275
2276 assert_eq!(events.len(), 2);
2278 assert_eq!(*events[0].ts_event, *start_ns); assert_eq!(*events[1].ts_event, *start_ns + interval_ns); }
2281
2282 #[rstest]
2283 fn test_set_time_alert_when_alert_time_equals_current_time(mut test_clock: TestClock) {
2284 let current_time = test_clock.timestamp_ns();
2285
2286 test_clock
2288 .set_time_alert_ns("alert_at_current_time", current_time, None, None)
2289 .unwrap();
2290
2291 assert_eq!(test_clock.timer_count(), 1);
2292
2293 let events = test_clock.advance_time(current_time, true);
2295
2296 assert_eq!(events.len(), 1);
2298 assert_eq!(events[0].name.as_str(), "alert_at_current_time");
2299 assert_eq!(*events[0].ts_event, *current_time);
2300 }
2301
2302 #[rstest]
2303 fn test_cancel_and_reschedule_same_name(mut test_clock: TestClock) {
2304 let start = test_clock.timestamp_ns();
2305
2306 test_clock
2307 .set_time_alert_ns("timer", UnixNanos::from(*start + 1000), None, None)
2308 .unwrap();
2309 assert_eq!(test_clock.timer_count(), 1);
2310
2311 test_clock.cancel_timer("timer");
2312 assert_eq!(test_clock.timer_count(), 0);
2313
2314 test_clock
2315 .set_time_alert_ns("timer", UnixNanos::from(*start + 2000), None, None)
2316 .unwrap();
2317 assert_eq!(test_clock.timer_count(), 1);
2318
2319 let events = test_clock.advance_time(UnixNanos::from(*start + 1500), true);
2320 assert!(events.is_empty());
2321
2322 let events = test_clock.advance_time(UnixNanos::from(*start + 2000), true);
2323 assert_eq!(events.len(), 1);
2324 assert_eq!(*events[0].ts_event, *start + 2000);
2325 }
2326
2327 #[rstest]
2328 fn test_multiple_timers_same_timestamp_all_fire(mut test_clock: TestClock) {
2329 let fire_time = UnixNanos::from(*test_clock.timestamp_ns() + 1000);
2330
2331 for i in 0..5 {
2332 test_clock
2333 .set_time_alert_ns(&format!("timer_{i}"), fire_time, None, None)
2334 .unwrap();
2335 }
2336 assert_eq!(test_clock.timer_count(), 5);
2337
2338 let events = test_clock.advance_time(fire_time, true);
2339 assert_eq!(events.len(), 5);
2340 for event in &events {
2341 assert_eq!(*event.ts_event, *fire_time);
2342 }
2343 }
2344
2345 #[rstest]
2346 fn test_events_ordered_by_timestamp_after_advance() {
2347 let mut clock = TestClock::new();
2348 clock.register_default_handler(TestCallback::default().into());
2349 let start = clock.timestamp_ns();
2350
2351 clock
2352 .set_time_alert_ns("third", UnixNanos::from(*start + 300), None, None)
2353 .unwrap();
2354 clock
2355 .set_time_alert_ns("first", UnixNanos::from(*start + 100), None, None)
2356 .unwrap();
2357 clock
2358 .set_time_alert_ns("second", UnixNanos::from(*start + 200), None, None)
2359 .unwrap();
2360
2361 let events = clock.advance_time(UnixNanos::from(*start + 400), true);
2362 assert_eq!(events.len(), 3);
2363 assert_eq!(events[0].name.as_str(), "first");
2364 assert_eq!(events[1].name.as_str(), "second");
2365 assert_eq!(events[2].name.as_str(), "third");
2366 }
2367
2368 #[rstest]
2369 fn test_large_interval_does_not_overflow(mut test_clock: TestClock) {
2370 let start = test_clock.timestamp_ns();
2371 let large_interval: u64 = 1_000_000_000 * 60 * 60 * 24 * 365; test_clock
2374 .set_timer_ns(
2375 "large_interval",
2376 large_interval,
2377 Some(start),
2378 None,
2379 None,
2380 None,
2381 None,
2382 )
2383 .unwrap();
2384
2385 let events = test_clock.advance_time(UnixNanos::from(*start + large_interval), true);
2386 assert_eq!(events.len(), 1);
2387 assert_eq!(*events[0].ts_event, *start + large_interval);
2388 }
2389
2390 #[rstest]
2391 fn test_near_zero_interval_fires_correctly(mut test_clock: TestClock) {
2392 let start = test_clock.timestamp_ns();
2393
2394 test_clock
2395 .set_timer_ns("tiny", 1, Some(start), None, None, None, None)
2396 .unwrap();
2397
2398 let events = test_clock.advance_time(UnixNanos::from(*start + 10), true);
2399 assert_eq!(events.len(), 10);
2400
2401 for i in 1..events.len() {
2402 assert!(events[i].ts_event >= events[i - 1].ts_event);
2403 }
2404 }
2405
2406 #[rstest]
2407 fn test_repeated_advance_to_same_time_no_double_fire(mut test_clock: TestClock) {
2408 let fire_time = UnixNanos::from(*test_clock.timestamp_ns() + 1000);
2409
2410 test_clock
2411 .set_time_alert_ns("once", fire_time, None, None)
2412 .unwrap();
2413
2414 let events1 = test_clock.advance_time(fire_time, true);
2415 assert_eq!(events1.len(), 1);
2416
2417 let events2 = test_clock.advance_time(fire_time, true);
2418 assert!(events2.is_empty());
2419 }
2420
2421 #[rstest]
2422 fn test_advance_with_no_timers(mut test_clock: TestClock) {
2423 let start = test_clock.timestamp_ns();
2424
2425 let events = test_clock.advance_time(UnixNanos::from(*start + 1000), true);
2426 assert!(events.is_empty());
2427 assert_eq!(*test_clock.timestamp_ns(), *start + 1000);
2428 }
2429
2430 #[rstest]
2431 fn test_set_time_alert_rejects_unconvertible_datetime(mut test_clock: TestClock) {
2432 let pre_epoch = Timestamp::from_nanosecond(-1).unwrap();
2433
2434 let err = test_clock
2435 .set_time_alert("pre_epoch_alert", pre_epoch, None, None)
2436 .unwrap_err();
2437 assert!(
2438 err.to_string().contains("cannot be negative"),
2439 "unexpected error: {err}"
2440 );
2441
2442 let err = test_clock
2443 .set_time_alert("out_of_range_alert", Timestamp::MAX, None, None)
2444 .unwrap_err();
2445 assert!(
2446 err.to_string().contains("out of range"),
2447 "unexpected error: {err}"
2448 );
2449
2450 assert_eq!(test_clock.timer_count(), 0);
2451 }
2452
2453 #[rstest]
2454 fn test_set_timer_rejects_unconvertible_datetime(mut test_clock: TestClock) {
2455 let pre_epoch = Timestamp::from_nanosecond(-1).unwrap();
2456 let valid_start = test_clock.utc_now() + jiff::SignedDuration::from_secs(1);
2457
2458 let err = test_clock
2459 .set_timer(
2460 "pre_epoch_start",
2461 Duration::from_secs(1),
2462 Some(pre_epoch),
2463 None,
2464 None,
2465 None,
2466 None,
2467 )
2468 .unwrap_err();
2469 assert!(
2470 err.to_string().contains("cannot be negative"),
2471 "unexpected error: {err}"
2472 );
2473
2474 let err = test_clock
2475 .set_timer(
2476 "pre_epoch_stop",
2477 Duration::from_secs(1),
2478 Some(valid_start),
2479 Some(pre_epoch),
2480 None,
2481 None,
2482 None,
2483 )
2484 .unwrap_err();
2485 assert!(
2486 err.to_string().contains("cannot be negative"),
2487 "unexpected error: {err}"
2488 );
2489
2490 assert_eq!(test_clock.timer_count(), 0);
2491 }
2492
2493 #[rstest]
2494 fn test_set_timer_rejects_interval_exceeding_u64_nanos(mut test_clock: TestClock) {
2495 let interval = Duration::from_secs(u64::MAX / NANOSECONDS_IN_SECOND + 1);
2496
2497 let err = test_clock
2498 .set_timer("overflow", interval, None, None, None, None, None)
2499 .unwrap_err();
2500
2501 assert_eq!(err.to_string(), "Interval exceeds u64 nanoseconds");
2502 assert_eq!(test_clock.timer_count(), 0);
2503 }
2504
2505 #[rstest]
2506 fn test_set_timer_ns_rejects_unrepresentable_first_event_without_replacing_timer(
2507 mut test_clock: TestClock,
2508 ) {
2509 test_clock.set_time(UnixNanos::from(1));
2510 test_clock
2511 .set_timer_ns("overflow", 1, None, None, None, None, None)
2512 .unwrap();
2513
2514 let err = test_clock
2515 .set_timer_ns("overflow", u64::MAX, None, None, None, None, None)
2516 .unwrap_err();
2517
2518 assert_eq!(
2519 err.to_string(),
2520 "Timer 'overflow' first event time exceeds UnixNanos range"
2521 );
2522 assert_eq!(test_clock.timer_count(), 1);
2523 assert_eq!(
2524 test_clock.next_time_ns("overflow"),
2525 Some(UnixNanos::from(2))
2526 );
2527 }
2528
2529 #[rstest]
2530 fn test_clock_api_handlers_reject_invalid_time_inputs() {
2531 let calls = Arc::new(Mutex::new(Vec::new()));
2532 let calls_for_alert = Arc::clone(&calls);
2533 let calls_for_timer = Arc::clone(&calls);
2534
2535 let clock = ClockApi::from_handlers(
2536 || UnixNanos::from(1_700_000_000_000_000_000),
2537 move |name, _, _, _| {
2538 calls_for_alert.lock().push(name.to_string());
2539 Ok(())
2540 },
2541 move |name, _, _, _, _, _, _| {
2542 calls_for_timer.lock().push(name.to_string());
2543 Ok(())
2544 },
2545 Vec::new,
2546 || 0,
2547 |_| false,
2548 |_| None,
2549 |_| {},
2550 || {},
2551 );
2552
2553 let pre_epoch = Timestamp::from_nanosecond(-1).unwrap();
2554 clock
2555 .set_time_alert("alert", pre_epoch, None, None)
2556 .unwrap_err();
2557 clock
2558 .set_timer(
2559 "timer",
2560 Duration::from_secs(1),
2561 Some(pre_epoch),
2562 None,
2563 None,
2564 None,
2565 None,
2566 )
2567 .unwrap_err();
2568 let interval = Duration::from_secs(u64::MAX / NANOSECONDS_IN_SECOND + 1);
2569 let err = clock
2570 .set_timer("overflow", interval, None, None, None, None, None)
2571 .unwrap_err();
2572
2573 assert_eq!(err.to_string(), "Interval exceeds u64 nanoseconds");
2574 assert!(calls.lock().is_empty());
2575 }
2576
2577 #[rstest]
2578 fn test_clock_api_new_uses_native_backing(test_clock: TestClock) {
2579 let clock = RefCell::new(test_clock);
2580 let api = ClockApi::new(&clock);
2581
2582 api.set_timer_ns(
2583 "native-timer",
2584 1_000,
2585 None,
2586 None,
2587 None,
2588 Some(true),
2589 Some(false),
2590 )
2591 .unwrap();
2592
2593 assert_eq!(api.timer_count(), 1);
2594 assert_eq!(api.timer_names(), vec!["native-timer".to_string()]);
2595 assert_eq!(
2596 api.next_time_ns("native-timer"),
2597 Some(UnixNanos::from(1_000))
2598 );
2599 }
2600
2601 #[rstest]
2602 fn test_clock_api_handlers_back_full_surface() {
2603 let alerts = Arc::new(Mutex::new(Vec::new()));
2604 let timers = Arc::new(Mutex::new(Vec::new()));
2605 let cancellations = Arc::new(Mutex::new(Vec::new()));
2606 let cancel_all = Arc::new(Mutex::new(false));
2607
2608 let alerts_for_handler = Arc::clone(&alerts);
2609 let timers_for_handler = Arc::clone(&timers);
2610 let cancellations_for_handler = Arc::clone(&cancellations);
2611 let cancel_all_for_handler = Arc::clone(&cancel_all);
2612
2613 let clock = ClockApi::from_handlers(
2614 || UnixNanos::from(1_700_000_000_123_456_789),
2615 move |name, alert_time_ns, _callback, allow_past| {
2616 alerts_for_handler
2617 .lock()
2618 .push((name.to_string(), alert_time_ns, allow_past));
2619 Ok(())
2620 },
2621 move |name,
2622 interval_ns,
2623 start_time_ns,
2624 stop_time_ns,
2625 _callback,
2626 allow_past,
2627 fire_immediately| {
2628 timers_for_handler.lock().push((
2629 name.to_string(),
2630 interval_ns,
2631 start_time_ns,
2632 stop_time_ns,
2633 allow_past,
2634 fire_immediately,
2635 ));
2636 Ok(())
2637 },
2638 || vec!["alpha".to_string(), "beta".to_string()],
2639 || 2,
2640 |name| name == "alpha",
2641 |name| (name == "alpha").then(|| UnixNanos::from(1_700_000_000_999_000_000)),
2642 move |name| {
2643 cancellations_for_handler.lock().push(name.to_string());
2644 },
2645 move || {
2646 *cancel_all_for_handler.lock() = true;
2647 },
2648 );
2649
2650 let alert_time = Timestamp::from_nanosecond(1_700_000_000_333_000_000).unwrap();
2651 let start_time = Timestamp::from_nanosecond(1_700_000_000_444_000_000).unwrap();
2652 let stop_time = Timestamp::from_nanosecond(1_700_000_001_444_000_000).unwrap();
2653 clock
2654 .set_time_alert("alert", alert_time, None, Some(false))
2655 .unwrap();
2656 clock
2657 .set_time_alert_ns(
2658 "alert-ns",
2659 UnixNanos::from(1_700_000_000_555_000_000),
2660 None,
2661 Some(true),
2662 )
2663 .unwrap();
2664 clock
2665 .set_timer(
2666 "timer",
2667 Duration::from_millis(250),
2668 Some(start_time),
2669 Some(stop_time),
2670 None,
2671 Some(true),
2672 Some(false),
2673 )
2674 .unwrap();
2675 clock
2676 .set_timer_ns(
2677 "timer-ns",
2678 500_000_000,
2679 Some(UnixNanos::from(1_700_000_000_666_000_000)),
2680 Some(UnixNanos::from(1_700_000_001_666_000_000)),
2681 None,
2682 Some(false),
2683 Some(true),
2684 )
2685 .unwrap();
2686 clock.cancel_timer("alpha");
2687 clock.cancel_timers();
2688
2689 assert_eq!(
2690 clock.timestamp_ns(),
2691 UnixNanos::from(1_700_000_000_123_456_789)
2692 );
2693 assert_eq!(clock.timestamp_us(), 1_700_000_000_123_456);
2694 assert_eq!(clock.timestamp_ms(), 1_700_000_000_123);
2695 assert_eq!(clock.timestamp(), 1_700_000_000.123_456_7);
2696 assert_eq!(
2697 clock.utc_now(),
2698 Timestamp::from_nanosecond(1_700_000_000_123_456_789).unwrap()
2699 );
2700 assert_eq!(clock.timer_names(), vec!["alpha", "beta"]);
2701 assert_eq!(clock.timer_count(), 2);
2702 assert!(clock.timer_exists("alpha"));
2703 assert!(!clock.timer_exists("gamma"));
2704 assert_eq!(
2705 clock.next_time_ns("alpha"),
2706 Some(UnixNanos::from(1_700_000_000_999_000_000))
2707 );
2708 assert_eq!(
2709 alerts.lock().as_slice(),
2710 &[
2711 (
2712 "alert".to_string(),
2713 UnixNanos::from(1_700_000_000_333_000_000),
2714 Some(false)
2715 ),
2716 (
2717 "alert-ns".to_string(),
2718 UnixNanos::from(1_700_000_000_555_000_000),
2719 Some(true)
2720 )
2721 ]
2722 );
2723 assert_eq!(
2724 timers.lock().as_slice(),
2725 &[
2726 (
2727 "timer".to_string(),
2728 250_000_000,
2729 Some(UnixNanos::from(1_700_000_000_444_000_000)),
2730 Some(UnixNanos::from(1_700_000_001_444_000_000)),
2731 Some(true),
2732 Some(false)
2733 ),
2734 (
2735 "timer-ns".to_string(),
2736 500_000_000,
2737 Some(UnixNanos::from(1_700_000_000_666_000_000)),
2738 Some(UnixNanos::from(1_700_000_001_666_000_000)),
2739 Some(false),
2740 Some(true)
2741 )
2742 ]
2743 );
2744 assert_eq!(cancellations.lock().as_slice(), &["alpha".to_string()]);
2745 assert!(*cancel_all.lock());
2746 }
2747
2748 proptest! {
2749 #[rstest]
2750 fn prop_test_clock_operations_match_reference(
2751 initial_time_ns in clock_time_strategy(),
2752 operations in prop::collection::vec(clock_operation_strategy(), 1..=50),
2753 ) {
2754 check_clock_operations(initial_time_ns, operations)?;
2755 }
2756
2757 #[rstest]
2758 fn prop_test_clock_max_time_alert(initial_time_ns in clock_time_strategy()) {
2759 check_clock_max_time_alert(initial_time_ns)?;
2760 }
2761 }
2762
2763 #[derive(Clone, Debug)]
2764 enum ClockOperation {
2765 Set {
2766 name_index: usize,
2767 interval_ns: u64,
2768 stop_after_ns: Option<u64>,
2769 fire_immediately: bool,
2770 },
2771 Cancel(usize),
2772 Advance {
2773 delta_ns: u64,
2774 set_time: bool,
2775 },
2776 }
2777
2778 #[derive(Clone, Debug)]
2779 struct TimerModel {
2780 interval: u64,
2781 next: u64,
2782 stop: Option<u64>,
2783 }
2784
2785 fn clock_operation_strategy() -> impl Strategy<Value = ClockOperation> {
2786 prop_oneof![
2787 5 => (
2788 0usize..CLOCK_TIMER_NAMES.len(),
2789 1u64..=15,
2790 prop::option::of(1u64..=60),
2791 prop::bool::ANY,
2792 )
2793 .prop_map(
2794 |(name_index, interval_ns, stop_after_ns, fire_immediately)| {
2795 ClockOperation::Set {
2796 name_index,
2797 interval_ns,
2798 stop_after_ns,
2799 fire_immediately,
2800 }
2801 },
2802 ),
2803 2 => (0usize..CLOCK_TIMER_NAMES.len()).prop_map(ClockOperation::Cancel),
2804 5 => (0u64..=30, prop::bool::ANY)
2805 .prop_map(|(delta_ns, set_time)| ClockOperation::Advance { delta_ns, set_time }),
2806 ]
2807 }
2808
2809 fn clock_time_strategy() -> impl Strategy<Value = u64> {
2810 prop_oneof![
2811 6 => 0u64..=u64::MAX - CLOCK_TIME_HEADROOM,
2812 2 => 0u64..=1_000_000,
2813 1 => Just(1_700_000_000_000_000_000),
2814 1 => Just(u64::MAX - CLOCK_TIME_HEADROOM),
2815 ]
2816 }
2817
2818 fn check_clock_operations(
2819 initial_time_ns: u64,
2820 operations: Vec<ClockOperation>,
2821 ) -> TestCaseResult {
2822 let mut clock = TestClock::new();
2823 clock.register_default_handler(TestCallback::default().into());
2824 clock.set_time(UnixNanos::from(initial_time_ns));
2825
2826 let mut time_ns = initial_time_ns;
2827 let mut timers = BTreeMap::new();
2828
2829 for operation in operations {
2830 match operation {
2831 ClockOperation::Set {
2832 name_index,
2833 interval_ns,
2834 stop_after_ns,
2835 fire_immediately,
2836 } => {
2837 let name = clock_timer_name(name_index);
2838 let stop_time_ns = stop_after_ns.map(|offset| time_ns + offset);
2839 clock
2840 .set_timer_ns(
2841 name.as_str(),
2842 interval_ns,
2843 Some(UnixNanos::from(time_ns)),
2844 stop_time_ns.map(UnixNanos::from),
2845 None,
2846 None,
2847 Some(fire_immediately),
2848 )
2849 .expect("generated timer configuration should be valid");
2850 timers.insert(
2851 name,
2852 TimerModel {
2853 interval: interval_ns,
2854 next: if fire_immediately {
2855 time_ns
2856 } else {
2857 time_ns + interval_ns
2858 },
2859 stop: stop_time_ns,
2860 },
2861 );
2862 }
2863 ClockOperation::Cancel(name_index) => {
2864 let name = clock_timer_name(name_index);
2865 clock.cancel_timer(name.as_str());
2866 timers.remove(&name);
2867 }
2868 ClockOperation::Advance { delta_ns, set_time } => {
2869 let to_time_ns = time_ns + delta_ns;
2870 let actual: Vec<(u64, Ustr, u64)> = clock
2871 .advance_time(UnixNanos::from(to_time_ns), set_time)
2872 .into_iter()
2873 .map(|event| (event.ts_event.as_u64(), event.name, event.ts_init.as_u64()))
2874 .collect();
2875 let expected = advance_clock_timers(&mut timers, to_time_ns);
2876
2877 prop_assert_eq!(actual, expected);
2878
2879 if set_time {
2880 time_ns = to_time_ns;
2881 }
2882 }
2883 }
2884
2885 assert_clock_state(&clock, &timers, time_ns)?;
2886 }
2887
2888 Ok(())
2889 }
2890
2891 fn check_clock_max_time_alert(initial_time_ns: u64) -> TestCaseResult {
2892 let mut clock = TestClock::new();
2893 clock.register_default_handler(TestCallback::default().into());
2894 clock.set_time(UnixNanos::from(initial_time_ns));
2895 let name = Ustr::from("terminal-alert");
2896 clock
2897 .set_time_alert_ns(name.as_str(), UnixNanos::max(), None, None)
2898 .expect("maximum timestamp should be a valid time alert");
2899
2900 let events: Vec<(u64, Ustr, u64)> = clock
2901 .advance_time(UnixNanos::max(), true)
2902 .into_iter()
2903 .map(|event| (event.ts_event.as_u64(), event.name, event.ts_init.as_u64()))
2904 .collect();
2905
2906 prop_assert_eq!(events, vec![(u64::MAX, name, u64::MAX)]);
2907 prop_assert_eq!(clock.timestamp_ns(), UnixNanos::max());
2908 prop_assert_eq!(clock.timer_count(), 0);
2909 prop_assert!(clock.timer_names().is_empty());
2910 prop_assert!(!clock.timer_exists(&name));
2911 prop_assert_eq!(clock.next_time_ns(name.as_str()), None);
2912
2913 Ok(())
2914 }
2915
2916 fn advance_clock_timers(
2917 timers: &mut BTreeMap<Ustr, TimerModel>,
2918 to_time_ns: u64,
2919 ) -> Vec<(u64, Ustr, u64)> {
2920 let mut events = Vec::new();
2921
2922 timers.retain(|name, timer| {
2923 while timer.next <= to_time_ns {
2924 if timer
2925 .stop
2926 .is_some_and(|stop_time_ns| timer.next > stop_time_ns)
2927 {
2928 return false;
2929 }
2930
2931 let event_time_ns = timer.next;
2932 events.push((event_time_ns, *name, event_time_ns));
2933 let Some(following_time_ns) = event_time_ns.checked_add(timer.interval) else {
2934 return false;
2935 };
2936 timer.next = following_time_ns;
2937 if timer.stop == Some(event_time_ns) {
2938 return false;
2939 }
2940 }
2941
2942 true
2943 });
2944
2945 events.sort_by(|a, b| a.0.cmp(&b.0).then_with(|| a.1.cmp(&b.1)));
2946 events
2947 }
2948
2949 fn assert_clock_state(
2950 clock: &TestClock,
2951 timers: &BTreeMap<Ustr, TimerModel>,
2952 time_ns: u64,
2953 ) -> TestCaseResult {
2954 let expected_names: Vec<&str> = timers.keys().map(Ustr::as_str).collect();
2955
2956 prop_assert_eq!(clock.timestamp_ns(), UnixNanos::from(time_ns));
2957 prop_assert_eq!(clock.timer_count(), timers.len());
2958 prop_assert_eq!(clock.timer_names(), expected_names);
2959
2960 for name in CLOCK_TIMER_NAMES.map(Ustr::from) {
2961 let expected = timers.get(&name);
2962 prop_assert_eq!(clock.timer_exists(&name), expected.is_some());
2963 prop_assert_eq!(
2964 clock
2965 .next_time_ns(name.as_str())
2966 .map(|next_time_ns| next_time_ns.as_u64()),
2967 expected.map(|timer| timer.next),
2968 );
2969 }
2970
2971 Ok(())
2972 }
2973
2974 const CLOCK_TIMER_NAMES: [&str; 4] = ["timer-0", "timer-1", "timer-2", "timer-3"];
2975 const CLOCK_TIME_HEADROOM: u64 = 100_000;
2976
2977 fn clock_timer_name(index: usize) -> Ustr {
2978 Ustr::from(CLOCK_TIMER_NAMES[index])
2979 }
2980}