1mod adapter_system;
123mod builder;
124mod combinator;
125mod commands;
126mod exclusive_function_system;
127mod exclusive_system_param;
128mod function_system;
129mod input;
130mod observer_system;
131mod query;
132mod schedule_system;
133mod system;
134mod system_name;
135mod system_param;
136mod system_registry;
137
138use core::any::TypeId;
139
140pub use adapter_system::*;
141pub use builder::*;
142pub use combinator::*;
143pub use commands::*;
144pub use exclusive_function_system::*;
145pub use exclusive_system_param::*;
146pub use function_system::*;
147pub use input::*;
148pub use observer_system::*;
149pub use query::*;
150pub use schedule_system::*;
151pub use system::*;
152pub use system_name::*;
153pub use system_param::*;
154pub use system_registry::*;
155
156use crate::world::World;
157
158#[diagnostic::on_unimplemented(
182 message = "`{Self}` is not a valid system with input `{In}` and output `{Out}`",
183 label = "invalid system"
184)]
185pub trait IntoSystem<In: SystemInput, Out, Marker>: Sized {
186 type System: System<In = In, Out = Out>;
188
189 fn into_system(this: Self) -> Self::System;
191
192 fn pipe<B, BIn, BOut, MarkerB>(self, system: B) -> IntoPipeSystem<Self, B>
197 where
198 Out: 'static,
199 B: IntoSystem<BIn, BOut, MarkerB>,
200 for<'a> BIn: SystemInput<Inner<'a> = Out>,
201 {
202 IntoPipeSystem::new(self, system)
203 }
204
205 fn map<T, F>(self, f: F) -> IntoAdapterSystem<F, Self>
225 where
226 F: Send + Sync + 'static + FnMut(Out) -> T,
227 {
228 IntoAdapterSystem::new(f, self)
229 }
230
231 #[inline]
233 fn system_type_id(&self) -> TypeId {
234 TypeId::of::<Self::System>()
235 }
236}
237
238impl<T: System> IntoSystem<T::In, T::Out, ()> for T {
240 type System = T;
241 fn into_system(this: Self) -> Self {
242 this
243 }
244}
245
246pub fn assert_is_system<In: SystemInput, Out: 'static, Marker>(
271 system: impl IntoSystem<In, Out, Marker>,
272) {
273 let mut system = IntoSystem::into_system(system);
274
275 let mut world = World::new();
277 system.initialize(&mut world);
278}
279
280pub fn assert_is_read_only_system<In, Out, Marker, S>(system: S)
304where
305 In: SystemInput,
306 Out: 'static,
307 S: IntoSystem<In, Out, Marker>,
308 S::System: ReadOnlySystem,
309{
310 assert_is_system(system);
311}
312
313pub fn assert_system_does_not_conflict<Out, Params, S: IntoSystem<(), Out, Params>>(sys: S) {
319 let mut world = World::new();
320 let mut system = IntoSystem::into_system(sys);
321 system.initialize(&mut world);
322 system.run((), &mut world);
323}
324
325#[cfg(test)]
326#[expect(clippy::print_stdout, reason = "Allowed in tests.")]
327mod tests {
328 use alloc::{vec, vec::Vec};
329 use bevy_utils::default;
330 use core::any::TypeId;
331 use std::println;
332
333 use crate::{
334 archetype::{ArchetypeComponentId, Archetypes},
335 bundle::Bundles,
336 change_detection::DetectChanges,
337 component::{Component, Components},
338 entity::{Entities, Entity},
339 error::Result,
340 name::Name,
341 prelude::{AnyOf, EntityRef, Trigger},
342 query::{Added, Changed, Or, With, Without},
343 removal_detection::RemovedComponents,
344 resource::Resource,
345 schedule::{
346 common_conditions::resource_exists, ApplyDeferred, Condition, IntoScheduleConfigs,
347 Schedule,
348 },
349 system::{
350 Commands, In, IntoSystem, Local, NonSend, NonSendMut, ParamSet, Query, Res, ResMut,
351 Single, StaticSystemParam, System, SystemState,
352 },
353 world::{DeferredWorld, EntityMut, FromWorld, OnAdd, World},
354 };
355
356 use super::ScheduleSystem;
357
358 #[derive(Resource, PartialEq, Debug)]
359 enum SystemRan {
360 Yes,
361 No,
362 }
363
364 #[derive(Component, Resource, Debug, Eq, PartialEq, Default)]
365 struct A;
366 #[derive(Component, Resource)]
367 struct B;
368 #[derive(Component, Resource)]
369 struct C;
370 #[derive(Component, Resource)]
371 struct D;
372 #[derive(Component, Resource)]
373 struct E;
374 #[derive(Component, Resource)]
375 struct F;
376
377 #[derive(Component, Debug)]
378 struct W<T>(T);
379
380 #[test]
381 fn simple_system() {
382 fn sys(query: Query<&A>) {
383 for a in &query {
384 println!("{a:?}");
385 }
386 }
387
388 let mut system = IntoSystem::into_system(sys);
389 let mut world = World::new();
390 world.spawn(A);
391
392 system.initialize(&mut world);
393 system.run((), &mut world);
394 }
395
396 fn run_system<Marker, S: IntoScheduleConfigs<ScheduleSystem, Marker>>(
397 world: &mut World,
398 system: S,
399 ) {
400 let mut schedule = Schedule::default();
401 schedule.add_systems(system);
402 schedule.run(world);
403 }
404
405 #[test]
406 fn get_many_is_ordered() {
407 use crate::resource::Resource;
408 const ENTITIES_COUNT: usize = 1000;
409
410 #[derive(Resource)]
411 struct EntitiesArray(Vec<Entity>);
412
413 fn query_system(
414 mut ran: ResMut<SystemRan>,
415 entities_array: Res<EntitiesArray>,
416 q: Query<&W<usize>>,
417 ) {
418 let entities_array: [Entity; ENTITIES_COUNT] =
419 entities_array.0.clone().try_into().unwrap();
420
421 for (i, w) in (0..ENTITIES_COUNT).zip(q.get_many(entities_array).unwrap()) {
422 assert_eq!(i, w.0);
423 }
424
425 *ran = SystemRan::Yes;
426 }
427
428 fn query_system_mut(
429 mut ran: ResMut<SystemRan>,
430 entities_array: Res<EntitiesArray>,
431 mut q: Query<&mut W<usize>>,
432 ) {
433 let entities_array: [Entity; ENTITIES_COUNT] =
434 entities_array.0.clone().try_into().unwrap();
435
436 for (i, w) in (0..ENTITIES_COUNT).zip(q.get_many_mut(entities_array).unwrap()) {
437 assert_eq!(i, w.0);
438 }
439
440 *ran = SystemRan::Yes;
441 }
442
443 let mut world = World::default();
444 world.insert_resource(SystemRan::No);
445 let entity_ids = (0..ENTITIES_COUNT)
446 .map(|i| world.spawn(W(i)).id())
447 .collect();
448 world.insert_resource(EntitiesArray(entity_ids));
449
450 run_system(&mut world, query_system);
451 assert_eq!(*world.resource::<SystemRan>(), SystemRan::Yes);
452
453 world.insert_resource(SystemRan::No);
454 run_system(&mut world, query_system_mut);
455 assert_eq!(*world.resource::<SystemRan>(), SystemRan::Yes);
456 }
457
458 #[test]
459 fn or_param_set_system() {
460 fn query_system(
462 mut ran: ResMut<SystemRan>,
463 mut set: ParamSet<(
464 Query<(), Or<(Changed<A>, Changed<B>)>>,
465 Query<(), Or<(Added<A>, Added<B>)>>,
466 )>,
467 ) {
468 let changed = set.p0().iter().count();
469 let added = set.p1().iter().count();
470
471 assert_eq!(changed, 1);
472 assert_eq!(added, 1);
473
474 *ran = SystemRan::Yes;
475 }
476
477 let mut world = World::default();
478 world.insert_resource(SystemRan::No);
479 world.spawn((A, B));
480
481 run_system(&mut world, query_system);
482
483 assert_eq!(*world.resource::<SystemRan>(), SystemRan::Yes);
484 }
485
486 #[test]
487 fn changed_resource_system() {
488 use crate::resource::Resource;
489
490 #[derive(Resource)]
491 struct Flipper(bool);
492
493 #[derive(Resource)]
494 struct Added(usize);
495
496 #[derive(Resource)]
497 struct Changed(usize);
498
499 fn incr_e_on_flip(
500 value: Res<Flipper>,
501 mut changed: ResMut<Changed>,
502 mut added: ResMut<Added>,
503 ) {
504 if value.is_added() {
505 added.0 += 1;
506 }
507
508 if value.is_changed() {
509 changed.0 += 1;
510 }
511 }
512
513 let mut world = World::default();
514 world.insert_resource(Flipper(false));
515 world.insert_resource(Added(0));
516 world.insert_resource(Changed(0));
517
518 let mut schedule = Schedule::default();
519
520 schedule.add_systems((incr_e_on_flip, ApplyDeferred, World::clear_trackers).chain());
521
522 schedule.run(&mut world);
523 assert_eq!(world.resource::<Added>().0, 1);
524 assert_eq!(world.resource::<Changed>().0, 1);
525
526 schedule.run(&mut world);
527 assert_eq!(world.resource::<Added>().0, 1);
528 assert_eq!(world.resource::<Changed>().0, 1);
529
530 world.resource_mut::<Flipper>().0 = true;
531 schedule.run(&mut world);
532 assert_eq!(world.resource::<Added>().0, 1);
533 assert_eq!(world.resource::<Changed>().0, 2);
534 }
535
536 #[test]
537 #[should_panic = "error[B0001]"]
538 fn option_has_no_filter_with() {
539 fn sys(_: Query<(Option<&A>, &mut B)>, _: Query<&mut B, Without<A>>) {}
540 let mut world = World::default();
541 run_system(&mut world, sys);
542 }
543
544 #[test]
545 fn option_doesnt_remove_unrelated_filter_with() {
546 fn sys(_: Query<(Option<&A>, &mut B, &A)>, _: Query<&mut B, Without<A>>) {}
547 let mut world = World::default();
548 run_system(&mut world, sys);
549 }
550
551 #[test]
552 fn any_of_working() {
553 fn sys(_: Query<AnyOf<(&mut A, &B)>>) {}
554 let mut world = World::default();
555 run_system(&mut world, sys);
556 }
557
558 #[test]
559 fn any_of_with_and_without_common() {
560 fn sys(_: Query<(&mut D, &C, AnyOf<(&A, &B)>)>, _: Query<&mut D, Without<C>>) {}
561 let mut world = World::default();
562 run_system(&mut world, sys);
563 }
564
565 #[test]
566 #[should_panic = "&bevy_ecs::system::tests::A conflicts with a previous access in this query."]
567 fn any_of_with_mut_and_ref() {
568 fn sys(_: Query<AnyOf<(&mut A, &A)>>) {}
569 let mut world = World::default();
570 run_system(&mut world, sys);
571 }
572
573 #[test]
574 #[should_panic = "&mut bevy_ecs::system::tests::A conflicts with a previous access in this query."]
575 fn any_of_with_ref_and_mut() {
576 fn sys(_: Query<AnyOf<(&A, &mut A)>>) {}
577 let mut world = World::default();
578 run_system(&mut world, sys);
579 }
580
581 #[test]
582 #[should_panic = "&bevy_ecs::system::tests::A conflicts with a previous access in this query."]
583 fn any_of_with_mut_and_option() {
584 fn sys(_: Query<AnyOf<(&mut A, Option<&A>)>>) {}
585 let mut world = World::default();
586 run_system(&mut world, sys);
587 }
588
589 #[test]
590 fn any_of_with_entity_and_mut() {
591 fn sys(_: Query<AnyOf<(Entity, &mut A)>>) {}
592 let mut world = World::default();
593 run_system(&mut world, sys);
594 }
595
596 #[test]
597 fn any_of_with_empty_and_mut() {
598 fn sys(_: Query<AnyOf<((), &mut A)>>) {}
599 let mut world = World::default();
600 run_system(&mut world, sys);
601 }
602
603 #[test]
604 #[should_panic = "error[B0001]"]
605 fn any_of_has_no_filter_with() {
606 fn sys(_: Query<(AnyOf<(&A, ())>, &mut B)>, _: Query<&mut B, Without<A>>) {}
607 let mut world = World::default();
608 run_system(&mut world, sys);
609 }
610
611 #[test]
612 #[should_panic = "&mut bevy_ecs::system::tests::A conflicts with a previous access in this query."]
613 fn any_of_with_conflicting() {
614 fn sys(_: Query<AnyOf<(&mut A, &mut A)>>) {}
615 let mut world = World::default();
616 run_system(&mut world, sys);
617 }
618
619 #[test]
620 fn any_of_has_filter_with_when_both_have_it() {
621 fn sys(_: Query<(AnyOf<(&A, &A)>, &mut B)>, _: Query<&mut B, Without<A>>) {}
622 let mut world = World::default();
623 run_system(&mut world, sys);
624 }
625
626 #[test]
627 fn any_of_doesnt_remove_unrelated_filter_with() {
628 fn sys(_: Query<(AnyOf<(&A, ())>, &mut B, &A)>, _: Query<&mut B, Without<A>>) {}
629 let mut world = World::default();
630 run_system(&mut world, sys);
631 }
632
633 #[test]
634 fn any_of_and_without() {
635 fn sys(_: Query<(AnyOf<(&A, &B)>, &mut C)>, _: Query<&mut C, (Without<A>, Without<B>)>) {}
636 let mut world = World::default();
637 run_system(&mut world, sys);
638 }
639
640 #[test]
641 #[should_panic = "error[B0001]"]
642 fn or_has_no_filter_with() {
643 fn sys(_: Query<&mut B, Or<(With<A>, With<B>)>>, _: Query<&mut B, Without<A>>) {}
644 let mut world = World::default();
645 run_system(&mut world, sys);
646 }
647
648 #[test]
649 fn or_has_filter_with_when_both_have_it() {
650 fn sys(_: Query<&mut B, Or<(With<A>, With<A>)>>, _: Query<&mut B, Without<A>>) {}
651 let mut world = World::default();
652 run_system(&mut world, sys);
653 }
654
655 #[test]
656 fn or_has_filter_with() {
657 fn sys(
658 _: Query<&mut C, Or<(With<A>, With<B>)>>,
659 _: Query<&mut C, (Without<A>, Without<B>)>,
660 ) {
661 }
662 let mut world = World::default();
663 run_system(&mut world, sys);
664 }
665
666 #[test]
667 fn or_expanded_with_and_without_common() {
668 fn sys(_: Query<&mut D, (With<A>, Or<(With<B>, With<C>)>)>, _: Query<&mut D, Without<A>>) {}
669 let mut world = World::default();
670 run_system(&mut world, sys);
671 }
672
673 #[test]
674 fn or_expanded_nested_with_and_without_common() {
675 fn sys(
676 _: Query<&mut E, (Or<((With<B>, With<C>), (With<C>, With<D>))>, With<A>)>,
677 _: Query<&mut E, (Without<B>, Without<D>)>,
678 ) {
679 }
680 let mut world = World::default();
681 run_system(&mut world, sys);
682 }
683
684 #[test]
685 #[should_panic = "error[B0001]"]
686 fn or_expanded_nested_with_and_disjoint_without() {
687 fn sys(
688 _: Query<&mut E, (Or<((With<B>, With<C>), (With<C>, With<D>))>, With<A>)>,
689 _: Query<&mut E, Without<D>>,
690 ) {
691 }
692 let mut world = World::default();
693 run_system(&mut world, sys);
694 }
695
696 #[test]
697 #[should_panic = "error[B0001]"]
698 fn or_expanded_nested_or_with_and_disjoint_without() {
699 fn sys(
700 _: Query<&mut D, Or<(Or<(With<A>, With<B>)>, Or<(With<A>, With<C>)>)>>,
701 _: Query<&mut D, Without<A>>,
702 ) {
703 }
704 let mut world = World::default();
705 run_system(&mut world, sys);
706 }
707
708 #[test]
709 fn or_expanded_nested_with_and_common_nested_without() {
710 fn sys(
711 _: Query<&mut D, Or<((With<A>, With<B>), (With<B>, With<C>))>>,
712 _: Query<&mut D, Or<(Without<D>, Without<B>)>>,
713 ) {
714 }
715 let mut world = World::default();
716 run_system(&mut world, sys);
717 }
718
719 #[test]
720 fn or_with_without_and_compatible_with_without() {
721 fn sys(
722 _: Query<&mut C, Or<(With<A>, Without<B>)>>,
723 _: Query<&mut C, (With<B>, Without<A>)>,
724 ) {
725 }
726 let mut world = World::default();
727 run_system(&mut world, sys);
728 }
729
730 #[test]
731 #[should_panic = "error[B0001]"]
732 fn with_and_disjoint_or_empty_without() {
733 fn sys(_: Query<&mut B, With<A>>, _: Query<&mut B, Or<((), Without<A>)>>) {}
734 let mut world = World::default();
735 run_system(&mut world, sys);
736 }
737
738 #[test]
739 #[should_panic = "error[B0001]"]
740 fn or_expanded_with_and_disjoint_nested_without() {
741 fn sys(
742 _: Query<&mut D, Or<(With<A>, With<B>)>>,
743 _: Query<&mut D, Or<(Without<A>, Without<B>)>>,
744 ) {
745 }
746 let mut world = World::default();
747 run_system(&mut world, sys);
748 }
749
750 #[test]
751 #[should_panic = "error[B0001]"]
752 fn or_expanded_nested_with_and_disjoint_nested_without() {
753 fn sys(
754 _: Query<&mut D, Or<((With<A>, With<B>), (With<B>, With<C>))>>,
755 _: Query<&mut D, Or<(Without<A>, Without<B>)>>,
756 ) {
757 }
758 let mut world = World::default();
759 run_system(&mut world, sys);
760 }
761
762 #[test]
763 fn or_doesnt_remove_unrelated_filter_with() {
764 fn sys(_: Query<&mut B, (Or<(With<A>, With<B>)>, With<A>)>, _: Query<&mut B, Without<A>>) {}
765 let mut world = World::default();
766 run_system(&mut world, sys);
767 }
768
769 #[test]
770 #[should_panic]
771 fn conflicting_query_mut_system() {
772 fn sys(_q1: Query<&mut A>, _q2: Query<&mut A>) {}
773
774 let mut world = World::default();
775 run_system(&mut world, sys);
776 }
777
778 #[test]
779 fn disjoint_query_mut_system() {
780 fn sys(_q1: Query<&mut A, With<B>>, _q2: Query<&mut A, Without<B>>) {}
781
782 let mut world = World::default();
783 run_system(&mut world, sys);
784 }
785
786 #[test]
787 fn disjoint_query_mut_read_component_system() {
788 fn sys(_q1: Query<(&mut A, &B)>, _q2: Query<&mut A, Without<B>>) {}
789
790 let mut world = World::default();
791 run_system(&mut world, sys);
792 }
793
794 #[test]
795 #[should_panic]
796 fn conflicting_query_immut_system() {
797 fn sys(_q1: Query<&A>, _q2: Query<&mut A>) {}
798
799 let mut world = World::default();
800 run_system(&mut world, sys);
801 }
802
803 #[test]
804 #[should_panic]
805 fn changed_trackers_or_conflict() {
806 fn sys(_: Query<&mut A>, _: Query<(), Or<(Changed<A>,)>>) {}
807
808 let mut world = World::default();
809 run_system(&mut world, sys);
810 }
811
812 #[test]
813 fn query_set_system() {
814 fn sys(mut _set: ParamSet<(Query<&mut A>, Query<&A>)>) {}
815 let mut world = World::default();
816 run_system(&mut world, sys);
817 }
818
819 #[test]
820 #[should_panic]
821 fn conflicting_query_with_query_set_system() {
822 fn sys(_query: Query<&mut A>, _set: ParamSet<(Query<&mut A>, Query<&B>)>) {}
823
824 let mut world = World::default();
825 run_system(&mut world, sys);
826 }
827
828 #[test]
829 #[should_panic]
830 fn conflicting_query_sets_system() {
831 fn sys(_set_1: ParamSet<(Query<&mut A>,)>, _set_2: ParamSet<(Query<&mut A>, Query<&B>)>) {}
832
833 let mut world = World::default();
834 run_system(&mut world, sys);
835 }
836
837 #[derive(Default, Resource)]
838 struct BufferRes {
839 _buffer: Vec<u8>,
840 }
841
842 fn test_for_conflicting_resources<Marker, S: IntoSystem<(), (), Marker>>(sys: S) {
843 let mut world = World::default();
844 world.insert_resource(BufferRes::default());
845 world.insert_resource(A);
846 world.insert_resource(B);
847 run_system(&mut world, sys);
848 }
849
850 #[test]
851 #[should_panic]
852 fn conflicting_system_resources() {
853 fn sys(_: ResMut<BufferRes>, _: Res<BufferRes>) {}
854 test_for_conflicting_resources(sys);
855 }
856
857 #[test]
858 #[should_panic]
859 fn conflicting_system_resources_reverse_order() {
860 fn sys(_: Res<BufferRes>, _: ResMut<BufferRes>) {}
861 test_for_conflicting_resources(sys);
862 }
863
864 #[test]
865 #[should_panic]
866 fn conflicting_system_resources_multiple_mutable() {
867 fn sys(_: ResMut<BufferRes>, _: ResMut<BufferRes>) {}
868 test_for_conflicting_resources(sys);
869 }
870
871 #[test]
872 fn nonconflicting_system_resources() {
873 fn sys(_: Local<BufferRes>, _: ResMut<BufferRes>, _: Local<A>, _: ResMut<A>) {}
874 test_for_conflicting_resources(sys);
875 }
876
877 #[test]
878 fn local_system() {
879 let mut world = World::default();
880 world.insert_resource(ProtoFoo { value: 1 });
881 world.insert_resource(SystemRan::No);
882
883 struct Foo {
884 value: u32,
885 }
886
887 #[derive(Resource)]
888 struct ProtoFoo {
889 value: u32,
890 }
891
892 impl FromWorld for Foo {
893 fn from_world(world: &mut World) -> Self {
894 Foo {
895 value: world.resource::<ProtoFoo>().value + 1,
896 }
897 }
898 }
899
900 fn sys(local: Local<Foo>, mut system_ran: ResMut<SystemRan>) {
901 assert_eq!(local.value, 2);
902 *system_ran = SystemRan::Yes;
903 }
904
905 run_system(&mut world, sys);
906
907 assert_eq!(*world.resource::<SystemRan>(), SystemRan::Yes);
909 }
910
911 #[test]
912 #[expect(
913 dead_code,
914 reason = "The `NotSend1` and `NotSend2` structs is used to verify that a system will run, even if the system params include a non-Send resource. As such, the inner value doesn't matter."
915 )]
916 fn non_send_option_system() {
917 let mut world = World::default();
918
919 world.insert_resource(SystemRan::No);
920 struct NotSend1(alloc::rc::Rc<i32>);
924 struct NotSend2(alloc::rc::Rc<i32>);
925 world.insert_non_send_resource(NotSend1(alloc::rc::Rc::new(0)));
926
927 fn sys(
928 op: Option<NonSend<NotSend1>>,
929 mut _op2: Option<NonSendMut<NotSend2>>,
930 mut system_ran: ResMut<SystemRan>,
931 ) {
932 op.expect("NonSend should exist");
933 *system_ran = SystemRan::Yes;
934 }
935
936 run_system(&mut world, sys);
937 assert_eq!(*world.resource::<SystemRan>(), SystemRan::Yes);
939 }
940
941 #[test]
942 #[expect(
943 dead_code,
944 reason = "The `NotSend1` and `NotSend2` structs are used to verify that a system will run, even if the system params include a non-Send resource. As such, the inner value doesn't matter."
945 )]
946 fn non_send_system() {
947 let mut world = World::default();
948
949 world.insert_resource(SystemRan::No);
950 struct NotSend1(alloc::rc::Rc<i32>);
951 struct NotSend2(alloc::rc::Rc<i32>);
952
953 world.insert_non_send_resource(NotSend1(alloc::rc::Rc::new(1)));
954 world.insert_non_send_resource(NotSend2(alloc::rc::Rc::new(2)));
955
956 fn sys(
957 _op: NonSend<NotSend1>,
958 mut _op2: NonSendMut<NotSend2>,
959 mut system_ran: ResMut<SystemRan>,
960 ) {
961 *system_ran = SystemRan::Yes;
962 }
963
964 run_system(&mut world, sys);
965 assert_eq!(*world.resource::<SystemRan>(), SystemRan::Yes);
966 }
967
968 #[test]
969 fn removal_tracking() {
970 let mut world = World::new();
971
972 let entity_to_despawn = world.spawn(W(1)).id();
973 let entity_to_remove_w_from = world.spawn(W(2)).id();
974 let spurious_entity = world.spawn_empty().id();
975
976 #[derive(Resource)]
978 struct Despawned(Entity);
979 world.insert_resource(Despawned(entity_to_despawn));
980
981 #[derive(Resource)]
982 struct Removed(Entity);
983 world.insert_resource(Removed(entity_to_remove_w_from));
984
985 #[derive(Default, Resource)]
987 struct NSystems(usize);
988 world.insert_resource(NSystems::default());
989
990 world.entity_mut(entity_to_despawn).despawn();
992 world.entity_mut(spurious_entity).despawn();
993
994 fn validate_despawn(
995 mut removed_i32: RemovedComponents<W<i32>>,
996 despawned: Res<Despawned>,
997 mut n_systems: ResMut<NSystems>,
998 ) {
999 assert_eq!(
1000 removed_i32.read().collect::<Vec<_>>(),
1001 &[despawned.0],
1002 "despawning causes the correct entity to show up in the 'RemovedComponent' system parameter."
1003 );
1004
1005 n_systems.0 += 1;
1006 }
1007
1008 run_system(&mut world, validate_despawn);
1009
1010 world.clear_trackers();
1013
1014 world.spawn(W(3));
1016 world.spawn(W(4));
1017 world.entity_mut(entity_to_remove_w_from).remove::<W<i32>>();
1018
1019 fn validate_remove(
1020 mut removed_i32: RemovedComponents<W<i32>>,
1021 despawned: Res<Despawned>,
1022 removed: Res<Removed>,
1023 mut n_systems: ResMut<NSystems>,
1024 ) {
1025 assert_eq!(
1028 removed_i32.read().collect::<Vec<_>>(),
1029 &[despawned.0, removed.0],
1030 "removing a component causes the correct entity to show up in the 'RemovedComponent' system parameter."
1031 );
1032
1033 n_systems.0 += 1;
1034 }
1035
1036 run_system(&mut world, validate_remove);
1037
1038 assert_eq!(world.resource::<NSystems>().0, 2);
1040 }
1041
1042 #[test]
1043 fn world_collections_system() {
1044 let mut world = World::default();
1045 world.insert_resource(SystemRan::No);
1046 world.spawn((W(42), W(true)));
1047 fn sys(
1048 archetypes: &Archetypes,
1049 components: &Components,
1050 entities: &Entities,
1051 bundles: &Bundles,
1052 query: Query<Entity, With<W<i32>>>,
1053 mut system_ran: ResMut<SystemRan>,
1054 ) {
1055 assert_eq!(query.iter().count(), 1, "entity exists");
1056 for entity in &query {
1057 let location = entities.get(entity).unwrap();
1058 let archetype = archetypes.get(location.archetype_id).unwrap();
1059 let archetype_components = archetype.components().collect::<Vec<_>>();
1060 let bundle_id = bundles
1061 .get_id(TypeId::of::<(W<i32>, W<bool>)>())
1062 .expect("Bundle used to spawn entity should exist");
1063 let bundle_info = bundles.get(bundle_id).unwrap();
1064 let mut bundle_components = bundle_info.contributed_components().to_vec();
1065 bundle_components.sort();
1066 for component_id in &bundle_components {
1067 assert!(
1068 components.get_info(*component_id).is_some(),
1069 "every bundle component exists in Components"
1070 );
1071 }
1072 assert_eq!(
1073 bundle_components, archetype_components,
1074 "entity's bundle components exactly match entity's archetype components"
1075 );
1076 }
1077 *system_ran = SystemRan::Yes;
1078 }
1079
1080 run_system(&mut world, sys);
1081
1082 assert_eq!(*world.resource::<SystemRan>(), SystemRan::Yes);
1084 }
1085
1086 #[test]
1087 fn get_system_conflicts() {
1088 fn sys_x(_: Res<A>, _: Res<B>, _: Query<(&C, &D)>) {}
1089
1090 fn sys_y(_: Res<A>, _: ResMut<B>, _: Query<(&C, &mut D)>) {}
1091
1092 let mut world = World::default();
1093 let mut x = IntoSystem::into_system(sys_x);
1094 let mut y = IntoSystem::into_system(sys_y);
1095 x.initialize(&mut world);
1096 y.initialize(&mut world);
1097
1098 let conflicts = x.component_access().get_conflicts(y.component_access());
1099 let b_id = world
1100 .components()
1101 .get_resource_id(TypeId::of::<B>())
1102 .unwrap();
1103 let d_id = world.components().get_id(TypeId::of::<D>()).unwrap();
1104 assert_eq!(conflicts, vec![b_id, d_id].into());
1105 }
1106
1107 #[test]
1108 fn query_is_empty() {
1109 fn without_filter(not_empty: Query<&A>, empty: Query<&B>) {
1110 assert!(!not_empty.is_empty());
1111 assert!(empty.is_empty());
1112 }
1113
1114 fn with_filter(not_empty: Query<&A, With<C>>, empty: Query<&A, With<D>>) {
1115 assert!(!not_empty.is_empty());
1116 assert!(empty.is_empty());
1117 }
1118
1119 let mut world = World::default();
1120 world.spawn(A).insert(C);
1121
1122 let mut without_filter = IntoSystem::into_system(without_filter);
1123 without_filter.initialize(&mut world);
1124 without_filter.run((), &mut world);
1125
1126 let mut with_filter = IntoSystem::into_system(with_filter);
1127 with_filter.initialize(&mut world);
1128 with_filter.run((), &mut world);
1129 }
1130
1131 #[test]
1132 fn can_have_16_parameters() {
1133 fn sys_x(
1134 _: Res<A>,
1135 _: Res<B>,
1136 _: Res<C>,
1137 _: Res<D>,
1138 _: Res<E>,
1139 _: Res<F>,
1140 _: Query<&A>,
1141 _: Query<&B>,
1142 _: Query<&C>,
1143 _: Query<&D>,
1144 _: Query<&E>,
1145 _: Query<&F>,
1146 _: Query<(&A, &B)>,
1147 _: Query<(&C, &D)>,
1148 _: Query<(&E, &F)>,
1149 ) {
1150 }
1151 fn sys_y(
1152 _: (
1153 Res<A>,
1154 Res<B>,
1155 Res<C>,
1156 Res<D>,
1157 Res<E>,
1158 Res<F>,
1159 Query<&A>,
1160 Query<&B>,
1161 Query<&C>,
1162 Query<&D>,
1163 Query<&E>,
1164 Query<&F>,
1165 Query<(&A, &B)>,
1166 Query<(&C, &D)>,
1167 Query<(&E, &F)>,
1168 ),
1169 ) {
1170 }
1171 let mut world = World::default();
1172 let mut x = IntoSystem::into_system(sys_x);
1173 let mut y = IntoSystem::into_system(sys_y);
1174 x.initialize(&mut world);
1175 y.initialize(&mut world);
1176 }
1177
1178 #[test]
1179 fn read_system_state() {
1180 #[derive(Eq, PartialEq, Debug, Resource)]
1181 struct A(usize);
1182
1183 #[derive(Component, Eq, PartialEq, Debug)]
1184 struct B(usize);
1185
1186 let mut world = World::default();
1187 world.insert_resource(A(42));
1188 world.spawn(B(7));
1189
1190 let mut system_state: SystemState<(
1191 Res<A>,
1192 Option<Single<&B>>,
1193 ParamSet<(Query<&C>, Query<&D>)>,
1194 )> = SystemState::new(&mut world);
1195 let (a, query, _) = system_state.get(&world);
1196 assert_eq!(*a, A(42), "returned resource matches initial value");
1197 assert_eq!(
1198 **query.unwrap(),
1199 B(7),
1200 "returned component matches initial value"
1201 );
1202 }
1203
1204 #[test]
1205 fn write_system_state() {
1206 #[derive(Resource, Eq, PartialEq, Debug)]
1207 struct A(usize);
1208
1209 #[derive(Component, Eq, PartialEq, Debug)]
1210 struct B(usize);
1211
1212 let mut world = World::default();
1213 world.insert_resource(A(42));
1214 world.spawn(B(7));
1215
1216 let mut system_state: SystemState<(ResMut<A>, Option<Single<&mut B>>)> =
1217 SystemState::new(&mut world);
1218
1219 let (a, query) = system_state.get_mut(&mut world);
1223 assert_eq!(*a, A(42), "returned resource matches initial value");
1224 assert_eq!(
1225 **query.unwrap(),
1226 B(7),
1227 "returned component matches initial value"
1228 );
1229 }
1230
1231 #[test]
1232 fn system_state_change_detection() {
1233 #[derive(Component, Eq, PartialEq, Debug)]
1234 struct A(usize);
1235
1236 let mut world = World::default();
1237 let entity = world.spawn(A(1)).id();
1238
1239 let mut system_state: SystemState<Option<Single<&A, Changed<A>>>> =
1240 SystemState::new(&mut world);
1241 {
1242 let query = system_state.get(&world);
1243 assert_eq!(**query.unwrap(), A(1));
1244 }
1245
1246 {
1247 let query = system_state.get(&world);
1248 assert!(query.is_none());
1249 }
1250
1251 world.entity_mut(entity).get_mut::<A>().unwrap().0 = 2;
1252 {
1253 let query = system_state.get(&world);
1254 assert_eq!(**query.unwrap(), A(2));
1255 }
1256 }
1257
1258 #[test]
1259 #[should_panic]
1260 fn system_state_invalid_world() {
1261 let mut world = World::default();
1262 let mut system_state = SystemState::<Query<&A>>::new(&mut world);
1263 let mismatched_world = World::default();
1264 system_state.get(&mismatched_world);
1265 }
1266
1267 #[test]
1268 fn system_state_archetype_update() {
1269 #[derive(Component, Eq, PartialEq, Debug)]
1270 struct A(usize);
1271
1272 #[derive(Component, Eq, PartialEq, Debug)]
1273 struct B(usize);
1274
1275 let mut world = World::default();
1276 world.spawn(A(1));
1277
1278 let mut system_state = SystemState::<Query<&A>>::new(&mut world);
1279 {
1280 let query = system_state.get(&world);
1281 assert_eq!(
1282 query.iter().collect::<Vec<_>>(),
1283 vec![&A(1)],
1284 "exactly one component returned"
1285 );
1286 }
1287
1288 world.spawn((A(2), B(2)));
1289 {
1290 let query = system_state.get(&world);
1291 assert_eq!(
1292 query.iter().collect::<Vec<_>>(),
1293 vec![&A(1), &A(2)],
1294 "components from both archetypes returned"
1295 );
1296 }
1297 }
1298
1299 #[test]
1300 #[expect(
1301 dead_code,
1302 reason = "This test exists to show that read-only world-only queries can return data that lives as long as `'world`."
1303 )]
1304 fn long_life_test() {
1305 struct Holder<'w> {
1306 value: &'w A,
1307 }
1308
1309 struct State {
1310 state: SystemState<Res<'static, A>>,
1311 state_q: SystemState<Query<'static, 'static, &'static A>>,
1312 }
1313
1314 impl State {
1315 fn hold_res<'w>(&mut self, world: &'w World) -> Holder<'w> {
1316 let a = self.state.get(world);
1317 Holder {
1318 value: a.into_inner(),
1319 }
1320 }
1321 fn hold_component<'w>(&mut self, world: &'w World, entity: Entity) -> Holder<'w> {
1322 let q = self.state_q.get(world);
1323 let a = q.get_inner(entity).unwrap();
1324 Holder { value: a }
1325 }
1326 fn hold_components<'w>(&mut self, world: &'w World) -> Vec<Holder<'w>> {
1327 let mut components = Vec::new();
1328 let q = self.state_q.get(world);
1329 for a in q.iter_inner() {
1330 components.push(Holder { value: a });
1331 }
1332 components
1333 }
1334 }
1335 }
1336
1337 #[test]
1338 fn immutable_mut_test() {
1339 #[derive(Component, Eq, PartialEq, Debug, Clone, Copy)]
1340 struct A(usize);
1341
1342 let mut world = World::default();
1343 world.spawn(A(1));
1344 world.spawn(A(2));
1345
1346 let mut system_state = SystemState::<Query<&mut A>>::new(&mut world);
1347 {
1348 let mut query = system_state.get_mut(&mut world);
1349 assert_eq!(
1350 query.iter_mut().map(|m| *m).collect::<Vec<A>>(),
1351 vec![A(1), A(2)],
1352 "both components returned by iter_mut of &mut"
1353 );
1354 assert_eq!(
1355 query.iter().collect::<Vec<&A>>(),
1356 vec![&A(1), &A(2)],
1357 "both components returned by iter of &mut"
1358 );
1359 }
1360 }
1361
1362 #[test]
1363 fn convert_mut_to_immut() {
1364 {
1365 let mut world = World::new();
1366
1367 fn mutable_query(mut query: Query<&mut A>) {
1368 for _ in &mut query {}
1369
1370 immutable_query(query.as_readonly());
1371 }
1372
1373 fn immutable_query(_: Query<&A>) {}
1374
1375 let mut sys = IntoSystem::into_system(mutable_query);
1376 sys.initialize(&mut world);
1377 }
1378
1379 {
1380 let mut world = World::new();
1381
1382 fn mutable_query(mut query: Query<Option<&mut A>>) {
1383 for _ in &mut query {}
1384
1385 immutable_query(query.as_readonly());
1386 }
1387
1388 fn immutable_query(_: Query<Option<&A>>) {}
1389
1390 let mut sys = IntoSystem::into_system(mutable_query);
1391 sys.initialize(&mut world);
1392 }
1393
1394 {
1395 let mut world = World::new();
1396
1397 fn mutable_query(mut query: Query<(&mut A, &B)>) {
1398 for _ in &mut query {}
1399
1400 immutable_query(query.as_readonly());
1401 }
1402
1403 fn immutable_query(_: Query<(&A, &B)>) {}
1404
1405 let mut sys = IntoSystem::into_system(mutable_query);
1406 sys.initialize(&mut world);
1407 }
1408
1409 {
1410 let mut world = World::new();
1411
1412 fn mutable_query(mut query: Query<(&mut A, &mut B)>) {
1413 for _ in &mut query {}
1414
1415 immutable_query(query.as_readonly());
1416 }
1417
1418 fn immutable_query(_: Query<(&A, &B)>) {}
1419
1420 let mut sys = IntoSystem::into_system(mutable_query);
1421 sys.initialize(&mut world);
1422 }
1423
1424 {
1425 let mut world = World::new();
1426
1427 fn mutable_query(mut query: Query<(&mut A, &mut B), With<C>>) {
1428 for _ in &mut query {}
1429
1430 immutable_query(query.as_readonly());
1431 }
1432
1433 fn immutable_query(_: Query<(&A, &B), With<C>>) {}
1434
1435 let mut sys = IntoSystem::into_system(mutable_query);
1436 sys.initialize(&mut world);
1437 }
1438
1439 {
1440 let mut world = World::new();
1441
1442 fn mutable_query(mut query: Query<(&mut A, &mut B), Without<C>>) {
1443 for _ in &mut query {}
1444
1445 immutable_query(query.as_readonly());
1446 }
1447
1448 fn immutable_query(_: Query<(&A, &B), Without<C>>) {}
1449
1450 let mut sys = IntoSystem::into_system(mutable_query);
1451 sys.initialize(&mut world);
1452 }
1453
1454 {
1455 let mut world = World::new();
1456
1457 fn mutable_query(mut query: Query<(&mut A, &mut B), Added<C>>) {
1458 for _ in &mut query {}
1459
1460 immutable_query(query.as_readonly());
1461 }
1462
1463 fn immutable_query(_: Query<(&A, &B), Added<C>>) {}
1464
1465 let mut sys = IntoSystem::into_system(mutable_query);
1466 sys.initialize(&mut world);
1467 }
1468
1469 {
1470 let mut world = World::new();
1471
1472 fn mutable_query(mut query: Query<(&mut A, &mut B), Changed<C>>) {
1473 for _ in &mut query {}
1474
1475 immutable_query(query.as_readonly());
1476 }
1477
1478 fn immutable_query(_: Query<(&A, &B), Changed<C>>) {}
1479
1480 let mut sys = IntoSystem::into_system(mutable_query);
1481 sys.initialize(&mut world);
1482 }
1483 }
1484
1485 #[test]
1486 fn update_archetype_component_access_works() {
1487 use std::collections::HashSet;
1488
1489 fn a_not_b_system(_query: Query<&A, Without<B>>) {}
1490
1491 let mut world = World::default();
1492 let mut system = IntoSystem::into_system(a_not_b_system);
1493 let mut expected_ids = HashSet::<ArchetypeComponentId>::new();
1494 let a_id = world.register_component::<A>();
1495
1496 system.initialize(&mut world);
1498 system.update_archetype_component_access(world.as_unsafe_world_cell());
1499 let archetype_component_access = system.archetype_component_access();
1500 assert!(expected_ids
1501 .iter()
1502 .all(|id| archetype_component_access.has_component_read(*id)));
1503
1504 expected_ids.insert(
1506 world
1507 .spawn(A)
1508 .archetype()
1509 .get_archetype_component_id(a_id)
1510 .unwrap(),
1511 );
1512 expected_ids.insert(
1513 world
1514 .spawn((A, C))
1515 .archetype()
1516 .get_archetype_component_id(a_id)
1517 .unwrap(),
1518 );
1519
1520 world.spawn((A, B));
1522 world.spawn((B, C));
1523
1524 system.update_archetype_component_access(world.as_unsafe_world_cell());
1526 let archetype_component_access = system.archetype_component_access();
1527 assert!(expected_ids
1528 .iter()
1529 .all(|id| archetype_component_access.has_component_read(*id)));
1530
1531 expected_ids.insert(
1533 world
1534 .spawn((A, D))
1535 .archetype()
1536 .get_archetype_component_id(a_id)
1537 .unwrap(),
1538 );
1539 world.spawn((A, B, D));
1540 system.update_archetype_component_access(world.as_unsafe_world_cell());
1541 let archetype_component_access = system.archetype_component_access();
1542 assert!(expected_ids
1543 .iter()
1544 .all(|id| archetype_component_access.has_component_read(*id)));
1545 }
1546
1547 #[test]
1548 fn commands_param_set() {
1549 let mut world = World::new();
1551 let entity = world.spawn_empty().id();
1552
1553 run_system(
1554 &mut world,
1555 move |mut commands_set: ParamSet<(Commands, Commands)>| {
1556 commands_set.p0().entity(entity).insert(A);
1557 commands_set.p1().entity(entity).insert(B);
1558 },
1559 );
1560
1561 let entity = world.entity(entity);
1562 assert!(entity.contains::<A>());
1563 assert!(entity.contains::<B>());
1564 }
1565
1566 #[test]
1567 fn into_iter_impl() {
1568 let mut world = World::new();
1569 world.spawn(W(42u32));
1570 run_system(&mut world, |mut q: Query<&mut W<u32>>| {
1571 for mut a in &mut q {
1572 assert_eq!(a.0, 42);
1573 a.0 = 0;
1574 }
1575 for a in &q {
1576 assert_eq!(a.0, 0);
1577 }
1578 });
1579 }
1580
1581 #[test]
1582 #[should_panic]
1583 fn assert_system_does_not_conflict() {
1584 fn system(_query: Query<(&mut W<u32>, &mut W<u32>)>) {}
1585 super::assert_system_does_not_conflict(system);
1586 }
1587
1588 #[test]
1589 #[should_panic(
1590 expected = "error[B0001]: Query<EntityMut, ()> in system bevy_ecs::system::tests::assert_world_and_entity_mut_system_does_conflict_first::system accesses component(s) in a way that conflicts with a previous system parameter. Consider using `Without<T>` to create disjoint Queries or merging conflicting Queries into a `ParamSet`. See: https://bevyengine.org/learn/errors/b0001"
1591 )]
1592 fn assert_world_and_entity_mut_system_does_conflict_first() {
1593 fn system(_query: &World, _q2: Query<EntityMut>) {}
1594 super::assert_system_does_not_conflict(system);
1595 }
1596
1597 #[test]
1598 #[should_panic(
1599 expected = "&World conflicts with a previous mutable system parameter. Allowing this would break Rust's mutability rules"
1600 )]
1601 fn assert_world_and_entity_mut_system_does_conflict_second() {
1602 fn system(_: Query<EntityMut>, _: &World) {}
1603 super::assert_system_does_not_conflict(system);
1604 }
1605
1606 #[test]
1607 #[should_panic(
1608 expected = "error[B0001]: Query<EntityMut, ()> in system bevy_ecs::system::tests::assert_entity_ref_and_entity_mut_system_does_conflict::system accesses component(s) in a way that conflicts with a previous system parameter. Consider using `Without<T>` to create disjoint Queries or merging conflicting Queries into a `ParamSet`. See: https://bevyengine.org/learn/errors/b0001"
1609 )]
1610 fn assert_entity_ref_and_entity_mut_system_does_conflict() {
1611 fn system(_query: Query<EntityRef>, _q2: Query<EntityMut>) {}
1612 super::assert_system_does_not_conflict(system);
1613 }
1614
1615 #[test]
1616 #[should_panic(
1617 expected = "error[B0001]: Query<EntityMut, ()> in system bevy_ecs::system::tests::assert_entity_mut_system_does_conflict::system accesses component(s) in a way that conflicts with a previous system parameter. Consider using `Without<T>` to create disjoint Queries or merging conflicting Queries into a `ParamSet`. See: https://bevyengine.org/learn/errors/b0001"
1618 )]
1619 fn assert_entity_mut_system_does_conflict() {
1620 fn system(_query: Query<EntityMut>, _q2: Query<EntityMut>) {}
1621 super::assert_system_does_not_conflict(system);
1622 }
1623
1624 #[test]
1625 #[should_panic(
1626 expected = "error[B0001]: Query<EntityRef, ()> in system bevy_ecs::system::tests::assert_deferred_world_and_entity_ref_system_does_conflict_first::system accesses component(s) in a way that conflicts with a previous system parameter. Consider using `Without<T>` to create disjoint Queries or merging conflicting Queries into a `ParamSet`. See: https://bevyengine.org/learn/errors/b0001"
1627 )]
1628 fn assert_deferred_world_and_entity_ref_system_does_conflict_first() {
1629 fn system(_world: DeferredWorld, _query: Query<EntityRef>) {}
1630 super::assert_system_does_not_conflict(system);
1631 }
1632
1633 #[test]
1634 #[should_panic(
1635 expected = "DeferredWorld in system bevy_ecs::system::tests::assert_deferred_world_and_entity_ref_system_does_conflict_second::system conflicts with a previous access."
1636 )]
1637 fn assert_deferred_world_and_entity_ref_system_does_conflict_second() {
1638 fn system(_query: Query<EntityRef>, _world: DeferredWorld) {}
1639 super::assert_system_does_not_conflict(system);
1640 }
1641
1642 #[test]
1643 fn assert_deferred_world_and_empty_query_does_not_conflict_first() {
1644 fn system(_world: DeferredWorld, _query: Query<Entity>) {}
1645 super::assert_system_does_not_conflict(system);
1646 }
1647
1648 #[test]
1649 fn assert_deferred_world_and_empty_query_does_not_conflict_second() {
1650 fn system(_query: Query<Entity>, _world: DeferredWorld) {}
1651 super::assert_system_does_not_conflict(system);
1652 }
1653
1654 #[test]
1655 #[should_panic]
1656 fn panic_inside_system() {
1657 let mut world = World::new();
1658 let system: fn() = || {
1659 panic!("this system panics");
1660 };
1661 run_system(&mut world, system);
1662 }
1663
1664 #[test]
1665 fn assert_systems() {
1666 use core::str::FromStr;
1667
1668 use crate::{prelude::*, system::assert_is_system};
1669
1670 fn returning<T>() -> T {
1672 unimplemented!()
1673 }
1674
1675 fn exclusive_in_out<A, B>(_: In<A>, _: &mut World) -> B {
1677 unimplemented!()
1678 }
1679
1680 fn static_system_param(_: StaticSystemParam<Query<'static, 'static, &W<u32>>>) {
1681 unimplemented!()
1682 }
1683
1684 fn exclusive_with_state(
1685 _: &mut World,
1686 _: Local<bool>,
1687 _: (&mut QueryState<&W<i32>>, &mut SystemState<Query<&W<u32>>>),
1688 _: (),
1689 ) {
1690 unimplemented!()
1691 }
1692
1693 fn not(In(val): In<bool>) -> bool {
1694 !val
1695 }
1696
1697 assert_is_system(returning::<Result<u32, std::io::Error>>.map(Result::unwrap));
1698 assert_is_system(returning::<Option<()>>.map(drop));
1699 assert_is_system(returning::<&str>.map(u64::from_str).map(Result::unwrap));
1700 assert_is_system(static_system_param);
1701 assert_is_system(
1702 exclusive_in_out::<(), Result<(), std::io::Error>>.map(|_out| {
1703 #[cfg(feature = "trace")]
1704 if let Err(error) = _out {
1705 tracing::error!("{}", error);
1706 }
1707 }),
1708 );
1709 assert_is_system(exclusive_with_state);
1710 assert_is_system(returning::<bool>.pipe(exclusive_in_out::<bool, ()>));
1711
1712 returning::<()>.run_if(returning::<bool>.pipe(not));
1713 }
1714
1715 #[test]
1716 fn pipe_change_detection() {
1717 #[derive(Resource, Default)]
1718 struct Flag;
1719
1720 #[derive(Default)]
1721 struct Info {
1722 do_first: bool,
1724 do_second: bool,
1725
1726 first_flag: bool,
1728 second_flag: bool,
1729 }
1730
1731 fn first(In(mut info): In<Info>, mut flag: ResMut<Flag>) -> Info {
1732 if flag.is_changed() {
1733 info.first_flag = true;
1734 }
1735 if info.do_first {
1736 *flag = Flag;
1737 }
1738
1739 info
1740 }
1741
1742 fn second(In(mut info): In<Info>, mut flag: ResMut<Flag>) -> Info {
1743 if flag.is_changed() {
1744 info.second_flag = true;
1745 }
1746 if info.do_second {
1747 *flag = Flag;
1748 }
1749
1750 info
1751 }
1752
1753 let mut world = World::new();
1754 world.init_resource::<Flag>();
1755 let mut sys = IntoSystem::into_system(first.pipe(second));
1756 sys.initialize(&mut world);
1757
1758 sys.run(default(), &mut world);
1759
1760 let info = sys.run(
1762 Info {
1763 do_first: true,
1764 ..default()
1765 },
1766 &mut world,
1767 );
1768 assert!(!info.first_flag);
1769 assert!(info.second_flag);
1770
1771 let info1 = sys.run(
1774 Info {
1775 do_second: true,
1776 ..default()
1777 },
1778 &mut world,
1779 );
1780 let info2 = sys.run(default(), &mut world);
1781 assert!(!info1.first_flag);
1782 assert!(!info1.second_flag);
1783 assert!(info2.first_flag);
1784 assert!(!info2.second_flag);
1785 }
1786
1787 #[test]
1788 fn test_combinator_clone() {
1789 let mut world = World::new();
1790 #[derive(Resource)]
1791 struct A;
1792 #[derive(Resource)]
1793 struct B;
1794 #[derive(Resource, PartialEq, Eq, Debug)]
1795 struct C(i32);
1796
1797 world.insert_resource(A);
1798 world.insert_resource(C(0));
1799 let mut sched = Schedule::default();
1800 sched.add_systems(
1801 (
1802 |mut res: ResMut<C>| {
1803 res.0 += 1;
1804 },
1805 |mut res: ResMut<C>| {
1806 res.0 += 2;
1807 },
1808 )
1809 .distributive_run_if(resource_exists::<A>.or(resource_exists::<B>)),
1810 );
1811 sched.initialize(&mut world).unwrap();
1812 sched.run(&mut world);
1813 assert_eq!(world.get_resource(), Some(&C(3)));
1814 }
1815
1816 #[test]
1817 #[should_panic]
1818 fn simple_fallible_system() {
1819 fn sys() -> Result {
1820 Err("error")?;
1821 Ok(())
1822 }
1823
1824 let mut world = World::new();
1825 run_system(&mut world, sys);
1826 }
1827
1828 #[test]
1842 fn nondiverging_never_trait_impls() {
1843 let mut world = World::new();
1848 let mut schedule = Schedule::default();
1849
1850 fn sys(_query: Query<&Name>) {
1851 todo!()
1852 }
1853
1854 schedule.add_systems(sys);
1855 schedule.add_systems(|_query: Query<&Name>| {});
1856 schedule.add_systems(|_query: Query<&Name>| todo!());
1857 #[expect(clippy::unused_unit, reason = "this forces the () return type")]
1858 schedule.add_systems(|_query: Query<&Name>| -> () { todo!() });
1859
1860 fn obs(_trigger: Trigger<OnAdd, Name>) {
1861 todo!()
1862 }
1863
1864 world.add_observer(obs);
1865 world.add_observer(|_trigger: Trigger<OnAdd, Name>| {});
1866 world.add_observer(|_trigger: Trigger<OnAdd, Name>| todo!());
1867 #[expect(clippy::unused_unit, reason = "this forces the () return type")]
1868 world.add_observer(|_trigger: Trigger<OnAdd, Name>| -> () { todo!() });
1869
1870 fn my_command(_world: &mut World) {
1871 todo!()
1872 }
1873
1874 world.commands().queue(my_command);
1875 world.commands().queue(|_world: &mut World| {});
1876 world.commands().queue(|_world: &mut World| todo!());
1877 #[expect(clippy::unused_unit, reason = "this forces the () return type")]
1878 world
1879 .commands()
1880 .queue(|_world: &mut World| -> () { todo!() });
1881 }
1882}