1use std::any::{Any, TypeId};
6use std::cell::OnceCell;
7use std::collections::VecDeque;
8use std::fmt::{Display, Formatter};
9use std::rc::Rc;
10
11use crate::data_plugin::DataPlugin;
12use crate::execution_stats::{
13 log_execution_statistics, print_execution_statistics, ExecutionProfilingCollector,
14 ExecutionStatistics,
15};
16use crate::plan::{PlanId, Queue};
17#[cfg(feature = "progress_bar")]
18use crate::progress::update_timeline_progress;
19#[cfg(feature = "debugger")]
20use crate::{debugger::enter_debugger, plan::PlanSchedule};
21use crate::{get_data_plugin_count, trace, warn, ContextPeopleExt, HashMap, HashMapExt};
22
23type Callback = dyn FnOnce(&mut Context);
25
26type EventHandler<E> = dyn Fn(&mut Context, E);
28
29pub trait IxaEvent {
30 fn on_subscribe(_context: &mut Context) {}
32}
33
34#[derive(PartialEq, Eq, Ord, Clone, Copy, PartialOrd, Hash, Debug)]
42pub enum ExecutionPhase {
43 First,
44 Normal,
45 Last,
46}
47
48impl Display for ExecutionPhase {
49 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
50 write!(f, "{self:?}")
51 }
52}
53
54pub struct Context {
82 plan_queue: Queue<Box<Callback>, ExecutionPhase>,
83 callback_queue: VecDeque<Box<Callback>>,
84 event_handlers: HashMap<TypeId, Box<dyn Any>>,
85 data_plugins: Vec<OnceCell<Box<dyn Any>>>,
86 #[cfg(feature = "debugger")]
87 breakpoints_scheduled: Queue<Box<Callback>, ExecutionPhase>,
88 current_time: f64,
89 shutdown_requested: bool,
90 #[cfg(feature = "debugger")]
91 break_requested: bool,
92 #[cfg(feature = "debugger")]
93 breakpoints_enabled: bool,
94 execution_profiler: ExecutionProfilingCollector,
95 pub print_execution_statistics: bool,
96}
97
98impl Context {
99 #[must_use]
101 pub fn new() -> Context {
102 let data_plugins = std::iter::repeat_with(OnceCell::new)
104 .take(get_data_plugin_count())
105 .collect();
106
107 Context {
108 plan_queue: Queue::new(),
109 callback_queue: VecDeque::new(),
110 event_handlers: HashMap::new(),
111 data_plugins,
112 #[cfg(feature = "debugger")]
113 breakpoints_scheduled: Queue::new(),
114 current_time: 0.0,
115 shutdown_requested: false,
116 #[cfg(feature = "debugger")]
117 break_requested: false,
118 #[cfg(feature = "debugger")]
119 breakpoints_enabled: true,
120 execution_profiler: ExecutionProfilingCollector::new(),
121 print_execution_statistics: false,
122 }
123 }
124
125 #[cfg(feature = "debugger")]
133 pub fn schedule_debugger(
134 &mut self,
135 time: f64,
136 priority: Option<ExecutionPhase>,
137 callback: Box<Callback>,
138 ) {
139 trace!("scheduling debugger");
140 let priority = priority.unwrap_or(ExecutionPhase::First);
141 self.breakpoints_scheduled
142 .add_plan(time, callback, priority);
143 }
144
145 #[allow(clippy::missing_panics_doc)]
150 pub fn subscribe_to_event<E: IxaEvent + Copy + 'static>(
151 &mut self,
152 handler: impl Fn(&mut Context, E) + 'static,
153 ) {
154 let handler_vec = self
155 .event_handlers
156 .entry(TypeId::of::<E>())
157 .or_insert_with(|| Box::<Vec<Rc<EventHandler<E>>>>::default());
158 let handler_vec: &mut Vec<Rc<EventHandler<E>>> = handler_vec.downcast_mut().unwrap();
159 handler_vec.push(Rc::new(handler));
160 E::on_subscribe(self);
161 }
162
163 #[allow(clippy::missing_panics_doc)]
168 pub fn emit_event<E: IxaEvent + Copy + 'static>(&mut self, event: E) {
169 let Context {
171 event_handlers,
172 callback_queue,
173 ..
174 } = self;
175 if let Some(handler_vec) = event_handlers.get(&TypeId::of::<E>()) {
176 let handler_vec: &Vec<Rc<EventHandler<E>>> = handler_vec.downcast_ref().unwrap();
177 for handler in handler_vec {
178 let handler_clone = Rc::clone(handler);
179 callback_queue.push_back(Box::new(move |context| handler_clone(context, event)));
180 }
181 }
182 }
183
184 pub fn add_plan(&mut self, time: f64, callback: impl FnOnce(&mut Context) + 'static) -> PlanId {
193 self.add_plan_with_phase(time, callback, ExecutionPhase::Normal)
194 }
195
196 pub fn add_plan_with_phase(
206 &mut self,
207 time: f64,
208 callback: impl FnOnce(&mut Context) + 'static,
209 phase: ExecutionPhase,
210 ) -> PlanId {
211 assert!(
212 !time.is_nan() && !time.is_infinite() && time >= self.current_time,
213 "Time is invalid"
214 );
215 self.plan_queue.add_plan(time, Box::new(callback), phase)
216 }
217
218 fn evaluate_periodic_and_schedule_next(
219 &mut self,
220 period: f64,
221 callback: impl Fn(&mut Context) + 'static,
222 phase: ExecutionPhase,
223 ) {
224 trace!(
225 "evaluate periodic at {} (period={})",
226 self.current_time,
227 period
228 );
229 callback(self);
230 if !self.plan_queue.is_empty() {
231 let next_time = self.current_time + period;
232 self.add_plan_with_phase(
233 next_time,
234 move |context| context.evaluate_periodic_and_schedule_next(period, callback, phase),
235 phase,
236 );
237 }
238 }
239
240 pub fn add_periodic_plan_with_phase(
248 &mut self,
249 period: f64,
250 callback: impl Fn(&mut Context) + 'static,
251 phase: ExecutionPhase,
252 ) {
253 assert!(
254 period > 0.0 && !period.is_nan() && !period.is_infinite(),
255 "Period must be greater than 0"
256 );
257
258 self.add_plan_with_phase(
259 0.0,
260 move |context| context.evaluate_periodic_and_schedule_next(period, callback, phase),
261 phase,
262 );
263 }
264
265 pub fn cancel_plan(&mut self, plan_id: &PlanId) {
272 trace!("canceling plan {plan_id:?}");
273 let result = self.plan_queue.cancel_plan(plan_id);
274 if result.is_none() {
275 warn!("Tried to cancel nonexistent plan with ID = {plan_id:?}");
276 }
277 }
278
279 #[doc(hidden)]
280 #[allow(dead_code)]
281 pub(crate) fn remaining_plan_count(&self) -> usize {
282 self.plan_queue.remaining_plan_count()
283 }
284
285 pub fn queue_callback(&mut self, callback: impl FnOnce(&mut Context) + 'static) {
287 trace!("queuing callback");
288 self.callback_queue.push_back(Box::new(callback));
289 }
290
291 #[must_use]
300 #[allow(clippy::missing_panics_doc)]
301 #[allow(clippy::needless_pass_by_value)]
302 pub fn get_data_mut<T: DataPlugin>(&mut self, _data_plugin: T) -> &mut T::DataContainer {
303 let index = T::index_within_context();
304
305 if self.data_plugins[index].get().is_some() {
307 return self.data_plugins[index]
308 .get_mut()
309 .unwrap()
310 .downcast_mut::<T::DataContainer>()
311 .expect("TypeID does not match data plugin type");
312 }
313
314 let data = T::init(self);
316 let cell = self
317 .data_plugins
318 .get_mut(index)
319 .unwrap_or_else(|| panic!("No data plugin found with index = {index:?}. You must use the `define_data_plugin!` macro to create a data plugin."));
320 let _ = cell.set(Box::new(data));
321 cell.get_mut()
322 .unwrap()
323 .downcast_mut::<T::DataContainer>()
324 .expect("TypeID does not match data plugin type. You must use the `define_data_plugin!` macro to create a data plugin.")
325 }
326
327 #[must_use]
332 #[allow(clippy::needless_pass_by_value)]
333 pub fn get_data<T: DataPlugin>(&self, _data_plugin: T) -> &T::DataContainer {
334 let index = T::index_within_context();
335 self.data_plugins
336 .get(index)
337 .unwrap_or_else(|| panic!("No data plugin found with index = {index:?}. You must use the `define_data_plugin!` macro to create a data plugin."))
338 .get_or_init(|| Box::new(T::init(self)))
339 .downcast_ref::<T::DataContainer>()
340 .expect("TypeID does not match data plugin type. You must use the `define_data_plugin!` macro to create a data plugin.")
341 }
342
343 pub fn shutdown(&mut self) {
346 trace!("shutdown context");
347 self.shutdown_requested = true;
348 }
349
350 #[must_use]
354 pub fn get_current_time(&self) -> f64 {
355 self.current_time
356 }
357
358 #[cfg(feature = "debugger")]
360 pub fn request_debugger(&mut self) {
361 self.break_requested = true;
362 }
363
364 #[cfg(feature = "debugger")]
366 pub fn cancel_debugger_request(&mut self) {
367 self.break_requested = false;
368 }
369
370 #[cfg(feature = "debugger")]
372 pub fn disable_breakpoints(&mut self) {
373 self.breakpoints_enabled = false;
374 }
375
376 #[cfg(feature = "debugger")]
378 pub fn enable_breakpoints(&mut self) {
379 self.breakpoints_enabled = true;
380 }
381
382 #[must_use]
384 #[cfg(feature = "debugger")]
385 pub fn breakpoints_are_enabled(&self) -> bool {
386 self.breakpoints_enabled
387 }
388
389 #[cfg(feature = "debugger")]
391 pub fn delete_breakpoint(&mut self, breakpoint_id: u64) -> Option<Box<Callback>> {
392 self.breakpoints_scheduled
393 .cancel_plan(&PlanId(breakpoint_id))
394 }
395
396 #[must_use]
399 #[cfg(feature = "debugger")]
400 pub fn list_breakpoints(&self, at_most: usize) -> Vec<&PlanSchedule<ExecutionPhase>> {
401 self.breakpoints_scheduled.list_schedules(at_most)
402 }
403
404 #[cfg(feature = "debugger")]
406 pub fn clear_breakpoints(&mut self) {
407 self.breakpoints_scheduled.clear();
408 }
409
410 pub fn execute(&mut self) {
412 trace!("entering event loop");
413 loop {
415 #[cfg(feature = "progress_bar")]
416 if crate::progress::MAX_TIME.get().is_some() {
417 update_timeline_progress(self.current_time);
418 }
419
420 #[cfg(feature = "debugger")]
421 if self.break_requested {
422 enter_debugger(self);
423 } else if self.shutdown_requested {
424 self.shutdown_requested = false;
425 break;
426 } else {
427 self.execute_single_step();
428 }
429
430 self.execution_profiler.refresh();
431
432 #[cfg(not(feature = "debugger"))]
433 if self.shutdown_requested {
434 self.shutdown_requested = false;
435 break;
436 } else {
437 self.execute_single_step();
438 }
439 }
440
441 let stats = self.get_execution_statistics();
442 if self.print_execution_statistics {
443 print_execution_statistics(&stats);
444 } else {
445 log_execution_statistics(&stats);
446 }
447 }
448
449 pub fn execute_single_step(&mut self) {
455 #[cfg(feature = "debugger")]
460 if let Some((bp, _)) = self.breakpoints_scheduled.peek() {
461 if let Some(plan_time) = self.plan_queue.next_time() {
466 if (bp.priority == ExecutionPhase::First && bp.time <= plan_time)
467 || (bp.priority == ExecutionPhase::Last && bp.time < plan_time)
468 {
469 self.breakpoints_scheduled.get_next_plan(); if self.breakpoints_enabled {
471 self.break_requested = true;
472 return;
473 }
474 }
475 } else {
476 self.breakpoints_scheduled.get_next_plan(); if self.breakpoints_enabled {
478 self.break_requested = true;
479 return;
480 }
481 }
482 }
483
484 if let Some(callback) = self.callback_queue.pop_front() {
486 trace!("calling callback");
487 callback(self);
488 }
489 else if let Some(plan) = self.plan_queue.get_next_plan() {
491 trace!("calling plan at {:.6}", plan.time);
492 self.current_time = plan.time;
493 (plan.data)(self);
494 } else {
495 trace!("No callbacks or plans; exiting event loop");
496 self.shutdown_requested = true;
498 }
499 }
500
501 pub fn get_execution_statistics(&mut self) -> ExecutionStatistics {
502 let population = self.get_current_population();
503 self.execution_profiler.compute_final_statistics(population)
504 }
505}
506
507pub trait ContextBase: Sized {
508 fn subscribe_to_event<E: IxaEvent + Copy + 'static>(
509 &mut self,
510 handler: impl Fn(&mut Context, E) + 'static,
511 );
512 fn emit_event<E: IxaEvent + Copy + 'static>(&mut self, event: E);
513 fn add_plan(&mut self, time: f64, callback: impl FnOnce(&mut Context) + 'static) -> PlanId;
514 fn add_plan_with_phase(
515 &mut self,
516 time: f64,
517 callback: impl FnOnce(&mut Context) + 'static,
518 phase: ExecutionPhase,
519 ) -> PlanId;
520 fn add_periodic_plan_with_phase(
521 &mut self,
522 period: f64,
523 callback: impl Fn(&mut Context) + 'static,
524 phase: ExecutionPhase,
525 );
526 fn cancel_plan(&mut self, plan_id: &PlanId);
527 fn queue_callback(&mut self, callback: impl FnOnce(&mut Context) + 'static);
528 fn get_data_mut<T: DataPlugin>(&mut self, plugin: T) -> &mut T::DataContainer;
529 fn get_data<T: DataPlugin>(&self, plugin: T) -> &T::DataContainer;
530 fn get_current_time(&self) -> f64;
531 fn get_execution_statistics(&mut self) -> ExecutionStatistics;
532}
533impl ContextBase for Context {
534 delegate::delegate! {
535 to self {
536 fn subscribe_to_event<E: IxaEvent + Copy + 'static>(&mut self, handler: impl Fn(&mut Context, E) + 'static);
537 fn emit_event<E: IxaEvent + Copy + 'static>(&mut self, event: E);
538 fn add_plan(&mut self, time: f64, callback: impl FnOnce(&mut Context) + 'static) -> PlanId;
539 fn add_plan_with_phase(&mut self, time: f64, callback: impl FnOnce(&mut Context) + 'static, phase: ExecutionPhase) -> PlanId;
540 fn add_periodic_plan_with_phase(&mut self, period: f64, callback: impl Fn(&mut Context) + 'static, phase: ExecutionPhase);
541 fn cancel_plan(&mut self, plan_id: &PlanId);
542 fn queue_callback(&mut self, callback: impl FnOnce(&mut Context) + 'static);
543 fn get_data_mut<T: DataPlugin>(&mut self, plugin: T) -> &mut T::DataContainer;
544 fn get_data<T: DataPlugin>(&self, plugin: T) -> &T::DataContainer;
545 fn get_current_time(&self) -> f64;
546 fn get_execution_statistics(&mut self) -> ExecutionStatistics;
547 }
548 }
549}
550
551impl Default for Context {
552 fn default() -> Self {
553 Self::new()
554 }
555}
556
557#[cfg(test)]
558#[allow(clippy::float_cmp)]
559mod tests {
560 use std::cell::RefCell;
561
562 use ixa_derive::IxaEvent;
563
564 use super::*;
565 use crate::define_data_plugin;
566
567 define_data_plugin!(ComponentA, Vec<u32>, vec![]);
568
569 #[test]
570 fn empty_context() {
571 let mut context = Context::new();
572 context.execute();
573 assert_eq!(context.get_current_time(), 0.0);
574 }
575
576 #[test]
577 fn get_data() {
578 let mut context = Context::new();
579 context.get_data_mut(ComponentA).push(1);
580 assert_eq!(*context.get_data(ComponentA), vec![1],);
581 }
582
583 fn add_plan(context: &mut Context, time: f64, value: u32) -> PlanId {
584 context.add_plan(time, move |context| {
585 context.get_data_mut(ComponentA).push(value);
586 })
587 }
588
589 fn add_plan_with_phase(
590 context: &mut Context,
591 time: f64,
592 value: u32,
593 phase: ExecutionPhase,
594 ) -> PlanId {
595 context.add_plan_with_phase(
596 time,
597 move |context| {
598 context.get_data_mut(ComponentA).push(value);
599 },
600 phase,
601 )
602 }
603
604 #[test]
605 #[should_panic(expected = "Time is invalid")]
606 fn negative_plan_time() {
607 let mut context = Context::new();
608 add_plan(&mut context, -1.0, 0);
609 }
610
611 #[test]
612 #[should_panic(expected = "Time is invalid")]
613 fn infinite_plan_time() {
614 let mut context = Context::new();
615 add_plan(&mut context, f64::INFINITY, 0);
616 }
617
618 #[test]
619 #[should_panic(expected = "Time is invalid")]
620 fn nan_plan_time() {
621 let mut context = Context::new();
622 add_plan(&mut context, f64::NAN, 0);
623 }
624
625 #[test]
626 fn timed_plan_only() {
627 let mut context = Context::new();
628 add_plan(&mut context, 1.0, 1);
629 context.execute();
630 assert_eq!(context.get_current_time(), 1.0);
631 assert_eq!(*context.get_data_mut(ComponentA), vec![1]);
632 }
633
634 #[test]
635 fn callback_only() {
636 let mut context = Context::new();
637 context.queue_callback(|context| {
638 context.get_data_mut(ComponentA).push(1);
639 });
640 context.execute();
641 assert_eq!(context.get_current_time(), 0.0);
642 assert_eq!(*context.get_data_mut(ComponentA), vec![1]);
643 }
644
645 #[test]
646 fn callback_before_timed_plan() {
647 let mut context = Context::new();
648 context.queue_callback(|context| {
649 context.get_data_mut(ComponentA).push(1);
650 });
651 add_plan(&mut context, 1.0, 2);
652 context.execute();
653 assert_eq!(context.get_current_time(), 1.0);
654 assert_eq!(*context.get_data_mut(ComponentA), vec![1, 2]);
655 }
656
657 #[test]
658 fn callback_adds_timed_plan() {
659 let mut context = Context::new();
660 context.queue_callback(|context| {
661 context.get_data_mut(ComponentA).push(1);
662 add_plan(context, 1.0, 2);
663 context.get_data_mut(ComponentA).push(3);
664 });
665 context.execute();
666 assert_eq!(context.get_current_time(), 1.0);
667 assert_eq!(*context.get_data_mut(ComponentA), vec![1, 3, 2]);
668 }
669
670 #[test]
671 fn callback_adds_callback_and_timed_plan() {
672 let mut context = Context::new();
673 context.queue_callback(|context| {
674 context.get_data_mut(ComponentA).push(1);
675 add_plan(context, 1.0, 2);
676 context.queue_callback(|context| {
677 context.get_data_mut(ComponentA).push(4);
678 });
679 context.get_data_mut(ComponentA).push(3);
680 });
681 context.execute();
682 assert_eq!(context.get_current_time(), 1.0);
683 assert_eq!(*context.get_data_mut(ComponentA), vec![1, 3, 4, 2]);
684 }
685
686 #[test]
687 fn timed_plan_adds_callback_and_timed_plan() {
688 let mut context = Context::new();
689 context.add_plan(1.0, |context| {
690 context.get_data_mut(ComponentA).push(1);
691 add_plan(context, 2.0, 3);
693 context.queue_callback(|context| {
694 context.get_data_mut(ComponentA).push(2);
695 });
696 });
697 context.execute();
698 assert_eq!(context.get_current_time(), 2.0);
699 assert_eq!(*context.get_data_mut(ComponentA), vec![1, 2, 3]);
700 }
701
702 #[test]
703 fn cancel_plan() {
704 let mut context = Context::new();
705 let to_cancel = add_plan(&mut context, 2.0, 1);
706 context.add_plan(1.0, move |context| {
707 context.cancel_plan(&to_cancel);
708 });
709 context.execute();
710 assert_eq!(context.get_current_time(), 1.0);
711 let test_vec: Vec<u32> = vec![];
712 assert_eq!(*context.get_data_mut(ComponentA), test_vec);
713 }
714
715 #[test]
716 fn add_plan_with_current_time() {
717 let mut context = Context::new();
718 context.add_plan(1.0, move |context| {
719 context.get_data_mut(ComponentA).push(1);
720 add_plan(context, 1.0, 2);
721 context.queue_callback(|context| {
722 context.get_data_mut(ComponentA).push(3);
723 });
724 });
725 context.execute();
726 assert_eq!(context.get_current_time(), 1.0);
727 assert_eq!(*context.get_data_mut(ComponentA), vec![1, 3, 2]);
728 }
729
730 #[test]
731 fn plans_at_same_time_fire_in_order() {
732 let mut context = Context::new();
733 add_plan(&mut context, 1.0, 1);
734 add_plan(&mut context, 1.0, 2);
735 context.execute();
736 assert_eq!(context.get_current_time(), 1.0);
737 assert_eq!(*context.get_data_mut(ComponentA), vec![1, 2]);
738 }
739
740 #[test]
741 fn check_plan_phase_ordering() {
742 assert!(ExecutionPhase::First < ExecutionPhase::Normal);
743 assert!(ExecutionPhase::Normal < ExecutionPhase::Last);
744 }
745
746 #[test]
747 fn plans_at_same_time_follow_phase() {
748 let mut context = Context::new();
749 add_plan(&mut context, 1.0, 1);
750 add_plan_with_phase(&mut context, 1.0, 5, ExecutionPhase::Last);
751 add_plan_with_phase(&mut context, 1.0, 3, ExecutionPhase::First);
752 add_plan(&mut context, 1.0, 2);
753 add_plan_with_phase(&mut context, 1.0, 6, ExecutionPhase::Last);
754 add_plan_with_phase(&mut context, 1.0, 4, ExecutionPhase::First);
755 context.execute();
756 assert_eq!(context.get_current_time(), 1.0);
757 assert_eq!(*context.get_data_mut(ComponentA), vec![3, 4, 1, 2, 5, 6]);
758 }
759
760 #[derive(Copy, Clone, IxaEvent)]
761 struct Event1 {
762 pub data: usize,
763 }
764
765 #[derive(Copy, Clone, IxaEvent)]
766 struct Event2 {
767 pub data: usize,
768 }
769
770 #[test]
771 fn simple_event() {
772 let mut context = Context::new();
773 let obs_data = Rc::new(RefCell::new(0));
774 let obs_data_clone = Rc::clone(&obs_data);
775
776 context.subscribe_to_event::<Event1>(move |_, event| {
777 *obs_data_clone.borrow_mut() = event.data;
778 });
779
780 context.emit_event(Event1 { data: 1 });
781 context.execute();
782 assert_eq!(*obs_data.borrow(), 1);
783 }
784
785 #[test]
786 fn multiple_events() {
787 let mut context = Context::new();
788 let obs_data = Rc::new(RefCell::new(0));
789 let obs_data_clone = Rc::clone(&obs_data);
790
791 context.subscribe_to_event::<Event1>(move |_, event| {
792 *obs_data_clone.borrow_mut() += event.data;
793 });
794
795 context.emit_event(Event1 { data: 1 });
796 context.emit_event(Event1 { data: 2 });
797 context.execute();
798
799 assert_eq!(*obs_data.borrow(), 3);
801 }
802
803 #[test]
804 fn multiple_event_handlers() {
805 let mut context = Context::new();
806 let obs_data1 = Rc::new(RefCell::new(0));
807 let obs_data1_clone = Rc::clone(&obs_data1);
808 let obs_data2 = Rc::new(RefCell::new(0));
809 let obs_data2_clone = Rc::clone(&obs_data2);
810
811 context.subscribe_to_event::<Event1>(move |_, event| {
812 *obs_data1_clone.borrow_mut() = event.data;
813 });
814 context.subscribe_to_event::<Event1>(move |_, event| {
815 *obs_data2_clone.borrow_mut() = event.data;
816 });
817 context.emit_event(Event1 { data: 1 });
818 context.execute();
819 assert_eq!(*obs_data1.borrow(), 1);
820 assert_eq!(*obs_data2.borrow(), 1);
821 }
822
823 #[test]
824 fn multiple_event_types() {
825 let mut context = Context::new();
826 let obs_data1 = Rc::new(RefCell::new(0));
827 let obs_data1_clone = Rc::clone(&obs_data1);
828 let obs_data2 = Rc::new(RefCell::new(0));
829 let obs_data2_clone = Rc::clone(&obs_data2);
830
831 context.subscribe_to_event::<Event1>(move |_, event| {
832 *obs_data1_clone.borrow_mut() = event.data;
833 });
834 context.subscribe_to_event::<Event2>(move |_, event| {
835 *obs_data2_clone.borrow_mut() = event.data;
836 });
837 context.emit_event(Event1 { data: 1 });
838 context.emit_event(Event2 { data: 2 });
839 context.execute();
840 assert_eq!(*obs_data1.borrow(), 1);
841 assert_eq!(*obs_data2.borrow(), 2);
842 }
843
844 #[test]
845 fn subscribe_after_event() {
846 let mut context = Context::new();
847 let obs_data = Rc::new(RefCell::new(0));
848 let obs_data_clone = Rc::clone(&obs_data);
849
850 context.emit_event(Event1 { data: 1 });
851 context.subscribe_to_event::<Event1>(move |_, event| {
852 *obs_data_clone.borrow_mut() = event.data;
853 });
854
855 context.execute();
856 assert_eq!(*obs_data.borrow(), 0);
857 }
858
859 #[test]
860 fn shutdown_cancels_plans() {
861 let mut context = Context::new();
862 add_plan(&mut context, 1.0, 1);
863 context.add_plan(1.5, Context::shutdown);
864 add_plan(&mut context, 2.0, 2);
865 context.execute();
866 assert_eq!(context.get_current_time(), 1.5);
867 assert_eq!(*context.get_data_mut(ComponentA), vec![1]);
868 }
869
870 #[test]
871 fn shutdown_cancels_callbacks() {
872 let mut context = Context::new();
873 add_plan(&mut context, 1.0, 1);
874 context.add_plan(1.5, |context| {
875 context.queue_callback(|context| {
878 context.get_data_mut(ComponentA).push(3);
879 });
880 context.shutdown();
881 });
882 context.execute();
883 assert_eq!(context.get_current_time(), 1.5);
884 assert_eq!(*context.get_data_mut(ComponentA), vec![1]);
885 }
886
887 #[test]
888 fn shutdown_cancels_events() {
889 let mut context = Context::new();
890 let obs_data = Rc::new(RefCell::new(0));
891 let obs_data_clone = Rc::clone(&obs_data);
892 context.subscribe_to_event::<Event1>(move |_, event| {
893 *obs_data_clone.borrow_mut() = event.data;
894 });
895 context.emit_event(Event1 { data: 1 });
896 context.shutdown();
897 context.execute();
898 assert_eq!(*obs_data.borrow(), 0);
899 }
900
901 #[test]
902 #[allow(clippy::cast_sign_loss)]
903 #[allow(clippy::cast_possible_truncation)]
904 fn periodic_plan_self_schedules() {
905 let mut context = Context::new();
908 context.add_periodic_plan_with_phase(
909 1.0,
910 |context| {
911 let time = context.get_current_time();
912 context.get_data_mut(ComponentA).push(time as u32);
913 },
914 ExecutionPhase::Last,
915 );
916 context.add_plan(1.0, move |_context| {});
917 context.add_plan(1.5, move |_context| {});
918 context.execute();
919 assert_eq!(context.get_current_time(), 2.0);
920
921 assert_eq!(*context.get_data(ComponentA), vec![0, 1, 2]); }
923
924 #[test]
925 fn shutdown_requested_reset() {
926 let mut context = Context::new();
929 context.add_person(()).unwrap();
930
931 context.add_plan(0.0, |ctx| {
933 ctx.shutdown();
934 });
935
936 context.execute();
938 assert_eq!(context.get_current_time(), 0.0);
939 assert_eq!(context.get_current_population(), 1);
940
941 context.add_plan(2.0, |ctx| {
943 ctx.add_person(()).unwrap();
944 });
945
946 context.execute();
950 assert_eq!(context.get_current_time(), 2.0);
951 assert_eq!(
952 context.get_current_population(),
953 2,
954 "If this fails, shutdown_requested was not properly reset"
955 );
956 }
957}