1use std::{
19 collections::{BTreeMap, BinaryHeap, HashMap},
20 ops::Deref,
21 pin::Pin,
22 sync::Arc,
23 task::{Context, Poll},
24};
25
26use chrono::{DateTime, Utc};
27use futures::Stream;
28use nautilus_core::{
29 AtomicTime, UnixNanos,
30 correctness::{check_positive_u64, check_predicate_true, check_valid_string},
31 time::get_atomic_clock_realtime,
32};
33use tokio::sync::Mutex;
34use ustr::Ustr;
35
36use crate::timer::{
37 LiveTimer, TestTimer, TimeEvent, TimeEventCallback, TimeEventHandlerV2, create_valid_interval,
38};
39
40pub trait Clock {
46 fn utc_now(&self) -> DateTime<Utc> {
48 DateTime::from_timestamp_nanos(self.timestamp_ns().as_i64())
49 }
50
51 fn timestamp_ns(&self) -> UnixNanos;
53
54 fn timestamp_us(&self) -> u64;
56
57 fn timestamp_ms(&self) -> u64;
59
60 fn timestamp(&self) -> f64;
62
63 fn timer_names(&self) -> Vec<&str>;
65
66 fn timer_count(&self) -> usize;
68
69 fn register_default_handler(&mut self, callback: TimeEventCallback);
72
73 fn get_handler(&self, event: TimeEvent) -> TimeEventHandlerV2;
77
78 fn set_time_alert_ns(
81 &mut self,
82 name: &str,
83 alert_time_ns: UnixNanos,
84 callback: Option<TimeEventCallback>,
85 allow_past: Option<bool>,
86 ) -> anyhow::Result<()>;
87
88 fn set_timer_ns(
92 &mut self,
93 name: &str,
94 interval_ns: u64,
95 start_time_ns: UnixNanos,
96 stop_time_ns: Option<UnixNanos>,
97 callback: Option<TimeEventCallback>,
98 allow_past: Option<bool>,
99 ) -> anyhow::Result<()>;
100
101 fn next_time_ns(&self, name: &str) -> UnixNanos;
105 fn cancel_timer(&mut self, name: &str);
106 fn cancel_timers(&mut self);
107
108 fn reset(&mut self);
109}
110
111#[derive(Debug)]
115pub struct TestClock {
116 time: AtomicTime,
117 timers: BTreeMap<Ustr, TestTimer>,
120 default_callback: Option<TimeEventCallback>,
121 callbacks: HashMap<Ustr, TimeEventCallback>,
122 heap: BinaryHeap<TimeEvent>,
123}
124
125impl TestClock {
126 #[must_use]
128 pub fn new() -> Self {
129 Self {
130 time: AtomicTime::new(false, UnixNanos::default()),
131 timers: BTreeMap::new(),
132 default_callback: None,
133 callbacks: HashMap::new(),
134 heap: BinaryHeap::new(),
135 }
136 }
137
138 #[must_use]
140 pub const fn get_timers(&self) -> &BTreeMap<Ustr, TestTimer> {
141 &self.timers
142 }
143
144 pub fn advance_time(&mut self, to_time_ns: UnixNanos, set_time: bool) -> Vec<TimeEvent> {
153 assert!(
155 to_time_ns >= self.time.get_time_ns(),
156 "`to_time_ns` {to_time_ns} was < `self.time.get_time_ns()` {}",
157 self.time.get_time_ns()
158 );
159
160 if set_time {
161 self.time.set_time(to_time_ns);
162 }
163
164 let mut events: Vec<TimeEvent> = Vec::new();
166 self.timers.retain(|_, timer| {
167 timer.advance(to_time_ns).for_each(|event| {
168 events.push(event);
169 });
170
171 !timer.is_expired()
172 });
173
174 events.sort_by(|a, b| a.ts_event.cmp(&b.ts_event));
175 events
176 }
177
178 pub fn advance_to_time_on_heap(&mut self, to_time_ns: UnixNanos) {
184 assert!(
186 to_time_ns >= self.time.get_time_ns(),
187 "`to_time_ns` {to_time_ns} was < `self.time.get_time_ns()` {}",
188 self.time.get_time_ns()
189 );
190
191 self.time.set_time(to_time_ns);
192
193 self.timers.retain(|_, timer| {
195 timer.advance(to_time_ns).for_each(|event| {
196 self.heap.push(event);
197 });
198
199 !timer.is_expired()
200 });
201 }
202
203 #[must_use]
209 pub fn match_handlers(&self, events: Vec<TimeEvent>) -> Vec<TimeEventHandlerV2> {
210 events
211 .into_iter()
212 .map(|event| {
213 let callback = self.callbacks.get(&event.name).cloned().unwrap_or_else(|| {
214 self.default_callback
217 .clone()
218 .expect("Default callback should exist")
219 });
220 TimeEventHandlerV2::new(event, callback)
221 })
222 .collect()
223 }
224}
225
226impl Iterator for TestClock {
227 type Item = TimeEventHandlerV2;
228
229 fn next(&mut self) -> Option<Self::Item> {
230 self.heap.pop().map(|event| self.get_handler(event))
231 }
232}
233
234impl Default for TestClock {
235 fn default() -> Self {
237 Self::new()
238 }
239}
240
241impl Deref for TestClock {
242 type Target = AtomicTime;
243
244 fn deref(&self) -> &Self::Target {
245 &self.time
246 }
247}
248
249impl Clock for TestClock {
250 fn timestamp_ns(&self) -> UnixNanos {
251 self.time.get_time_ns()
252 }
253
254 fn timestamp_us(&self) -> u64 {
255 self.time.get_time_us()
256 }
257
258 fn timestamp_ms(&self) -> u64 {
259 self.time.get_time_ms()
260 }
261
262 fn timestamp(&self) -> f64 {
263 self.time.get_time()
264 }
265
266 fn timer_names(&self) -> Vec<&str> {
267 self.timers
268 .iter()
269 .filter(|(_, timer)| !timer.is_expired())
270 .map(|(k, _)| k.as_str())
271 .collect()
272 }
273
274 fn timer_count(&self) -> usize {
275 self.timers
276 .iter()
277 .filter(|(_, timer)| !timer.is_expired())
278 .count()
279 }
280
281 fn register_default_handler(&mut self, callback: TimeEventCallback) {
282 self.default_callback = Some(callback);
283 }
284
285 fn get_handler(&self, event: TimeEvent) -> TimeEventHandlerV2 {
286 let callback = self
288 .callbacks
289 .get(&event.name)
290 .cloned()
291 .or_else(|| self.default_callback.clone())
292 .unwrap_or_else(|| panic!("Event '{}' should have associated handler", event.name));
293
294 TimeEventHandlerV2::new(event, callback)
295 }
296
297 fn set_time_alert_ns(
298 &mut self,
299 name: &str,
300 mut alert_time_ns: UnixNanos, callback: Option<TimeEventCallback>,
302 allow_past: Option<bool>,
303 ) -> anyhow::Result<()> {
304 check_valid_string(name, stringify!(name))?;
305
306 let name = Ustr::from(name);
307 let allow_past = allow_past.unwrap_or(true);
308
309 check_predicate_true(
310 callback.is_some()
311 | self.callbacks.contains_key(&name)
312 | self.default_callback.is_some(),
313 "No callbacks provided",
314 )?;
315
316 match callback {
317 Some(callback_py) => self.callbacks.insert(name, callback_py),
318 None => None,
319 };
320
321 self.cancel_timer(name.as_str());
323
324 let ts_now = self.get_time_ns();
325
326 if alert_time_ns < ts_now {
327 if allow_past {
328 alert_time_ns = ts_now;
329 log::warn!(
330 "Timer '{name}' alert time {} was in the past, adjusted to current time for immediate firing",
331 alert_time_ns.to_rfc3339(),
332 );
333 } else {
334 anyhow::bail!(
335 "Timer '{name}' alert time {} was in the past (current time is {})",
336 alert_time_ns.to_rfc3339(),
337 ts_now.to_rfc3339(),
338 );
339 }
340 }
341
342 let interval_ns = create_valid_interval((alert_time_ns - ts_now).into());
344
345 let timer = TestTimer::new(name, interval_ns, ts_now, Some(alert_time_ns));
346 self.timers.insert(name, timer);
347
348 Ok(())
349 }
350
351 fn set_timer_ns(
352 &mut self,
353 name: &str,
354 interval_ns: u64,
355 start_time_ns: UnixNanos,
356 stop_time_ns: Option<UnixNanos>,
357 callback: Option<TimeEventCallback>,
358 allow_past: Option<bool>,
359 ) -> anyhow::Result<()> {
360 check_valid_string(name, stringify!(name))?;
361 check_positive_u64(interval_ns, stringify!(interval_ns))?;
362 check_predicate_true(
363 callback.is_some() | self.default_callback.is_some(),
364 "No callbacks provided",
365 )?;
366
367 let name = Ustr::from(name);
368 let allow_past = allow_past.unwrap_or(true);
369
370 match callback {
371 Some(callback_py) => self.callbacks.insert(name, callback_py),
372 None => None,
373 };
374
375 let mut start_time_ns = start_time_ns;
376 let ts_now = self.get_time_ns();
377
378 if start_time_ns == 0 {
379 start_time_ns = self.timestamp_ns();
381 } else if start_time_ns < ts_now && !allow_past {
382 anyhow::bail!(
383 "Timer '{name}' start time {} was in the past (current time is {})",
384 start_time_ns.to_rfc3339(),
385 ts_now.to_rfc3339(),
386 );
387 }
388
389 if let Some(stop_time) = stop_time_ns {
390 if stop_time <= start_time_ns {
391 anyhow::bail!(
392 "Timer '{name}' stop time {} must be after start time {}",
393 stop_time.to_rfc3339(),
394 start_time_ns.to_rfc3339(),
395 );
396 }
397 }
398
399 let interval_ns = create_valid_interval(interval_ns);
400
401 let timer = TestTimer::new(name, interval_ns, start_time_ns, stop_time_ns);
402 self.timers.insert(name, timer);
403
404 Ok(())
405 }
406
407 fn next_time_ns(&self, name: &str) -> UnixNanos {
408 let timer = self.timers.get(&Ustr::from(name));
409 match timer {
410 None => 0.into(),
411 Some(timer) => timer.next_time_ns(),
412 }
413 }
414
415 fn cancel_timer(&mut self, name: &str) {
416 let timer = self.timers.remove(&Ustr::from(name));
417 match timer {
418 None => {}
419 Some(mut timer) => timer.cancel(),
420 }
421 }
422
423 fn cancel_timers(&mut self) {
424 for timer in &mut self.timers.values_mut() {
425 timer.cancel();
426 }
427 self.timers = BTreeMap::new();
428 }
429
430 fn reset(&mut self) {
431 self.time = AtomicTime::new(false, UnixNanos::default());
432 self.timers = BTreeMap::new();
433 self.heap = BinaryHeap::new();
434 self.callbacks = HashMap::new();
435 }
436}
437
438#[derive(Debug)]
442pub struct LiveClock {
443 time: &'static AtomicTime,
444 timers: HashMap<Ustr, LiveTimer>,
445 default_callback: Option<TimeEventCallback>,
446 pub heap: Arc<Mutex<BinaryHeap<TimeEvent>>>,
447 #[allow(dead_code)]
448 callbacks: HashMap<Ustr, TimeEventCallback>,
449}
450
451impl LiveClock {
452 #[must_use]
454 pub fn new() -> Self {
455 Self {
456 time: get_atomic_clock_realtime(),
457 timers: HashMap::new(),
458 default_callback: None,
459 heap: Arc::new(Mutex::new(BinaryHeap::new())),
460 callbacks: HashMap::new(),
461 }
462 }
463
464 #[must_use]
465 pub fn get_event_stream(&self) -> TimeEventStream {
466 TimeEventStream::new(self.heap.clone())
467 }
468
469 #[must_use]
470 pub const fn get_timers(&self) -> &HashMap<Ustr, LiveTimer> {
471 &self.timers
472 }
473
474 fn clear_expired_timers(&mut self) {
476 self.timers.retain(|_, timer| !timer.is_expired());
477 }
478}
479
480impl Default for LiveClock {
481 fn default() -> Self {
483 Self::new()
484 }
485}
486
487impl Deref for LiveClock {
488 type Target = AtomicTime;
489
490 fn deref(&self) -> &Self::Target {
491 self.time
492 }
493}
494
495impl Clock for LiveClock {
496 fn timestamp_ns(&self) -> UnixNanos {
497 self.time.get_time_ns()
498 }
499
500 fn timestamp_us(&self) -> u64 {
501 self.time.get_time_us()
502 }
503
504 fn timestamp_ms(&self) -> u64 {
505 self.time.get_time_ms()
506 }
507
508 fn timestamp(&self) -> f64 {
509 self.time.get_time()
510 }
511
512 fn timer_names(&self) -> Vec<&str> {
513 self.timers
514 .iter()
515 .filter(|(_, timer)| !timer.is_expired())
516 .map(|(k, _)| k.as_str())
517 .collect()
518 }
519
520 fn timer_count(&self) -> usize {
521 self.timers
522 .iter()
523 .filter(|(_, timer)| !timer.is_expired())
524 .count()
525 }
526
527 fn register_default_handler(&mut self, handler: TimeEventCallback) {
528 self.default_callback = Some(handler);
529 }
530
531 #[allow(unused_variables)]
532 fn get_handler(&self, event: TimeEvent) -> TimeEventHandlerV2 {
533 #[cfg(not(feature = "clock_v2"))]
534 panic!("Cannot get live clock handler without 'clock_v2' feature");
535
536 #[cfg(feature = "clock_v2")]
538 {
539 let callback = self
540 .callbacks
541 .get(&event.name)
542 .cloned()
543 .or_else(|| self.default_callback.clone())
544 .unwrap_or_else(|| panic!("Event '{}' should have associated handler", event.name));
545
546 TimeEventHandlerV2::new(event, callback)
547 }
548 }
549
550 fn set_time_alert_ns(
551 &mut self,
552 name: &str,
553 mut alert_time_ns: UnixNanos, callback: Option<TimeEventCallback>,
555 allow_past: Option<bool>,
556 ) -> anyhow::Result<()> {
557 check_valid_string(name, stringify!(name))?;
558
559 let name = Ustr::from(name);
560 let allow_past = allow_past.unwrap_or(true);
561
562 check_predicate_true(
563 callback.is_some()
564 | self.callbacks.contains_key(&name)
565 | self.default_callback.is_some(),
566 "No callbacks provided",
567 )?;
568
569 #[cfg(feature = "clock_v2")]
570 {
571 match callback.clone() {
572 Some(callback) => self.callbacks.insert(name, callback),
573 None => None,
574 };
575 }
576
577 let callback = match callback {
578 Some(callback) => callback,
579 None => {
580 if self.callbacks.contains_key(&name) {
581 self.callbacks.get(&name).unwrap().clone()
582 } else {
583 self.default_callback.clone().unwrap()
584 }
585 }
586 };
587
588 self.cancel_timer(name.as_str());
590
591 let ts_now = self.get_time_ns();
592
593 if alert_time_ns < ts_now {
595 if allow_past {
596 alert_time_ns = ts_now;
597 log::warn!(
598 "Timer '{name}' alert time {} was in the past, adjusted to current time for immediate firing",
599 alert_time_ns.to_rfc3339(),
600 );
601 } else {
602 anyhow::bail!(
603 "Timer '{name}' alert time {} was in the past (current time is {})",
604 alert_time_ns.to_rfc3339(),
605 ts_now.to_rfc3339(),
606 );
607 }
608 }
609
610 let interval_ns = create_valid_interval((alert_time_ns - ts_now).into());
612
613 #[cfg(not(feature = "clock_v2"))]
614 let mut timer = LiveTimer::new(name, interval_ns, ts_now, Some(alert_time_ns), callback);
615
616 #[cfg(feature = "clock_v2")]
617 let mut timer = LiveTimer::new(
618 name,
619 interval_ns,
620 ts_now,
621 Some(alert_time_ns),
622 callback,
623 self.heap.clone(),
624 );
625
626 timer.start();
627
628 self.clear_expired_timers();
629 self.timers.insert(name, timer);
630
631 Ok(())
632 }
633
634 fn set_timer_ns(
635 &mut self,
636 name: &str,
637 interval_ns: u64,
638 start_time_ns: UnixNanos,
639 stop_time_ns: Option<UnixNanos>,
640 callback: Option<TimeEventCallback>,
641 allow_past: Option<bool>,
642 ) -> anyhow::Result<()> {
643 check_valid_string(name, stringify!(name))?;
644 check_positive_u64(interval_ns, stringify!(interval_ns))?;
645 check_predicate_true(
646 callback.is_some() | self.default_callback.is_some(),
647 "No callbacks provided",
648 )?;
649
650 let name = Ustr::from(name);
651 let allow_past = allow_past.unwrap_or(true);
652
653 let callback = match callback {
654 Some(callback) => callback,
655 None => self.default_callback.clone().unwrap(),
656 };
657
658 #[cfg(feature = "clock_v2")]
659 {
660 self.callbacks.insert(name, callback.clone());
661 }
662
663 let mut start_time_ns = start_time_ns;
664 let ts_now = self.get_time_ns();
665
666 if start_time_ns == 0 {
667 start_time_ns = self.timestamp_ns();
669 } else if start_time_ns < ts_now && !allow_past {
670 anyhow::bail!(
671 "Timer '{name}' start time {} was in the past (current time is {})",
672 start_time_ns.to_rfc3339(),
673 ts_now.to_rfc3339(),
674 );
675 }
676
677 if let Some(stop_time) = stop_time_ns {
678 if stop_time <= start_time_ns {
679 anyhow::bail!(
680 "Timer '{name}' stop time {} must be after start time {}",
681 stop_time.to_rfc3339(),
682 start_time_ns.to_rfc3339(),
683 );
684 }
685 }
686
687 let interval_ns = create_valid_interval(interval_ns);
688
689 #[cfg(not(feature = "clock_v2"))]
690 let mut timer = LiveTimer::new(name, interval_ns, start_time_ns, stop_time_ns, callback);
691
692 #[cfg(feature = "clock_v2")]
693 let mut timer = LiveTimer::new(
694 name,
695 interval_ns,
696 start_time_ns,
697 stop_time_ns,
698 callback,
699 self.heap.clone(),
700 );
701 timer.start();
702
703 self.clear_expired_timers();
704 self.timers.insert(name, timer);
705
706 Ok(())
707 }
708
709 fn next_time_ns(&self, name: &str) -> UnixNanos {
710 let timer = self.timers.get(&Ustr::from(name));
711 match timer {
712 None => 0.into(),
713 Some(timer) => timer.next_time_ns(),
714 }
715 }
716
717 fn cancel_timer(&mut self, name: &str) {
718 let timer = self.timers.remove(&Ustr::from(name));
719 match timer {
720 None => {}
721 Some(mut timer) => {
722 timer.cancel();
723 }
724 }
725 }
726
727 fn cancel_timers(&mut self) {
728 for timer in &mut self.timers.values_mut() {
729 timer.cancel();
730 }
731 self.timers.clear();
732 }
733
734 fn reset(&mut self) {
735 self.timers = HashMap::new();
736 self.heap = Arc::new(Mutex::new(BinaryHeap::new()));
737 self.callbacks = HashMap::new();
738 }
739}
740
741#[derive(Debug)]
743pub struct TimeEventStream {
744 heap: Arc<Mutex<BinaryHeap<TimeEvent>>>,
745}
746
747impl TimeEventStream {
748 pub const fn new(heap: Arc<Mutex<BinaryHeap<TimeEvent>>>) -> Self {
749 Self { heap }
750 }
751}
752
753impl Stream for TimeEventStream {
754 type Item = TimeEvent;
755
756 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
757 let mut heap = match self.heap.try_lock() {
758 Ok(guard) => guard,
759 Err(e) => {
760 tracing::error!("Unable to get LiveClock heap lock: {e}");
761 cx.waker().wake_by_ref();
762 return Poll::Pending;
763 }
764 };
765
766 if let Some(event) = heap.pop() {
767 Poll::Ready(Some(event))
768 } else {
769 cx.waker().wake_by_ref();
770 Poll::Pending
771 }
772 }
773}
774
775#[cfg(test)]
779mod tests {
780 use std::{cell::RefCell, rc::Rc};
781
782 use rstest::{fixture, rstest};
783
784 use super::*;
785
786 #[derive(Default)]
787 struct TestCallback {
788 called: Rc<RefCell<bool>>,
789 }
790
791 impl TestCallback {
792 const fn new(called: Rc<RefCell<bool>>) -> Self {
793 Self { called }
794 }
795 }
796
797 impl From<TestCallback> for TimeEventCallback {
798 fn from(callback: TestCallback) -> Self {
799 Self::Rust(Rc::new(move |_event: TimeEvent| {
800 *callback.called.borrow_mut() = true;
801 }))
802 }
803 }
804
805 #[fixture]
806 pub fn test_clock() -> TestClock {
807 let mut clock = TestClock::new();
808 clock.register_default_handler(TestCallback::default().into());
809 clock
810 }
811
812 #[rstest]
813 fn test_time_monotonicity(mut test_clock: TestClock) {
814 let initial_time = test_clock.timestamp_ns();
815 test_clock.advance_time((*initial_time + 1000).into(), true);
816 assert!(test_clock.timestamp_ns() > initial_time);
817 }
818
819 #[rstest]
820 fn test_timer_registration(mut test_clock: TestClock) {
821 test_clock
822 .set_time_alert_ns(
823 "test_timer",
824 (*test_clock.timestamp_ns() + 1000).into(),
825 None,
826 None,
827 )
828 .unwrap();
829 assert_eq!(test_clock.timer_count(), 1);
830 assert_eq!(test_clock.timer_names(), vec!["test_timer"]);
831 }
832
833 #[rstest]
834 fn test_timer_expiration(mut test_clock: TestClock) {
835 let alert_time = (*test_clock.timestamp_ns() + 1000).into();
836 test_clock
837 .set_time_alert_ns("test_timer", alert_time, None, None)
838 .unwrap();
839 let events = test_clock.advance_time(alert_time, true);
840 assert_eq!(events.len(), 1);
841 assert_eq!(events[0].name.as_str(), "test_timer");
842 }
843
844 #[rstest]
845 fn test_timer_cancellation(mut test_clock: TestClock) {
846 test_clock
847 .set_time_alert_ns(
848 "test_timer",
849 (*test_clock.timestamp_ns() + 1000).into(),
850 None,
851 None,
852 )
853 .unwrap();
854 assert_eq!(test_clock.timer_count(), 1);
855 test_clock.cancel_timer("test_timer");
856 assert_eq!(test_clock.timer_count(), 0);
857 }
858
859 #[rstest]
860 fn test_time_advancement(mut test_clock: TestClock) {
861 let start_time = test_clock.timestamp_ns();
862 test_clock
863 .set_timer_ns("test_timer", 1000, start_time, None, None, None)
864 .unwrap();
865 let events = test_clock.advance_time((*start_time + 2500).into(), true);
866 assert_eq!(events.len(), 2);
867 assert_eq!(*events[0].ts_event, *start_time + 1000);
868 assert_eq!(*events[1].ts_event, *start_time + 2000);
869 }
870
871 #[rstest]
872 fn test_default_and_custom_callbacks() {
873 let mut clock = TestClock::new();
874 let default_called = Rc::new(RefCell::new(false));
875 let custom_called = Rc::new(RefCell::new(false));
876
877 let default_callback = TestCallback::new(Rc::clone(&default_called));
878 let custom_callback = TestCallback::new(Rc::clone(&custom_called));
879
880 clock.register_default_handler(TimeEventCallback::from(default_callback));
881 clock
882 .set_time_alert_ns(
883 "default_timer",
884 (*clock.timestamp_ns() + 1000).into(),
885 None,
886 None,
887 )
888 .unwrap();
889 clock
890 .set_time_alert_ns(
891 "custom_timer",
892 (*clock.timestamp_ns() + 1000).into(),
893 Some(TimeEventCallback::from(custom_callback)),
894 None,
895 )
896 .unwrap();
897
898 let events = clock.advance_time((*clock.timestamp_ns() + 1000).into(), true);
899 let handlers = clock.match_handlers(events);
900
901 for handler in handlers {
902 handler.callback.call(handler.event);
903 }
904
905 assert!(*default_called.borrow());
906 assert!(*custom_called.borrow());
907 }
908
909 #[rstest]
910 fn test_multiple_timers(mut test_clock: TestClock) {
911 let start_time = test_clock.timestamp_ns();
912 test_clock
913 .set_timer_ns("timer1", 1000, start_time, None, None, None)
914 .unwrap();
915 test_clock
916 .set_timer_ns("timer2", 2000, start_time, None, None, None)
917 .unwrap();
918 let events = test_clock.advance_time((*start_time + 2000).into(), true);
919 assert_eq!(events.len(), 3);
920 assert_eq!(events[0].name.as_str(), "timer1");
921 assert_eq!(events[1].name.as_str(), "timer1");
922 assert_eq!(events[2].name.as_str(), "timer2");
923 }
924
925 #[rstest]
926 fn test_allow_past_parameter_true(mut test_clock: TestClock) {
927 test_clock.set_time(UnixNanos::from(2000));
928 let current_time = test_clock.timestamp_ns();
929 let past_time = UnixNanos::from(current_time.as_u64() - 1000);
930
931 test_clock
933 .set_time_alert_ns("past_timer", past_time, None, Some(true))
934 .unwrap();
935
936 assert_eq!(test_clock.timer_count(), 1);
938 assert_eq!(test_clock.timer_names(), vec!["past_timer"]);
939
940 let next_time = test_clock.next_time_ns("past_timer");
942 assert!(next_time >= current_time);
943 }
944
945 #[rstest]
946 fn test_allow_past_parameter_false(mut test_clock: TestClock) {
947 test_clock.set_time(UnixNanos::from(2000));
948 let current_time = test_clock.timestamp_ns();
949 let past_time = current_time - 1000;
950
951 let result = test_clock.set_time_alert_ns("past_timer", past_time, None, Some(false));
953
954 assert!(result.is_err());
956 assert!(format!("{}", result.unwrap_err()).contains("was in the past"));
957
958 assert_eq!(test_clock.timer_count(), 0);
960 assert!(test_clock.timer_names().is_empty());
961 }
962
963 #[rstest]
964 fn test_invalid_stop_time_validation(mut test_clock: TestClock) {
965 test_clock.set_time(UnixNanos::from(2000));
966 let current_time = test_clock.timestamp_ns();
967 let start_time = current_time + 1000;
968 let stop_time = current_time + 500; let result = test_clock.set_timer_ns(
972 "invalid_timer",
973 100,
974 start_time,
975 Some(stop_time),
976 None,
977 None,
978 );
979
980 assert!(result.is_err());
982 assert!(format!("{}", result.unwrap_err()).contains("must be after start time"));
983
984 assert_eq!(test_clock.timer_count(), 0);
986 }
987}