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