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