nautilus_common/
clock.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Real-time and static test `Clock` implementations.
17
18use 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
40/// Represents a type of clock.
41///
42/// # Notes
43///
44/// An active timer is one which has not expired (`timer.is_expired == False`).
45pub trait Clock {
46    /// Returns the current date and time as a timezone-aware `DateTime<UTC>`.
47    fn utc_now(&self) -> DateTime<Utc> {
48        DateTime::from_timestamp_nanos(self.timestamp_ns().as_i64())
49    }
50
51    /// Returns the current UNIX timestamp in nanoseconds (ns).
52    fn timestamp_ns(&self) -> UnixNanos;
53
54    /// Returns the current UNIX timestamp in microseconds (μs).
55    fn timestamp_us(&self) -> u64;
56
57    /// Returns the current UNIX timestamp in milliseconds (ms).
58    fn timestamp_ms(&self) -> u64;
59
60    /// Returns the current UNIX timestamp in seconds.
61    fn timestamp(&self) -> f64;
62
63    /// Returns the names of active timers in the clock.
64    fn timer_names(&self) -> Vec<&str>;
65
66    /// Returns the count of active timers in the clock.
67    fn timer_count(&self) -> usize;
68
69    /// Register a default event handler for the clock. If a `Timer`
70    /// does not have an event handler, then this handler is used.
71    fn register_default_handler(&mut self, callback: TimeEventCallback);
72
73    /// Get handler for [`TimeEvent`]
74    ///
75    /// Note: Panics if the event does not have an associated handler
76    fn get_handler(&self, event: TimeEvent) -> TimeEventHandlerV2;
77
78    /// Set a `Timer` to alert at a particular time. Optional
79    /// callback gets used to handle generated events.
80    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    /// Set a `Timer` to start alerting at every interval
89    /// between start and stop time. Optional callback gets
90    /// used to handle generated event.
91    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    /// Returns the time interval in which the timer `name` is triggered.
102    ///
103    /// If the timer doesn't exist 0 is returned.
104    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/// A static test clock.
112///
113/// Stores the current timestamp internally which can be advanced.
114#[derive(Debug)]
115pub struct TestClock {
116    time: AtomicTime,
117    // use btree map to ensure stable ordering when scanning for timers
118    // in `advance_time`
119    timers: BTreeMap<Ustr, TestTimer>,
120    default_callback: Option<TimeEventCallback>,
121    callbacks: HashMap<Ustr, TimeEventCallback>,
122    heap: BinaryHeap<TimeEvent>,
123}
124
125impl TestClock {
126    /// Creates a new [`TestClock`] instance.
127    #[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    /// Returns a reference to the internal timers for the clock.
139    #[must_use]
140    pub const fn get_timers(&self) -> &BTreeMap<Ustr, TestTimer> {
141        &self.timers
142    }
143
144    /// Advances the internal clock to the specified `to_time_ns` and optionally sets the clock to that time.
145    ///
146    /// This function ensures that the clock behaves in a non-decreasing manner. If `set_time` is `true`,
147    /// the internal clock will be updated to the value of `to_time_ns`. Otherwise, the clock will advance
148    /// without explicitly setting the time.
149    ///
150    /// The method processes active timers, advancing them to `to_time_ns`, and collects any `TimeEvent`
151    /// objects that are triggered as a result. Only timers that are not expired are processed.
152    pub fn advance_time(&mut self, to_time_ns: UnixNanos, set_time: bool) -> Vec<TimeEvent> {
153        // Time should be non-decreasing
154        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        // Iterate and advance timers and collect events. Only retain alive timers.
165        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    /// Advances the internal clock to the specified `to_time_ns` and optionally sets the clock to that time.
179    ///
180    /// Pushes the [`TimeEvent`]s on the heap to ensure ordering
181    ///
182    /// Note: `set_time` is not used but present to keep backward compatible api call
183    pub fn advance_to_time_on_heap(&mut self, to_time_ns: UnixNanos) {
184        // Time should be non-decreasing
185        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        // Iterate and advance timers and push events to heap. Only retain alive timers.
194        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    /// Matches `TimeEvent` objects with their corresponding event handlers.
204    ///
205    /// This function takes an `events` vector of `TimeEvent` objects, assumes they are already sorted
206    /// by their `ts_event`, and matches them with the appropriate callback handler from the internal
207    /// registry of callbacks. If no specific callback is found for an event, the default callback is used.
208    #[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                    // If callback_py is None, use the default_callback_py
215                    // TODO: clone for now
216                    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    /// Creates a new default [`TestClock`] instance.
236    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        // Get the callback from either the event-specific callbacks or default callback
287        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, // mut allows adjustment based on allow_past
301        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        // This allows to reuse a time alert without updating the callback, for example for non regular monthly alerts
322        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        // Safe to calculate interval now that we've ensured alert_time_ns >= ts_now
343        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            // Zero start time indicates no explicit start; we use the current time
380            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/// A real-time clock which uses system time.
439///
440/// Timestamps are guaranteed to be unique and monotonically increasing.
441#[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    /// Creates a new [`LiveClock`] instance.
453    #[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    // Clean up expired timers. Retain only live ones
475    fn clear_expired_timers(&mut self) {
476        self.timers.retain(|_, timer| !timer.is_expired());
477    }
478}
479
480impl Default for LiveClock {
481    /// Creates a new default [`LiveClock`] instance.
482    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        // Get the callback from either the event-specific callbacks or default callback
537        #[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, // mut allows adjustment based on allow_past
554        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        // This allows to reuse a time alert without updating the callback, for example for non regular monthly alerts
589        self.cancel_timer(name.as_str());
590
591        let ts_now = self.get_time_ns();
592
593        // Handle past timestamps based on flag
594        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        // Safe to calculate interval now that we've ensured alert_time_ns >= ts_now
611        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            // Zero start time indicates no explicit start; we use the current time
668            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// Helper struct to stream events from the heap
742#[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////////////////////////////////////////////////////////////////////////////////
776// Tests
777////////////////////////////////////////////////////////////////////////////////
778#[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        // With allow_past=true (default), should adjust to current time and succeed
932        test_clock
933            .set_time_alert_ns("past_timer", past_time, None, Some(true))
934            .unwrap();
935
936        // Verify timer was created with adjusted time
937        assert_eq!(test_clock.timer_count(), 1);
938        assert_eq!(test_clock.timer_names(), vec!["past_timer"]);
939
940        // Next time should be at or after current time, not in the past
941        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        // With allow_past=false, should fail for past times
952        let result = test_clock.set_time_alert_ns("past_timer", past_time, None, Some(false));
953
954        // Verify the operation failed with appropriate error
955        assert!(result.is_err());
956        assert!(format!("{}", result.unwrap_err()).contains("was in the past"));
957
958        // Verify no timer was created
959        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; // Stop time before start time
969
970        // Should fail because stop_time < start_time
971        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        // Verify the operation failed with appropriate error
981        assert!(result.is_err());
982        assert!(format!("{}", result.unwrap_err()).contains("must be after start time"));
983
984        // Verify no timer was created
985        assert_eq!(test_clock.timer_count(), 0);
986    }
987}