1#[cfg(feature = "hotpatching")]
2use crate::{change_detection::DetectChanges, HotPatchChanges};
3use crate::{
4 change_detection::Mut,
5 entity::Entity,
6 error::BevyError,
7 system::{
8 input::SystemInput, BoxedSystem, IntoSystem, RunSystemError, SystemParamValidationError,
9 },
10 world::World,
11};
12use alloc::boxed::Box;
13use bevy_ecs_macros::{Component, Resource};
14use bevy_utils::prelude::DebugName;
15use core::{any::TypeId, marker::PhantomData};
16use thiserror::Error;
17
18#[derive(Component)]
20#[require(SystemIdMarker = SystemIdMarker::typed_system_id_marker::<I, O>())]
21pub(crate) struct RegisteredSystem<I, O> {
22 initialized: bool,
23 system: Option<BoxedSystem<I, O>>,
24}
25
26impl<I, O> RegisteredSystem<I, O> {
27 pub fn new(system: BoxedSystem<I, O>) -> Self {
28 RegisteredSystem {
29 initialized: false,
30 system: Some(system),
31 }
32 }
33}
34
35#[derive(Debug, Clone)]
36struct TypeIdAndName {
37 type_id: TypeId,
38 name: DebugName,
39}
40
41impl TypeIdAndName {
42 fn new<T: 'static>() -> Self {
43 Self {
44 type_id: TypeId::of::<T>(),
45 name: DebugName::type_name::<T>(),
46 }
47 }
48}
49
50impl Default for TypeIdAndName {
51 fn default() -> Self {
52 Self {
53 type_id: TypeId::of::<()>(),
54 name: DebugName::type_name::<()>(),
55 }
56 }
57}
58
59#[derive(Debug, Default, Clone, Component)]
61pub struct SystemIdMarker {
62 input_type_id: TypeIdAndName,
63 output_type_id: TypeIdAndName,
64}
65
66impl SystemIdMarker {
67 fn typed_system_id_marker<I: 'static, O: 'static>() -> Self {
68 Self {
69 input_type_id: TypeIdAndName::new::<I>(),
70 output_type_id: TypeIdAndName::new::<O>(),
71 }
72 }
73}
74
75pub struct RemovedSystem<I = (), O = ()> {
80 initialized: bool,
81 system: BoxedSystem<I, O>,
82}
83
84impl<I, O> RemovedSystem<I, O> {
85 pub fn initialized(&self) -> bool {
88 self.initialized
89 }
90
91 pub fn system(self) -> BoxedSystem<I, O> {
93 self.system
94 }
95}
96
97pub struct SystemId<I: SystemInput = (), O = ()> {
102 pub(crate) entity: Entity,
103 pub(crate) marker: PhantomData<fn(I) -> O>,
104}
105
106impl<I: SystemInput, O> SystemId<I, O> {
107 pub fn entity(self) -> Entity {
114 self.entity
115 }
116
117 pub fn from_entity(entity: Entity) -> Self {
122 Self {
123 entity,
124 marker: PhantomData,
125 }
126 }
127}
128
129impl<I: SystemInput, O> Eq for SystemId<I, O> {}
130
131impl<I: SystemInput, O> Copy for SystemId<I, O> {}
133
134impl<I: SystemInput, O> Clone for SystemId<I, O> {
136 fn clone(&self) -> Self {
137 *self
138 }
139}
140
141impl<I: SystemInput, O> PartialEq for SystemId<I, O> {
143 fn eq(&self, other: &Self) -> bool {
144 self.entity == other.entity && self.marker == other.marker
145 }
146}
147
148impl<I: SystemInput, O> core::hash::Hash for SystemId<I, O> {
150 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
151 self.entity.hash(state);
152 }
153}
154
155impl<I: SystemInput, O> core::fmt::Debug for SystemId<I, O> {
156 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
157 f.debug_tuple("SystemId").field(&self.entity).finish()
158 }
159}
160
161#[derive(Resource)]
165pub struct CachedSystemId<S> {
166 pub entity: Entity,
168 _marker: PhantomData<fn() -> S>,
169}
170
171impl<S> CachedSystemId<S> {
172 pub fn new<I: SystemInput, O>(id: SystemId<I, O>) -> Self {
174 Self {
175 entity: id.entity(),
176 _marker: PhantomData,
177 }
178 }
179}
180
181impl World {
182 pub fn register_system<I, O, M>(
194 &mut self,
195 system: impl IntoSystem<I, O, M> + 'static,
196 ) -> SystemId<I, O>
197 where
198 I: SystemInput + 'static,
199 O: 'static,
200 {
201 self.register_boxed_system(Box::new(IntoSystem::into_system(system)))
202 }
203
204 pub fn register_boxed_system<I, O>(&mut self, system: BoxedSystem<I, O>) -> SystemId<I, O>
209 where
210 I: SystemInput + 'static,
211 O: 'static,
212 {
213 let entity = self.spawn(RegisteredSystem::new(system)).id();
214 SystemId::from_entity(entity)
215 }
216
217 pub fn unregister_system<I, O>(
224 &mut self,
225 id: SystemId<I, O>,
226 ) -> Result<RemovedSystem<I, O>, RegisteredSystemError<I, O>>
227 where
228 I: SystemInput + 'static,
229 O: 'static,
230 {
231 match self.get_entity_mut(id.entity) {
232 Ok(mut entity) => {
233 let registered_system = entity
234 .take::<RegisteredSystem<I, O>>()
235 .ok_or(RegisteredSystemError::SelfRemove(id))?;
236 entity.despawn();
237 Ok(RemovedSystem {
238 initialized: registered_system.initialized,
239 system: registered_system
240 .system
241 .ok_or(RegisteredSystemError::SystemMissing(id))?,
242 })
243 }
244 Err(_) => Err(RegisteredSystemError::SystemIdNotRegistered(id)),
245 }
246 }
247
248 pub fn run_system<O: 'static>(
334 &mut self,
335 id: SystemId<(), O>,
336 ) -> Result<O, RegisteredSystemError<(), O>> {
337 self.run_system_with(id, ())
338 }
339
340 pub fn run_system_with<I, O>(
365 &mut self,
366 id: SystemId<I, O>,
367 input: I::Inner<'_>,
368 ) -> Result<O, RegisteredSystemError<I, O>>
369 where
370 I: SystemInput + 'static,
371 O: 'static,
372 {
373 let mut entity = self
375 .get_entity_mut(id.entity)
376 .map_err(|_| RegisteredSystemError::SystemIdNotRegistered(id))?;
377
378 let Some(mut registered_system) = entity.get_mut::<RegisteredSystem<I, O>>() else {
380 let Some(system_id_marker) = entity.get::<SystemIdMarker>() else {
381 return Err(RegisteredSystemError::SystemIdNotRegistered(id));
382 };
383 if system_id_marker.input_type_id.type_id != TypeId::of::<I>()
384 || system_id_marker.output_type_id.type_id != TypeId::of::<O>()
385 {
386 return Err(RegisteredSystemError::IncorrectType(
387 id,
388 system_id_marker.clone(),
389 ));
390 }
391 return Err(RegisteredSystemError::MissingRegisteredSystemComponent(id));
392 };
393
394 let mut system = registered_system
395 .system
396 .take()
397 .ok_or(RegisteredSystemError::SystemMissing(id))?;
398
399 if !registered_system.initialized {
401 system.initialize(self);
402 }
403
404 #[cfg(feature = "hotpatching")]
406 if self
407 .get_resource_ref::<HotPatchChanges>()
408 .map(|r| r.last_changed())
409 .unwrap_or_default()
410 .is_newer_than(system.get_last_run(), self.change_tick())
411 {
412 system.refresh_hotpatch();
413 }
414
415 let result = system.run_without_applying_deferred(input, self);
418 system.queue_deferred(self.into());
419
420 if let Ok(mut entity) = self.get_entity_mut(id.entity)
422 && let Some(mut registered_system) = entity.get_mut::<RegisteredSystem<I, O>>()
423 {
424 registered_system.system = Some(system);
425 registered_system.initialized = true;
426 }
427
428 self.flush();
430 Ok(result?)
431 }
432
433 pub fn register_system_cached<I, O, M, S>(&mut self, system: S) -> SystemId<I, O>
453 where
454 I: SystemInput + 'static,
455 O: 'static,
456 S: IntoSystem<I, O, M> + 'static,
457 {
458 const {
459 assert!(
460 size_of::<S>() == 0,
461 "Non-ZST systems (e.g. capturing closures, function pointers) cannot be cached.",
462 );
463 }
464
465 if !self.contains_resource::<CachedSystemId<S>>() {
466 let id = self.register_system(system);
467 self.insert_resource(CachedSystemId::<S>::new(id));
468 return id;
469 }
470
471 self.resource_scope(|world, mut id: Mut<CachedSystemId<S>>| {
472 if let Ok(mut entity) = world.get_entity_mut(id.entity) {
473 if !entity.contains::<RegisteredSystem<I, O>>() {
474 entity.insert(RegisteredSystem::new(Box::new(IntoSystem::into_system(
475 system,
476 ))));
477 }
478 } else {
479 id.entity = world.register_system(system).entity();
480 }
481 SystemId::from_entity(id.entity)
482 })
483 }
484
485 pub fn unregister_system_cached<I, O, M, S>(
489 &mut self,
490 _system: S,
491 ) -> Result<RemovedSystem<I, O>, RegisteredSystemError<I, O>>
492 where
493 I: SystemInput + 'static,
494 O: 'static,
495 S: IntoSystem<I, O, M> + 'static,
496 {
497 let id = self
498 .remove_resource::<CachedSystemId<S>>()
499 .ok_or(RegisteredSystemError::SystemNotCached)?;
500 self.unregister_system(SystemId::<I, O>::from_entity(id.entity))
501 }
502
503 pub fn run_system_cached<O: 'static, M, S: IntoSystem<(), O, M> + 'static>(
507 &mut self,
508 system: S,
509 ) -> Result<O, RegisteredSystemError<(), O>> {
510 self.run_system_cached_with(system, ())
511 }
512
513 pub fn run_system_cached_with<I, O, M, S>(
517 &mut self,
518 system: S,
519 input: I::Inner<'_>,
520 ) -> Result<O, RegisteredSystemError<I, O>>
521 where
522 I: SystemInput + 'static,
523 O: 'static,
524 S: IntoSystem<I, O, M> + 'static,
525 {
526 let id = self.register_system_cached(system);
527 self.run_system_with(id, input)
528 }
529}
530
531#[derive(Error)]
533pub enum RegisteredSystemError<I: SystemInput = (), O = ()> {
534 #[error("System {0:?} was not registered")]
538 SystemIdNotRegistered(SystemId<I, O>),
539 #[error("Cached system was not found")]
543 SystemNotCached,
544 #[error("System {0:?} does not have a RegisteredSystem component. This only happens if app code removed the component.")]
546 MissingRegisteredSystemComponent(SystemId<I, O>),
547 #[error("System {0:?} tried to remove itself")]
549 SelfRemove(SystemId<I, O>),
550 #[error("System did not run due to failed parameter validation: {0}")]
553 Skipped(SystemParamValidationError),
554 #[error("System returned error: {0}")]
556 Failed(BevyError),
557 #[error("Could not get system from `{}`, entity was `SystemId<{}, {}>`", DebugName::type_name::<SystemId<I, O>>(), .1.input_type_id.name, .1.output_type_id.name)]
559 IncorrectType(SystemId<I, O>, SystemIdMarker),
560 #[error("The system is not present in the RegisteredSystem component. This can happen if the system was called recursively or if the system panicked on the last run.")]
563 SystemMissing(SystemId<I, O>),
564}
565
566impl<I: SystemInput, O> From<RunSystemError> for RegisteredSystemError<I, O> {
567 fn from(value: RunSystemError) -> Self {
568 match value {
569 RunSystemError::Skipped(err) => Self::Skipped(err),
570 RunSystemError::Failed(err) => Self::Failed(err),
571 }
572 }
573}
574
575impl<I: SystemInput, O> core::fmt::Debug for RegisteredSystemError<I, O> {
576 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
577 match self {
578 Self::SystemIdNotRegistered(arg0) => {
579 f.debug_tuple("SystemIdNotRegistered").field(arg0).finish()
580 }
581 Self::SystemNotCached => write!(f, "SystemNotCached"),
582 Self::MissingRegisteredSystemComponent(arg0) => f
583 .debug_tuple("MissingRegisteredSystemComponent")
584 .field(arg0)
585 .finish(),
586 Self::SelfRemove(arg0) => f.debug_tuple("SelfRemove").field(arg0).finish(),
587 Self::Skipped(arg0) => f.debug_tuple("Skipped").field(arg0).finish(),
588 Self::Failed(arg0) => f.debug_tuple("Failed").field(arg0).finish(),
589 Self::IncorrectType(arg0, arg1) => f
590 .debug_tuple("IncorrectType")
591 .field(arg0)
592 .field(arg1)
593 .finish(),
594 Self::SystemMissing(arg0) => f.debug_tuple("SystemMissing").field(arg0).finish(),
595 }
596 }
597}
598
599#[cfg(test)]
600mod tests {
601 use core::cell::Cell;
602
603 use bevy_utils::default;
604
605 use crate::{
606 prelude::*,
607 system::{RegisteredSystemError, SystemId},
608 };
609
610 #[derive(Resource, Default, PartialEq, Debug)]
611 struct Counter(u8);
612
613 #[test]
614 fn change_detection() {
615 #[derive(Resource, Default)]
616 struct ChangeDetector;
617
618 fn count_up_iff_changed(
619 mut counter: ResMut<Counter>,
620 change_detector: ResMut<ChangeDetector>,
621 ) {
622 if change_detector.is_changed() {
623 counter.0 += 1;
624 }
625 }
626
627 let mut world = World::new();
628 world.init_resource::<ChangeDetector>();
629 world.init_resource::<Counter>();
630 assert_eq!(*world.resource::<Counter>(), Counter(0));
631 let id = world.register_system(count_up_iff_changed);
633 world.run_system(id).expect("system runs successfully");
634 assert_eq!(*world.resource::<Counter>(), Counter(1));
635 world.run_system(id).expect("system runs successfully");
637 assert_eq!(*world.resource::<Counter>(), Counter(1));
638 world.resource_mut::<ChangeDetector>().set_changed();
640 world.run_system(id).expect("system runs successfully");
641 assert_eq!(*world.resource::<Counter>(), Counter(2));
642 }
643
644 #[test]
645 fn local_variables() {
646 fn doubling(last_counter: Local<Counter>, mut counter: ResMut<Counter>) {
648 counter.0 += last_counter.0 .0;
649 last_counter.0 .0 = counter.0;
650 }
651
652 let mut world = World::new();
653 world.insert_resource(Counter(1));
654 assert_eq!(*world.resource::<Counter>(), Counter(1));
655 let id = world.register_system(doubling);
656 world.run_system(id).expect("system runs successfully");
657 assert_eq!(*world.resource::<Counter>(), Counter(1));
658 world.run_system(id).expect("system runs successfully");
659 assert_eq!(*world.resource::<Counter>(), Counter(2));
660 world.run_system(id).expect("system runs successfully");
661 assert_eq!(*world.resource::<Counter>(), Counter(4));
662 world.run_system(id).expect("system runs successfully");
663 assert_eq!(*world.resource::<Counter>(), Counter(8));
664 }
665
666 #[test]
667 fn input_values() {
668 struct NonCopy(u8);
670
671 fn increment_sys(In(NonCopy(increment_by)): In<NonCopy>, mut counter: ResMut<Counter>) {
672 counter.0 += increment_by;
673 }
674
675 let mut world = World::new();
676
677 let id = world.register_system(increment_sys);
678
679 world.insert_resource(Counter(1));
681 assert_eq!(*world.resource::<Counter>(), Counter(1));
682
683 world
684 .run_system_with(id, NonCopy(1))
685 .expect("system runs successfully");
686 assert_eq!(*world.resource::<Counter>(), Counter(2));
687
688 world
689 .run_system_with(id, NonCopy(1))
690 .expect("system runs successfully");
691 assert_eq!(*world.resource::<Counter>(), Counter(3));
692
693 world
694 .run_system_with(id, NonCopy(20))
695 .expect("system runs successfully");
696 assert_eq!(*world.resource::<Counter>(), Counter(23));
697
698 world
699 .run_system_with(id, NonCopy(1))
700 .expect("system runs successfully");
701 assert_eq!(*world.resource::<Counter>(), Counter(24));
702 }
703
704 #[test]
705 fn output_values() {
706 #[derive(Eq, PartialEq, Debug)]
708 struct NonCopy(u8);
709
710 fn increment_sys(mut counter: ResMut<Counter>) -> NonCopy {
711 counter.0 += 1;
712 NonCopy(counter.0)
713 }
714
715 let mut world = World::new();
716
717 let id = world.register_system(increment_sys);
718
719 world.insert_resource(Counter(1));
721 assert_eq!(*world.resource::<Counter>(), Counter(1));
722
723 let output = world.run_system(id).expect("system runs successfully");
724 assert_eq!(*world.resource::<Counter>(), Counter(2));
725 assert_eq!(output, NonCopy(2));
726
727 let output = world.run_system(id).expect("system runs successfully");
728 assert_eq!(*world.resource::<Counter>(), Counter(3));
729 assert_eq!(output, NonCopy(3));
730 }
731
732 #[test]
733 fn fallible_system() {
734 fn sys() -> Result<()> {
735 Err("error")?;
736 Ok(())
737 }
738
739 let mut world = World::new();
740 let fallible_system_id = world.register_system(sys);
741 let output = world.run_system(fallible_system_id);
742 assert!(matches!(output, Ok(Err(_))));
743 }
744
745 #[test]
746 fn exclusive_system() {
747 let mut world = World::new();
748 let exclusive_system_id = world.register_system(|world: &mut World| {
749 world.spawn_empty();
750 });
751 let entity_count = world.entities.count_spawned();
752 let _ = world.run_system(exclusive_system_id);
753 assert_eq!(world.entities.count_spawned(), entity_count + 1);
754 }
755
756 #[test]
757 fn nested_systems() {
758 use crate::system::SystemId;
759
760 #[derive(Component)]
761 struct Callback(SystemId);
762
763 fn nested(query: Query<&Callback>, mut commands: Commands) {
764 for callback in query.iter() {
765 commands.run_system(callback.0);
766 }
767 }
768
769 let mut world = World::new();
770 world.insert_resource(Counter(0));
771
772 let increment_two = world.register_system(|mut counter: ResMut<Counter>| {
773 counter.0 += 2;
774 });
775 let increment_three = world.register_system(|mut counter: ResMut<Counter>| {
776 counter.0 += 3;
777 });
778 let nested_id = world.register_system(nested);
779
780 world.spawn(Callback(increment_two));
781 world.spawn(Callback(increment_three));
782 let _ = world.run_system(nested_id);
783 assert_eq!(*world.resource::<Counter>(), Counter(5));
784 }
785
786 #[test]
787 fn nested_systems_with_inputs() {
788 use crate::system::SystemId;
789
790 #[derive(Component)]
791 struct Callback(SystemId<In<u8>>, u8);
792
793 fn nested(query: Query<&Callback>, mut commands: Commands) {
794 for callback in query.iter() {
795 commands.run_system_with(callback.0, callback.1);
796 }
797 }
798
799 let mut world = World::new();
800 world.insert_resource(Counter(0));
801
802 let increment_by =
803 world.register_system(|In(amt): In<u8>, mut counter: ResMut<Counter>| {
804 counter.0 += amt;
805 });
806 let nested_id = world.register_system(nested);
807
808 world.spawn(Callback(increment_by, 2));
809 world.spawn(Callback(increment_by, 3));
810 let _ = world.run_system(nested_id);
811 assert_eq!(*world.resource::<Counter>(), Counter(5));
812 }
813
814 #[test]
815 fn cached_system() {
816 use crate::system::RegisteredSystemError;
817
818 fn four() -> i32 {
819 4
820 }
821
822 let mut world = World::new();
823 let old = world.register_system_cached(four);
824 let new = world.register_system_cached(four);
825 assert_eq!(old, new);
826
827 let result = world.unregister_system_cached(four);
828 assert!(result.is_ok());
829 let new = world.register_system_cached(four);
830 assert_ne!(old, new);
831
832 let output = world.run_system(old);
833 assert!(matches!(
834 output,
835 Err(RegisteredSystemError::SystemIdNotRegistered(x)) if x == old,
836 ));
837 let output = world.run_system(new);
838 assert!(matches!(output, Ok(x) if x == four()));
839 let output = world.run_system_cached(four);
840 assert!(matches!(output, Ok(x) if x == four()));
841 let output = world.run_system_cached_with(four, ());
842 assert!(matches!(output, Ok(x) if x == four()));
843 }
844
845 #[test]
846 fn cached_fallible_system() {
847 fn sys() -> Result<()> {
848 Err("error")?;
849 Ok(())
850 }
851
852 let mut world = World::new();
853 let fallible_system_id = world.register_system_cached(sys);
854 let output = world.run_system(fallible_system_id);
855 assert!(matches!(output, Ok(Err(_))));
856 let output = world.run_system_cached(sys);
857 assert!(matches!(output, Ok(Err(_))));
858 let output = world.run_system_cached_with(sys, ());
859 assert!(matches!(output, Ok(Err(_))));
860 }
861
862 #[test]
863 fn cached_system_commands() {
864 fn sys(mut counter: ResMut<Counter>) {
865 counter.0 += 1;
866 }
867
868 let mut world = World::new();
869 world.insert_resource(Counter(0));
870 world.commands().run_system_cached(sys);
871 world.flush_commands();
872 assert_eq!(world.resource::<Counter>().0, 1);
873 world.commands().run_system_cached_with(sys, ());
874 world.flush_commands();
875 assert_eq!(world.resource::<Counter>().0, 2);
876 }
877
878 #[test]
879 fn cached_fallible_system_commands() {
880 fn sys(mut counter: ResMut<Counter>) -> Result {
881 counter.0 += 1;
882 Ok(())
883 }
884
885 let mut world = World::new();
886 world.insert_resource(Counter(0));
887 world.commands().run_system_cached(sys);
888 world.flush_commands();
889 assert_eq!(world.resource::<Counter>().0, 1);
890 world.commands().run_system_cached_with(sys, ());
891 world.flush_commands();
892 assert_eq!(world.resource::<Counter>().0, 2);
893 }
894
895 #[test]
896 #[should_panic(expected = "This system always fails")]
897 fn cached_fallible_system_commands_can_fail() {
898 use crate::system::command;
899 fn sys() -> Result {
900 Err("This system always fails".into())
901 }
902
903 let mut world = World::new();
904 world.commands().queue(command::run_system_cached(sys));
905 world.flush_commands();
906 }
907
908 #[test]
909 fn cached_system_adapters() {
910 fn four() -> i32 {
911 4
912 }
913
914 fn double(In(i): In<i32>) -> i32 {
915 i * 2
916 }
917
918 let mut world = World::new();
919
920 let output = world.run_system_cached(four.pipe(double));
921 assert!(matches!(output, Ok(8)));
922
923 let output = world.run_system_cached(four.map(|i| i * 2));
924 assert!(matches!(output, Ok(8)));
925 }
926
927 #[test]
928 fn cached_system_into_same_system_type() {
929 struct Foo;
930 impl IntoSystem<(), (), ()> for Foo {
931 type System = ApplyDeferred;
932 fn into_system(_: Self) -> Self::System {
933 ApplyDeferred
934 }
935 }
936
937 struct Bar;
938 impl IntoSystem<(), (), ()> for Bar {
939 type System = ApplyDeferred;
940 fn into_system(_: Self) -> Self::System {
941 ApplyDeferred
942 }
943 }
944
945 let mut world = World::new();
946 let foo1 = world.register_system_cached(Foo);
947 let foo2 = world.register_system_cached(Foo);
948 let bar1 = world.register_system_cached(Bar);
949 let bar2 = world.register_system_cached(Bar);
950
951 assert_ne!(foo1, bar1);
955
956 assert_eq!(foo1, foo2);
959 assert_eq!(bar1, bar2);
960 }
961
962 #[test]
963 fn system_with_input_ref() {
964 fn with_ref(InRef(input): InRef<u8>, mut counter: ResMut<Counter>) {
965 counter.0 += *input;
966 }
967
968 let mut world = World::new();
969 world.insert_resource(Counter(0));
970
971 let id = world.register_system(with_ref);
972 world.run_system_with(id, &2).unwrap();
973 assert_eq!(*world.resource::<Counter>(), Counter(2));
974 }
975
976 #[test]
977 fn system_with_input_mut() {
978 #[derive(Event)]
979 struct MyEvent {
980 cancelled: bool,
981 }
982
983 fn post(InMut(event): InMut<MyEvent>, counter: ResMut<Counter>) {
984 if counter.0 > 0 {
985 event.cancelled = true;
986 }
987 }
988
989 let mut world = World::new();
990 world.insert_resource(Counter(0));
991 let post_system = world.register_system(post);
992
993 let mut event = MyEvent { cancelled: false };
994 world.run_system_with(post_system, &mut event).unwrap();
995 assert!(!event.cancelled);
996
997 world.resource_mut::<Counter>().0 = 1;
998 world.run_system_with(post_system, &mut event).unwrap();
999 assert!(event.cancelled);
1000 }
1001
1002 #[test]
1003 fn run_system_invalid_params() {
1004 use crate::system::RegisteredSystemError;
1005 use alloc::string::ToString;
1006
1007 #[derive(Resource)]
1008 struct T;
1009
1010 fn system(_: Res<T>) {}
1011
1012 let mut world = World::new();
1013 let id = world.register_system(system);
1014 let result = world.run_system(id);
1016
1017 assert!(matches!(result, Err(RegisteredSystemError::Failed { .. })));
1018 let expected = "Resource does not exist";
1019 let actual = result.unwrap_err().to_string();
1020
1021 assert!(
1022 actual.contains(expected),
1023 "Expected error message to contain `{}` but got `{}`",
1024 expected,
1025 actual
1026 );
1027 }
1028
1029 #[test]
1030 fn run_system_recursive() {
1031 std::thread_local! {
1032 static INVOCATIONS_LEFT: Cell<i32> = const { Cell::new(3) };
1033 static SYSTEM_ID: Cell<Option<SystemId>> = default();
1034 }
1035
1036 fn system(mut commands: Commands) {
1037 let count = INVOCATIONS_LEFT.get() - 1;
1038 INVOCATIONS_LEFT.set(count);
1039 if count > 0 {
1040 commands.run_system(SYSTEM_ID.get().unwrap());
1041 }
1042 }
1043
1044 let mut world = World::new();
1045 let id = world.register_system(system);
1046 SYSTEM_ID.set(Some(id));
1047 world.run_system(id).unwrap();
1048
1049 assert_eq!(INVOCATIONS_LEFT.get(), 0);
1050 }
1051
1052 #[test]
1053 fn run_system_exclusive_adapters() {
1054 let mut world = World::new();
1055 fn system(_: &mut World) {}
1056 world.run_system_cached(system).unwrap();
1057 world.run_system_cached(system.pipe(system)).unwrap();
1058 world.run_system_cached(system.map(|()| {})).unwrap();
1059 }
1060
1061 #[test]
1062 fn wrong_system_type() {
1063 fn test() -> Result<(), u8> {
1064 Ok(())
1065 }
1066
1067 let mut world = World::new();
1068
1069 let entity = world.register_system_cached(test).entity();
1070
1071 match world.run_system::<u8>(SystemId::from_entity(entity)) {
1072 Ok(_) => panic!("Should fail since called `run_system` with wrong SystemId type."),
1073 Err(RegisteredSystemError::IncorrectType(_, _)) => (),
1074 Err(err) => panic!("Failed with wrong error. `{:?}`", err),
1075 }
1076 }
1077}