1use smallbox::space::S4;
41use smallbox::SmallBox;
42
43use crate::entity::property::{Property, PropertyInitializationKind};
44use crate::entity::{ContextEntitiesExt, Entity, EntityId};
45use crate::{Context, IxaEvent};
46
47pub(crate) type PartialPropertyChangeEventBox = SmallBox<dyn PartialPropertyChangeEvent, S4>;
60
61pub(crate) trait PartialPropertyChangeEvent {
64 fn emit_in_context(&mut self, context: &mut Context);
66}
67
68impl<E: Entity, P: Property<E>> PartialPropertyChangeEvent
69 for PartialPropertyChangeEventCore<E, P>
70{
71 fn emit_in_context(&mut self, context: &mut Context) {
73 self.0.current = context.get_property(self.0.entity_id);
74
75 {
76 let property_value_store = context.get_property_value_store::<E, P>();
78 if self.0.current != self.0.previous {
79 for counter in &property_value_store.value_change_counters {
80 counter
81 .borrow_mut()
82 .update(self.0.entity_id, self.0.current, context);
83 }
84 }
85 }
86
87 let property_value_store = context.get_property_value_store_mut::<E, P>();
89 if let Some(index) = property_value_store.index.as_mut() {
90 index.remove_entity(&self.0.previous, self.0.entity_id);
92 index.add_entity(&self.0.current, self.0.entity_id);
94 }
95
96 context.emit_event(self.to_event());
103 }
104}
105
106#[repr(transparent)]
113pub(crate) struct PartialPropertyChangeEventCore<E: Entity, P: Property<E>>(
114 PropertyChangeEvent<E, P>,
115);
116impl<E: Entity, P: Property<E>> Clone for PartialPropertyChangeEventCore<E, P> {
119 fn clone(&self) -> Self {
120 *self
121 }
122}
123impl<E: Entity, P: Property<E>> Copy for PartialPropertyChangeEventCore<E, P> {}
124
125impl<E: Entity, P: Property<E>> PartialPropertyChangeEventCore<E, P> {
126 pub fn new(entity_id: EntityId<E>, previous_value: P) -> Self {
127 Self(PropertyChangeEvent {
128 entity_id,
129 current: previous_value,
130 previous: previous_value,
131 })
132 }
133
134 pub fn to_event(self) -> PropertyChangeEvent<E, P> {
135 self.0
136 }
137}
138
139pub struct EntityCreatedEvent<E: Entity> {
142 pub entity_id: EntityId<E>,
144}
145
146impl<E: Entity> Clone for EntityCreatedEvent<E> {
148 fn clone(&self) -> Self {
149 *self
150 }
151}
152
153impl<E: Entity> Copy for EntityCreatedEvent<E> {}
154
155impl<E: Entity> IxaEvent for EntityCreatedEvent<E> {
156 fn on_subscribe(context: &mut Context) {
157 context.entity_store.items[E::id()].entity_created_event_subscribed = true;
158 }
159
160 fn on_unsubscribe(context: &mut Context) {
161 if !context.has_event_handlers::<Self>() {
162 context.entity_store.items[E::id()].entity_created_event_subscribed = false;
163 }
164 }
165}
166
167impl<E: Entity> EntityCreatedEvent<E> {
168 #[must_use]
169 pub fn new(entity_id: EntityId<E>) -> Self {
170 Self { entity_id }
171 }
172}
173
174pub struct PropertyInitializedEvent<E: Entity, P: Property<E>> {
176 pub entity_id: EntityId<E>,
178 pub value: P,
180}
181
182impl<E: Entity, P: Property<E>> Clone for PropertyInitializedEvent<E, P> {
184 fn clone(&self) -> Self {
185 *self
186 }
187}
188
189impl<E: Entity, P: Property<E>> Copy for PropertyInitializedEvent<E, P> {}
190
191impl<E: Entity, P: Property<E>> IxaEvent for PropertyInitializedEvent<E, P> {
192 fn on_subscribe(context: &mut Context) {
193 match P::initialization_kind() {
194 PropertyInitializationKind::Explicit | PropertyInitializationKind::Constant => {
195 context
196 .entity_store
197 .get_property_store_mut::<E>()
198 .property_initialized_event_subscriptions
199 .set(P::id());
200 }
201 PropertyInitializationKind::Derived => {}
202 }
203 }
204
205 fn on_unsubscribe(context: &mut Context) {
206 match P::initialization_kind() {
207 PropertyInitializationKind::Explicit | PropertyInitializationKind::Constant
208 if !context.has_event_handlers::<Self>() =>
209 {
210 context
211 .entity_store
212 .get_property_store_mut::<E>()
213 .property_initialized_event_subscriptions
214 .reset(P::id());
215 }
216 PropertyInitializationKind::Explicit
217 | PropertyInitializationKind::Constant
218 | PropertyInitializationKind::Derived => {}
219 }
220 }
221}
222
223impl<E: Entity, P: Property<E>> PropertyInitializedEvent<E, P> {
224 #[must_use]
225 pub fn new(entity_id: EntityId<E>, value: P) -> Self {
226 Self { entity_id, value }
227 }
228}
229
230#[derive(IxaEvent)]
233pub struct PropertyChangeEvent<E: Entity, P: Property<E>> {
234 pub entity_id: EntityId<E>,
236 pub current: P,
238 pub previous: P,
240}
241
242#[cfg(test)]
243mod tests {
244 use std::cell::{Cell, RefCell};
245 use std::rc::Rc;
246
247 use super::*;
248 use crate::{define_derived_property, define_entity, define_property, with, Context};
249
250 define_entity!(Person);
251
252 define_property!(struct Age(u8), Person );
253
254 define_derived_property!(
258 enum AgeGroup {
259 Child,
260 Adult,
261 },
262 Person,
263 [Age], [], |age| {
266 let age: Age = age;
267 if age.0 < 18 {
268 AgeGroup::Child
269 } else {
270 AgeGroup::Adult
271 }
272 }
273 );
274
275 define_property!(
276 enum RiskCategory {
277 High,
278 Low,
279 },
280 Person
281 );
282
283 define_property!(struct IsRunner(bool), Person, default_const = IsRunner(false));
284
285 define_property!(struct RunningShoes(u8), Person );
286
287 define_entity!(InitializationPerson);
288
289 define_property!(
290 struct InitConstant(u8),
291 InitializationPerson,
292 default_const = InitConstant(0)
293 );
294
295 define_property!(
296 struct InitSecond(u8),
297 InitializationPerson,
298 default_const = InitSecond(0)
299 );
300
301 define_property!(struct InitRequired(u8), InitializationPerson);
302
303 define_entity!(OtherInitializationPerson);
304 crate::impl_property!(
305 InitConstant,
306 OtherInitializationPerson,
307 default_const = InitConstant(0)
308 );
309
310 define_derived_property!(
311 struct InitDerived(u8),
312 InitializationPerson,
313 [InitConstant],
314 [],
315 |value| {
316 let value: InitConstant = value;
317 InitDerived(value.0 + 1)
318 }
319 );
320
321 #[test]
322 fn initialization_event_contains_nondefault_constant_value() {
323 let mut context = Context::new();
324 let received = Rc::new(RefCell::new(None));
325 let received_clone = received.clone();
326 context.subscribe_to_event(
327 move |context, event: PropertyInitializedEvent<InitializationPerson, InitConstant>| {
328 assert_eq!(
329 context.get_property::<InitializationPerson, InitConstant>(event.entity_id),
330 event.value
331 );
332 *received_clone.borrow_mut() = Some((event.entity_id, event.value));
333 },
334 );
335
336 let entity_id = context
337 .add_entity(with!(
338 InitializationPerson,
339 InitConstant(7),
340 InitRequired(11)
341 ))
342 .unwrap();
343 context.execute();
344
345 assert_eq!(*received.borrow(), Some((entity_id, InitConstant(7))));
346 }
347
348 #[test]
349 fn required_property_initialization_emits_without_default() {
350 let mut context = Context::new();
351 let received = Rc::new(RefCell::new(None));
352 let received_clone = received.clone();
353 context.subscribe_to_event(
354 move |_context, event: PropertyInitializedEvent<InitializationPerson, InitRequired>| {
355 *received_clone.borrow_mut() = Some(event.value);
356 },
357 );
358
359 context
360 .add_entity(with!(InitializationPerson, InitRequired(23)))
361 .unwrap();
362 context.execute();
363
364 assert_eq!(*received.borrow(), Some(InitRequired(23)));
365 }
366
367 #[test]
368 fn explicit_constant_default_does_not_emit_initialization_event() {
369 let mut context = Context::new();
370 context.subscribe_to_event(
371 |_context, _event: PropertyInitializedEvent<InitializationPerson, InitConstant>| {
372 panic!("the explicit default must not emit");
373 },
374 );
375
376 context
377 .add_entity(with!(
378 InitializationPerson,
379 InitConstant(0),
380 InitRequired(1)
381 ))
382 .unwrap();
383 context.execute();
384 }
385
386 #[test]
387 fn omitted_constant_default_does_not_emit_initialization_event() {
388 let mut context = Context::new();
389 context.subscribe_to_event(
390 |_context, _event: PropertyInitializedEvent<InitializationPerson, InitConstant>| {
391 panic!("an omitted property must not emit");
392 },
393 );
394
395 context
396 .add_entity(with!(InitializationPerson, InitRequired(1)))
397 .unwrap();
398 context.execute();
399 }
400
401 #[test]
402 fn derived_property_does_not_emit_initialization_event() {
403 let mut context = Context::new();
404 context.subscribe_to_event(
405 |_context, _event: PropertyInitializedEvent<InitializationPerson, InitDerived>| {
406 panic!("a dependent derived property must not emit");
407 },
408 );
409
410 context
411 .add_entity(with!(
412 InitializationPerson,
413 InitConstant(9),
414 InitRequired(1)
415 ))
416 .unwrap();
417 context.execute();
418 }
419
420 #[test]
421 fn initialization_events_are_queued_before_creation() {
422 let mut context = Context::new();
423 context.index_property::<InitializationPerson, InitConstant>();
424 context.index_property_counts::<InitializationPerson, InitSecond>();
425
426 let order = Rc::new(RefCell::new(Vec::new()));
427 let order_clone = order.clone();
428 context.subscribe_to_event(
429 move |context, event: PropertyInitializedEvent<InitializationPerson, InitConstant>| {
430 assert_eq!(
431 context.query_entity_count(with!(InitializationPerson, event.value)),
432 1
433 );
434 assert_eq!(
435 context.query_entity_count(with!(InitializationPerson, InitSecond(4))),
436 1
437 );
438 order_clone.borrow_mut().push("constant");
439 },
440 );
441
442 let order_clone = order.clone();
443 context.subscribe_to_event(
444 move |_context, _event: PropertyInitializedEvent<InitializationPerson, InitSecond>| {
445 order_clone.borrow_mut().push("second");
446 },
447 );
448
449 let order_clone = order.clone();
450 context.subscribe_to_event(
451 move |_context,
452 _event: PropertyInitializedEvent<InitializationPerson, InitRequired>| {
453 order_clone.borrow_mut().push("required");
454 },
455 );
456
457 let order_clone = order.clone();
458 context.subscribe_to_event(
459 move |_context, _event: EntityCreatedEvent<InitializationPerson>| {
460 order_clone.borrow_mut().push("created");
461 },
462 );
463
464 context
465 .add_entity(with!(
466 InitializationPerson,
467 InitConstant(3),
468 InitSecond(4),
469 InitRequired(5)
470 ))
471 .unwrap();
472 assert!(order.borrow().is_empty());
473
474 context.execute();
475 let order = order.borrow();
476 assert_eq!(order.len(), 4);
477 assert_eq!(order[3], "created");
478 assert!(order[..3].contains(&"constant"));
479 assert!(order[..3].contains(&"second"));
480 assert!(order[..3].contains(&"required"));
481 }
482
483 #[test]
484 fn property_initialized_subscription_cache_tracks_listener_lifecycle() {
485 let mut context = Context::new();
486 let property_id = <InitConstant as Property<InitializationPerson>>::id();
487
488 let first = context.subscribe_to_event(
489 |_context, _event: PropertyInitializedEvent<InitializationPerson, InitConstant>| {},
490 );
491 assert!(context
492 .entity_store
493 .get_property_store::<InitializationPerson>()
494 .property_initialized_event_subscriptions
495 .get(property_id));
496
497 let second = context.subscribe_to_event(
498 |_context, _event: PropertyInitializedEvent<InitializationPerson, InitConstant>| {},
499 );
500 assert!(context.unsubscribe_from_event(&first));
501 assert!(context
502 .entity_store
503 .get_property_store::<InitializationPerson>()
504 .property_initialized_event_subscriptions
505 .get(property_id));
506
507 assert!(context.unsubscribe_from_event(&second));
508 assert!(!context
509 .entity_store
510 .get_property_store::<InitializationPerson>()
511 .property_initialized_event_subscriptions
512 .get(property_id));
513 }
514
515 #[test]
516 fn property_initialized_subscription_cache_ignores_failed_unsubscription() {
517 let mut context = Context::new();
518 let listener = context.subscribe_to_event(
519 |_context, _event: PropertyInitializedEvent<InitializationPerson, InitConstant>| {},
520 );
521 let property_id = <InitConstant as Property<InitializationPerson>>::id();
522
523 assert!(context.unsubscribe_from_event(&listener));
524 assert!(!context.unsubscribe_from_event(&listener));
525 assert!(!context
526 .entity_store
527 .get_property_store::<InitializationPerson>()
528 .property_initialized_event_subscriptions
529 .get(property_id));
530 }
531
532 #[test]
533 fn property_initialized_subscription_cache_ignores_derived_property() {
534 let mut context = Context::new();
535 context.subscribe_to_event(
536 |_context, _event: PropertyInitializedEvent<InitializationPerson, InitDerived>| {},
537 );
538
539 assert!(context
540 .entity_store
541 .get_property_store::<InitializationPerson>()
542 .property_initialized_event_subscriptions
543 .is_empty());
544 }
545
546 #[test]
547 fn property_initialized_subscription_cache_tracks_distinct_properties() {
548 let mut context = Context::new();
549 context.subscribe_to_event(
550 |_context, _event: PropertyInitializedEvent<InitializationPerson, InitConstant>| {},
551 );
552 context.subscribe_to_event(
553 |_context, _event: PropertyInitializedEvent<InitializationPerson, InitRequired>| {},
554 );
555
556 let subscriptions = &context
557 .entity_store
558 .get_property_store::<InitializationPerson>()
559 .property_initialized_event_subscriptions;
560 assert!(subscriptions.get(<InitConstant as Property<InitializationPerson>>::id()));
561 assert!(subscriptions.get(InitRequired::id()));
562 }
563
564 #[test]
565 fn property_initialized_subscription_cache_is_scoped_to_entity_type() {
566 let mut context = Context::new();
567 context.subscribe_to_event(
568 |_context, _event: PropertyInitializedEvent<InitializationPerson, InitConstant>| {},
569 );
570
571 assert!(!context
572 .entity_store
573 .get_property_store::<InitializationPerson>()
574 .property_initialized_event_subscriptions
575 .is_empty());
576 assert!(context
577 .entity_store
578 .get_property_store::<OtherInitializationPerson>()
579 .property_initialized_event_subscriptions
580 .is_empty());
581 }
582
583 #[test]
584 fn property_initialized_subscription_cache_ignores_unrelated_event() {
585 let mut context = Context::new();
586 context.subscribe_to_event(|_context, _event: EntityCreatedEvent<InitializationPerson>| {});
587
588 assert!(context
589 .entity_store
590 .get_property_store::<InitializationPerson>()
591 .property_initialized_event_subscriptions
592 .is_empty());
593 }
594
595 #[test]
596 fn property_initialized_subscription_cache_controls_delivery() {
597 let mut context = Context::new();
598 let count = Rc::new(Cell::new(0));
599 let count_clone = count.clone();
600 let listener = context.subscribe_to_event(
601 move |_context,
602 _event: PropertyInitializedEvent<InitializationPerson, InitRequired>| {
603 count_clone.set(count_clone.get() + 1);
604 },
605 );
606
607 context
608 .add_entity(with!(
609 InitializationPerson,
610 InitConstant(1),
611 InitRequired(1)
612 ))
613 .unwrap();
614 context.execute();
615 assert_eq!(count.get(), 1);
616
617 assert!(context.unsubscribe_from_event(&listener));
618 context
619 .add_entity(with!(
620 InitializationPerson,
621 InitConstant(2),
622 InitRequired(2)
623 ))
624 .unwrap();
625 context.execute();
626 assert_eq!(count.get(), 1);
627 }
628
629 #[test]
630 fn entity_created_subscription_cache_tracks_listener_lifecycle() {
631 let mut context = Context::new();
632 assert!(
633 !context
634 .entity_store
635 .new_entity_id::<InitializationPerson>()
636 .1
637 );
638
639 let first = context
640 .subscribe_to_event(|_context, _event: EntityCreatedEvent<InitializationPerson>| {});
641 assert!(
642 context
643 .entity_store
644 .new_entity_id::<InitializationPerson>()
645 .1
646 );
647
648 let second = context
649 .subscribe_to_event(|_context, _event: EntityCreatedEvent<InitializationPerson>| {});
650 assert!(context.unsubscribe_from_event(&first));
651 assert!(
652 context
653 .entity_store
654 .new_entity_id::<InitializationPerson>()
655 .1
656 );
657
658 assert!(context.unsubscribe_from_event(&second));
659 assert!(
660 !context
661 .entity_store
662 .new_entity_id::<InitializationPerson>()
663 .1
664 );
665 }
666
667 #[test]
668 fn entity_created_subscription_cache_ignores_failed_unsubscription() {
669 let mut context = Context::new();
670 let listener = context
671 .subscribe_to_event(|_context, _event: EntityCreatedEvent<InitializationPerson>| {});
672
673 assert!(context.unsubscribe_from_event(&listener));
674 assert!(!context.unsubscribe_from_event(&listener));
675 assert!(
676 !context
677 .entity_store
678 .new_entity_id::<InitializationPerson>()
679 .1
680 );
681 }
682
683 #[test]
684 fn entity_created_subscription_cache_is_scoped_to_entity_type() {
685 let mut context = Context::new();
686 context.subscribe_to_event(|_context, _event: EntityCreatedEvent<InitializationPerson>| {});
687
688 assert!(
689 context
690 .entity_store
691 .new_entity_id::<InitializationPerson>()
692 .1
693 );
694 assert!(
695 !context
696 .entity_store
697 .new_entity_id::<OtherInitializationPerson>()
698 .1
699 );
700 }
701
702 #[test]
703 fn entity_created_subscription_cache_ignores_unrelated_event() {
704 let mut context = Context::new();
705 context.subscribe_to_event(
706 |_context, _event: PropertyInitializedEvent<InitializationPerson, InitConstant>| {},
707 );
708
709 assert!(
710 !context
711 .entity_store
712 .new_entity_id::<InitializationPerson>()
713 .1
714 );
715 }
716
717 #[test]
718 fn entity_created_subscription_cache_controls_delivery() {
719 let mut context = Context::new();
720 let count = Rc::new(Cell::new(0));
721 let count_clone = count.clone();
722 let listener = context.subscribe_to_event(
723 move |_context, _event: EntityCreatedEvent<InitializationPerson>| {
724 count_clone.set(count_clone.get() + 1);
725 },
726 );
727
728 context
729 .add_entity(with!(InitializationPerson, InitRequired(1)))
730 .unwrap();
731 context.execute();
732 assert_eq!(count.get(), 1);
733
734 assert!(context.unsubscribe_from_event(&listener));
735 context
736 .add_entity(with!(InitializationPerson, InitRequired(2)))
737 .unwrap();
738 context.execute();
739 assert_eq!(count.get(), 1);
740 }
741
742 #[test]
743 fn entity_created_subscription_cache_preserves_listener_order() {
744 let mut context = Context::new();
745 let order = Rc::new(RefCell::new(Vec::new()));
746
747 let order_clone = order.clone();
748 context.subscribe_to_event(
749 move |_context, _event: EntityCreatedEvent<InitializationPerson>| {
750 order_clone.borrow_mut().push(1);
751 },
752 );
753 let order_clone = order.clone();
754 context.subscribe_to_event(
755 move |_context, _event: EntityCreatedEvent<InitializationPerson>| {
756 order_clone.borrow_mut().push(2);
757 },
758 );
759
760 context
761 .add_entity(with!(InitializationPerson, InitRequired(1)))
762 .unwrap();
763 context.execute();
764
765 assert_eq!(*order.borrow(), vec![1, 2]);
766 }
767
768 #[test]
769 fn initialization_event_runs_once_per_subscriber() {
770 let mut context = Context::new();
771 let count = Rc::new(Cell::new(0));
772 for _ in 0..2 {
773 let count_clone = count.clone();
774 context.subscribe_to_event(
775 move |_context,
776 _event: PropertyInitializedEvent<InitializationPerson, InitConstant>| {
777 count_clone.set(count_clone.get() + 1);
778 },
779 );
780 }
781
782 context
783 .add_entity(with!(
784 InitializationPerson,
785 InitConstant(2),
786 InitRequired(1)
787 ))
788 .unwrap();
789 context.execute();
790
791 assert_eq!(count.get(), 2);
792 }
793
794 #[test]
795 fn repeated_creation_emits_matching_entity_ids_and_values() {
796 let mut context = Context::new();
797 let received = Rc::new(RefCell::new(Vec::new()));
798 let received_clone = received.clone();
799 context.subscribe_to_event(
800 move |_context, event: PropertyInitializedEvent<InitializationPerson, InitConstant>| {
801 received_clone
802 .borrow_mut()
803 .push((event.entity_id, event.value));
804 },
805 );
806
807 let first = context
808 .add_entity(with!(
809 InitializationPerson,
810 InitConstant(5),
811 InitRequired(1)
812 ))
813 .unwrap();
814 let second = context
815 .add_entity(with!(
816 InitializationPerson,
817 InitConstant(8),
818 InitRequired(2)
819 ))
820 .unwrap();
821 context.execute();
822
823 assert_eq!(
824 *received.borrow(),
825 vec![(first, InitConstant(5)), (second, InitConstant(8))]
826 );
827 }
828
829 #[test]
830 fn observe_entity_addition() {
831 let mut context = Context::new();
832 context.index_property::<Person, Age>();
833
834 let flag = Rc::new(RefCell::new(false));
835 let flag_clone = flag.clone();
836 context.subscribe_to_event(move |context, event: EntityCreatedEvent<Person>| {
837 let matching = context.query(with!(Person, Age(18)));
838 assert!(matching.contains(event.entity_id));
839 *flag_clone.borrow_mut() = true;
840 assert_eq!(event.entity_id.0, 0);
841 });
842
843 let _ = context
844 .add_entity::<Person, _>(with!(Person, Age(18), RunningShoes(33), RiskCategory::Low))
845 .unwrap();
846 context.execute();
847 assert!(*flag.borrow());
848 }
849
850 #[test]
851 fn observe_entity_property_change() {
852 let mut context = Context::new();
853
854 let flag = Rc::new(RefCell::new(false));
855 let flag_clone = flag.clone();
856 context.subscribe_to_event(
857 move |_context, event: PropertyChangeEvent<Person, RiskCategory>| {
858 *flag_clone.borrow_mut() = true;
859 assert_eq!(event.entity_id.0, 0, "Entity id is correct");
860 assert_eq!(
861 event.previous,
862 RiskCategory::Low,
863 "Previous value is correct"
864 );
865 assert_eq!(
866 event.current,
867 RiskCategory::High,
868 "Current value is correct"
869 );
870 },
871 );
872
873 let person_id = context
874 .add_entity(with!(Person, Age(9), RunningShoes(33), RiskCategory::Low))
875 .unwrap();
876
877 context.set_property(person_id, RiskCategory::High);
878 context.execute();
879 assert!(*flag.borrow());
880 }
881
882 #[test]
883 fn observe_entity_property_change_with_set() {
884 let mut context = Context::new();
885
886 let flag = Rc::new(RefCell::new(false));
887 let flag_clone = flag.clone();
888 context.subscribe_to_event(
889 move |_context, _event: PropertyChangeEvent<Person, RunningShoes>| {
890 *flag_clone.borrow_mut() = true;
891 },
892 );
893 let person_id = context
895 .add_entity(with!(Person, Age(9), RunningShoes(33), RiskCategory::Low))
896 .unwrap();
897 context.set_property(person_id, RunningShoes(42));
899 context.execute();
900 assert!(*flag.borrow());
901 }
902
903 #[test]
904 fn get_entity_property_change_event() {
905 let mut context = Context::new();
906 let person = context
907 .add_entity(with!(Person, Age(17), RunningShoes(33), RiskCategory::Low))
908 .unwrap();
909
910 let flag = Rc::new(RefCell::new(false));
911
912 let flag_clone = flag.clone();
913 context.subscribe_to_event(
914 move |_context, event: PropertyChangeEvent<Person, AgeGroup>| {
915 assert_eq!(event.entity_id.0, 0);
916 assert_eq!(event.previous, AgeGroup::Child);
917 assert_eq!(event.current, AgeGroup::Adult);
918 *flag_clone.borrow_mut() = true;
919 },
920 );
921 context.set_property(person, Age(18));
922 context.execute();
923 assert!(*flag.borrow());
924 }
925
926 #[test]
927 fn test_person_property_change_event_no_people() {
928 let mut context = Context::new();
929 context.subscribe_to_event(|_context, _event: PropertyChangeEvent<Person, IsRunner>| {
931 unreachable!();
932 });
933
934 context.subscribe_to_event(|_context, _event: PropertyChangeEvent<Person, AgeGroup>| {
936 unreachable!();
937 });
938 }
939}