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::{error, get_data_plugin_count, trace, 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 error!("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 PluginContext: Sized {
528 fn subscribe_to_event<E: IxaEvent + Copy + 'static>(
529 &mut self,
530 handler: impl Fn(&mut Context, E) + 'static,
531 );
532 fn emit_event<E: IxaEvent + Copy + 'static>(&mut self, event: E);
533 fn add_plan(&mut self, time: f64, callback: impl FnOnce(&mut Context) + 'static) -> PlanId;
534 fn add_plan_with_phase(
535 &mut self,
536 time: f64,
537 callback: impl FnOnce(&mut Context) + 'static,
538 phase: ExecutionPhase,
539 ) -> PlanId;
540 fn add_periodic_plan_with_phase(
541 &mut self,
542 period: f64,
543 callback: impl Fn(&mut Context) + 'static,
544 phase: ExecutionPhase,
545 );
546 fn cancel_plan(&mut self, plan_id: &PlanId);
547 fn queue_callback(&mut self, callback: impl FnOnce(&mut Context) + 'static);
548 fn get_data_mut<T: DataPlugin>(&mut self, plugin: T) -> &mut T::DataContainer;
549 fn get_data<T: DataPlugin>(&self, plugin: T) -> &T::DataContainer;
550 fn get_current_time(&self) -> f64;
551 fn get_execution_statistics(&mut self) -> ExecutionStatistics;
552}
553impl PluginContext for Context {
554 delegate::delegate! {
555 to self {
556 fn subscribe_to_event<E: IxaEvent + Copy + 'static>(&mut self, handler: impl Fn(&mut Context, E) + 'static);
557 fn emit_event<E: IxaEvent + Copy + 'static>(&mut self, event: E);
558 fn add_plan(&mut self, time: f64, callback: impl FnOnce(&mut Context) + 'static) -> PlanId;
559 fn add_plan_with_phase(&mut self, time: f64, callback: impl FnOnce(&mut Context) + 'static, phase: ExecutionPhase) -> PlanId;
560 fn add_periodic_plan_with_phase(&mut self, period: f64, callback: impl Fn(&mut Context) + 'static, phase: ExecutionPhase);
561 fn cancel_plan(&mut self, plan_id: &PlanId);
562 fn queue_callback(&mut self, callback: impl FnOnce(&mut Context) + 'static);
563 fn get_data_mut<T: DataPlugin>(&mut self, plugin: T) -> &mut T::DataContainer;
564 fn get_data<T: DataPlugin>(&self, plugin: T) -> &T::DataContainer;
565 fn get_current_time(&self) -> f64;
566 fn get_execution_statistics(&mut self) -> ExecutionStatistics;
567 }
568 }
569}
570
571#[cfg(test)]
576mod test_plugin_context {
577 use crate::prelude_for_plugins::*;
578 #[derive(Copy, Clone, IxaEvent)]
579 struct MyEvent {
580 pub data: usize,
581 }
582
583 define_data_plugin!(MyData, i32, 0);
584
585 fn do_stuff_with_context(context: &mut impl PluginContext) {
586 context.add_plan(1.0, |context| {
587 let data = context.get_data(MyData);
588 assert_eq!(*data, 42);
589 });
590 }
591
592 trait MyDataExt: PluginContext {
593 fn all_methods(&mut self) {
594 assert_eq!(self.get_current_time(), 0.0);
595 }
596 fn all_methods_mut(&mut self) {
597 self.setup();
598 self.subscribe_to_event(|_: &mut Context, event: MyEvent| {
599 assert_eq!(event.data, 42);
600 });
601 self.emit_event(MyEvent { data: 42 });
602 self.add_plan_with_phase(
603 1.0,
604 |context| {
605 let data = context.get_data(MyData);
606 assert_eq!(*data, 42);
607 context.set_my_data(100);
608 },
609 crate::ExecutionPhase::Last,
610 );
611 self.add_plan(1.0, |context| {
612 assert_eq!(context.get_my_data(), 42);
613 });
614 self.add_periodic_plan_with_phase(
615 1.0,
616 |context| {
617 println!(
618 "Periodic plan at time {} with data {}",
619 context.get_current_time(),
620 context.get_my_data()
621 );
622 },
623 crate::ExecutionPhase::Normal,
624 );
625 self.queue_callback(|context| {
626 let data = context.get_data(MyData);
627 assert_eq!(*data, 42);
628 });
629 }
630 fn setup(&mut self) {
631 let data = self.get_data_mut(MyData);
632 *data = 42;
633 do_stuff_with_context(self);
634 }
635 fn get_my_data(&self) -> i32 {
636 *self.get_data(MyData)
637 }
638 fn set_my_data(&mut self, value: i32) {
639 let data = self.get_data_mut(MyData);
640 *data = value;
641 }
642 fn test_external_function(&mut self) {
643 self.setup();
644 do_stuff_with_context(self);
645 }
646 }
647 impl MyDataExt for Context {}
648
649 #[test]
650 fn test_all_methods() {
651 let mut context = Context::new();
652 context.all_methods_mut();
653 context.all_methods();
654 context.execute();
655 }
656
657 #[test]
658 fn test_plugin_context() {
659 let mut context = Context::new();
660 context.setup();
661 assert_eq!(context.get_my_data(), 42);
662 }
663
664 #[test]
665 fn test_external_function() {
666 let mut context = Context::new();
667 context.test_external_function();
668 assert_eq!(context.get_my_data(), 42);
669 }
670}
671
672impl Default for Context {
673 fn default() -> Self {
674 Self::new()
675 }
676}
677
678#[cfg(test)]
679#[allow(clippy::float_cmp)]
680mod tests {
681 use std::cell::RefCell;
682
683 use super::*;
684 use crate::define_data_plugin;
685 use ixa_derive::IxaEvent;
686
687 define_data_plugin!(ComponentA, Vec<u32>, vec![]);
688
689 #[test]
690 fn empty_context() {
691 let mut context = Context::new();
692 context.execute();
693 assert_eq!(context.get_current_time(), 0.0);
694 }
695
696 #[test]
697 fn get_data() {
698 let mut context = Context::new();
699 context.get_data_mut(ComponentA).push(1);
700 assert_eq!(*context.get_data(ComponentA), vec![1],);
701 }
702
703 fn add_plan(context: &mut Context, time: f64, value: u32) -> PlanId {
704 context.add_plan(time, move |context| {
705 context.get_data_mut(ComponentA).push(value);
706 })
707 }
708
709 fn add_plan_with_phase(
710 context: &mut Context,
711 time: f64,
712 value: u32,
713 phase: ExecutionPhase,
714 ) -> PlanId {
715 context.add_plan_with_phase(
716 time,
717 move |context| {
718 context.get_data_mut(ComponentA).push(value);
719 },
720 phase,
721 )
722 }
723
724 #[test]
725 #[should_panic(expected = "Time is invalid")]
726 fn negative_plan_time() {
727 let mut context = Context::new();
728 add_plan(&mut context, -1.0, 0);
729 }
730
731 #[test]
732 #[should_panic(expected = "Time is invalid")]
733 fn infinite_plan_time() {
734 let mut context = Context::new();
735 add_plan(&mut context, f64::INFINITY, 0);
736 }
737
738 #[test]
739 #[should_panic(expected = "Time is invalid")]
740 fn nan_plan_time() {
741 let mut context = Context::new();
742 add_plan(&mut context, f64::NAN, 0);
743 }
744
745 #[test]
746 fn timed_plan_only() {
747 let mut context = Context::new();
748 add_plan(&mut context, 1.0, 1);
749 context.execute();
750 assert_eq!(context.get_current_time(), 1.0);
751 assert_eq!(*context.get_data_mut(ComponentA), vec![1]);
752 }
753
754 #[test]
755 fn callback_only() {
756 let mut context = Context::new();
757 context.queue_callback(|context| {
758 context.get_data_mut(ComponentA).push(1);
759 });
760 context.execute();
761 assert_eq!(context.get_current_time(), 0.0);
762 assert_eq!(*context.get_data_mut(ComponentA), vec![1]);
763 }
764
765 #[test]
766 fn callback_before_timed_plan() {
767 let mut context = Context::new();
768 context.queue_callback(|context| {
769 context.get_data_mut(ComponentA).push(1);
770 });
771 add_plan(&mut context, 1.0, 2);
772 context.execute();
773 assert_eq!(context.get_current_time(), 1.0);
774 assert_eq!(*context.get_data_mut(ComponentA), vec![1, 2]);
775 }
776
777 #[test]
778 fn callback_adds_timed_plan() {
779 let mut context = Context::new();
780 context.queue_callback(|context| {
781 context.get_data_mut(ComponentA).push(1);
782 add_plan(context, 1.0, 2);
783 context.get_data_mut(ComponentA).push(3);
784 });
785 context.execute();
786 assert_eq!(context.get_current_time(), 1.0);
787 assert_eq!(*context.get_data_mut(ComponentA), vec![1, 3, 2]);
788 }
789
790 #[test]
791 fn callback_adds_callback_and_timed_plan() {
792 let mut context = Context::new();
793 context.queue_callback(|context| {
794 context.get_data_mut(ComponentA).push(1);
795 add_plan(context, 1.0, 2);
796 context.queue_callback(|context| {
797 context.get_data_mut(ComponentA).push(4);
798 });
799 context.get_data_mut(ComponentA).push(3);
800 });
801 context.execute();
802 assert_eq!(context.get_current_time(), 1.0);
803 assert_eq!(*context.get_data_mut(ComponentA), vec![1, 3, 4, 2]);
804 }
805
806 #[test]
807 fn timed_plan_adds_callback_and_timed_plan() {
808 let mut context = Context::new();
809 context.add_plan(1.0, |context| {
810 context.get_data_mut(ComponentA).push(1);
811 add_plan(context, 2.0, 3);
813 context.queue_callback(|context| {
814 context.get_data_mut(ComponentA).push(2);
815 });
816 });
817 context.execute();
818 assert_eq!(context.get_current_time(), 2.0);
819 assert_eq!(*context.get_data_mut(ComponentA), vec![1, 2, 3]);
820 }
821
822 #[test]
823 fn cancel_plan() {
824 let mut context = Context::new();
825 let to_cancel = add_plan(&mut context, 2.0, 1);
826 context.add_plan(1.0, move |context| {
827 context.cancel_plan(&to_cancel);
828 });
829 context.execute();
830 assert_eq!(context.get_current_time(), 1.0);
831 let test_vec: Vec<u32> = vec![];
832 assert_eq!(*context.get_data_mut(ComponentA), test_vec);
833 }
834
835 #[test]
836 fn add_plan_with_current_time() {
837 let mut context = Context::new();
838 context.add_plan(1.0, move |context| {
839 context.get_data_mut(ComponentA).push(1);
840 add_plan(context, 1.0, 2);
841 context.queue_callback(|context| {
842 context.get_data_mut(ComponentA).push(3);
843 });
844 });
845 context.execute();
846 assert_eq!(context.get_current_time(), 1.0);
847 assert_eq!(*context.get_data_mut(ComponentA), vec![1, 3, 2]);
848 }
849
850 #[test]
851 fn plans_at_same_time_fire_in_order() {
852 let mut context = Context::new();
853 add_plan(&mut context, 1.0, 1);
854 add_plan(&mut context, 1.0, 2);
855 context.execute();
856 assert_eq!(context.get_current_time(), 1.0);
857 assert_eq!(*context.get_data_mut(ComponentA), vec![1, 2]);
858 }
859
860 #[test]
861 fn check_plan_phase_ordering() {
862 assert!(ExecutionPhase::First < ExecutionPhase::Normal);
863 assert!(ExecutionPhase::Normal < ExecutionPhase::Last);
864 }
865
866 #[test]
867 fn plans_at_same_time_follow_phase() {
868 let mut context = Context::new();
869 add_plan(&mut context, 1.0, 1);
870 add_plan_with_phase(&mut context, 1.0, 5, ExecutionPhase::Last);
871 add_plan_with_phase(&mut context, 1.0, 3, ExecutionPhase::First);
872 add_plan(&mut context, 1.0, 2);
873 add_plan_with_phase(&mut context, 1.0, 6, ExecutionPhase::Last);
874 add_plan_with_phase(&mut context, 1.0, 4, ExecutionPhase::First);
875 context.execute();
876 assert_eq!(context.get_current_time(), 1.0);
877 assert_eq!(*context.get_data_mut(ComponentA), vec![3, 4, 1, 2, 5, 6]);
878 }
879
880 #[derive(Copy, Clone, IxaEvent)]
881 struct Event1 {
882 pub data: usize,
883 }
884
885 #[derive(Copy, Clone, IxaEvent)]
886 struct Event2 {
887 pub data: usize,
888 }
889
890 #[test]
891 fn simple_event() {
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
896 context.subscribe_to_event::<Event1>(move |_, event| {
897 *obs_data_clone.borrow_mut() = event.data;
898 });
899
900 context.emit_event(Event1 { data: 1 });
901 context.execute();
902 assert_eq!(*obs_data.borrow(), 1);
903 }
904
905 #[test]
906 fn multiple_events() {
907 let mut context = Context::new();
908 let obs_data = Rc::new(RefCell::new(0));
909 let obs_data_clone = Rc::clone(&obs_data);
910
911 context.subscribe_to_event::<Event1>(move |_, event| {
912 *obs_data_clone.borrow_mut() += event.data;
913 });
914
915 context.emit_event(Event1 { data: 1 });
916 context.emit_event(Event1 { data: 2 });
917 context.execute();
918
919 assert_eq!(*obs_data.borrow(), 3);
921 }
922
923 #[test]
924 fn multiple_event_handlers() {
925 let mut context = Context::new();
926 let obs_data1 = Rc::new(RefCell::new(0));
927 let obs_data1_clone = Rc::clone(&obs_data1);
928 let obs_data2 = Rc::new(RefCell::new(0));
929 let obs_data2_clone = Rc::clone(&obs_data2);
930
931 context.subscribe_to_event::<Event1>(move |_, event| {
932 *obs_data1_clone.borrow_mut() = event.data;
933 });
934 context.subscribe_to_event::<Event1>(move |_, event| {
935 *obs_data2_clone.borrow_mut() = event.data;
936 });
937 context.emit_event(Event1 { data: 1 });
938 context.execute();
939 assert_eq!(*obs_data1.borrow(), 1);
940 assert_eq!(*obs_data2.borrow(), 1);
941 }
942
943 #[test]
944 fn multiple_event_types() {
945 let mut context = Context::new();
946 let obs_data1 = Rc::new(RefCell::new(0));
947 let obs_data1_clone = Rc::clone(&obs_data1);
948 let obs_data2 = Rc::new(RefCell::new(0));
949 let obs_data2_clone = Rc::clone(&obs_data2);
950
951 context.subscribe_to_event::<Event1>(move |_, event| {
952 *obs_data1_clone.borrow_mut() = event.data;
953 });
954 context.subscribe_to_event::<Event2>(move |_, event| {
955 *obs_data2_clone.borrow_mut() = event.data;
956 });
957 context.emit_event(Event1 { data: 1 });
958 context.emit_event(Event2 { data: 2 });
959 context.execute();
960 assert_eq!(*obs_data1.borrow(), 1);
961 assert_eq!(*obs_data2.borrow(), 2);
962 }
963
964 #[test]
965 fn subscribe_after_event() {
966 let mut context = Context::new();
967 let obs_data = Rc::new(RefCell::new(0));
968 let obs_data_clone = Rc::clone(&obs_data);
969
970 context.emit_event(Event1 { data: 1 });
971 context.subscribe_to_event::<Event1>(move |_, event| {
972 *obs_data_clone.borrow_mut() = event.data;
973 });
974
975 context.execute();
976 assert_eq!(*obs_data.borrow(), 0);
977 }
978
979 #[test]
980 fn shutdown_cancels_plans() {
981 let mut context = Context::new();
982 add_plan(&mut context, 1.0, 1);
983 context.add_plan(1.5, Context::shutdown);
984 add_plan(&mut context, 2.0, 2);
985 context.execute();
986 assert_eq!(context.get_current_time(), 1.5);
987 assert_eq!(*context.get_data_mut(ComponentA), vec![1]);
988 }
989
990 #[test]
991 fn shutdown_cancels_callbacks() {
992 let mut context = Context::new();
993 add_plan(&mut context, 1.0, 1);
994 context.add_plan(1.5, |context| {
995 context.queue_callback(|context| {
998 context.get_data_mut(ComponentA).push(3);
999 });
1000 context.shutdown();
1001 });
1002 context.execute();
1003 assert_eq!(context.get_current_time(), 1.5);
1004 assert_eq!(*context.get_data_mut(ComponentA), vec![1]);
1005 }
1006
1007 #[test]
1008 fn shutdown_cancels_events() {
1009 let mut context = Context::new();
1010 let obs_data = Rc::new(RefCell::new(0));
1011 let obs_data_clone = Rc::clone(&obs_data);
1012 context.subscribe_to_event::<Event1>(move |_, event| {
1013 *obs_data_clone.borrow_mut() = event.data;
1014 });
1015 context.emit_event(Event1 { data: 1 });
1016 context.shutdown();
1017 context.execute();
1018 assert_eq!(*obs_data.borrow(), 0);
1019 }
1020
1021 #[test]
1022 #[allow(clippy::cast_sign_loss)]
1023 #[allow(clippy::cast_possible_truncation)]
1024 fn periodic_plan_self_schedules() {
1025 let mut context = Context::new();
1028 context.add_periodic_plan_with_phase(
1029 1.0,
1030 |context| {
1031 let time = context.get_current_time();
1032 context.get_data_mut(ComponentA).push(time as u32);
1033 },
1034 ExecutionPhase::Last,
1035 );
1036 context.add_plan(1.0, move |_context| {});
1037 context.add_plan(1.5, move |_context| {});
1038 context.execute();
1039 assert_eq!(context.get_current_time(), 2.0);
1040
1041 assert_eq!(*context.get_data(ComponentA), vec![0, 1, 2]); }
1043}