Skip to main content

ixa/entity/
events.rs

1/*!
2
3Entity events report creation, explicitly supplied initial values, and later property changes:
4
5- [`EntityCreatedEvent`] is emitted when an entity has been created.
6- [`PropertyInitializedEvent`] is emitted for each non-default, non-derived property value
7  explicitly supplied during creation.
8- [`PropertyChangeEvent`] is emitted when a property is subsequently updated.
9
10Client code can subscribe to these events with the `Context::subscribe_to_event<IxaEvent>(handler)` method:
11
12```rust,ignore
13// Suppose `InfectionStatus` is a property of the entity `Person`.
14// A type alias for property change events makes code more concise and readable.
15pub type InfectionStatusEvent = PropertyChangeEvent<Person, InfectionStatus>;
16// Suppose we want to execute the following function whenever `InfectionStatus` changes.
17fn handle_infection_status_change(context: &mut Context, event: InfectionStatusEvent){
18    // ... handle the infection status change event ...
19}
20// We do so by subscribing to this event.
21context.subscribe_to_event::<InfectionStatusEvent>(handle_infection_status_change);
22
23// Observe a non-default value explicitly supplied when a person is created.
24context.subscribe_to_event(
25    |_context, event: PropertyInitializedEvent<Person, InfectionStatus>| {
26        // ... use event.entity_id and event.value ...
27    },
28);
29```
30
31
32A non-derived property sits on the type-erased side of the boundary of its dependent's `PropertyValueStore`, so it
33needs to somehow trigger the creation of and emit the change events for its dependents in a type-erased way.
34
35Property change events are triggered and collected on the outside of the type-erased `PropertyValueStore` boundary,
36because a non-derived p
37
38*/
39
40use smallbox::space::S4;
41use smallbox::SmallBox;
42
43use crate::entity::property::{Property, PropertyInitializationKind};
44use crate::entity::{ContextEntitiesExt, Entity, EntityId};
45use crate::{Context, IxaEvent};
46
47// We choose the size parameter for `PartialPropertyChangeEventBox` based on the assumption that
48// most properties are 64 bits or fewer. The concrete object behind `PartialPropertyChangeEventBox`
49// (the alias for the `SmallBox`) is `PartialPropertyChangeEventCore<E, P>`, which is
50// `#[repr(transparent)]` over `PropertyChangeEvent<E, P>`. That event stores
51//
52// - `EntityId<E>`, one `usize`, so 8 bytes.
53// - `current`: a property value, typically <= 8 bytes
54// - `current`: a property value, typically <= 8 bytes
55//
56// That puts the payload at 24 bytes, with 8-byte alignment. The `S4` size is 4 `usize`s of inline
57// storage, i.e. 32 bytes, and inline storage is used when the payload size and alignment fit. So
58// `S4` comfortably holds the common 24-byte case inline with 8 bytes of slack.
59pub(crate) type PartialPropertyChangeEventBox = SmallBox<dyn PartialPropertyChangeEvent, S4>;
60
61/// Type-erased interface to `PartialPropertyChangeEvent<E, P>`.
62/// Interacts with the index on behalf of the erased type.
63pub(crate) trait PartialPropertyChangeEvent {
64    /// Updates the index with the current property value and emits a change event.
65    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    /// Updates the index with the current property value and emits a change event.
72    fn emit_in_context(&mut self, context: &mut Context) {
73        self.0.current = context.get_property(self.0.entity_id);
74
75        {
76            // Update value change counters
77            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        // Now update the indexes
88        let property_value_store = context.get_property_value_store_mut::<E, P>();
89        if let Some(index) = property_value_store.index.as_mut() {
90            // Out with the old
91            index.remove_entity(&self.0.previous, self.0.entity_id);
92            // In with the new
93            index.add_entity(&self.0.current, self.0.entity_id);
94        }
95
96        // We decided not to do the following check.
97        // See `src/entity/context_extension::ContextEntitiesExt::set_property`.
98        // if self.0.current != self.0.previous {
99        //     context.emit_event(self.to_event());
100        // }
101
102        context.emit_event(self.to_event());
103    }
104}
105
106/// Represents a partially created `PropertyChangeEvent` of a derived property during the computation of property
107/// changes during the update of one of its non-derived property dependencies.
108///
109/// A `PartialPropertyChangeEventCore<E, P>` is layout-compatible with
110/// `PropertyChangeEvent<E, P>`, so converting via `to_event()` does not require an extra heap
111/// allocation.
112#[repr(transparent)]
113pub(crate) struct PartialPropertyChangeEventCore<E: Entity, P: Property<E>>(
114    PropertyChangeEvent<E, P>,
115);
116// We provide blanket impls for these because the compiler isn't smart enough to know
117// `PartialPropertyChangeEvent<E, P>` is always `Copy`/`Clone` if we derive them.
118impl<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
139/// Emitted when a new entity is created.
140/// These should not be emitted outside this module.
141pub struct EntityCreatedEvent<E: Entity> {
142    /// The [`EntityId<E>`] of the new entity.
143    pub entity_id: EntityId<E>,
144}
145
146// Implemented manually to maintain the subscription flag through the event hooks.
147impl<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
174/// Emitted for a non-default, non-derived property value supplied when an entity is created.
175pub struct PropertyInitializedEvent<E: Entity, P: Property<E>> {
176    /// The newly created entity.
177    pub entity_id: EntityId<E>,
178    /// The initial property value supplied by the caller.
179    pub value: P,
180}
181
182// Implemented manually to maintain the subscription bit set through the event hooks.
183impl<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/// Emitted when a property is updated.
231/// These should not be emitted outside this module.
232#[derive(IxaEvent)]
233pub struct PropertyChangeEvent<E: Entity, P: Property<E>> {
234    /// The [`EntityId<E>`] that changed
235    pub entity_id: EntityId<E>,
236    /// The new value
237    pub current: P,
238    /// The old value
239    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_global_property!(Threshold, u8);
255
256    // An enum
257    define_derived_property!(
258        enum AgeGroup {
259            Child,
260            Adult,
261        },
262        Person,
263        [Age], // Depends only on age
264        [],    // No global dependencies
265        |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        // Does not emit a change event.
894        let person_id = context
895            .add_entity(with!(Person, Age(9), RunningShoes(33), RiskCategory::Low))
896            .unwrap();
897        // Emits a change event.
898        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        // Non derived person property -- no problems
930        context.subscribe_to_event(|_context, _event: PropertyChangeEvent<Person, IsRunner>| {
931            unreachable!();
932        });
933
934        // Derived person property -- can't add an event without people being present
935        context.subscribe_to_event(|_context, _event: PropertyChangeEvent<Person, AgeGroup>| {
936            unreachable!();
937        });
938    }
939}