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