1#[cfg(feature = "std")]
2mod multi_threaded;
3mod single_threaded;
4
5use alloc::{boxed::Box, vec, vec::Vec};
6use bevy_utils::prelude::DebugName;
7use core::any::TypeId;
8
9pub use self::single_threaded::SingleThreadedExecutor;
10
11#[cfg(feature = "std")]
12pub use self::multi_threaded::{MainThreadExecutor, MultiThreadedExecutor};
13
14pub use fixedbitset::FixedBitSet;
15
16use crate::{
17 change_detection::{CheckChangeTicks, Tick},
18 error::{BevyError, ErrorContext, Result},
19 prelude::{IntoSystemSet, SystemSet},
20 schedule::{
21 ConditionWithAccess, InternedSystemSet, SystemKey, SystemSetKey, SystemTypeSet,
22 SystemWithAccess,
23 },
24 system::{RunSystemError, System, SystemAccess, SystemIn, SystemStateFlags},
25 world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World},
26};
27
28pub trait SystemExecutor: Send + Sync {
30 fn init(&mut self, schedule: &SystemSchedule);
32 fn run(
34 &mut self,
35 schedule: &mut SystemSchedule,
36 world: &mut World,
37 skip_systems: Option<&FixedBitSet>,
38 error_handler: fn(BevyError, ErrorContext),
39 );
40 fn set_apply_final_deferred(&mut self, value: bool);
42}
43
44pub fn default_executor() -> Box<dyn SystemExecutor> {
49 #[cfg(all(
50 not(target_arch = "wasm32"),
51 feature = "std",
52 feature = "multi_threaded"
53 ))]
54 {
55 Box::new(MultiThreadedExecutor::new())
56 }
57 #[cfg(any(
58 target_arch = "wasm32",
59 not(feature = "std"),
60 not(feature = "multi_threaded")
61 ))]
62 {
63 Box::new(SingleThreadedExecutor::new())
64 }
65}
66
67#[derive(Default)]
73pub struct SystemSchedule {
74 pub(super) system_ids: Vec<SystemKey>,
76 pub(super) systems: Vec<SystemWithAccess>,
78 pub(super) system_conditions: Vec<Vec<ConditionWithAccess>>,
80 #[cfg_attr(
83 not(feature = "std"),
84 expect(dead_code, reason = "currently only used with the std feature")
85 )]
86 pub(super) system_dependencies: Vec<usize>,
87 #[cfg_attr(
90 not(feature = "std"),
91 expect(dead_code, reason = "currently only used with the std feature")
92 )]
93 pub(super) system_dependents: Vec<Vec<usize>>,
94 pub(super) sets_with_conditions_of_systems: Vec<FixedBitSet>,
97 pub(super) set_ids: Vec<SystemSetKey>,
99 pub(super) set_conditions: Vec<Vec<ConditionWithAccess>>,
101 pub(super) systems_in_sets_with_conditions: Vec<FixedBitSet>,
106}
107
108impl SystemSchedule {
109 pub const fn new() -> Self {
111 Self {
112 systems: Vec::new(),
113 system_conditions: Vec::new(),
114 set_conditions: Vec::new(),
115 system_ids: Vec::new(),
116 set_ids: Vec::new(),
117 system_dependencies: Vec::new(),
118 system_dependents: Vec::new(),
119 sets_with_conditions_of_systems: Vec::new(),
120 systems_in_sets_with_conditions: Vec::new(),
121 }
122 }
123
124 pub unsafe fn systems_mut(&mut self) -> &mut Vec<SystemWithAccess> {
130 &mut self.systems
131 }
132}
133
134#[doc(alias = "apply_system_buffers")]
159pub struct ApplyDeferred;
160
161pub(super) fn is_apply_deferred(system: &dyn System<In = (), Out = ()>) -> bool {
163 system.system_type() == TypeId::of::<ApplyDeferred>()
164}
165
166impl System for ApplyDeferred {
167 type In = ();
168 type Out = ();
169
170 fn name(&self) -> DebugName {
171 DebugName::borrowed("bevy_ecs::apply_deferred")
172 }
173
174 fn flags(&self) -> SystemStateFlags {
175 SystemStateFlags::NON_SEND
177 }
178
179 unsafe fn run_unsafe(
180 &mut self,
181 _input: SystemIn<'_, Self>,
182 _world: UnsafeWorldCell,
183 ) -> Result<Self::Out, RunSystemError> {
184 Ok(())
187 }
188
189 #[cfg(feature = "hotpatching")]
190 #[inline]
191 fn refresh_hotpatch(&mut self) {}
192
193 fn run(
194 &mut self,
195 _input: SystemIn<'_, Self>,
196 _world: &mut World,
197 ) -> Result<Self::Out, RunSystemError> {
198 Ok(())
201 }
202
203 fn apply_deferred(&mut self, _world: &mut World) {}
204
205 fn queue_deferred(&mut self, _world: DeferredWorld) {}
206
207 fn initialize(&mut self, _world: &mut World) -> SystemAccess {
208 SystemAccess::Exclusive
211 }
212
213 fn check_change_tick(&mut self, _check: CheckChangeTicks) {}
214
215 fn default_system_sets(&self) -> Vec<InternedSystemSet> {
216 vec![SystemTypeSet::<Self>::new().intern()]
217 }
218
219 fn get_last_run(&self) -> Tick {
220 Tick::MAX
222 }
223
224 fn set_last_run(&mut self, _last_run: Tick) {}
225}
226
227impl IntoSystemSet<()> for ApplyDeferred {
228 type Set = SystemTypeSet<Self>;
229
230 fn into_system_set(self) -> Self::Set {
231 SystemTypeSet::<Self>::new()
232 }
233}
234
235mod __rust_begin_short_backtrace {
244 use core::hint::black_box;
245
246 #[cfg(feature = "std")]
247 use crate::world::unsafe_world_cell::UnsafeWorldCell;
248 use crate::{
249 error::Result,
250 system::{ReadOnlySystem, RunSystemError, ScheduleSystem},
251 world::World,
252 };
253
254 #[cfg(feature = "std")]
258 #[inline(never)]
259 pub(super) unsafe fn run_unsafe(
260 system: &mut ScheduleSystem,
261 world: UnsafeWorldCell,
262 ) -> Result<(), RunSystemError> {
263 let result = unsafe { system.run_unsafe((), world) };
265 black_box(());
267 result
268 }
269
270 #[cfg(feature = "std")]
274 #[inline(never)]
275 pub(super) unsafe fn readonly_run_unsafe<O: 'static>(
276 system: &mut dyn ReadOnlySystem<In = (), Out = O>,
277 world: UnsafeWorldCell,
278 ) -> Result<O, RunSystemError> {
279 black_box(unsafe { system.run_unsafe((), world) })
282 }
283
284 #[cfg(feature = "std")]
285 #[inline(never)]
286 pub(super) fn run(
287 system: &mut ScheduleSystem,
288 world: &mut World,
289 ) -> Result<(), RunSystemError> {
290 let result = system.run((), world);
291 black_box(());
293 result
294 }
295
296 #[inline(never)]
297 pub(super) fn run_without_applying_deferred(
298 system: &mut ScheduleSystem,
299 world: &mut World,
300 ) -> Result<(), RunSystemError> {
301 let result = system.run_without_applying_deferred((), world);
302 black_box(());
304 result
305 }
306
307 #[inline(never)]
308 pub(super) fn readonly_run<O: 'static>(
309 system: &mut dyn ReadOnlySystem<In = (), Out = O>,
310 world: &mut World,
311 ) -> Result<O, RunSystemError> {
312 black_box(system.run((), world))
314 }
315
316 #[inline(never)]
317 #[cfg(feature = "std")]
318 pub(super) fn error_handler(
319 error_handler: crate::error::ErrorHandler,
320 err: crate::error::BevyError,
321 err_context: crate::error::ErrorContext,
322 ) {
323 error_handler(err, err_context);
324 black_box(());
325 }
326}
327
328#[cfg(test)]
329mod tests {
330 use crate::{
331 prelude::{Component, In, IntoSystem, Resource, Schedule},
332 schedule::{MultiThreadedExecutor, SingleThreadedExecutor},
333 system::{Populated, Res, ResMut, Single},
334 world::World,
335 };
336
337 #[derive(Component)]
338 struct TestComponent;
339
340 #[derive(Resource, Default)]
341 struct TestState {
342 populated_ran: bool,
343 single_ran: bool,
344 }
345
346 #[derive(Resource, Default)]
347 struct Counter(u8);
348
349 fn set_single_state(mut _single: Single<&TestComponent>, mut state: ResMut<TestState>) {
350 state.single_ran = true;
351 }
352
353 fn set_populated_state(
354 mut _populated: Populated<&TestComponent>,
355 mut state: ResMut<TestState>,
356 ) {
357 state.populated_ran = true;
358 }
359
360 #[test]
361 fn single_and_populated_skipped_and_run_singlethreaded() {
362 let mut schedule = Schedule::default();
363 schedule.set_executor(SingleThreadedExecutor::new());
364 single_and_populated_skipped_and_run("SingleThreaded", schedule);
365 }
366
367 #[test]
368 fn single_and_populated_skipped_and_run_multithreaded() {
369 let mut schedule = Schedule::default();
370 schedule.set_executor(MultiThreadedExecutor::new());
371 single_and_populated_skipped_and_run("MultiThreaded", schedule);
372 }
373
374 #[expect(clippy::print_stdout, reason = "std and println are allowed in tests")]
375 fn single_and_populated_skipped_and_run(name: &str, mut schedule: Schedule) {
376 std::println!("Testing executor: {name}");
377
378 let mut world = World::new();
379 world.init_resource::<TestState>();
380
381 schedule.add_systems((set_single_state, set_populated_state));
382 schedule.run(&mut world);
383
384 let state = world.get_resource::<TestState>().unwrap();
385 assert!(!state.single_ran);
386 assert!(!state.populated_ran);
387
388 world.spawn(TestComponent);
389
390 schedule.run(&mut world);
391 let state = world.get_resource::<TestState>().unwrap();
392 assert!(state.single_ran);
393 assert!(state.populated_ran);
394 }
395
396 fn look_for_missing_resource(_res: Res<TestState>) {}
397
398 #[test]
399 #[should_panic]
400 fn missing_resource_panics_single_threaded() {
401 let mut world = World::new();
402 let mut schedule = Schedule::default();
403
404 schedule.set_executor(SingleThreadedExecutor::new());
405 schedule.add_systems(look_for_missing_resource);
406 schedule.run(&mut world);
407 }
408
409 #[test]
410 #[should_panic]
411 fn missing_resource_panics_multi_threaded() {
412 let mut world = World::new();
413 let mut schedule = Schedule::default();
414
415 schedule.set_executor(MultiThreadedExecutor::new());
416 schedule.add_systems(look_for_missing_resource);
417 schedule.run(&mut world);
418 }
419
420 #[test]
421 fn piped_systems_first_system_skipped() {
422 fn pipe_out(_single: Single<&TestComponent>) -> u8 {
424 42
425 }
426
427 fn pipe_in(_input: In<u8>, mut counter: ResMut<Counter>) {
428 counter.0 += 1;
429 }
430
431 let mut world = World::new();
432 world.init_resource::<Counter>();
433 let mut schedule = Schedule::default();
434
435 schedule.add_systems(pipe_out.pipe(pipe_in));
436 schedule.run(&mut world);
437
438 let counter = world.resource::<Counter>();
439 assert_eq!(counter.0, 0);
440 }
441
442 #[test]
443 fn piped_system_second_system_skipped() {
444 fn pipe_out(mut counter: ResMut<Counter>) -> u8 {
446 counter.0 += 1;
447 42
448 }
449
450 fn pipe_in(_input: In<u8>, _single: Single<&TestComponent>, mut counter: ResMut<Counter>) {
452 counter.0 += 1;
453 }
454
455 let mut world = World::new();
456 world.init_resource::<Counter>();
457 let mut schedule = Schedule::default();
458
459 schedule.add_systems(pipe_out.pipe(pipe_in));
460 schedule.run(&mut world);
461 let counter = world.resource::<Counter>();
462 assert_eq!(counter.0, 1);
463 }
464
465 #[test]
466 #[should_panic]
467 fn piped_system_first_system_panics() {
468 fn pipe_out(_res: Res<TestState>) -> u8 {
470 42
471 }
472
473 fn pipe_in(_input: In<u8>) {}
474
475 let mut world = World::new();
476 let mut schedule = Schedule::default();
477
478 schedule.add_systems(pipe_out.pipe(pipe_in));
479 schedule.run(&mut world);
480 }
481
482 #[test]
483 #[should_panic]
484 fn piped_system_second_system_panics() {
485 fn pipe_out() -> u8 {
486 42
487 }
488
489 fn pipe_in(_input: In<u8>, _res: Res<TestState>) {}
491
492 let mut world = World::new();
493 let mut schedule = Schedule::default();
494
495 schedule.add_systems(pipe_out.pipe(pipe_in));
496 schedule.run(&mut world);
497 }
498
499 #[test]
502 fn piped_system_skip_and_panic() {
503 fn pipe_out(_single: Single<&TestComponent>) -> u8 {
505 42
506 }
507
508 fn pipe_in(_input: In<u8>, _res: Res<TestState>) {}
510
511 let mut world = World::new();
512 let mut schedule = Schedule::default();
513
514 schedule.add_systems(pipe_out.pipe(pipe_in));
515 schedule.run(&mut world);
516 }
517
518 #[test]
519 #[should_panic]
520 fn piped_system_panic_and_skip() {
521 fn pipe_out(_res: Res<TestState>) -> u8 {
524 42
525 }
526
527 fn pipe_in(_input: In<u8>, _single: Single<&TestComponent>) {}
529
530 let mut world = World::new();
531 let mut schedule = Schedule::default();
532
533 schedule.add_systems(pipe_out.pipe(pipe_in));
534 schedule.run(&mut world);
535 }
536
537 #[test]
538 #[should_panic]
539 fn piped_system_panic_and_panic() {
540 fn pipe_out(_res: Res<TestState>) -> u8 {
543 42
544 }
545
546 fn pipe_in(_input: In<u8>, _res: Res<TestState>) {}
548
549 let mut world = World::new();
550 let mut schedule = Schedule::default();
551
552 schedule.add_systems(pipe_out.pipe(pipe_in));
553 schedule.run(&mut world);
554 }
555
556 #[test]
557 fn piped_system_skip_and_skip() {
558 fn pipe_out(_single: Single<&TestComponent>, mut counter: ResMut<Counter>) -> u8 {
561 counter.0 += 1;
562 42
563 }
564
565 fn pipe_in(_input: In<u8>, _single: Single<&TestComponent>, mut counter: ResMut<Counter>) {
567 counter.0 += 1;
568 }
569
570 let mut world = World::new();
571 world.init_resource::<Counter>();
572 let mut schedule = Schedule::default();
573
574 schedule.add_systems(pipe_out.pipe(pipe_in));
575 schedule.run(&mut world);
576
577 let counter = world.resource::<Counter>();
578 assert_eq!(counter.0, 0);
579 }
580}
581
582#[cfg(test)]
583mod validation_tests {
584 use crate::{
585 prelude::{Component, In, IntoSystem, Resource, Schedule},
586 schedule::{MultiThreadedExecutor, SingleThreadedExecutor},
587 system::{
588 DynParamBuilder, DynSystemParam, Local, ParamBuilder, ParamSet, Query, Res, ResMut,
589 RunSystemError, RunSystemOnce, Single, SystemAccess, SystemMeta, SystemParam,
590 SystemParamBuilder, SystemParamValidationError,
591 },
592 world::World,
593 };
594
595 #[derive(Component)]
596 struct TestComponent;
597
598 #[derive(Resource, Default)]
599 struct Counter(u8);
600
601 #[derive(Resource)]
603 struct MissingResource;
604
605 struct AlwaysInvalid;
607
608 unsafe impl SystemParam for AlwaysInvalid {
610 type State = ();
611 type Item<'world, 'state> = AlwaysInvalid;
612
613 fn init_state(_world: &mut World) -> Self::State {}
614
615 fn init_access(
616 _state: &Self::State,
617 _system_meta: &mut SystemMeta,
618 _system_access: &mut SystemAccess,
619 _world: &mut World,
620 ) {
621 }
622
623 unsafe fn get_param<'world, 'state>(
624 _state: &'state mut Self::State,
625 _system_meta: &SystemMeta,
626 _world: crate::world::unsafe_world_cell::UnsafeWorldCell<'world>,
627 _change_tick: crate::change_detection::Tick,
628 ) -> Result<Self::Item<'world, 'state>, SystemParamValidationError> {
629 Err(SystemParamValidationError::invalid::<Self>(
630 "always invalid",
631 ))
632 }
633 }
634
635 #[test]
636 fn function_system_validation_failure_is_error() {
637 fn system(_res: Res<MissingResource>) {}
638
639 let mut world = World::new();
640 let result = world.run_system_once(system);
641 assert!(
642 matches!(result, Err(RunSystemError::Failed(_))),
643 "Expected Failed, got {result:?}"
644 );
645 }
646
647 #[test]
648 fn function_system_validation_skip() {
649 fn system(_single: Single<&TestComponent>) {}
650
651 let mut world = World::new();
652 let result = world.run_system_once(system);
653 assert!(
654 matches!(result, Err(RunSystemError::Skipped(_))),
655 "Expected Skipped, got {result:?}"
656 );
657 }
658
659 #[test]
660 fn function_system_validation_success() {
661 fn system(_res: Res<Counter>) {}
662
663 let mut world = World::new();
664 world.init_resource::<Counter>();
665 let result = world.run_system_once(system);
666 assert!(result.is_ok(), "Expected Ok, got {result:?}");
667 }
668
669 #[test]
670 fn adapter_system_validation_failure() {
671 fn system(_res: Res<MissingResource>) -> u32 {
672 42
673 }
674
675 let mut world = World::new();
676 let result = world.run_system_once(system.map(|_x| {}));
677 assert!(
678 matches!(result, Err(RunSystemError::Failed(_))),
679 "Expected Failed from adapter system, got {result:?}"
680 );
681 }
682
683 #[test]
684 fn adapter_system_validation_skip() {
685 fn system(_single: Single<&TestComponent>) -> u32 {
686 42
687 }
688
689 let mut world = World::new();
690 let result = world.run_system_once(system.map(|_x| {}));
691 assert!(
692 matches!(result, Err(RunSystemError::Skipped(_))),
693 "Expected Skipped from adapter system, got {result:?}"
694 );
695 }
696
697 #[test]
698 fn pipe_system_validation_failure_in_first() {
699 fn first(_res: Res<MissingResource>) -> u32 {
700 42
701 }
702 fn second(_input: In<u32>) {}
703
704 let mut world = World::new();
705 let result = world.run_system_once(first.pipe(second));
706 assert!(
707 matches!(result, Err(RunSystemError::Failed(_))),
708 "Expected Failed from pipe first system, got {result:?}"
709 );
710 }
711
712 #[test]
713 fn pipe_system_validation_failure_in_second() {
714 fn first() -> u32 {
715 42
716 }
717 fn second(_input: In<u32>, _res: Res<MissingResource>) {}
718
719 let mut world = World::new();
720 let result = world.run_system_once(first.pipe(second));
721 assert!(
722 matches!(result, Err(RunSystemError::Failed(_))),
723 "Expected Failed from pipe second system, got {result:?}"
724 );
725 }
726
727 #[test]
728 fn pipe_system_validation_skip_in_first() {
729 fn first(_single: Single<&TestComponent>) -> u32 {
730 42
731 }
732 fn second(_input: In<u32>) {}
733
734 let mut world = World::new();
735 let result = world.run_system_once(first.pipe(second));
736 assert!(
737 matches!(result, Err(RunSystemError::Skipped(_))),
738 "Expected Skipped from pipe first system, got {result:?}"
739 );
740 }
741
742 #[test]
743 fn pipe_system_validation_skip_in_second() {
744 fn first() -> u32 {
745 42
746 }
747
748 fn second(_input: In<u32>, _single: Single<&TestComponent>) {}
749
750 let mut world = World::new();
751 let result = world.run_system_once(first.pipe(second));
752 assert!(
753 matches!(result, Err(RunSystemError::Skipped(_))),
754 "Expected Skipped from pipe second system, got {result:?}"
755 );
756 }
757
758 #[test]
759 fn builder_system_validation_failure() {
760 fn system(_res: Res<MissingResource>) {}
761
762 let mut world = World::new();
763 let result = world.run_system_once(ParamBuilder.build_system(system));
764 assert!(
765 matches!(result, Err(RunSystemError::Failed(_))),
766 "Expected Failed from builder system, got {result:?}"
767 );
768 }
769
770 #[test]
771 fn builder_system_validation_skip() {
772 fn system(_single: Single<&TestComponent>) {}
773
774 let mut world = World::new();
775 let result = world.run_system_once(ParamBuilder.build_system(system));
776 assert!(
777 matches!(result, Err(RunSystemError::Skipped(_))),
778 "Expected Skipped from builder system, got {result:?}"
779 );
780 }
781
782 #[test]
783 fn dyn_system_param_validation_failure() {
784 let mut world = World::new();
785 let system = (DynParamBuilder::new::<Res<MissingResource>>(ParamBuilder),)
786 .build_state(&mut world)
787 .build_system(|_param: DynSystemParam| {});
788 let result = world.run_system_once(system);
789 assert!(
790 matches!(result, Err(RunSystemError::Failed(_))),
791 "Expected Failed from DynSystemParam system, got {result:?}"
792 );
793 }
794
795 #[test]
796 fn dyn_system_param_validation_skip() {
797 let mut world = World::new();
798 let system = (DynParamBuilder::new::<Single<&TestComponent>>(ParamBuilder),)
799 .build_state(&mut world)
800 .build_system(|_param: DynSystemParam| {});
801 let result = world.run_system_once(system);
802 assert!(
803 matches!(result, Err(RunSystemError::Skipped(_))),
804 "Expected Skipped from DynSystemParam system, got {result:?}"
805 );
806 }
807
808 #[test]
809 fn dyn_system_param_validation_success() {
810 let mut world = World::new();
811 world.init_resource::<Counter>();
812 let system = (DynParamBuilder::new::<Res<Counter>>(ParamBuilder),)
813 .build_state(&mut world)
814 .build_system(|_param: DynSystemParam| {});
815 let result = world.run_system_once(system);
816 assert!(
817 result.is_ok(),
818 "Expected Ok from DynSystemParam system, got {result:?}"
819 );
820 }
821
822 #[test]
823 fn exclusive_system_validation_failure() {
824 fn system(_world: &mut World, _param: AlwaysInvalid) {}
825
826 let mut world = World::new();
827 let result = world.run_system_once(system);
828 assert!(
829 matches!(result, Err(RunSystemError::Failed(_))),
830 "Expected Failed from exclusive system, got {result:?}"
831 );
832 }
833
834 #[test]
835 fn exclusive_system_validation_success() {
836 fn system(_world: &mut World, mut _local: Local<u32>) {}
837
838 let mut world = World::new();
839 let result = world.run_system_once(system);
840 assert!(
841 result.is_ok(),
842 "Expected Ok from exclusive system, got {result:?}"
843 );
844 }
845
846 #[test]
847 fn validation_skips_system_in_schedule_singlethreaded() {
848 let mut schedule = Schedule::default();
849 schedule.set_executor(SingleThreadedExecutor::new());
850 validation_skips_system_in_schedule("SingleThreaded", schedule);
851 }
852
853 #[test]
854 fn validation_skips_system_in_schedule_multithreaded() {
855 let mut schedule = Schedule::default();
856 schedule.set_executor(MultiThreadedExecutor::new());
857 validation_skips_system_in_schedule("MultiThreaded", schedule);
858 }
859
860 fn validation_skips_system_in_schedule(name: &str, mut schedule: Schedule) {
861 fn skippable_system(_single: Single<&TestComponent>, mut counter: ResMut<Counter>) {
864 counter.0 += 1;
865 }
866
867 let mut world = World::new();
868 world.init_resource::<Counter>();
869
870 schedule.add_systems(skippable_system);
871
872 schedule.run(&mut world);
874 assert_eq!(
875 world.resource::<Counter>().0,
876 0,
877 "System should have been skipped with {name}"
878 );
879 }
880
881 #[test]
882 fn param_set_validation_skip() {
883 fn system(mut _set: ParamSet<(Single<&TestComponent>,)>) {}
886
887 let mut world = World::new();
888 let result = world.run_system_once(system);
889 assert!(
890 matches!(result, Err(RunSystemError::Skipped(_))),
891 "Expected Skipped from ParamSet with invalid Single, got {result:?}"
892 );
893 }
894
895 #[test]
896 fn param_set_validation_failure() {
897 fn system(mut _set: ParamSet<(Query<&TestComponent>, Res<MissingResource>)>) {}
900
901 let mut world = World::new();
902 let result = world.run_system_once(system);
903 assert!(
904 matches!(result, Err(RunSystemError::Failed(_))),
905 "Expected Failed from ParamSet with missing resource, got {result:?}"
906 );
907 }
908
909 #[test]
910 fn param_set_validation_success() {
911 fn system(mut set: ParamSet<(Query<&TestComponent>, Res<Counter>)>) {
913 let _q = set.p0();
914 }
915
916 let mut world = World::new();
917 world.init_resource::<Counter>();
918 let result = world.run_system_once(system);
919 assert!(
920 result.is_ok(),
921 "Expected Ok from ParamSet with valid params, got {result:?}"
922 );
923 }
924}