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