1use crate::context::ExecutorStatus;
9use crate::execution::SimulationResult;
10use crate::execution::engine::Engine;
11use crate::execution::phase::MicroStepResult;
12use crate::execution::runner::Runner;
13use crate::execution::strategy::{AlwaysContinueStrategy, ContinueStrategy};
14use crate::modeling::model::Model;
15use crate::primitive::time::TickStatus;
16use std::thread;
17use std::time::{Duration as StdDuration, Instant};
18
19#[derive(Clone)]
31pub struct RealtimeRunner<CS> {
32 continue_strategy: CS,
33 tick_unit_duration: StdDuration,
35}
36
37impl<E, M: Model<E>, CS: ContinueStrategy<E, M, ()>> Runner<E, M, CS> for RealtimeRunner<CS> {
38 type Err = ();
39
40 fn run<F>(
41 &mut self,
42 engine: Engine<E, M>,
43 mut model: M,
44 mut should_stop: F,
45 ) -> SimulationResult<M, CS::Err>
46 where
47 F: FnMut(&M, ExecutorStatus, TickStatus) -> bool,
48 {
49 let mut runner_error: Option<CS::Err> = None;
50
51 let start_instant = Instant::now();
53 let mut executor = engine.begin_simulation(&model);
54
55 loop {
56 let (executor_status, tick_status) = executor.peek_next_tick();
57 if should_stop(&model, executor_status, tick_status) {
58 break;
59 }
60
61 let next_tick_value = tick_status.current().as_time_tick();
64 let target_elapsed = self.tick_unit_duration * next_tick_value as u32;
65 let now = Instant::now();
66 let expected_instant = start_instant + target_elapsed;
67 if now < expected_instant {
68 thread::sleep(expected_instant - now);
69 }
70
71 let mut active_executor = executor.begin_tick(&model);
74
75 loop {
76 let micro_step_handler = active_executor.begin_micro_step(&model);
78
79 let mut source_phase = micro_step_handler.start_source_phase(&model);
81 while let Some(source_ready) = source_phase.take_one() {
82 source_phase.fire_and_schedule(&model, source_ready);
83 }
84 let micro_step_handler = source_phase.complete_source_phase(&model);
85
86 let mut event_phase = micro_step_handler.to_event_phase(&model);
88 while let Some(event_ready) = event_phase.take_one() {
89 event_phase.handle_event(&mut model, event_ready);
90 }
91 let micro_step_handler = event_phase.complete_event_phase(&model);
92
93 match micro_step_handler.end_micro_step(&model) {
95 MicroStepResult::Continue(unchecked) => {
96 match self
97 .continue_strategy
98 .handle_micro_step_continue(&model, unchecked)
99 {
100 Ok(new_active_executor) => {
101 active_executor = new_active_executor;
102 continue;
103 }
104 Err((new_active_executor, error)) => {
105 active_executor = new_active_executor;
106 runner_error = Some(error);
107 break;
108 }
109 }
110 }
111 MicroStepResult::Complete(new_active_executor, _) => {
112 active_executor = new_active_executor;
113 break;
114 }
115 }
116 }
117
118 executor = active_executor.end_tick_with_jump_to_next_tick(&model);
120
121 if runner_error.is_some() {
122 break;
123 }
124 }
125
126 if let Some(error) = runner_error.take() {
127 executor.end_simulation_as_error(model, error)
128 } else {
129 executor.end_simulation_as_ok(model)
130 }
131 }
132}
133
134impl RealtimeRunner<AlwaysContinueStrategy> {
135 pub fn new(tick_unit_duration: StdDuration) -> Self {
140 RealtimeRunner {
141 continue_strategy: AlwaysContinueStrategy::new(),
142 tick_unit_duration,
143 }
144 }
145}
146
147impl<CS> RealtimeRunner<CS> {
148 pub fn new_with_continue_strategy(
150 tick_unit_duration: StdDuration,
151 continue_strategy: CS,
152 ) -> Self {
153 RealtimeRunner {
154 continue_strategy,
155 tick_unit_duration,
156 }
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163 use crate::context::{EventContext, SourceContext, UserContext};
164 use crate::execution::strategy::LimitAbortStrategy;
165 use crate::modeling::event::{Event, EventPriority};
166 use crate::modeling::hook::Hook;
167 use crate::modeling::hook::instance::SharedHook;
168 use crate::modeling::source::Source;
169 use crate::primitive::time::{Duration, MicroStep, SimTime, TickStatus};
170 use crate::source_handler::{SourceReadyEntry, SourceView};
171 use std::sync::{Arc, Mutex};
172
173 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
174 enum TestEvent {
175 A,
176 }
177
178 #[derive(Debug)]
179 struct TestModel {
180 event_count: usize,
181 }
182
183 impl Model<TestEvent> for TestModel {
184 fn handle_event(
185 &mut self,
186 _context: &mut EventContext<TestEvent, Self>,
187 _event: &Event<TestEvent>,
188 ) {
189 self.event_count += 1;
190 }
191 }
192
193 #[test]
194 fn test_realtime_runner_new() {
195 let runner_always = RealtimeRunner::new(std::time::Duration::from_millis(100));
196 assert_eq!(
197 runner_always.tick_unit_duration,
198 std::time::Duration::from_millis(100)
199 );
200
201 let strategy = AlwaysContinueStrategy::new();
202 let runner_custom = RealtimeRunner::new_with_continue_strategy(
203 std::time::Duration::from_millis(100),
204 strategy,
205 );
206 assert_eq!(
207 runner_custom.tick_unit_duration,
208 std::time::Duration::from_millis(100)
209 );
210 }
211
212 #[test]
213 fn test_realtime_runner_run_success() {
214 let model = TestModel { event_count: 0 };
215 let mut engine = Engine::new();
216
217 engine.schedule_event_at(
219 SimTime::from_ticks(5),
220 EventPriority::minimum(),
221 TestEvent::A,
222 );
223
224 let mut runner = RealtimeRunner::new(std::time::Duration::from_millis(100));
225
226 let should_stop = |_m: &TestModel, _status: ExecutorStatus, tick: TickStatus| {
228 tick.is_done_ticks(false, 10)
229 };
230
231 let result = runner.run(engine, model, should_stop);
232
233 assert!(result.is_ok());
235 let output = result.unwrap();
236 assert_eq!(output.model().event_count, 1);
237 }
238
239 #[test]
240 fn test_realtime_runner_run_with_strategy_error() {
241 let model = TestModel { event_count: 0 };
242 let mut engine = Engine::new();
243
244 struct TestSource;
245
246 impl Source<TestEvent, TestModel> for TestSource {
247 fn on_registered(
248 &mut self,
249 _context: &mut dyn UserContext<TestEvent, TestModel>,
250 _model: &TestModel,
251 ) -> Option<Duration> {
252 Some(Duration::zero())
254 }
255
256 fn fire(
257 &mut self,
258 context: &mut SourceContext<TestEvent, TestModel>,
259 _model: &TestModel,
260 ) -> Option<Duration> {
261 context.schedule_event(Duration::zero(), EventPriority::minimum(), TestEvent::A);
263 Some(Duration::one())
264 }
265 }
266
267 engine.add_source("test source", TestSource);
268
269 let strategy = LimitAbortStrategy::new(0, 0);
271 let mut runner = RealtimeRunner::new_with_continue_strategy(
272 std::time::Duration::from_millis(100),
273 strategy,
274 );
275
276 let mut loop_count = 0;
278 let should_stop = |_m: &TestModel, _status: ExecutorStatus, _tick: TickStatus| {
279 loop_count += 1;
280 loop_count > 10
281 };
282
283 let result = runner.run(engine, model, should_stop);
284
285 assert!(result.is_err());
287 }
288
289 #[test]
290 fn test_realtime_runner_run_without_strategy_error() {
291 let model = TestModel { event_count: 0 };
292 let mut engine = Engine::new();
293
294 engine.schedule_event_at(SimTime::zero(), EventPriority::minimum(), TestEvent::A);
296
297 let strategy = LimitAbortStrategy::new(0, 0);
300 let mut runner = RealtimeRunner::new_with_continue_strategy(
301 std::time::Duration::from_millis(100),
302 strategy,
303 );
304
305 let mut loop_count = 0;
307 let should_stop = |_m: &TestModel, _status: ExecutorStatus, _tick: TickStatus| {
308 loop_count += 1;
309 loop_count > 10
310 };
311
312 let result = runner.run(engine, model, should_stop);
313
314 assert!(result.is_ok());
316 }
317
318 #[derive(Debug, PartialEq, Eq, Clone)]
320 enum LifecycleEvent {
321 BeforeSimulation,
322 BeforeTick(SimTime),
323 BeforeFireSource(SimTime),
324 BeforeScheduleEvent,
325 AfterScheduleEvent,
326 AfterFireSource(SimTime),
327 AfterTick(SimTime),
328 AfterSimulation,
329 }
330
331 struct TraceSource {
333 trace: Arc<Mutex<Vec<LifecycleEvent>>>,
334 initial_delay: Duration,
335 interval_delay: Option<Duration>,
336 }
337
338 impl Source<TestEvent, TestModel> for TraceSource {
339 fn on_registered(
340 &mut self,
341 _context: &mut dyn UserContext<TestEvent, TestModel>,
342 _model: &TestModel,
343 ) -> Option<Duration> {
344 self.trace
345 .lock()
346 .unwrap()
347 .push(LifecycleEvent::BeforeSimulation);
348 Some(self.initial_delay)
349 }
350
351 fn fire(
352 &mut self,
353 context: &mut SourceContext<TestEvent, TestModel>,
354 _model: &TestModel,
355 ) -> Option<Duration> {
356 let mut t = self.trace.lock().unwrap();
357 t.push(LifecycleEvent::BeforeFireSource(context.current_tick()));
358 t.push(LifecycleEvent::BeforeScheduleEvent);
359
360 context.schedule_event(Duration::zero(), EventPriority::minimum(), TestEvent::A);
361
362 t.push(LifecycleEvent::AfterScheduleEvent);
363 t.push(LifecycleEvent::AfterFireSource(context.current_tick()));
364 self.interval_delay
365 }
366 }
367
368 #[test]
369 fn test_runner_lifecycle_execution_order_scenario() {
370 let trace = Arc::new(Mutex::new(Vec::new()));
371 let model = TestModel { event_count: 0 };
372 let mut engine = Engine::new();
373
374 engine.add_source(
376 "trace_source",
377 TraceSource {
378 trace: Arc::clone(&trace),
379 initial_delay: Duration::ticks(1),
380 interval_delay: None, },
382 );
383
384 let mut runner = RealtimeRunner::new(std::time::Duration::from_millis(100));
385
386 let should_stop = move |_m: &TestModel, _status: ExecutorStatus, tick: TickStatus| {
388 tick.is_done_ticks(false, 2)
389 };
390
391 let result = runner.run(engine, model, should_stop);
392 assert!(result.is_ok());
393
394 trace.lock().unwrap().push(LifecycleEvent::AfterSimulation);
396
397 let final_trace = trace.lock().unwrap();
398
399 let expected = vec![
405 LifecycleEvent::BeforeSimulation,
406 LifecycleEvent::BeforeFireSource(SimTime::from_ticks(1)),
408 LifecycleEvent::BeforeScheduleEvent,
409 LifecycleEvent::AfterScheduleEvent,
410 LifecycleEvent::AfterFireSource(SimTime::from_ticks(1)),
411 LifecycleEvent::AfterSimulation,
412 ];
413
414 assert_eq!(*final_trace, expected);
415 }
416
417 #[test]
418 fn test_lifecycle_interruption_on_strategy_error() {
419 let trace = Arc::new(Mutex::new(Vec::new()));
420 let model = TestModel { event_count: 0 };
421 let mut engine = Engine::new();
422
423 engine.add_source(
425 "loop_source",
426 TraceSource {
427 trace: Arc::clone(&trace),
428 initial_delay: Duration::zero(),
429 interval_delay: Some(Duration::zero()),
431 },
432 );
433
434 let strategy = LimitAbortStrategy::new(0, 0);
436 let mut runner = RealtimeRunner::new_with_continue_strategy(
437 std::time::Duration::from_millis(100),
438 strategy,
439 );
440
441 let trace_for_stop = Arc::clone(&trace);
442 let should_stop = move |_m: &TestModel, _status: ExecutorStatus, tick: TickStatus| {
443 trace_for_stop
444 .lock()
445 .unwrap()
446 .push(LifecycleEvent::BeforeTick(tick.current()));
447 false
448 };
449
450 let result = runner.run(engine, model, should_stop);
451
452 assert!(result.is_err());
454
455 let final_trace = trace.lock().unwrap();
456
457 assert!(final_trace.contains(&LifecycleEvent::BeforeSimulation));
461 assert!(final_trace.contains(&LifecycleEvent::BeforeTick(SimTime::zero())));
462 assert!(!final_trace.contains(&LifecycleEvent::BeforeTick(SimTime::from_ticks(1))));
463
464 let last_event = final_trace.last().unwrap();
467 assert_ne!(last_event, &LifecycleEvent::AfterTick(SimTime::zero()));
468 }
469
470 #[derive(Debug, PartialEq, Eq, Clone)]
472 enum HookCall {
473 BeforeSimulation,
474 AfterSimulation(SimTime),
475 BeforeTick {
476 current: SimTime,
477 skipped: Duration,
478 },
479 AfterTick {
480 current: SimTime,
481 last_micro: MicroStep,
482 },
483 BeforeMicroStep {
484 current: SimTime,
485 micro: MicroStep,
486 },
487 AfterMicroStep {
488 current: SimTime,
489 micro: MicroStep,
490 },
491 BeforeSourcePhase {
492 current: SimTime,
493 micro: MicroStep,
494 },
495 AfterSourcePhase {
496 current: SimTime,
497 micro: MicroStep,
498 },
499 BeforeEventPhase {
500 current: SimTime,
501 micro: MicroStep,
502 },
503 AfterEventPhase {
504 current: SimTime,
505 micro: MicroStep,
506 },
507 }
508
509 struct MockHook {
511 calls: Arc<Mutex<Vec<HookCall>>>,
512 }
513
514 impl<E, M: Model<E>> Hook<E, M> for MockHook {
515 fn before_simulation(&self, _model: &M) {
516 self.calls.lock().unwrap().push(HookCall::BeforeSimulation);
517 }
518 fn after_simulation(&self, _model: &M, end_tick: SimTime) {
519 self.calls
520 .lock()
521 .unwrap()
522 .push(HookCall::AfterSimulation(end_tick));
523 }
524 fn before_tick(&self, _model: &M, current_tick: SimTime, skipped_duration: Duration) {
525 self.calls.lock().unwrap().push(HookCall::BeforeTick {
526 current: current_tick,
527 skipped: skipped_duration,
528 });
529 }
530 fn after_tick(&self, _model: &M, current_tick: SimTime, last_micro_step: MicroStep) {
531 self.calls.lock().unwrap().push(HookCall::AfterTick {
532 current: current_tick,
533 last_micro: last_micro_step,
534 });
535 }
536 fn before_micro_step(
537 &self,
538 _model: &M,
539 current_tick: SimTime,
540 current_micro_step: MicroStep,
541 ) {
542 self.calls.lock().unwrap().push(HookCall::BeforeMicroStep {
543 current: current_tick,
544 micro: current_micro_step,
545 });
546 }
547 fn after_micro_step(
548 &self,
549 _model: &M,
550 current_tick: SimTime,
551 current_micro_step: MicroStep,
552 ) {
553 self.calls.lock().unwrap().push(HookCall::AfterMicroStep {
554 current: current_tick,
555 micro: current_micro_step,
556 });
557 }
558 fn on_discard_remain_micro_step(
559 &self,
560 _model: &M,
561 _current_tick: SimTime,
562 _first_discarded_micro_step: MicroStep,
563 _discarded_sources: &[SourceReadyEntry],
564 _discarded_events: &[Event<E>],
565 ) {
566 }
567 fn before_register_source(&self, _model: &M, _name: &str) {}
568 fn after_register_source(&self, _model: &M, _name: &str) {}
569 fn before_source_phase(
570 &self,
571 _model: &M,
572 current_tick: SimTime,
573 current_micro_step: MicroStep,
574 ) {
575 self.calls
576 .lock()
577 .unwrap()
578 .push(HookCall::BeforeSourcePhase {
579 current: current_tick,
580 micro: current_micro_step,
581 });
582 }
583 fn before_source(
584 &self,
585 _model: &M,
586 _current_tick: SimTime,
587 _current_micro_step: MicroStep,
588 _source_view: &SourceView,
589 ) {
590 }
591 fn after_source(
592 &self,
593 _model: &M,
594 _current_tick: SimTime,
595 _current_micro_step: MicroStep,
596 _source_view: &SourceView,
597 _computed_next_fire: Option<SimTime>,
598 ) {
599 }
600 fn cancel_source(
601 &self,
602 _model: &M,
603 _current_tick: SimTime,
604 _current_micro_step: MicroStep,
605 _scheduled_at: SimTime,
606 _source_view: &SourceView,
607 ) {
608 }
609 fn discard_source(
610 &self,
611 _model: &M,
612 _current_tick: SimTime,
613 _current_micro_step: MicroStep,
614 _source_view: &SourceView,
615 ) {
616 }
617 fn after_source_phase(
618 &self,
619 _model: &M,
620 current_tick: SimTime,
621 current_micro_step: MicroStep,
622 ) {
623 self.calls.lock().unwrap().push(HookCall::AfterSourcePhase {
624 current: current_tick,
625 micro: current_micro_step,
626 });
627 }
628 fn before_event_phase(
629 &self,
630 _model: &M,
631 current_tick: SimTime,
632 current_micro_step: MicroStep,
633 ) {
634 self.calls.lock().unwrap().push(HookCall::BeforeEventPhase {
635 current: current_tick,
636 micro: current_micro_step,
637 });
638 }
639 fn before_event(
640 &self,
641 _model: &M,
642 _current_tick: SimTime,
643 _current_micro_step: MicroStep,
644 _event: &Event<E>,
645 ) {
646 }
647 fn after_event(
648 &self,
649 _model: &M,
650 _current_tick: SimTime,
651 _current_micro_step: MicroStep,
652 _event: &Event<E>,
653 ) {
654 }
655 fn cancel_event(
656 &self,
657 _model: &M,
658 _current_tick: SimTime,
659 _current_micro_step: MicroStep,
660 _scheduled_at: SimTime,
661 _event: &Event<E>,
662 ) {
663 }
664 fn discard_event(
665 &self,
666 _model: &M,
667 _current_tick: SimTime,
668 _current_micro_step: MicroStep,
669 _event: &Event<E>,
670 ) {
671 }
672 fn after_event_phase(
673 &self,
674 _model: &M,
675 current_tick: SimTime,
676 current_micro_step: MicroStep,
677 ) {
678 self.calls.lock().unwrap().push(HookCall::AfterEventPhase {
679 current: current_tick,
680 micro: current_micro_step,
681 });
682 }
683 }
684
685 #[test]
686 fn test_standard_runner_hook_lifecycle_flow_with_include_zero_tick() {
687 let hook = MockHook {
688 calls: Arc::new(Mutex::new(Vec::new())),
689 };
690 let shared_hook = SharedHook::new(hook);
691
692 let model = TestModel { event_count: 0 };
693 let mut engine = Engine::new();
694
695 engine.add_shared_hook(shared_hook.clone());
697
698 engine.schedule_event_at(
700 SimTime::from_ticks(1),
701 EventPriority::minimum(),
702 TestEvent::A,
703 );
704
705 let mut runner = RealtimeRunner::new(std::time::Duration::from_millis(100));
706
707 let should_stop = |_m: &TestModel, _status: ExecutorStatus, tick: TickStatus| {
709 tick.is_done_ticks(true, 2)
711 };
712
713 let _result = runner.run(engine, model, should_stop);
714
715 let final_calls = shared_hook.get_ref().calls.lock().unwrap();
716
717 let expected = vec![
719 HookCall::BeforeSimulation,
720 HookCall::BeforeTick {
722 current: SimTime::from_ticks(0),
723 skipped: Duration::zero(),
724 },
725 HookCall::BeforeMicroStep {
726 current: SimTime::from_ticks(0),
727 micro: MicroStep::zero(),
728 },
729 HookCall::BeforeSourcePhase {
730 current: SimTime::from_ticks(0),
731 micro: MicroStep::zero(),
732 },
733 HookCall::AfterSourcePhase {
734 current: SimTime::from_ticks(0),
735 micro: MicroStep::zero(),
736 },
737 HookCall::BeforeEventPhase {
738 current: SimTime::from_ticks(0),
739 micro: MicroStep::zero(),
740 },
741 HookCall::AfterEventPhase {
742 current: SimTime::from_ticks(0),
743 micro: MicroStep::zero(),
744 },
745 HookCall::AfterMicroStep {
746 current: SimTime::from_ticks(0),
747 micro: MicroStep::zero(),
748 },
749 HookCall::AfterTick {
750 current: SimTime::from_ticks(0),
751 last_micro: MicroStep::zero(),
752 },
753 HookCall::BeforeTick {
755 current: SimTime::from_ticks(1),
756 skipped: Duration::ticks(0),
757 },
758 HookCall::BeforeMicroStep {
759 current: SimTime::from_ticks(1),
760 micro: MicroStep::zero(),
761 },
762 HookCall::BeforeSourcePhase {
763 current: SimTime::from_ticks(1),
764 micro: MicroStep::zero(),
765 },
766 HookCall::AfterSourcePhase {
767 current: SimTime::from_ticks(1),
768 micro: MicroStep::zero(),
769 },
770 HookCall::BeforeEventPhase {
771 current: SimTime::from_ticks(1),
772 micro: MicroStep::zero(),
773 },
774 HookCall::AfterEventPhase {
776 current: SimTime::from_ticks(1),
777 micro: MicroStep::zero(),
778 },
779 HookCall::AfterMicroStep {
780 current: SimTime::from_ticks(1),
781 micro: MicroStep::zero(),
782 },
783 HookCall::AfterTick {
784 current: SimTime::from_ticks(1),
785 last_micro: MicroStep::zero(),
786 },
787 HookCall::AfterSimulation(SimTime::from_ticks(1)),
789 ];
790
791 assert_eq!(*final_calls, expected);
792 }
793
794 #[test]
795 fn test_standard_runner_hook_lifecycle_flow_without_include_zero_tick() {
796 let hook = MockHook {
797 calls: Arc::new(Mutex::new(Vec::new())),
798 };
799 let shared_hook = SharedHook::new(hook);
800
801 let model = TestModel { event_count: 0 };
802 let mut engine = Engine::new();
803
804 engine.add_shared_hook(shared_hook.clone());
806
807 engine.schedule_event_at(
809 SimTime::from_ticks(1),
810 EventPriority::minimum(),
811 TestEvent::A,
812 );
813
814 let mut runner = RealtimeRunner::new(std::time::Duration::from_millis(100));
815
816 let should_stop = |_m: &TestModel, _status: ExecutorStatus, tick: TickStatus| {
818 tick.is_done_ticks(false, 2)
820 };
821
822 let _result = runner.run(engine, model, should_stop);
823
824 let final_calls = shared_hook.get_ref().calls.lock().unwrap();
825
826 let expected = vec![
828 HookCall::BeforeSimulation,
829 HookCall::BeforeTick {
831 current: SimTime::from_ticks(0),
832 skipped: Duration::zero(),
833 },
834 HookCall::BeforeMicroStep {
835 current: SimTime::from_ticks(0),
836 micro: MicroStep::zero(),
837 },
838 HookCall::BeforeSourcePhase {
839 current: SimTime::from_ticks(0),
840 micro: MicroStep::zero(),
841 },
842 HookCall::AfterSourcePhase {
843 current: SimTime::from_ticks(0),
844 micro: MicroStep::zero(),
845 },
846 HookCall::BeforeEventPhase {
847 current: SimTime::from_ticks(0),
848 micro: MicroStep::zero(),
849 },
850 HookCall::AfterEventPhase {
851 current: SimTime::from_ticks(0),
852 micro: MicroStep::zero(),
853 },
854 HookCall::AfterMicroStep {
855 current: SimTime::from_ticks(0),
856 micro: MicroStep::zero(),
857 },
858 HookCall::AfterTick {
859 current: SimTime::from_ticks(0),
860 last_micro: MicroStep::zero(),
861 },
862 HookCall::BeforeTick {
864 current: SimTime::from_ticks(1),
865 skipped: Duration::ticks(0),
866 },
867 HookCall::BeforeMicroStep {
868 current: SimTime::from_ticks(1),
869 micro: MicroStep::zero(),
870 },
871 HookCall::BeforeSourcePhase {
872 current: SimTime::from_ticks(1),
873 micro: MicroStep::zero(),
874 },
875 HookCall::AfterSourcePhase {
876 current: SimTime::from_ticks(1),
877 micro: MicroStep::zero(),
878 },
879 HookCall::BeforeEventPhase {
880 current: SimTime::from_ticks(1),
881 micro: MicroStep::zero(),
882 },
883 HookCall::AfterEventPhase {
885 current: SimTime::from_ticks(1),
886 micro: MicroStep::zero(),
887 },
888 HookCall::AfterMicroStep {
889 current: SimTime::from_ticks(1),
890 micro: MicroStep::zero(),
891 },
892 HookCall::AfterTick {
893 current: SimTime::from_ticks(1),
894 last_micro: MicroStep::zero(),
895 },
896 HookCall::BeforeTick {
898 current: SimTime::from_ticks(2),
899 skipped: Duration::zero(),
900 },
901 HookCall::BeforeMicroStep {
902 current: SimTime::from_ticks(2),
903 micro: MicroStep::zero(),
904 },
905 HookCall::BeforeSourcePhase {
906 current: SimTime::from_ticks(2),
907 micro: MicroStep::zero(),
908 },
909 HookCall::AfterSourcePhase {
910 current: SimTime::from_ticks(2),
911 micro: MicroStep::zero(),
912 },
913 HookCall::BeforeEventPhase {
914 current: SimTime::from_ticks(2),
915 micro: MicroStep::zero(),
916 },
917 HookCall::AfterEventPhase {
918 current: SimTime::from_ticks(2),
919 micro: MicroStep::zero(),
920 },
921 HookCall::AfterMicroStep {
922 current: SimTime::from_ticks(2),
923 micro: MicroStep::zero(),
924 },
925 HookCall::AfterTick {
926 current: SimTime::from_ticks(2),
927 last_micro: MicroStep::zero(),
928 },
929 HookCall::AfterSimulation(SimTime::from_ticks(2)),
931 ];
932
933 assert_eq!(*final_calls, expected);
934 }
935}