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!(result, Err(ScheduleBuildError::DependencyLoop(_))));
574 }
575
576 #[test]
577 fn dependency_loop_from_chain() {
578 let mut schedule = Schedule::default();
579 schedule.configure_sets((TestSystems::X, TestSystems::X).chain());
580 let mut world = World::new();
581 let result = schedule.initialize(&mut world);
582 assert!(matches!(result, Err(ScheduleBuildError::DependencyLoop(_))));
583 }
584
585 #[test]
586 fn dependency_cycle() {
587 let mut world = World::new();
588 let mut schedule = Schedule::default();
589
590 schedule.configure_sets(TestSystems::A.after(TestSystems::B));
591 schedule.configure_sets(TestSystems::B.after(TestSystems::A));
592
593 let result = schedule.initialize(&mut world);
594 assert!(matches!(
595 result,
596 Err(ScheduleBuildError::DependencyCycle(_))
597 ));
598
599 fn foo() {}
600 fn bar() {}
601
602 let mut world = World::new();
603 let mut schedule = Schedule::default();
604
605 schedule.add_systems((foo.after(bar), bar.after(foo)));
606 let result = schedule.initialize(&mut world);
607 assert!(matches!(
608 result,
609 Err(ScheduleBuildError::DependencyCycle(_))
610 ));
611 }
612
613 #[test]
614 fn hierarchy_loop() {
615 let mut schedule = Schedule::default();
616 schedule.configure_sets(TestSystems::X.in_set(TestSystems::X));
617 let mut world = World::new();
618 let result = schedule.initialize(&mut world);
619 assert!(matches!(result, Err(ScheduleBuildError::HierarchyLoop(_))));
620 }
621
622 #[test]
623 fn hierarchy_cycle() {
624 let mut world = World::new();
625 let mut schedule = Schedule::default();
626
627 schedule.configure_sets(TestSystems::A.in_set(TestSystems::B));
628 schedule.configure_sets(TestSystems::B.in_set(TestSystems::A));
629
630 let result = schedule.initialize(&mut world);
631 assert!(matches!(result, Err(ScheduleBuildError::HierarchyCycle(_))));
632 }
633
634 #[test]
635 fn system_type_set_ambiguity() {
636 fn foo() {}
638 fn bar() {}
639
640 let mut world = World::new();
641 let mut schedule = Schedule::default();
642
643 schedule.add_systems((foo, bar.after(foo)));
645
646 let result = schedule.initialize(&mut world);
648 assert!(result.is_ok());
649
650 schedule.add_systems(foo);
652
653 let result = schedule.initialize(&mut world);
656 assert!(matches!(
657 result,
658 Err(ScheduleBuildError::SystemTypeSetAmbiguity(_))
659 ));
660
661 let mut schedule = Schedule::default();
663 schedule.add_systems(foo);
664 schedule.add_systems(bar.ambiguous_with(foo));
665 let result = schedule.initialize(&mut world);
666 assert!(result.is_ok());
667 schedule.add_systems(foo);
668 let result = schedule.initialize(&mut world);
669 assert!(matches!(
670 result,
671 Err(ScheduleBuildError::SystemTypeSetAmbiguity(_))
672 ));
673 }
674
675 #[test]
676 #[should_panic]
677 fn configure_system_type_set() {
678 fn foo() {}
679 let mut schedule = Schedule::default();
680 schedule.configure_sets(foo.into_system_set());
681 }
682
683 #[test]
684 fn hierarchy_redundancy() {
685 let mut world = World::new();
686 let mut schedule = Schedule::default();
687
688 schedule.set_build_settings(ScheduleBuildSettings {
689 hierarchy_detection: LogLevel::Error,
690 ..Default::default()
691 });
692
693 schedule.configure_sets(TestSystems::A);
695
696 schedule.configure_sets(TestSystems::B.in_set(TestSystems::A));
698
699 schedule.configure_sets(TestSystems::X.in_set(TestSystems::A).in_set(TestSystems::B));
701
702 let result = schedule.initialize(&mut world);
704 assert!(matches!(
705 result,
706 Err(ScheduleBuildError::Elevated(
707 ScheduleBuildWarning::HierarchyRedundancy(_)
708 ))
709 ));
710 }
711
712 #[test]
713 fn cross_dependency() {
714 let mut world = World::new();
715 let mut schedule = Schedule::default();
716
717 schedule.configure_sets(TestSystems::B.in_set(TestSystems::A));
719 schedule.configure_sets(TestSystems::B.after(TestSystems::A));
720 let result = schedule.initialize(&mut world);
721 assert!(matches!(
722 result,
723 Err(ScheduleBuildError::CrossDependency(_, _))
724 ));
725 }
726
727 #[test]
728 fn sets_have_order_but_intersect() {
729 let mut world = World::new();
730 let mut schedule = Schedule::default();
731
732 fn foo() {}
733
734 schedule.add_systems(foo.in_set(TestSystems::A).in_set(TestSystems::C));
736
737 schedule.configure_sets((
739 TestSystems::A,
740 TestSystems::B.after(TestSystems::A),
741 TestSystems::C.after(TestSystems::B),
742 ));
743
744 let result = schedule.initialize(&mut world);
745 assert!(matches!(
747 result,
748 Err(ScheduleBuildError::SetsHaveOrderButIntersect(_, _))
749 ));
750 }
751
752 #[test]
753 fn ambiguity() {
754 #[derive(Resource)]
755 struct X;
756
757 fn res_ref(_x: Res<X>) {}
758 fn res_mut(_x: ResMut<X>) {}
759
760 let mut world = World::new();
761 let mut schedule = Schedule::default();
762
763 schedule.set_build_settings(ScheduleBuildSettings {
764 ambiguity_detection: LogLevel::Error,
765 ..Default::default()
766 });
767
768 schedule.add_systems((res_ref, res_mut));
769 let result = schedule.initialize(&mut world);
770 assert!(matches!(
771 result,
772 Err(ScheduleBuildError::Elevated(
773 ScheduleBuildWarning::Ambiguity(_)
774 ))
775 ));
776 }
777 }
778
779 mod system_ambiguity {
780 #[cfg(feature = "trace")]
781 use alloc::collections::BTreeSet;
782
783 use super::*;
784 use crate::prelude::*;
785
786 #[derive(Resource)]
787 struct R;
788
789 #[derive(Component)]
790 struct A;
791
792 #[derive(Component)]
793 struct B;
794
795 #[derive(Message)]
796 struct E;
797
798 #[derive(Resource, Component)]
799 struct RC;
800
801 fn empty_system() {}
802 fn res_system(_res: Res<R>) {}
803 fn resmut_system(_res: ResMut<R>) {}
804 fn nonsend_system(_ns: NonSend<R>) {}
805 fn nonsendmut_system(_ns: NonSendMut<R>) {}
806 fn read_component_system(_query: Query<&A>) {}
807 fn write_component_system(_query: Query<&mut A>) {}
808 fn with_filtered_component_system(_query: Query<&mut A, With<B>>) {}
809 fn without_filtered_component_system(_query: Query<&mut A, Without<B>>) {}
810 fn entity_ref_system(_query: Query<EntityRef>) {}
811 fn entity_mut_system(_query: Query<EntityMut>) {}
812 fn message_reader_system(_reader: MessageReader<E>) {}
813 fn message_writer_system(_writer: MessageWriter<E>) {}
814 fn message_resource_system(_events: ResMut<Messages<E>>) {}
815 fn read_world_system(_world: &World) {}
816 fn write_world_system(_world: &mut World) {}
817
818 #[test]
821 fn one_of_everything() {
822 let mut world = World::new();
823 world.insert_resource(R);
824 world.spawn(A);
825 world.init_resource::<Messages<E>>();
826
827 let mut schedule = Schedule::default();
828 schedule
829 .add_systems((resmut_system, write_component_system, message_writer_system));
831
832 let _ = schedule.initialize(&mut world);
833
834 assert_eq!(schedule.graph().conflicting_systems().len(), 0);
835 }
836
837 #[test]
838 fn read_only() {
839 let mut world = World::new();
840 world.insert_resource(R);
841 world.spawn(A);
842 world.init_resource::<Messages<E>>();
843
844 let mut schedule = Schedule::default();
845 schedule.add_systems((
846 empty_system,
847 empty_system,
848 res_system,
849 res_system,
850 nonsend_system,
851 nonsend_system,
852 read_component_system,
853 read_component_system,
854 entity_ref_system,
855 entity_ref_system,
856 message_reader_system,
857 message_reader_system,
858 read_world_system,
859 read_world_system,
860 ));
861
862 let _ = schedule.initialize(&mut world);
863
864 assert_eq!(schedule.graph().conflicting_systems().len(), 0);
865 }
866
867 #[test]
868 fn read_world() {
869 let mut world = World::new();
870 world.insert_resource(R);
871 world.spawn(A);
872 world.init_resource::<Messages<E>>();
873
874 let mut schedule = Schedule::default();
875 schedule.add_systems((
876 resmut_system,
877 write_component_system,
878 message_writer_system,
879 read_world_system,
880 ));
881
882 let _ = schedule.initialize(&mut world);
883
884 assert_eq!(schedule.graph().conflicting_systems().len(), 3);
885 }
886
887 #[test]
888 fn resources() {
889 let mut world = World::new();
890 world.insert_resource(R);
891
892 let mut schedule = Schedule::default();
893 schedule.add_systems((resmut_system, res_system));
894
895 let _ = schedule.initialize(&mut world);
896
897 assert_eq!(schedule.graph().conflicting_systems().len(), 1);
898 }
899
900 #[test]
901 fn nonsend() {
902 let mut world = World::new();
903 world.insert_resource(R);
904
905 let mut schedule = Schedule::default();
906 schedule.add_systems((nonsendmut_system, nonsend_system));
907
908 let _ = schedule.initialize(&mut world);
909
910 assert_eq!(schedule.graph().conflicting_systems().len(), 1);
911 }
912
913 #[test]
914 fn components() {
915 let mut world = World::new();
916 world.spawn(A);
917
918 let mut schedule = Schedule::default();
919 schedule.add_systems((read_component_system, write_component_system));
920
921 let _ = schedule.initialize(&mut world);
922
923 assert_eq!(schedule.graph().conflicting_systems().len(), 1);
924 }
925
926 #[test]
927 fn filtered_components() {
928 let mut world = World::new();
929 world.spawn(A);
930
931 let mut schedule = Schedule::default();
932 schedule.add_systems((
933 with_filtered_component_system,
934 without_filtered_component_system,
935 ));
936
937 let _ = schedule.initialize(&mut world);
938
939 assert_eq!(schedule.graph().conflicting_systems().len(), 0);
940 }
941
942 #[test]
943 fn events() {
944 let mut world = World::new();
945 world.init_resource::<Messages<E>>();
946
947 let mut schedule = Schedule::default();
948 schedule.add_systems((
949 message_reader_system,
951 message_writer_system,
952 message_resource_system,
953 ));
954
955 let _ = schedule.initialize(&mut world);
956
957 assert_eq!(schedule.graph().conflicting_systems().len(), 3);
958 }
959
960 #[test]
963 fn shared_resource_mut_component() {
964 let mut world = World::new();
965 world.insert_resource(RC);
966
967 let mut schedule = Schedule::default();
968 schedule.add_systems((|_: ResMut<RC>| {}, |_: Query<&mut RC>| {}));
969
970 let _ = schedule.initialize(&mut world);
971
972 assert_eq!(schedule.graph().conflicting_systems().len(), 0);
973 }
974
975 #[test]
976 fn resource_mut_and_entity_ref() {
977 let mut world = World::new();
978 world.insert_resource(R);
979
980 let mut schedule = Schedule::default();
981 schedule.add_systems((resmut_system, entity_ref_system));
982
983 let _ = schedule.initialize(&mut world);
984
985 assert_eq!(schedule.graph().conflicting_systems().len(), 0);
986 }
987
988 #[test]
989 fn resource_and_entity_mut() {
990 let mut world = World::new();
991 world.insert_resource(R);
992
993 let mut schedule = Schedule::default();
994 schedule.add_systems((res_system, nonsend_system, entity_mut_system));
995
996 let _ = schedule.initialize(&mut world);
997
998 assert_eq!(schedule.graph().conflicting_systems().len(), 0);
999 }
1000
1001 #[test]
1002 fn write_component_and_entity_ref() {
1003 let mut world = World::new();
1004 world.insert_resource(R);
1005
1006 let mut schedule = Schedule::default();
1007 schedule.add_systems((write_component_system, entity_ref_system));
1008
1009 let _ = schedule.initialize(&mut world);
1010
1011 assert_eq!(schedule.graph().conflicting_systems().len(), 1);
1012 }
1013
1014 #[test]
1015 fn read_component_and_entity_mut() {
1016 let mut world = World::new();
1017 world.insert_resource(R);
1018
1019 let mut schedule = Schedule::default();
1020 schedule.add_systems((read_component_system, entity_mut_system));
1021
1022 let _ = schedule.initialize(&mut world);
1023
1024 assert_eq!(schedule.graph().conflicting_systems().len(), 1);
1025 }
1026
1027 #[test]
1028 fn exclusive() {
1029 let mut world = World::new();
1030 world.insert_resource(R);
1031 world.spawn(A);
1032 world.init_resource::<Messages<E>>();
1033
1034 let mut schedule = Schedule::default();
1035 schedule.add_systems((
1036 write_world_system,
1038 write_world_system,
1039 res_system,
1040 ));
1041
1042 let _ = schedule.initialize(&mut world);
1043
1044 assert_eq!(schedule.graph().conflicting_systems().len(), 3);
1045 }
1046
1047 #[test]
1049 fn before_and_after() {
1050 let mut world = World::new();
1051 world.init_resource::<Messages<E>>();
1052
1053 let mut schedule = Schedule::default();
1054 schedule.add_systems((
1055 message_reader_system.before(message_writer_system),
1056 message_writer_system,
1057 message_resource_system.after(message_writer_system),
1058 ));
1059
1060 let _ = schedule.initialize(&mut world);
1061
1062 assert_eq!(schedule.graph().conflicting_systems().len(), 0);
1063 }
1064
1065 #[test]
1066 fn ignore_all_ambiguities() {
1067 let mut world = World::new();
1068 world.insert_resource(R);
1069
1070 let mut schedule = Schedule::default();
1071 schedule.add_systems((
1072 resmut_system.ambiguous_with_all(),
1073 res_system,
1074 nonsend_system,
1075 ));
1076
1077 let _ = schedule.initialize(&mut world);
1078
1079 assert_eq!(schedule.graph().conflicting_systems().len(), 0);
1080 }
1081
1082 #[test]
1083 fn ambiguous_with_label() {
1084 let mut world = World::new();
1085 world.insert_resource(R);
1086
1087 #[derive(SystemSet, Hash, PartialEq, Eq, Debug, Clone)]
1088 struct IgnoreMe;
1089
1090 let mut schedule = Schedule::default();
1091 schedule.add_systems((
1092 resmut_system.ambiguous_with(IgnoreMe),
1093 res_system.in_set(IgnoreMe),
1094 nonsend_system.in_set(IgnoreMe),
1095 ));
1096
1097 let _ = schedule.initialize(&mut world);
1098
1099 assert_eq!(schedule.graph().conflicting_systems().len(), 0);
1100 }
1101
1102 #[test]
1103 fn ambiguous_with_system() {
1104 let mut world = World::new();
1105
1106 let mut schedule = Schedule::default();
1107 schedule.add_systems((
1108 write_component_system.ambiguous_with(read_component_system),
1109 read_component_system,
1110 ));
1111 let _ = schedule.initialize(&mut world);
1112
1113 assert_eq!(schedule.graph().conflicting_systems().len(), 0);
1114 }
1115
1116 #[derive(ScheduleLabel, Hash, PartialEq, Eq, Debug, Clone)]
1117 struct TestSchedule;
1118
1119 #[test]
1121 #[cfg(feature = "trace")]
1122 fn correct_ambiguities() {
1123 fn system_a(_res: ResMut<R>) {}
1124 fn system_b(_res: ResMut<R>) {}
1125 fn system_c(_res: ResMut<R>) {}
1126 fn system_d(_res: ResMut<R>) {}
1127 fn system_e(_res: ResMut<R>) {}
1128
1129 let mut world = World::new();
1130 world.insert_resource(R);
1131
1132 let mut schedule = Schedule::new(TestSchedule);
1133 schedule.add_systems((
1134 system_a,
1135 system_b,
1136 system_c.ambiguous_with_all(),
1137 system_d.ambiguous_with(system_b),
1138 system_e.after(system_a),
1139 ));
1140
1141 schedule.graph_mut().initialize(&mut world);
1142 let _ = schedule
1143 .graph_mut()
1144 .build_schedule(&mut world, &BTreeSet::new());
1145
1146 let ambiguities: Vec<_> = schedule
1147 .graph()
1148 .conflicts_to_string(schedule.graph().conflicting_systems(), world.components())
1149 .map(|item| {
1150 (
1151 item.0,
1152 item.1,
1153 item.2
1154 .into_iter()
1155 .map(|name| name.to_string())
1156 .collect::<Vec<_>>(),
1157 )
1158 })
1159 .collect();
1160
1161 let expected = &[
1162 (
1163 "system_d".to_string(),
1164 "system_a".to_string(),
1165 vec!["bevy_ecs::schedule::tests::system_ambiguity::R".into()],
1166 ),
1167 (
1168 "system_d".to_string(),
1169 "system_e".to_string(),
1170 vec!["bevy_ecs::schedule::tests::system_ambiguity::R".into()],
1171 ),
1172 (
1173 "system_b".to_string(),
1174 "system_a".to_string(),
1175 vec!["bevy_ecs::schedule::tests::system_ambiguity::R".into()],
1176 ),
1177 (
1178 "system_b".to_string(),
1179 "system_e".to_string(),
1180 vec!["bevy_ecs::schedule::tests::system_ambiguity::R".into()],
1181 ),
1182 ];
1183
1184 for entry in expected {
1186 assert!(ambiguities.contains(entry));
1187 }
1188 }
1189
1190 #[test]
1193 #[cfg(feature = "trace")]
1194 fn anonymous_set_name() {
1195 let mut schedule = Schedule::new(TestSchedule);
1196 schedule.add_systems((resmut_system, resmut_system).run_if(|| true));
1197
1198 let mut world = World::new();
1199 schedule.graph_mut().initialize(&mut world);
1200 let _ = schedule
1201 .graph_mut()
1202 .build_schedule(&mut world, &BTreeSet::new());
1203
1204 let ambiguities: Vec<_> = schedule
1205 .graph()
1206 .conflicts_to_string(schedule.graph().conflicting_systems(), world.components())
1207 .map(|item| {
1208 (
1209 item.0,
1210 item.1,
1211 item.2
1212 .into_iter()
1213 .map(|name| name.to_string())
1214 .collect::<Vec<_>>(),
1215 )
1216 })
1217 .collect();
1218
1219 assert_eq!(
1220 ambiguities[0],
1221 (
1222 "resmut_system (in set (resmut_system, resmut_system))".to_string(),
1223 "resmut_system (in set (resmut_system, resmut_system))".to_string(),
1224 vec!["bevy_ecs::schedule::tests::system_ambiguity::R".into()],
1225 )
1226 );
1227 }
1228
1229 #[test]
1230 fn ignore_component_resource_ambiguities() {
1231 let mut world = World::new();
1232 world.insert_resource(R);
1233 world.allow_ambiguous_resource::<R>();
1234 let mut schedule = Schedule::new(TestSchedule);
1235
1236 schedule.add_systems((resmut_system, res_system));
1238 schedule.initialize(&mut world).unwrap();
1239 assert!(schedule.graph().conflicting_systems().is_empty());
1240
1241 world.allow_ambiguous_component::<A>();
1243 schedule.add_systems((write_component_system, read_component_system));
1244 schedule.initialize(&mut world).unwrap();
1245 assert!(schedule.graph().conflicting_systems().is_empty());
1246 }
1247 }
1248
1249 #[cfg(feature = "bevy_debug_stepping")]
1250 mod stepping {
1251 use super::*;
1252 use bevy_ecs::system::SystemState;
1253
1254 #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
1255 pub struct TestSchedule;
1256
1257 macro_rules! assert_executor_supports_stepping {
1258 ($executor:expr) => {
1259 let mut schedule = Schedule::new(TestSchedule);
1261 schedule
1262 .set_executor_kind($executor)
1263 .add_systems(|| -> () { panic!("Executor ignored Stepping") });
1264
1265 let mut stepping = Stepping::default();
1268 stepping.add_schedule(TestSchedule).enable();
1269
1270 let mut world = World::default();
1272 world.insert_resource(stepping);
1273
1274 let mut system_state: SystemState<Option<ResMut<Stepping>>> =
1276 SystemState::new(&mut world);
1277 let res = system_state.get_mut(&mut world);
1278 Stepping::begin_frame(res);
1279
1280 schedule.run(&mut world);
1283 };
1284 }
1285
1286 #[test]
1288 #[expect(deprecated, reason = "We still need to test this.")]
1289 fn simple_executor() {
1290 assert_executor_supports_stepping!(ExecutorKind::Simple);
1291 }
1292
1293 #[test]
1295 fn single_threaded_executor() {
1296 assert_executor_supports_stepping!(ExecutorKind::SingleThreaded);
1297 }
1298
1299 #[test]
1301 fn multi_threaded_executor() {
1302 assert_executor_supports_stepping!(ExecutorKind::MultiThreaded);
1303 }
1304 }
1305}