1use crate::plan::{PlanId, Queue};
6#[cfg(feature = "progress_bar")]
7use crate::progress::update_timeline_progress;
8#[cfg(feature = "debugger")]
9use crate::{debugger::enter_debugger, plan::PlanSchedule};
10use crate::{error, trace};
11use crate::{HashMap, HashMapExt};
12use std::fmt::{Display, Formatter};
13use std::{
14 any::{Any, TypeId},
15 collections::VecDeque,
16 rc::Rc,
17};
18
19type Callback = dyn FnOnce(&mut Context);
21
22type EventHandler<E> = dyn Fn(&mut Context, E);
24
25pub trait IxaEvent {
26 fn on_subscribe(_context: &mut Context) {}
28}
29
30#[derive(PartialEq, Eq, Ord, Clone, Copy, PartialOrd, Hash, Debug)]
38pub enum ExecutionPhase {
39 First,
40 Normal,
41 Last,
42}
43
44impl Display for ExecutionPhase {
45 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
46 write!(f, "{self:?}")
47 }
48}
49
50pub struct Context {
78 plan_queue: Queue<Box<Callback>, ExecutionPhase>,
79 callback_queue: VecDeque<Box<Callback>>,
80 event_handlers: HashMap<TypeId, Box<dyn Any>>,
81 data_plugins: HashMap<TypeId, Box<dyn Any>>,
82 #[cfg(feature = "debugger")]
83 breakpoints_scheduled: Queue<Box<Callback>, ExecutionPhase>,
84 current_time: f64,
85 shutdown_requested: bool,
86 #[cfg(feature = "debugger")]
87 break_requested: bool,
88 #[cfg(feature = "debugger")]
89 breakpoints_enabled: bool,
90}
91
92impl Context {
93 #[must_use]
95 pub fn new() -> Context {
96 Context {
97 plan_queue: Queue::new(),
98 callback_queue: VecDeque::new(),
99 event_handlers: HashMap::new(),
100 data_plugins: HashMap::new(),
101 #[cfg(feature = "debugger")]
102 breakpoints_scheduled: Queue::new(),
103 current_time: 0.0,
104 shutdown_requested: false,
105 #[cfg(feature = "debugger")]
106 break_requested: false,
107 #[cfg(feature = "debugger")]
108 breakpoints_enabled: true,
109 }
110 }
111
112 #[cfg(feature = "debugger")]
120 pub fn schedule_debugger(
121 &mut self,
122 time: f64,
123 priority: Option<ExecutionPhase>,
124 callback: Box<Callback>,
125 ) {
126 trace!("scheduling debugger");
127 let priority = priority.unwrap_or(ExecutionPhase::First);
128 self.breakpoints_scheduled
129 .add_plan(time, callback, priority);
130 }
131
132 #[allow(clippy::missing_panics_doc)]
137 pub fn subscribe_to_event<E: IxaEvent + Copy + 'static>(
138 &mut self,
139 handler: impl Fn(&mut Context, E) + 'static,
140 ) {
141 let handler_vec = self
142 .event_handlers
143 .entry(TypeId::of::<E>())
144 .or_insert_with(|| Box::<Vec<Rc<EventHandler<E>>>>::default());
145 let handler_vec: &mut Vec<Rc<EventHandler<E>>> = handler_vec.downcast_mut().unwrap();
146 handler_vec.push(Rc::new(handler));
147 E::on_subscribe(self);
148 }
149
150 #[allow(clippy::missing_panics_doc)]
155 pub fn emit_event<E: IxaEvent + Copy + 'static>(&mut self, event: E) {
156 let Context {
158 event_handlers,
159 callback_queue,
160 ..
161 } = self;
162 if let Some(handler_vec) = event_handlers.get(&TypeId::of::<E>()) {
163 let handler_vec: &Vec<Rc<EventHandler<E>>> = handler_vec.downcast_ref().unwrap();
164 for handler in handler_vec {
165 let handler_clone = Rc::clone(handler);
166 callback_queue.push_back(Box::new(move |context| handler_clone(context, event)));
167 }
168 }
169 }
170
171 pub fn add_plan(&mut self, time: f64, callback: impl FnOnce(&mut Context) + 'static) -> PlanId {
180 self.add_plan_with_phase(time, callback, ExecutionPhase::Normal)
181 }
182
183 pub fn add_plan_with_phase(
193 &mut self,
194 time: f64,
195 callback: impl FnOnce(&mut Context) + 'static,
196 phase: ExecutionPhase,
197 ) -> PlanId {
198 assert!(
199 !time.is_nan() && !time.is_infinite() && time >= self.current_time,
200 "Time is invalid"
201 );
202 self.plan_queue.add_plan(time, Box::new(callback), phase)
203 }
204
205 fn evaluate_periodic_and_schedule_next(
206 &mut self,
207 period: f64,
208 callback: impl Fn(&mut Context) + 'static,
209 phase: ExecutionPhase,
210 ) {
211 trace!(
212 "evaluate periodic at {} (period={})",
213 self.current_time,
214 period
215 );
216 callback(self);
217 if !self.plan_queue.is_empty() {
218 let next_time = self.current_time + period;
219 self.add_plan_with_phase(
220 next_time,
221 move |context| context.evaluate_periodic_and_schedule_next(period, callback, phase),
222 phase,
223 );
224 }
225 }
226
227 pub fn add_periodic_plan_with_phase(
235 &mut self,
236 period: f64,
237 callback: impl Fn(&mut Context) + 'static,
238 phase: ExecutionPhase,
239 ) {
240 assert!(
241 period > 0.0 && !period.is_nan() && !period.is_infinite(),
242 "Period must be greater than 0"
243 );
244
245 self.add_plan_with_phase(
246 0.0,
247 move |context| context.evaluate_periodic_and_schedule_next(period, callback, phase),
248 phase,
249 );
250 }
251
252 pub fn cancel_plan(&mut self, plan_id: &PlanId) {
259 trace!("canceling plan {plan_id:?}");
260 let result = self.plan_queue.cancel_plan(plan_id);
261 if result.is_none() {
262 error!("Tried to cancel nonexistent plan with ID = {plan_id:?}");
263 }
264 }
265
266 #[doc(hidden)]
267 #[allow(dead_code)]
268 pub(crate) fn remaining_plan_count(&self) -> usize {
269 self.plan_queue.remaining_plan_count()
270 }
271
272 pub fn queue_callback(&mut self, callback: impl FnOnce(&mut Context) + 'static) {
274 trace!("queuing callback");
275 self.callback_queue.push_back(Box::new(callback));
276 }
277
278 #[must_use]
287 #[allow(clippy::missing_panics_doc)]
288 #[allow(clippy::needless_pass_by_value)]
289 pub fn get_data_container_mut<T: DataPlugin>(
290 &mut self,
291 _data_plugin: T,
292 ) -> &mut T::DataContainer {
293 self.data_plugins
294 .entry(TypeId::of::<T>())
295 .or_insert_with(|| Box::new(T::create_data_container()))
296 .downcast_mut::<T::DataContainer>()
297 .unwrap() }
299
300 #[must_use]
305 #[allow(clippy::needless_pass_by_value)]
306 pub fn get_data_container<T: DataPlugin>(&self, _data_plugin: T) -> Option<&T::DataContainer> {
307 if let Some(data) = self.data_plugins.get(&TypeId::of::<T>()) {
308 data.downcast_ref::<T::DataContainer>()
309 } else {
310 None
311 }
312 }
313
314 pub fn shutdown(&mut self) {
317 trace!("shutdown context");
318 self.shutdown_requested = true;
319 }
320
321 #[must_use]
325 pub fn get_current_time(&self) -> f64 {
326 self.current_time
327 }
328
329 #[cfg(feature = "debugger")]
331 pub fn request_debugger(&mut self) {
332 self.break_requested = true;
333 }
334
335 #[cfg(feature = "debugger")]
337 pub fn cancel_debugger_request(&mut self) {
338 self.break_requested = false;
339 }
340
341 #[cfg(feature = "debugger")]
343 pub fn disable_breakpoints(&mut self) {
344 self.breakpoints_enabled = false;
345 }
346
347 #[cfg(feature = "debugger")]
349 pub fn enable_breakpoints(&mut self) {
350 self.breakpoints_enabled = true;
351 }
352
353 #[must_use]
355 #[cfg(feature = "debugger")]
356 pub fn breakpoints_are_enabled(&self) -> bool {
357 self.breakpoints_enabled
358 }
359
360 #[cfg(feature = "debugger")]
362 pub fn delete_breakpoint(&mut self, breakpoint_id: u64) -> Option<Box<Callback>> {
363 self.breakpoints_scheduled
364 .cancel_plan(&PlanId(breakpoint_id))
365 }
366
367 #[must_use]
370 #[cfg(feature = "debugger")]
371 pub fn list_breakpoints(&self, at_most: usize) -> Vec<&PlanSchedule<ExecutionPhase>> {
372 self.breakpoints_scheduled.list_schedules(at_most)
373 }
374
375 #[cfg(feature = "debugger")]
377 pub fn clear_breakpoints(&mut self) {
378 self.breakpoints_scheduled.clear();
379 }
380
381 pub fn execute(&mut self) {
383 trace!("entering event loop");
384 loop {
386 #[cfg(feature = "progress_bar")]
387 if crate::progress::MAX_TIME.get().is_some() {
388 update_timeline_progress(self.current_time);
389 }
390
391 #[cfg(feature = "debugger")]
392 if self.break_requested {
393 enter_debugger(self);
394 } else if self.shutdown_requested {
395 break;
396 } else {
397 self.execute_single_step();
398 }
399
400 #[cfg(not(feature = "debugger"))]
401 if self.shutdown_requested {
402 break;
403 } else {
404 self.execute_single_step();
405 }
406 }
407 }
408
409 pub fn execute_single_step(&mut self) {
415 #[cfg(feature = "debugger")]
420 if let Some((bp, _)) = self.breakpoints_scheduled.peek() {
421 if let Some(plan_time) = self.plan_queue.next_time() {
426 if (bp.priority == ExecutionPhase::First && bp.time <= plan_time)
427 || (bp.priority == ExecutionPhase::Last && bp.time < plan_time)
428 {
429 self.breakpoints_scheduled.get_next_plan(); if self.breakpoints_enabled {
431 self.break_requested = true;
432 return;
433 }
434 }
435 } else {
436 self.breakpoints_scheduled.get_next_plan(); if self.breakpoints_enabled {
438 self.break_requested = true;
439 return;
440 }
441 }
442 }
443
444 if let Some(callback) = self.callback_queue.pop_front() {
446 trace!("calling callback");
447 callback(self);
448 }
449 else if let Some(plan) = self.plan_queue.get_next_plan() {
451 trace!("calling plan at {:.6}", plan.time);
452 self.current_time = plan.time;
453 (plan.data)(self);
454 } else {
455 trace!("No callbacks or plans; exiting event loop");
456 self.shutdown_requested = true;
458 }
459 }
460}
461
462pub trait PluginContext: Sized {
476 fn subscribe_to_event<E: IxaEvent + Copy + 'static>(
477 &mut self,
478 handler: impl Fn(&mut Context, E) + 'static,
479 );
480 fn emit_event<E: IxaEvent + Copy + 'static>(&mut self, event: E);
481 fn add_plan(&mut self, time: f64, callback: impl FnOnce(&mut Context) + 'static) -> PlanId;
482 fn add_plan_with_phase(
483 &mut self,
484 time: f64,
485 callback: impl FnOnce(&mut Context) + 'static,
486 phase: ExecutionPhase,
487 ) -> PlanId;
488 fn add_periodic_plan_with_phase(
489 &mut self,
490 period: f64,
491 callback: impl Fn(&mut Context) + 'static,
492 phase: ExecutionPhase,
493 );
494 fn cancel_plan(&mut self, plan_id: &PlanId);
495 fn queue_callback(&mut self, callback: impl FnOnce(&mut Context) + 'static);
496 fn get_data_container_mut<T: DataPlugin>(&mut self, plugin: T) -> &mut T::DataContainer;
497 fn get_data_container<T: DataPlugin>(&self, plugin: T) -> Option<&T::DataContainer>;
498 fn get_current_time(&self) -> f64;
499}
500
501impl PluginContext for Context {
502 delegate::delegate! {
503 to self {
504 fn subscribe_to_event<E: IxaEvent + Copy + 'static>(&mut self, handler: impl Fn(&mut Context, E) + 'static);
505 fn emit_event<E: IxaEvent + Copy + 'static>(&mut self, event: E);
506 fn add_plan(&mut self, time: f64, callback: impl FnOnce(&mut Context) + 'static) -> PlanId;
507 fn add_plan_with_phase(&mut self, time: f64, callback: impl FnOnce(&mut Context) + 'static, phase: ExecutionPhase) -> PlanId;
508 fn add_periodic_plan_with_phase(&mut self, period: f64, callback: impl Fn(&mut Context) + 'static, phase: ExecutionPhase);
509 fn cancel_plan(&mut self, plan_id: &PlanId);
510 fn queue_callback(&mut self, callback: impl FnOnce(&mut Context) + 'static);
511 fn get_data_container_mut<T: DataPlugin>(&mut self, plugin: T) -> &mut T::DataContainer;
512 fn get_data_container<T: DataPlugin>(&self, plugin: T) -> Option<&T::DataContainer>;
513 fn get_current_time(&self) -> f64;
514 }
515 }
516}
517
518#[cfg(test)]
523mod test_plugin_context {
524 use crate::prelude_for_plugins::*;
525 #[derive(Copy, Clone, IxaEvent)]
526 struct MyEvent {
527 pub data: usize,
528 }
529
530 define_data_plugin!(MyData, i32, 0);
531
532 fn do_stuff_with_context(context: &mut impl PluginContext) {
533 context.add_plan(1.0, |context| {
534 let data = context.get_data_container(MyData).unwrap();
535 assert_eq!(*data, 42);
536 });
537 }
538
539 trait MyDataExt: PluginContext {
540 fn all_methods(&mut self) {
541 assert_eq!(self.get_current_time(), 0.0);
542 }
543 fn all_methods_mut(&mut self) {
544 self.setup();
545 self.subscribe_to_event(|_: &mut Context, event: MyEvent| {
546 assert_eq!(event.data, 42);
547 });
548 self.emit_event(MyEvent { data: 42 });
549 self.add_plan_with_phase(
550 1.0,
551 |context| {
552 let data = context.get_data_container(MyData).unwrap();
553 assert_eq!(*data, 42);
554 context.set_my_data(100);
555 },
556 crate::ExecutionPhase::Last,
557 );
558 self.add_plan(1.0, |context| {
559 assert_eq!(context.get_my_data(), 42);
560 });
561 self.add_periodic_plan_with_phase(
562 1.0,
563 |context| {
564 println!(
565 "Periodic plan at time {} with data {}",
566 context.get_current_time(),
567 context.get_my_data()
568 );
569 },
570 crate::ExecutionPhase::Normal,
571 );
572 self.queue_callback(|context| {
573 let data = context.get_data_container(MyData).unwrap();
574 assert_eq!(*data, 42);
575 });
576 }
577 fn setup(&mut self) {
578 let data = self.get_data_container_mut(MyData);
579 *data = 42;
580 do_stuff_with_context(self);
581 }
582 fn get_my_data(&self) -> i32 {
583 *self.get_data_container(MyData).unwrap()
584 }
585 fn set_my_data(&mut self, value: i32) {
586 let data = self.get_data_container_mut(MyData);
587 *data = value;
588 }
589 fn test_external_function(&mut self) {
590 self.setup();
591 do_stuff_with_context(self);
592 }
593 }
594 impl MyDataExt for Context {}
595
596 #[test]
597 fn test_all_methods() {
598 let mut context = Context::new();
599 context.all_methods_mut();
600 context.all_methods();
601 context.execute();
602 }
603
604 #[test]
605 fn test_plugin_context() {
606 let mut context = Context::new();
607 context.setup();
608 assert_eq!(context.get_my_data(), 42);
609 }
610
611 #[test]
612 fn test_external_function() {
613 let mut context = Context::new();
614 context.test_external_function();
615 assert_eq!(context.get_my_data(), 42);
616 }
617}
618
619#[cfg(feature = "debugger")]
625pub(crate) fn run_with_plugin<T: DataPlugin>(
626 context: &mut Context,
627 f: impl Fn(&mut Context, &mut T::DataContainer),
628) {
629 let mut data_container_box = context.data_plugins.remove(&TypeId::of::<T>()).unwrap();
632 let data_container = data_container_box
633 .downcast_mut::<T::DataContainer>()
634 .unwrap();
635
636 f(context, data_container);
638
639 context
641 .data_plugins
642 .insert(TypeId::of::<T>(), data_container_box);
643}
644
645impl Default for Context {
646 fn default() -> Self {
647 Self::new()
648 }
649}
650
651pub trait DataPlugin: Any {
653 type DataContainer;
654
655 fn create_data_container() -> Self::DataContainer;
656}
657
658#[macro_export]
660macro_rules! define_data_plugin {
661 ($plugin:ident, $data_container:ty, $default: expr) => {
662 struct $plugin;
663
664 impl $crate::context::DataPlugin for $plugin {
665 type DataContainer = $data_container;
666
667 fn create_data_container() -> Self::DataContainer {
668 $default
669 }
670 }
671 };
672}
673pub use define_data_plugin;
674
675#[cfg(test)]
676#[allow(clippy::float_cmp)]
677mod tests {
678 use std::cell::RefCell;
679
680 use super::*;
681 use ixa_derive::IxaEvent;
682
683 define_data_plugin!(ComponentA, Vec<u32>, vec![]);
684
685 #[test]
686 fn empty_context() {
687 let mut context = Context::new();
688 context.execute();
689 assert_eq!(context.get_current_time(), 0.0);
690 }
691
692 #[test]
693 fn get_data_container() {
694 let mut context = Context::new();
695 context.get_data_container_mut(ComponentA).push(1);
696 assert_eq!(*context.get_data_container(ComponentA).unwrap(), vec![1],);
697 }
698
699 #[test]
700 fn get_uninitialized_data_container() {
701 let context = Context::new();
702 assert!(context.get_data_container(ComponentA).is_none());
703 }
704
705 fn add_plan(context: &mut Context, time: f64, value: u32) -> PlanId {
706 context.add_plan(time, move |context| {
707 context.get_data_container_mut(ComponentA).push(value);
708 })
709 }
710
711 fn add_plan_with_phase(
712 context: &mut Context,
713 time: f64,
714 value: u32,
715 phase: ExecutionPhase,
716 ) -> PlanId {
717 context.add_plan_with_phase(
718 time,
719 move |context| {
720 context.get_data_container_mut(ComponentA).push(value);
721 },
722 phase,
723 )
724 }
725
726 #[test]
727 #[should_panic(expected = "Time is invalid")]
728 fn negative_plan_time() {
729 let mut context = Context::new();
730 add_plan(&mut context, -1.0, 0);
731 }
732
733 #[test]
734 #[should_panic(expected = "Time is invalid")]
735 fn infinite_plan_time() {
736 let mut context = Context::new();
737 add_plan(&mut context, f64::INFINITY, 0);
738 }
739
740 #[test]
741 #[should_panic(expected = "Time is invalid")]
742 fn nan_plan_time() {
743 let mut context = Context::new();
744 add_plan(&mut context, f64::NAN, 0);
745 }
746
747 #[test]
748 fn timed_plan_only() {
749 let mut context = Context::new();
750 add_plan(&mut context, 1.0, 1);
751 context.execute();
752 assert_eq!(context.get_current_time(), 1.0);
753 assert_eq!(*context.get_data_container_mut(ComponentA), vec![1]);
754 }
755
756 #[test]
757 fn callback_only() {
758 let mut context = Context::new();
759 context.queue_callback(|context| {
760 context.get_data_container_mut(ComponentA).push(1);
761 });
762 context.execute();
763 assert_eq!(context.get_current_time(), 0.0);
764 assert_eq!(*context.get_data_container_mut(ComponentA), vec![1]);
765 }
766
767 #[test]
768 fn callback_before_timed_plan() {
769 let mut context = Context::new();
770 context.queue_callback(|context| {
771 context.get_data_container_mut(ComponentA).push(1);
772 });
773 add_plan(&mut context, 1.0, 2);
774 context.execute();
775 assert_eq!(context.get_current_time(), 1.0);
776 assert_eq!(*context.get_data_container_mut(ComponentA), vec![1, 2]);
777 }
778
779 #[test]
780 fn callback_adds_timed_plan() {
781 let mut context = Context::new();
782 context.queue_callback(|context| {
783 context.get_data_container_mut(ComponentA).push(1);
784 add_plan(context, 1.0, 2);
785 context.get_data_container_mut(ComponentA).push(3);
786 });
787 context.execute();
788 assert_eq!(context.get_current_time(), 1.0);
789 assert_eq!(*context.get_data_container_mut(ComponentA), vec![1, 3, 2]);
790 }
791
792 #[test]
793 fn callback_adds_callback_and_timed_plan() {
794 let mut context = Context::new();
795 context.queue_callback(|context| {
796 context.get_data_container_mut(ComponentA).push(1);
797 add_plan(context, 1.0, 2);
798 context.queue_callback(|context| {
799 context.get_data_container_mut(ComponentA).push(4);
800 });
801 context.get_data_container_mut(ComponentA).push(3);
802 });
803 context.execute();
804 assert_eq!(context.get_current_time(), 1.0);
805 assert_eq!(
806 *context.get_data_container_mut(ComponentA),
807 vec![1, 3, 4, 2]
808 );
809 }
810
811 #[test]
812 fn timed_plan_adds_callback_and_timed_plan() {
813 let mut context = Context::new();
814 context.add_plan(1.0, |context| {
815 context.get_data_container_mut(ComponentA).push(1);
816 add_plan(context, 2.0, 3);
818 context.queue_callback(|context| {
819 context.get_data_container_mut(ComponentA).push(2);
820 });
821 });
822 context.execute();
823 assert_eq!(context.get_current_time(), 2.0);
824 assert_eq!(*context.get_data_container_mut(ComponentA), vec![1, 2, 3]);
825 }
826
827 #[test]
828 fn cancel_plan() {
829 let mut context = Context::new();
830 let to_cancel = add_plan(&mut context, 2.0, 1);
831 context.add_plan(1.0, move |context| {
832 context.cancel_plan(&to_cancel);
833 });
834 context.execute();
835 assert_eq!(context.get_current_time(), 1.0);
836 let test_vec: Vec<u32> = vec![];
837 assert_eq!(*context.get_data_container_mut(ComponentA), test_vec);
838 }
839
840 #[test]
841 fn add_plan_with_current_time() {
842 let mut context = Context::new();
843 context.add_plan(1.0, move |context| {
844 context.get_data_container_mut(ComponentA).push(1);
845 add_plan(context, 1.0, 2);
846 context.queue_callback(|context| {
847 context.get_data_container_mut(ComponentA).push(3);
848 });
849 });
850 context.execute();
851 assert_eq!(context.get_current_time(), 1.0);
852 assert_eq!(*context.get_data_container_mut(ComponentA), vec![1, 3, 2]);
853 }
854
855 #[test]
856 fn plans_at_same_time_fire_in_order() {
857 let mut context = Context::new();
858 add_plan(&mut context, 1.0, 1);
859 add_plan(&mut context, 1.0, 2);
860 context.execute();
861 assert_eq!(context.get_current_time(), 1.0);
862 assert_eq!(*context.get_data_container_mut(ComponentA), vec![1, 2]);
863 }
864
865 #[test]
866 fn check_plan_phase_ordering() {
867 assert!(ExecutionPhase::First < ExecutionPhase::Normal);
868 assert!(ExecutionPhase::Normal < ExecutionPhase::Last);
869 }
870
871 #[test]
872 fn plans_at_same_time_follow_phase() {
873 let mut context = Context::new();
874 add_plan(&mut context, 1.0, 1);
875 add_plan_with_phase(&mut context, 1.0, 5, ExecutionPhase::Last);
876 add_plan_with_phase(&mut context, 1.0, 3, ExecutionPhase::First);
877 add_plan(&mut context, 1.0, 2);
878 add_plan_with_phase(&mut context, 1.0, 6, ExecutionPhase::Last);
879 add_plan_with_phase(&mut context, 1.0, 4, ExecutionPhase::First);
880 context.execute();
881 assert_eq!(context.get_current_time(), 1.0);
882 assert_eq!(
883 *context.get_data_container_mut(ComponentA),
884 vec![3, 4, 1, 2, 5, 6]
885 );
886 }
887
888 #[derive(Copy, Clone, IxaEvent)]
889 struct Event1 {
890 pub data: usize,
891 }
892
893 #[derive(Copy, Clone, IxaEvent)]
894 struct Event2 {
895 pub data: usize,
896 }
897
898 #[test]
899 fn simple_event() {
900 let mut context = Context::new();
901 let obs_data = Rc::new(RefCell::new(0));
902 let obs_data_clone = Rc::clone(&obs_data);
903
904 context.subscribe_to_event::<Event1>(move |_, event| {
905 *obs_data_clone.borrow_mut() = event.data;
906 });
907
908 context.emit_event(Event1 { data: 1 });
909 context.execute();
910 assert_eq!(*obs_data.borrow(), 1);
911 }
912
913 #[test]
914 fn multiple_events() {
915 let mut context = Context::new();
916 let obs_data = Rc::new(RefCell::new(0));
917 let obs_data_clone = Rc::clone(&obs_data);
918
919 context.subscribe_to_event::<Event1>(move |_, event| {
920 *obs_data_clone.borrow_mut() += event.data;
921 });
922
923 context.emit_event(Event1 { data: 1 });
924 context.emit_event(Event1 { data: 2 });
925 context.execute();
926
927 assert_eq!(*obs_data.borrow(), 3);
929 }
930
931 #[test]
932 fn multiple_event_handlers() {
933 let mut context = Context::new();
934 let obs_data1 = Rc::new(RefCell::new(0));
935 let obs_data1_clone = Rc::clone(&obs_data1);
936 let obs_data2 = Rc::new(RefCell::new(0));
937 let obs_data2_clone = Rc::clone(&obs_data2);
938
939 context.subscribe_to_event::<Event1>(move |_, event| {
940 *obs_data1_clone.borrow_mut() = event.data;
941 });
942 context.subscribe_to_event::<Event1>(move |_, event| {
943 *obs_data2_clone.borrow_mut() = event.data;
944 });
945 context.emit_event(Event1 { data: 1 });
946 context.execute();
947 assert_eq!(*obs_data1.borrow(), 1);
948 assert_eq!(*obs_data2.borrow(), 1);
949 }
950
951 #[test]
952 fn multiple_event_types() {
953 let mut context = Context::new();
954 let obs_data1 = Rc::new(RefCell::new(0));
955 let obs_data1_clone = Rc::clone(&obs_data1);
956 let obs_data2 = Rc::new(RefCell::new(0));
957 let obs_data2_clone = Rc::clone(&obs_data2);
958
959 context.subscribe_to_event::<Event1>(move |_, event| {
960 *obs_data1_clone.borrow_mut() = event.data;
961 });
962 context.subscribe_to_event::<Event2>(move |_, event| {
963 *obs_data2_clone.borrow_mut() = event.data;
964 });
965 context.emit_event(Event1 { data: 1 });
966 context.emit_event(Event2 { data: 2 });
967 context.execute();
968 assert_eq!(*obs_data1.borrow(), 1);
969 assert_eq!(*obs_data2.borrow(), 2);
970 }
971
972 #[test]
973 fn subscribe_after_event() {
974 let mut context = Context::new();
975 let obs_data = Rc::new(RefCell::new(0));
976 let obs_data_clone = Rc::clone(&obs_data);
977
978 context.emit_event(Event1 { data: 1 });
979 context.subscribe_to_event::<Event1>(move |_, event| {
980 *obs_data_clone.borrow_mut() = event.data;
981 });
982
983 context.execute();
984 assert_eq!(*obs_data.borrow(), 0);
985 }
986
987 #[test]
988 fn shutdown_cancels_plans() {
989 let mut context = Context::new();
990 add_plan(&mut context, 1.0, 1);
991 context.add_plan(1.5, Context::shutdown);
992 add_plan(&mut context, 2.0, 2);
993 context.execute();
994 assert_eq!(context.get_current_time(), 1.5);
995 assert_eq!(*context.get_data_container_mut(ComponentA), vec![1]);
996 }
997
998 #[test]
999 fn shutdown_cancels_callbacks() {
1000 let mut context = Context::new();
1001 add_plan(&mut context, 1.0, 1);
1002 context.add_plan(1.5, |context| {
1003 context.queue_callback(|context| {
1006 context.get_data_container_mut(ComponentA).push(3);
1007 });
1008 context.shutdown();
1009 });
1010 context.execute();
1011 assert_eq!(context.get_current_time(), 1.5);
1012 assert_eq!(*context.get_data_container_mut(ComponentA), vec![1]);
1013 }
1014
1015 #[test]
1016 fn shutdown_cancels_events() {
1017 let mut context = Context::new();
1018 let obs_data = Rc::new(RefCell::new(0));
1019 let obs_data_clone = Rc::clone(&obs_data);
1020 context.subscribe_to_event::<Event1>(move |_, event| {
1021 *obs_data_clone.borrow_mut() = event.data;
1022 });
1023 context.emit_event(Event1 { data: 1 });
1024 context.shutdown();
1025 context.execute();
1026 assert_eq!(*obs_data.borrow(), 0);
1027 }
1028
1029 #[test]
1030 #[allow(clippy::cast_sign_loss)]
1031 #[allow(clippy::cast_possible_truncation)]
1032 fn periodic_plan_self_schedules() {
1033 let mut context = Context::new();
1036 context.add_periodic_plan_with_phase(
1037 1.0,
1038 |context| {
1039 let time = context.get_current_time();
1040 context.get_data_container_mut(ComponentA).push(time as u32);
1041 },
1042 ExecutionPhase::Last,
1043 );
1044 context.add_plan(1.0, move |_context| {});
1045 context.add_plan(1.5, move |_context| {});
1046 context.execute();
1047 assert_eq!(context.get_current_time(), 2.0);
1048
1049 assert_eq!(
1050 *context.get_data_container(ComponentA).unwrap(),
1051 vec![0, 1, 2]
1052 ); }
1054}