1mod auto_insert_apply_deferred;
4mod condition;
5mod config;
6mod error;
7mod executor;
8mod node;
9mod pass;
10mod schedule;
11mod set;
12mod stepping;
13
14pub use self::graph::GraphInfo;
15pub use self::{condition::*, config::*, error::*, executor::*, node::*, schedule::*, set::*};
16pub use pass::{FlattenedDependencies, ScheduleBuildPass};
17
18pub mod graph;
20
21pub mod passes {
23 pub use crate::schedule::auto_insert_apply_deferred::*;
24}
25
26use self::graph::*;
27
28#[cfg(test)]
29mod tests {
30 use super::*;
31 #[cfg(feature = "trace")]
32 use alloc::string::ToString;
33 use alloc::{vec, vec::Vec};
34 use core::sync::atomic::{AtomicU32, Ordering};
35
36 pub use crate::{
37 prelude::World,
38 resource::IsResource,
39 resource::Resource,
40 schedule::{Schedule, SystemSet},
41 system::{Res, ResMut},
42 };
43
44 #[derive(SystemSet, Clone, Debug, PartialEq, Eq, Hash)]
45 enum TestSystems {
46 A,
47 B,
48 C,
49 D,
50 X,
51 }
52
53 #[derive(Resource, Default)]
54 struct SystemOrder(Vec<u32>);
55
56 #[derive(Resource, Default)]
57 struct RunConditionBool(bool);
58
59 #[derive(Resource, Default)]
60 struct Counter(AtomicU32);
61
62 fn make_exclusive_system(tag: u32) -> impl FnMut(&mut World) {
63 move |world| world.resource_mut::<SystemOrder>().0.push(tag)
64 }
65
66 fn make_function_system(tag: u32) -> impl FnMut(ResMut<SystemOrder>) {
67 move |mut resource: ResMut<SystemOrder>| resource.0.push(tag)
68 }
69
70 fn named_system(mut resource: ResMut<SystemOrder>) {
71 resource.0.push(u32::MAX);
72 }
73
74 fn named_exclusive_system(world: &mut World) {
75 world.resource_mut::<SystemOrder>().0.push(u32::MAX);
76 }
77
78 fn counting_system(counter: Res<Counter>) {
79 counter.0.fetch_add(1, Ordering::Relaxed);
80 }
81
82 mod system_execution {
83 use super::*;
84
85 #[test]
86 fn run_system() {
87 let mut world = World::default();
88 let mut schedule = Schedule::default();
89
90 world.init_resource::<SystemOrder>();
91
92 schedule.add_systems(make_function_system(0));
93 schedule.run(&mut world);
94
95 assert_eq!(world.resource::<SystemOrder>().0, vec![0]);
96 }
97
98 #[test]
99 fn run_exclusive_system() {
100 let mut world = World::default();
101 let mut schedule = Schedule::default();
102
103 world.init_resource::<SystemOrder>();
104
105 schedule.add_systems(make_exclusive_system(0));
106 schedule.run(&mut world);
107
108 assert_eq!(world.resource::<SystemOrder>().0, vec![0]);
109 }
110
111 #[test]
112 #[cfg(not(miri))]
113 fn parallel_execution() {
114 use alloc::sync::Arc;
115 use bevy_tasks::{ComputeTaskPool, TaskPool};
116 use std::sync::Barrier;
117
118 let mut world = World::default();
119 let mut schedule = Schedule::default();
120 let thread_count = ComputeTaskPool::get_or_init(TaskPool::default).thread_num();
121
122 let barrier = Arc::new(Barrier::new(thread_count));
123
124 for _ in 0..thread_count {
125 let inner = barrier.clone();
126 schedule.add_systems(move || {
127 inner.wait();
128 });
129 }
130
131 schedule.run(&mut world);
132 }
133 }
134
135 mod system_ordering {
136 use super::*;
137
138 #[test]
139 fn order_systems() {
140 let mut world = World::default();
141 let mut schedule = Schedule::default();
142
143 world.init_resource::<SystemOrder>();
144
145 schedule.add_systems((
146 named_system,
147 make_function_system(1).before(named_system),
148 make_function_system(0)
149 .after(named_system)
150 .in_set(TestSystems::A),
151 ));
152 schedule.run(&mut world);
153
154 assert_eq!(world.resource::<SystemOrder>().0, vec![1, u32::MAX, 0]);
155
156 world.insert_resource(SystemOrder::default());
157
158 assert_eq!(world.resource::<SystemOrder>().0, vec![]);
159
160 schedule.configure_sets(TestSystems::A.after(named_system));
162 schedule.add_systems((
163 make_function_system(3)
164 .before(TestSystems::A)
165 .after(named_system),
166 make_function_system(4).after(TestSystems::A),
167 ));
168 schedule.run(&mut world);
169
170 assert_eq!(
171 world.resource::<SystemOrder>().0,
172 vec![1, u32::MAX, 3, 0, 4]
173 );
174 }
175
176 #[test]
177 fn order_exclusive_systems() {
178 let mut world = World::default();
179 let mut schedule = Schedule::default();
180
181 world.init_resource::<SystemOrder>();
182
183 schedule.add_systems((
184 named_exclusive_system,
185 make_exclusive_system(1).before(named_exclusive_system),
186 make_exclusive_system(0).after(named_exclusive_system),
187 ));
188 schedule.run(&mut world);
189
190 assert_eq!(world.resource::<SystemOrder>().0, vec![1, u32::MAX, 0]);
191 }
192
193 #[test]
194 fn add_systems_correct_order() {
195 let mut world = World::new();
196 let mut schedule = Schedule::default();
197
198 world.init_resource::<SystemOrder>();
199
200 schedule.add_systems(
201 (
202 make_function_system(0),
203 make_function_system(1),
204 make_exclusive_system(2),
205 make_function_system(3),
206 )
207 .chain(),
208 );
209
210 schedule.run(&mut world);
211 assert_eq!(world.resource::<SystemOrder>().0, vec![0, 1, 2, 3]);
212 }
213
214 #[test]
215 fn add_systems_correct_order_nested() {
216 let mut world = World::new();
217 let mut schedule = Schedule::default();
218
219 world.init_resource::<SystemOrder>();
220
221 schedule.add_systems(
222 (
223 (make_function_system(0), make_function_system(1)).chain(),
224 make_function_system(2),
225 (make_function_system(3), make_function_system(4)).chain(),
226 (
227 make_function_system(5),
228 (make_function_system(6), make_function_system(7)),
229 ),
230 (
231 (make_function_system(8), make_function_system(9)).chain(),
232 make_function_system(10),
233 ),
234 )
235 .chain(),
236 );
237
238 schedule.run(&mut world);
239 let order = &world.resource::<SystemOrder>().0;
240 assert_eq!(
241 &order[0..5],
242 &[0, 1, 2, 3, 4],
243 "first five items should be exactly ordered"
244 );
245 let unordered = &order[5..8];
246 assert!(
247 unordered.contains(&5) && unordered.contains(&6) && unordered.contains(&7),
248 "unordered must be 5, 6, and 7 in any order"
249 );
250 let partially_ordered = &order[8..11];
251 assert!(
252 partially_ordered == [8, 9, 10] || partially_ordered == [10, 8, 9],
253 "partially_ordered must be [8, 9, 10] or [10, 8, 9]"
254 );
255 assert_eq!(order.len(), 11, "must have exactly 11 order entries");
256 }
257 }
258
259 mod conditions {
260
261 use crate::{
262 change_detection::DetectChanges,
263 error::{ignore, FallbackErrorHandler, Result},
264 };
265
266 use super::*;
267
268 #[test]
269 fn system_with_condition_bool() {
270 let mut world = World::default();
271 let mut schedule = Schedule::default();
272
273 world.init_resource::<RunConditionBool>();
274 world.init_resource::<SystemOrder>();
275
276 schedule.add_systems(
277 make_function_system(0).run_if(|condition: Res<RunConditionBool>| condition.0),
278 );
279
280 schedule.run(&mut world);
281 assert_eq!(world.resource::<SystemOrder>().0, vec![]);
282
283 world.resource_mut::<RunConditionBool>().0 = true;
284 schedule.run(&mut world);
285 assert_eq!(world.resource::<SystemOrder>().0, vec![0]);
286 }
287
288 #[test]
289 fn system_with_condition_result_bool() {
290 let mut world = World::default();
291 world.insert_resource(FallbackErrorHandler(ignore));
292 let mut schedule = Schedule::default();
293
294 world.init_resource::<SystemOrder>();
295
296 schedule.add_systems((
297 make_function_system(0).run_if(|| -> Result<bool> { Err(core::fmt::Error.into()) }),
298 make_function_system(1).run_if(|| -> Result<bool> { Ok(false) }),
299 ));
300
301 schedule.run(&mut world);
302 assert_eq!(world.resource::<SystemOrder>().0, vec![]);
303
304 schedule.add_systems(make_function_system(2).run_if(|| -> Result<bool> { Ok(true) }));
305
306 schedule.run(&mut world);
307 assert_eq!(world.resource::<SystemOrder>().0, vec![2]);
308 }
309
310 #[test]
311 fn systems_with_distributive_condition() {
312 let mut world = World::default();
313 let mut schedule = Schedule::default();
314
315 world.insert_resource(RunConditionBool(true));
316 world.init_resource::<SystemOrder>();
317
318 fn change_condition(mut condition: ResMut<RunConditionBool>) {
319 condition.0 = false;
320 }
321
322 schedule.add_systems(
323 (
324 make_function_system(0),
325 change_condition,
326 make_function_system(1),
327 )
328 .chain()
329 .distributive_run_if(|condition: Res<RunConditionBool>| condition.0),
330 );
331
332 schedule.run(&mut world);
333 assert_eq!(world.resource::<SystemOrder>().0, vec![0]);
334 }
335
336 #[test]
337 fn run_exclusive_system_with_condition() {
338 let mut world = World::default();
339 let mut schedule = Schedule::default();
340
341 world.init_resource::<RunConditionBool>();
342 world.init_resource::<SystemOrder>();
343
344 schedule.add_systems(
345 make_exclusive_system(0).run_if(|condition: Res<RunConditionBool>| condition.0),
346 );
347
348 schedule.run(&mut world);
349 assert_eq!(world.resource::<SystemOrder>().0, vec![]);
350
351 world.resource_mut::<RunConditionBool>().0 = true;
352 schedule.run(&mut world);
353 assert_eq!(world.resource::<SystemOrder>().0, vec![0]);
354 }
355
356 #[test]
357 fn multiple_conditions_on_system() {
358 let mut world = World::default();
359 let mut schedule = Schedule::default();
360
361 world.init_resource::<Counter>();
362
363 schedule.add_systems((
364 counting_system.run_if(|| false).run_if(|| false),
365 counting_system.run_if(|| true).run_if(|| false),
366 counting_system.run_if(|| false).run_if(|| true),
367 counting_system.run_if(|| true).run_if(|| true),
368 ));
369
370 schedule.run(&mut world);
371 assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
372 }
373
374 #[test]
375 fn multiple_conditions_on_system_sets() {
376 let mut world = World::default();
377 let mut schedule = Schedule::default();
378
379 world.init_resource::<Counter>();
380
381 schedule.configure_sets(TestSystems::A.run_if(|| false).run_if(|| false));
382 schedule.add_systems(counting_system.in_set(TestSystems::A));
383 schedule.configure_sets(TestSystems::B.run_if(|| true).run_if(|| false));
384 schedule.add_systems(counting_system.in_set(TestSystems::B));
385 schedule.configure_sets(TestSystems::C.run_if(|| false).run_if(|| true));
386 schedule.add_systems(counting_system.in_set(TestSystems::C));
387 schedule.configure_sets(TestSystems::D.run_if(|| true).run_if(|| true));
388 schedule.add_systems(counting_system.in_set(TestSystems::D));
389
390 schedule.run(&mut world);
391 assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
392 }
393
394 #[test]
395 fn systems_nested_in_system_sets() {
396 let mut world = World::default();
397 let mut schedule = Schedule::default();
398
399 world.init_resource::<Counter>();
400
401 schedule.configure_sets(TestSystems::A.run_if(|| false));
402 schedule.add_systems(counting_system.in_set(TestSystems::A).run_if(|| false));
403 schedule.configure_sets(TestSystems::B.run_if(|| true));
404 schedule.add_systems(counting_system.in_set(TestSystems::B).run_if(|| false));
405 schedule.configure_sets(TestSystems::C.run_if(|| false));
406 schedule.add_systems(counting_system.in_set(TestSystems::C).run_if(|| true));
407 schedule.configure_sets(TestSystems::D.run_if(|| true));
408 schedule.add_systems(counting_system.in_set(TestSystems::D).run_if(|| true));
409
410 schedule.run(&mut world);
411 assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
412 }
413
414 #[test]
415 fn system_conditions_and_change_detection() {
416 #[derive(Resource, Default)]
417 struct Bool2(pub bool);
418
419 let mut world = World::default();
420 world.init_resource::<Counter>();
421 world.init_resource::<RunConditionBool>();
422 world.init_resource::<Bool2>();
423 let mut schedule = Schedule::default();
424
425 schedule.add_systems(
426 counting_system
427 .run_if(|res1: Res<RunConditionBool>| res1.is_changed())
428 .run_if(|res2: Res<Bool2>| res2.is_changed()),
429 );
430
431 schedule.run(&mut world);
433 assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
434
435 schedule.run(&mut world);
437 assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
438
439 world.get_resource_mut::<RunConditionBool>().unwrap().0 = false;
441 schedule.run(&mut world);
442 assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
443
444 world.get_resource_mut::<Bool2>().unwrap().0 = false;
447 schedule.run(&mut world);
448 assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
449
450 world.get_resource_mut::<RunConditionBool>().unwrap().0 = false;
452 schedule.run(&mut world);
453 assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
454
455 world.get_resource_mut::<Bool2>().unwrap().0 = false;
457 world.get_resource_mut::<RunConditionBool>().unwrap().0 = false;
458 schedule.run(&mut world);
459 assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 2);
460 }
461
462 #[test]
463 fn system_set_conditions_and_change_detection() {
464 #[derive(Resource, Default)]
465 struct Bool2(pub bool);
466
467 let mut world = World::default();
468 world.init_resource::<Counter>();
469 world.init_resource::<RunConditionBool>();
470 world.init_resource::<Bool2>();
471 let mut schedule = Schedule::default();
472
473 schedule.configure_sets(
474 TestSystems::A
475 .run_if(|res1: Res<RunConditionBool>| res1.is_changed())
476 .run_if(|res2: Res<Bool2>| res2.is_changed()),
477 );
478
479 schedule.add_systems(counting_system.in_set(TestSystems::A));
480
481 schedule.run(&mut world);
483 assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
484
485 schedule.run(&mut world);
487 assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
488
489 world.get_resource_mut::<RunConditionBool>().unwrap().0 = false;
491 schedule.run(&mut world);
492 assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
493
494 world.get_resource_mut::<Bool2>().unwrap().0 = false;
497 schedule.run(&mut world);
498 assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
499
500 world.get_resource_mut::<RunConditionBool>().unwrap().0 = false;
502 schedule.run(&mut world);
503 assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
504
505 world.get_resource_mut::<Bool2>().unwrap().0 = false;
507 world.get_resource_mut::<RunConditionBool>().unwrap().0 = false;
508 schedule.run(&mut world);
509 assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 2);
510 }
511
512 #[test]
513 fn mixed_conditions_and_change_detection() {
514 #[derive(Resource, Default)]
515 struct Bool2(pub bool);
516
517 let mut world = World::default();
518 world.init_resource::<Counter>();
519 world.init_resource::<RunConditionBool>();
520 world.init_resource::<Bool2>();
521 let mut schedule = Schedule::default();
522
523 schedule.configure_sets(
524 TestSystems::A.run_if(|res1: Res<RunConditionBool>| res1.is_changed()),
525 );
526
527 schedule.add_systems(
528 counting_system
529 .run_if(|res2: Res<Bool2>| res2.is_changed())
530 .in_set(TestSystems::A),
531 );
532
533 schedule.run(&mut world);
535 assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
536
537 schedule.run(&mut world);
539 assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
540
541 world.get_resource_mut::<RunConditionBool>().unwrap().0 = false;
543 schedule.run(&mut world);
544 assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
545
546 world.get_resource_mut::<Bool2>().unwrap().0 = false;
548 schedule.run(&mut world);
549 assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
550
551 world.get_resource_mut::<RunConditionBool>().unwrap().0 = false;
554 schedule.run(&mut world);
555 assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
556
557 world.get_resource_mut::<Bool2>().unwrap().0 = false;
559 world.get_resource_mut::<RunConditionBool>().unwrap().0 = false;
560 schedule.run(&mut world);
561 assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 2);
562 }
563 }
564
565 mod schedule_build_errors {
566 use super::*;
567
568 #[test]
569 fn dependency_loop() {
570 let mut schedule = Schedule::default();
571 schedule.configure_sets(TestSystems::X.after(TestSystems::X));
572 let mut world = World::new();
573 let result = schedule.initialize(&mut world);
574 assert!(matches!(
575 result,
576 Err(ScheduleBuildError::DependencySort(
577 DiGraphToposortError::Loop(_)
578 ))
579 ));
580 }
581
582 #[test]
583 fn dependency_loop_from_chain() {
584 let mut schedule = Schedule::default();
585 schedule.configure_sets((TestSystems::X, TestSystems::X).chain());
586 let mut world = World::new();
587 let result = schedule.initialize(&mut world);
588 assert!(matches!(
589 result,
590 Err(ScheduleBuildError::DependencySort(
591 DiGraphToposortError::Loop(_)
592 ))
593 ));
594 }
595
596 #[test]
597 fn dependency_cycle() {
598 let mut world = World::new();
599 let mut schedule = Schedule::default();
600
601 schedule.configure_sets(TestSystems::A.after(TestSystems::B));
602 schedule.configure_sets(TestSystems::B.after(TestSystems::A));
603
604 let result = schedule.initialize(&mut world);
605 assert!(matches!(
606 result,
607 Err(ScheduleBuildError::DependencySort(
608 DiGraphToposortError::Cycle(_)
609 ))
610 ));
611
612 fn foo() {}
613 fn bar() {}
614
615 let mut world = World::new();
616 let mut schedule = Schedule::default();
617
618 schedule.add_systems((foo.after(bar), bar.after(foo)));
619 let result = schedule.initialize(&mut world);
620 assert!(matches!(
621 result,
622 Err(ScheduleBuildError::FlatDependencySort(
623 DiGraphToposortError::Cycle(_)
624 ))
625 ));
626 }
627
628 #[test]
629 fn hierarchy_loop() {
630 let mut schedule = Schedule::default();
631 schedule.configure_sets(TestSystems::X.in_set(TestSystems::X));
632 let mut world = World::new();
633 let result = schedule.initialize(&mut world);
634 assert!(matches!(
635 result,
636 Err(ScheduleBuildError::HierarchySort(
637 DiGraphToposortError::Loop(_)
638 ))
639 ));
640 }
641
642 #[test]
643 fn hierarchy_cycle() {
644 let mut world = World::new();
645 let mut schedule = Schedule::default();
646
647 schedule.configure_sets(TestSystems::A.in_set(TestSystems::B));
648 schedule.configure_sets(TestSystems::B.in_set(TestSystems::A));
649
650 let result = schedule.initialize(&mut world);
651 assert!(matches!(
652 result,
653 Err(ScheduleBuildError::HierarchySort(
654 DiGraphToposortError::Cycle(_)
655 ))
656 ));
657 }
658
659 #[test]
660 fn system_type_set_ambiguity() {
661 fn foo() {}
663 fn bar() {}
664
665 let mut world = World::new();
666 let mut schedule = Schedule::default();
667
668 schedule.add_systems((foo, bar.after(foo)));
670
671 let result = schedule.initialize(&mut world);
673 assert!(result.is_ok());
674
675 schedule.add_systems(foo);
677
678 let result = schedule.initialize(&mut world);
681 assert!(matches!(
682 result,
683 Err(ScheduleBuildError::SystemTypeSetAmbiguity(_))
684 ));
685
686 let mut schedule = Schedule::default();
688 schedule.add_systems(foo);
689 schedule.add_systems(bar.ambiguous_with(foo));
690 let result = schedule.initialize(&mut world);
691 assert!(result.is_ok());
692 schedule.add_systems(foo);
693 let result = schedule.initialize(&mut world);
694 assert!(matches!(
695 result,
696 Err(ScheduleBuildError::SystemTypeSetAmbiguity(_))
697 ));
698 }
699
700 #[test]
701 #[should_panic]
702 fn configure_system_type_set() {
703 fn foo() {}
704 let mut schedule = Schedule::default();
705 schedule.configure_sets(foo.into_system_set());
706 }
707
708 #[test]
709 fn hierarchy_redundancy() {
710 let mut world = World::new();
711 let mut schedule = Schedule::default();
712
713 schedule.set_build_settings(ScheduleBuildSettings {
714 hierarchy_detection: LogLevel::Error,
715 ..Default::default()
716 });
717
718 schedule.configure_sets(TestSystems::A);
720
721 schedule.configure_sets(TestSystems::B.in_set(TestSystems::A));
723
724 schedule.configure_sets(TestSystems::X.in_set(TestSystems::A).in_set(TestSystems::B));
726
727 let result = schedule.initialize(&mut world);
729 assert!(matches!(
730 result,
731 Err(ScheduleBuildError::Elevated(
732 ScheduleBuildWarning::HierarchyRedundancy(_)
733 ))
734 ));
735 }
736
737 #[test]
738 fn cross_dependency() {
739 let mut world = World::new();
740 let mut schedule = Schedule::default();
741
742 schedule.configure_sets(TestSystems::B.in_set(TestSystems::A));
744 schedule.configure_sets(TestSystems::B.after(TestSystems::A));
745 let result = schedule.initialize(&mut world);
746 assert!(matches!(
747 result,
748 Err(ScheduleBuildError::CrossDependency(_))
749 ));
750 }
751
752 #[test]
753 fn sets_have_order_but_intersect() {
754 let mut world = World::new();
755 let mut schedule = Schedule::default();
756
757 fn foo() {}
758
759 schedule.add_systems(foo.in_set(TestSystems::A).in_set(TestSystems::C));
761
762 schedule.configure_sets((
764 TestSystems::A,
765 TestSystems::B.after(TestSystems::A),
766 TestSystems::C.after(TestSystems::B),
767 ));
768
769 let result = schedule.initialize(&mut world);
770 assert!(matches!(
772 result,
773 Err(ScheduleBuildError::SetsHaveOrderButIntersect(_))
774 ));
775 }
776
777 #[test]
778 fn ambiguity() {
779 #[derive(Resource)]
780 struct X;
781
782 fn res_ref(_x: Res<X>) {}
783 fn res_mut(_x: ResMut<X>) {}
784
785 let mut world = World::new();
786 let mut schedule = Schedule::default();
787
788 schedule.set_build_settings(ScheduleBuildSettings {
789 ambiguity_detection: LogLevel::Error,
790 ..Default::default()
791 });
792
793 schedule.add_systems((res_ref, res_mut));
794 let result = schedule.initialize(&mut world);
795 assert!(matches!(
796 result,
797 Err(ScheduleBuildError::Elevated(
798 ScheduleBuildWarning::Ambiguity(_)
799 ))
800 ));
801 }
802 }
803
804 mod system_ambiguity {
805 #[cfg(feature = "trace")]
806 use alloc::collections::BTreeSet;
807
808 use super::*;
809 use crate::prelude::*;
810
811 #[derive(Resource)]
812 struct R;
813
814 #[derive(Component)]
815 struct A;
816
817 #[derive(Component)]
818 struct B;
819
820 #[derive(Message)]
821 struct E;
822
823 fn empty_system() {}
824 fn res_system(_res: Res<R>) {}
825 fn resmut_system(_res: ResMut<R>) {}
826 fn nonsend_system(_ns: NonSend<R>) {}
827 fn nonsendmut_system(_ns: NonSendMut<R>) {}
828 fn read_component_system(_query: Query<&A>) {}
829 fn write_component_system(_query: Query<&mut A>) {}
830 fn with_filtered_component_system(_query: Query<&mut A, With<B>>) {}
831 fn without_filtered_component_system(_query: Query<&mut A, Without<B>>) {}
832 fn entity_ref_system(_query: Query<EntityRef>) {}
833 fn entity_mut_system(_query: Query<EntityMut>) {}
834 fn message_reader_system(_reader: MessageReader<E>) {}
835 fn message_writer_system(_writer: MessageWriter<E>) {}
836 fn message_resource_system(_events: ResMut<Messages<E>>) {}
837 fn read_world_system(_world: &World) {}
838 fn write_world_system(_world: &mut World) {}
839
840 #[test]
843 fn one_of_everything() {
844 let mut world = World::new();
845 world.insert_resource(R);
846 world.spawn(A);
847 world.init_resource::<Messages<E>>();
848
849 let mut schedule = Schedule::default();
850 schedule
851 .add_systems((resmut_system, write_component_system, message_writer_system));
853
854 let _ = schedule.initialize(&mut world);
855
856 assert_eq!(schedule.graph().conflicting_systems().len(), 0);
857 }
858
859 #[test]
860 fn read_only() {
861 let mut world = World::new();
862 world.insert_resource(R);
863 world.spawn(A);
864 world.init_resource::<Messages<E>>();
865
866 let mut schedule = Schedule::default();
867 schedule.add_systems((
868 empty_system,
869 empty_system,
870 res_system,
871 res_system,
872 nonsend_system,
873 nonsend_system,
874 read_component_system,
875 read_component_system,
876 entity_ref_system,
877 entity_ref_system,
878 message_reader_system,
879 message_reader_system,
880 read_world_system,
881 read_world_system,
882 ));
883
884 let _ = schedule.initialize(&mut world);
885
886 assert_eq!(schedule.graph().conflicting_systems().len(), 0);
887 }
888
889 #[test]
890 fn read_world() {
891 let mut world = World::new();
892 world.insert_resource(R);
893 world.spawn(A);
894 world.init_resource::<Messages<E>>();
895
896 let mut schedule = Schedule::default();
897 schedule.add_systems((
898 resmut_system,
899 write_component_system,
900 message_writer_system,
901 read_world_system,
902 ));
903
904 let _ = schedule.initialize(&mut world);
905
906 assert_eq!(schedule.graph().conflicting_systems().len(), 3);
907 }
908
909 #[test]
910 fn resources() {
911 let mut world = World::new();
912 world.insert_resource(R);
913
914 let mut schedule = Schedule::default();
915 schedule.add_systems((resmut_system, res_system));
916
917 let _ = schedule.initialize(&mut world);
918
919 assert_eq!(schedule.graph().conflicting_systems().len(), 1);
920 }
921
922 #[test]
923 fn nonsend() {
924 let mut world = World::new();
925 world.insert_resource(R);
926
927 let mut schedule = Schedule::default();
928 schedule.add_systems((nonsendmut_system, nonsend_system));
929
930 let _ = schedule.initialize(&mut world);
931
932 assert_eq!(schedule.graph().conflicting_systems().len(), 1);
933 }
934
935 #[test]
936 fn components() {
937 let mut world = World::new();
938 world.spawn(A);
939
940 let mut schedule = Schedule::default();
941 schedule.add_systems((read_component_system, write_component_system));
942
943 let _ = schedule.initialize(&mut world);
944
945 assert_eq!(schedule.graph().conflicting_systems().len(), 1);
946 }
947
948 #[test]
949 fn filtered_components() {
950 let mut world = World::new();
951 world.spawn(A);
952
953 let mut schedule = Schedule::default();
954 schedule.add_systems((
955 with_filtered_component_system,
956 without_filtered_component_system,
957 ));
958
959 let _ = schedule.initialize(&mut world);
960
961 assert_eq!(schedule.graph().conflicting_systems().len(), 0);
962 }
963
964 #[test]
965 fn events() {
966 let mut world = World::new();
967 world.init_resource::<Messages<E>>();
968
969 let mut schedule = Schedule::default();
970 schedule.add_systems((
971 message_reader_system,
973 message_writer_system,
974 message_resource_system,
975 ));
976
977 let _ = schedule.initialize(&mut world);
978
979 assert_eq!(schedule.graph().conflicting_systems().len(), 3);
980 }
981
982 #[test]
983 fn resource_mut_and_entity_ref() {
984 let mut world = World::new();
985 world.insert_resource(R);
986
987 let mut schedule = Schedule::default();
988 schedule.add_systems((resmut_system, entity_ref_system));
989
990 let _ = schedule.initialize(&mut world);
991
992 assert_eq!(schedule.graph().conflicting_systems().len(), 1);
994
995 schedule = Schedule::default();
996 schedule.add_systems((
997 resmut_system,
998 |_query: Query<EntityRef, Without<IsResource>>| {},
999 ));
1000
1001 assert_eq!(schedule.graph().conflicting_systems().len(), 0);
1003 }
1004
1005 #[test]
1006 fn resource_and_entity_mut() {
1007 let mut world = World::new();
1008 world.insert_resource(R);
1009
1010 let mut schedule = Schedule::default();
1011 schedule.add_systems((res_system, nonsend_system, entity_mut_system));
1012
1013 let _ = schedule.initialize(&mut world);
1014
1015 assert_eq!(schedule.graph().conflicting_systems().len(), 2);
1017
1018 schedule = Schedule::default();
1019 schedule.add_systems((
1020 res_system,
1021 nonsend_system,
1022 |_query: Query<EntityMut, Without<IsResource>>| {},
1023 ));
1024
1025 assert_eq!(schedule.graph().conflicting_systems().len(), 0);
1027 }
1028
1029 #[test]
1030 fn write_component_and_entity_ref() {
1031 let mut world = World::new();
1032 world.insert_resource(R);
1033
1034 let mut schedule = Schedule::default();
1035 schedule.add_systems((write_component_system, entity_ref_system));
1036
1037 let _ = schedule.initialize(&mut world);
1038
1039 assert_eq!(schedule.graph().conflicting_systems().len(), 1);
1040 }
1041
1042 #[test]
1043 fn read_component_and_entity_mut() {
1044 let mut world = World::new();
1045 world.insert_resource(R);
1046
1047 let mut schedule = Schedule::default();
1048 schedule.add_systems((read_component_system, entity_mut_system));
1049
1050 let _ = schedule.initialize(&mut world);
1051
1052 assert_eq!(schedule.graph().conflicting_systems().len(), 1);
1053 }
1054
1055 #[test]
1056 fn exclusive() {
1057 let mut world = World::new();
1058 world.insert_resource(R);
1059 world.spawn(A);
1060 world.init_resource::<Messages<E>>();
1061
1062 let mut schedule = Schedule::default();
1063 schedule.add_systems((
1064 write_world_system,
1066 write_world_system,
1067 res_system,
1068 ));
1069
1070 let _ = schedule.initialize(&mut world);
1071
1072 assert_eq!(schedule.graph().conflicting_systems().len(), 3);
1073 }
1074
1075 #[test]
1077 fn before_and_after() {
1078 let mut world = World::new();
1079 world.init_resource::<Messages<E>>();
1080
1081 let mut schedule = Schedule::default();
1082 schedule.add_systems((
1083 message_reader_system.before(message_writer_system),
1084 message_writer_system,
1085 message_resource_system.after(message_writer_system),
1086 ));
1087
1088 let _ = schedule.initialize(&mut world);
1089
1090 assert_eq!(schedule.graph().conflicting_systems().len(), 0);
1091 }
1092
1093 #[test]
1094 fn ignore_all_ambiguities() {
1095 let mut world = World::new();
1096 world.insert_resource(R);
1097
1098 let mut schedule = Schedule::default();
1099 schedule.add_systems((
1100 resmut_system.ambiguous_with_all(),
1101 res_system,
1102 nonsend_system,
1103 ));
1104
1105 let _ = schedule.initialize(&mut world);
1106
1107 assert_eq!(schedule.graph().conflicting_systems().len(), 0);
1108 }
1109
1110 #[test]
1111 fn ambiguous_with_label() {
1112 let mut world = World::new();
1113 world.insert_resource(R);
1114
1115 #[derive(SystemSet, Hash, PartialEq, Eq, Debug, Clone)]
1116 struct IgnoreMe;
1117
1118 let mut schedule = Schedule::default();
1119 schedule.add_systems((
1120 resmut_system.ambiguous_with(IgnoreMe),
1121 res_system.in_set(IgnoreMe),
1122 nonsend_system.in_set(IgnoreMe),
1123 ));
1124
1125 let _ = schedule.initialize(&mut world);
1126
1127 assert_eq!(schedule.graph().conflicting_systems().len(), 0);
1128 }
1129
1130 #[test]
1131 fn ambiguous_with_system() {
1132 let mut world = World::new();
1133
1134 let mut schedule = Schedule::default();
1135 schedule.add_systems((
1136 write_component_system.ambiguous_with(read_component_system),
1137 read_component_system,
1138 ));
1139 let _ = schedule.initialize(&mut world);
1140
1141 assert_eq!(schedule.graph().conflicting_systems().len(), 0);
1142 }
1143
1144 #[derive(ScheduleLabel, Hash, PartialEq, Eq, Debug, Clone)]
1145 struct TestSchedule;
1146
1147 #[test]
1149 #[cfg(feature = "trace")]
1150 fn correct_ambiguities() {
1151 fn system_a(_res: ResMut<R>) {}
1152 fn system_b(_res: ResMut<R>) {}
1153 fn system_c(_res: ResMut<R>) {}
1154 fn system_d(_res: ResMut<R>) {}
1155 fn system_e(_res: ResMut<R>) {}
1156
1157 let mut world = World::new();
1158 world.insert_resource(R);
1159
1160 let mut schedule = Schedule::new(TestSchedule);
1161 schedule.add_systems((
1162 system_a,
1163 system_b,
1164 system_c.ambiguous_with_all(),
1165 system_d.ambiguous_with(system_b),
1166 system_e.after(system_a),
1167 ));
1168
1169 schedule.graph_mut().initialize(&mut world);
1170 let _ = schedule
1171 .graph_mut()
1172 .build_schedule(&mut world, &BTreeSet::new());
1173
1174 let ambiguities: Vec<_> = schedule
1175 .graph()
1176 .conflicting_systems()
1177 .to_string(schedule.graph(), world.components())
1178 .map(|item| {
1179 (
1180 item.0,
1181 item.1,
1182 item.2
1183 .into_iter()
1184 .map(|name| name.to_string())
1185 .collect::<Vec<_>>(),
1186 )
1187 })
1188 .collect();
1189
1190 let expected = &[
1191 (
1192 "system_d".to_string(),
1193 "system_a".to_string(),
1194 vec!["bevy_ecs::schedule::tests::system_ambiguity::R".into()],
1195 ),
1196 (
1197 "system_d".to_string(),
1198 "system_e".to_string(),
1199 vec!["bevy_ecs::schedule::tests::system_ambiguity::R".into()],
1200 ),
1201 (
1202 "system_b".to_string(),
1203 "system_a".to_string(),
1204 vec!["bevy_ecs::schedule::tests::system_ambiguity::R".into()],
1205 ),
1206 (
1207 "system_b".to_string(),
1208 "system_e".to_string(),
1209 vec!["bevy_ecs::schedule::tests::system_ambiguity::R".into()],
1210 ),
1211 ];
1212
1213 for entry in expected {
1215 assert!(ambiguities.contains(entry));
1216 }
1217 }
1218
1219 #[test]
1222 #[cfg(feature = "trace")]
1223 fn anonymous_set_name() {
1224 let mut schedule = Schedule::new(TestSchedule);
1225 schedule.add_systems((resmut_system, resmut_system).run_if(|| true));
1226
1227 let mut world = World::new();
1228 schedule.graph_mut().initialize(&mut world);
1229 let _ = schedule
1230 .graph_mut()
1231 .build_schedule(&mut world, &BTreeSet::new());
1232
1233 let ambiguities: Vec<_> = schedule
1234 .graph()
1235 .conflicting_systems()
1236 .to_string(schedule.graph(), world.components())
1237 .map(|item| {
1238 (
1239 item.0,
1240 item.1,
1241 item.2
1242 .into_iter()
1243 .map(|name| name.to_string())
1244 .collect::<Vec<_>>(),
1245 )
1246 })
1247 .collect();
1248
1249 assert_eq!(
1250 ambiguities[0],
1251 (
1252 "resmut_system (in set (resmut_system, resmut_system))".to_string(),
1253 "resmut_system (in set (resmut_system, resmut_system))".to_string(),
1254 vec!["bevy_ecs::schedule::tests::system_ambiguity::R".into()],
1255 )
1256 );
1257 }
1258
1259 #[test]
1260 fn ignore_component_resource_ambiguities() {
1261 let mut world = World::new();
1262 world.insert_resource(R);
1263 world.allow_ambiguous_resource::<R>();
1264 let mut schedule = Schedule::new(TestSchedule);
1265
1266 schedule.add_systems((resmut_system, res_system));
1268 schedule.initialize(&mut world).unwrap();
1269 assert!(schedule.graph().conflicting_systems().is_empty());
1270
1271 world.allow_ambiguous_component::<A>();
1273 schedule.add_systems((write_component_system, read_component_system));
1274 schedule.initialize(&mut world).unwrap();
1275 assert!(schedule.graph().conflicting_systems().is_empty());
1276 }
1277 }
1278
1279 #[cfg(feature = "bevy_debug_stepping")]
1280 mod stepping {
1281 use super::*;
1282 use bevy_ecs::system::SystemState;
1283
1284 #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
1285 pub struct TestSchedule;
1286
1287 fn assert_executor_supports_stepping(executor: impl SystemExecutor + 'static) {
1288 let mut schedule = Schedule::new(TestSchedule);
1290 schedule.set_executor(executor);
1291 schedule.add_systems(|| -> () { panic!("Executor ignored Stepping") });
1292
1293 let mut stepping = Stepping::default();
1296 stepping.add_schedule(TestSchedule).enable();
1297
1298 let mut world = World::default();
1300 world.insert_resource(stepping);
1301
1302 let mut system_state: SystemState<Option<ResMut<Stepping>>> =
1304 SystemState::new(&mut world);
1305 let res = system_state.get_mut(&mut world).unwrap();
1306 Stepping::begin_frame(res);
1307
1308 schedule.run(&mut world);
1311 }
1312
1313 #[test]
1315 fn single_threaded_executor() {
1316 assert_executor_supports_stepping(SingleThreadedExecutor::new());
1317 }
1318
1319 #[test]
1321 fn multi_threaded_executor() {
1322 assert_executor_supports_stepping(MultiThreadedExecutor::new());
1323 }
1324 }
1325}