mirror of
https://github.com/bevyengine/bevy
synced 2025-01-03 00:38:56 +00:00
9733613c07
# Objective Base sets, added in #7466 are a special type of system set. Systems can only be added to base sets via `in_base_set`, while non-base sets can only be added via `in_set`. Unfortunately this is currently guarded by a runtime panic, which presents an unfortunate toe-stub when the wrong method is used. The delayed response between writing code and encountering the error (possibly hours) makes the distinction between base sets and other sets much more difficult to learn. ## Solution Add the marker traits `BaseSystemSet` and `FreeSystemSet`. `in_base_set` and `in_set` now respectively accept these traits, which moves the runtime panic to a compile time error. --- ## Changelog + Added the marker trait `BaseSystemSet`, which is distinguished from a `FreeSystemSet`. These are both subtraits of `SystemSet`. ## Migration Guide None if merged with 0.10
800 lines
27 KiB
Rust
800 lines
27 KiB
Rust
mod condition;
|
|
mod config;
|
|
mod executor;
|
|
mod graph_utils;
|
|
#[allow(clippy::module_inception)]
|
|
mod schedule;
|
|
mod set;
|
|
mod state;
|
|
|
|
pub use self::condition::*;
|
|
pub use self::config::*;
|
|
pub use self::executor::*;
|
|
use self::graph_utils::*;
|
|
pub use self::schedule::*;
|
|
pub use self::set::*;
|
|
pub use self::state::*;
|
|
|
|
pub use self::graph_utils::NodeId;
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use std::sync::atomic::{AtomicU32, Ordering};
|
|
|
|
pub use crate as bevy_ecs;
|
|
pub use crate::schedule::{IntoSystemConfig, IntoSystemSetConfig, Schedule, SystemSet};
|
|
pub use crate::system::{Res, ResMut};
|
|
pub use crate::{prelude::World, system::Resource};
|
|
|
|
#[derive(SystemSet, Clone, Debug, PartialEq, Eq, Hash)]
|
|
enum TestSet {
|
|
A,
|
|
B,
|
|
C,
|
|
D,
|
|
X,
|
|
}
|
|
|
|
#[derive(Resource, Default)]
|
|
struct SystemOrder(Vec<u32>);
|
|
|
|
#[derive(Resource, Default)]
|
|
struct RunConditionBool(pub bool);
|
|
|
|
#[derive(Resource, Default)]
|
|
struct Counter(pub AtomicU32);
|
|
|
|
fn make_exclusive_system(tag: u32) -> impl FnMut(&mut World) {
|
|
move |world| world.resource_mut::<SystemOrder>().0.push(tag)
|
|
}
|
|
|
|
fn make_function_system(tag: u32) -> impl FnMut(ResMut<SystemOrder>) {
|
|
move |mut resource: ResMut<SystemOrder>| resource.0.push(tag)
|
|
}
|
|
|
|
fn named_system(mut resource: ResMut<SystemOrder>) {
|
|
resource.0.push(u32::MAX);
|
|
}
|
|
|
|
fn named_exclusive_system(world: &mut World) {
|
|
world.resource_mut::<SystemOrder>().0.push(u32::MAX);
|
|
}
|
|
|
|
fn counting_system(counter: Res<Counter>) {
|
|
counter.0.fetch_add(1, Ordering::Relaxed);
|
|
}
|
|
|
|
mod system_execution {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn run_system() {
|
|
let mut world = World::default();
|
|
let mut schedule = Schedule::default();
|
|
|
|
world.init_resource::<SystemOrder>();
|
|
|
|
schedule.add_system(make_function_system(0));
|
|
schedule.run(&mut world);
|
|
|
|
assert_eq!(world.resource::<SystemOrder>().0, vec![0]);
|
|
}
|
|
|
|
#[test]
|
|
fn run_exclusive_system() {
|
|
let mut world = World::default();
|
|
let mut schedule = Schedule::default();
|
|
|
|
world.init_resource::<SystemOrder>();
|
|
|
|
schedule.add_system(make_exclusive_system(0));
|
|
schedule.run(&mut world);
|
|
|
|
assert_eq!(world.resource::<SystemOrder>().0, vec![0]);
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(not(miri))]
|
|
fn parallel_execution() {
|
|
use bevy_tasks::{ComputeTaskPool, TaskPool};
|
|
use std::sync::{Arc, Barrier};
|
|
|
|
let mut world = World::default();
|
|
let mut schedule = Schedule::default();
|
|
let thread_count = ComputeTaskPool::init(TaskPool::default).thread_num();
|
|
|
|
let barrier = Arc::new(Barrier::new(thread_count));
|
|
|
|
for _ in 0..thread_count {
|
|
let inner = barrier.clone();
|
|
schedule.add_system(move || {
|
|
inner.wait();
|
|
});
|
|
}
|
|
|
|
schedule.run(&mut world);
|
|
}
|
|
}
|
|
|
|
mod system_ordering {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn order_systems() {
|
|
let mut world = World::default();
|
|
let mut schedule = Schedule::default();
|
|
|
|
world.init_resource::<SystemOrder>();
|
|
|
|
schedule.add_system(named_system);
|
|
schedule.add_system(make_function_system(1).before(named_system));
|
|
schedule.add_system(
|
|
make_function_system(0)
|
|
.after(named_system)
|
|
.in_set(TestSet::A),
|
|
);
|
|
schedule.run(&mut world);
|
|
|
|
assert_eq!(world.resource::<SystemOrder>().0, vec![1, u32::MAX, 0]);
|
|
|
|
world.insert_resource(SystemOrder::default());
|
|
|
|
assert_eq!(world.resource::<SystemOrder>().0, vec![]);
|
|
|
|
// modify the schedule after it's been initialized and test ordering with sets
|
|
schedule.configure_set(TestSet::A.after(named_system));
|
|
schedule.add_system(
|
|
make_function_system(3)
|
|
.before(TestSet::A)
|
|
.after(named_system),
|
|
);
|
|
schedule.add_system(make_function_system(4).after(TestSet::A));
|
|
schedule.run(&mut world);
|
|
|
|
assert_eq!(
|
|
world.resource::<SystemOrder>().0,
|
|
vec![1, u32::MAX, 3, 0, 4]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn order_exclusive_systems() {
|
|
let mut world = World::default();
|
|
let mut schedule = Schedule::default();
|
|
|
|
world.init_resource::<SystemOrder>();
|
|
|
|
schedule.add_systems((
|
|
named_exclusive_system,
|
|
make_exclusive_system(1).before(named_exclusive_system),
|
|
make_exclusive_system(0).after(named_exclusive_system),
|
|
));
|
|
schedule.run(&mut world);
|
|
|
|
assert_eq!(world.resource::<SystemOrder>().0, vec![1, u32::MAX, 0]);
|
|
}
|
|
|
|
#[test]
|
|
fn add_systems_correct_order() {
|
|
let mut world = World::new();
|
|
let mut schedule = Schedule::new();
|
|
|
|
world.init_resource::<SystemOrder>();
|
|
|
|
schedule.add_systems(
|
|
(
|
|
make_function_system(0),
|
|
make_function_system(1),
|
|
make_exclusive_system(2),
|
|
make_function_system(3),
|
|
)
|
|
.chain(),
|
|
);
|
|
|
|
schedule.run(&mut world);
|
|
assert_eq!(world.resource::<SystemOrder>().0, vec![0, 1, 2, 3]);
|
|
}
|
|
}
|
|
|
|
mod conditions {
|
|
use crate::change_detection::DetectChanges;
|
|
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn system_with_condition() {
|
|
let mut world = World::default();
|
|
let mut schedule = Schedule::default();
|
|
|
|
world.init_resource::<RunConditionBool>();
|
|
world.init_resource::<SystemOrder>();
|
|
|
|
schedule.add_system(
|
|
make_function_system(0).run_if(|condition: Res<RunConditionBool>| condition.0),
|
|
);
|
|
|
|
schedule.run(&mut world);
|
|
assert_eq!(world.resource::<SystemOrder>().0, vec![]);
|
|
|
|
world.resource_mut::<RunConditionBool>().0 = true;
|
|
schedule.run(&mut world);
|
|
assert_eq!(world.resource::<SystemOrder>().0, vec![0]);
|
|
}
|
|
|
|
#[test]
|
|
fn systems_with_distributive_condition() {
|
|
let mut world = World::default();
|
|
let mut schedule = Schedule::default();
|
|
|
|
world.insert_resource(RunConditionBool(true));
|
|
world.init_resource::<SystemOrder>();
|
|
|
|
fn change_condition(mut condition: ResMut<RunConditionBool>) {
|
|
condition.0 = false;
|
|
}
|
|
|
|
schedule.add_systems(
|
|
(
|
|
make_function_system(0),
|
|
change_condition,
|
|
make_function_system(1),
|
|
)
|
|
.chain()
|
|
.distributive_run_if(|condition: Res<RunConditionBool>| condition.0),
|
|
);
|
|
|
|
schedule.run(&mut world);
|
|
assert_eq!(world.resource::<SystemOrder>().0, vec![0]);
|
|
}
|
|
|
|
#[test]
|
|
fn run_exclusive_system_with_condition() {
|
|
let mut world = World::default();
|
|
let mut schedule = Schedule::default();
|
|
|
|
world.init_resource::<RunConditionBool>();
|
|
world.init_resource::<SystemOrder>();
|
|
|
|
schedule.add_system(
|
|
make_exclusive_system(0).run_if(|condition: Res<RunConditionBool>| condition.0),
|
|
);
|
|
|
|
schedule.run(&mut world);
|
|
assert_eq!(world.resource::<SystemOrder>().0, vec![]);
|
|
|
|
world.resource_mut::<RunConditionBool>().0 = true;
|
|
schedule.run(&mut world);
|
|
assert_eq!(world.resource::<SystemOrder>().0, vec![0]);
|
|
}
|
|
|
|
#[test]
|
|
fn multiple_conditions_on_system() {
|
|
let mut world = World::default();
|
|
let mut schedule = Schedule::default();
|
|
|
|
world.init_resource::<Counter>();
|
|
|
|
schedule.add_system(counting_system.run_if(|| false).run_if(|| false));
|
|
schedule.add_system(counting_system.run_if(|| true).run_if(|| false));
|
|
schedule.add_system(counting_system.run_if(|| false).run_if(|| true));
|
|
schedule.add_system(counting_system.run_if(|| true).run_if(|| true));
|
|
|
|
schedule.run(&mut world);
|
|
assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
|
|
}
|
|
|
|
#[test]
|
|
fn multiple_conditions_on_system_sets() {
|
|
let mut world = World::default();
|
|
let mut schedule = Schedule::default();
|
|
|
|
world.init_resource::<Counter>();
|
|
|
|
schedule.configure_set(TestSet::A.run_if(|| false).run_if(|| false));
|
|
schedule.add_system(counting_system.in_set(TestSet::A));
|
|
schedule.configure_set(TestSet::B.run_if(|| true).run_if(|| false));
|
|
schedule.add_system(counting_system.in_set(TestSet::B));
|
|
schedule.configure_set(TestSet::C.run_if(|| false).run_if(|| true));
|
|
schedule.add_system(counting_system.in_set(TestSet::C));
|
|
schedule.configure_set(TestSet::D.run_if(|| true).run_if(|| true));
|
|
schedule.add_system(counting_system.in_set(TestSet::D));
|
|
|
|
schedule.run(&mut world);
|
|
assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
|
|
}
|
|
|
|
#[test]
|
|
fn systems_nested_in_system_sets() {
|
|
let mut world = World::default();
|
|
let mut schedule = Schedule::default();
|
|
|
|
world.init_resource::<Counter>();
|
|
|
|
schedule.configure_set(TestSet::A.run_if(|| false));
|
|
schedule.add_system(counting_system.in_set(TestSet::A).run_if(|| false));
|
|
schedule.configure_set(TestSet::B.run_if(|| true));
|
|
schedule.add_system(counting_system.in_set(TestSet::B).run_if(|| false));
|
|
schedule.configure_set(TestSet::C.run_if(|| false));
|
|
schedule.add_system(counting_system.in_set(TestSet::C).run_if(|| true));
|
|
schedule.configure_set(TestSet::D.run_if(|| true));
|
|
schedule.add_system(counting_system.in_set(TestSet::D).run_if(|| true));
|
|
|
|
schedule.run(&mut world);
|
|
assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
|
|
}
|
|
|
|
#[test]
|
|
fn system_conditions_and_change_detection() {
|
|
#[derive(Resource, Default)]
|
|
struct Bool2(pub bool);
|
|
|
|
let mut world = World::default();
|
|
world.init_resource::<Counter>();
|
|
world.init_resource::<RunConditionBool>();
|
|
world.init_resource::<Bool2>();
|
|
let mut schedule = Schedule::default();
|
|
|
|
schedule.add_system(
|
|
counting_system
|
|
.run_if(|res1: Res<RunConditionBool>| res1.is_changed())
|
|
.run_if(|res2: Res<Bool2>| res2.is_changed()),
|
|
);
|
|
|
|
// both resource were just added.
|
|
schedule.run(&mut world);
|
|
assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
|
|
|
|
// nothing has changed
|
|
schedule.run(&mut world);
|
|
assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
|
|
|
|
// RunConditionBool has changed, but counting_system did not run
|
|
world.get_resource_mut::<RunConditionBool>().unwrap().0 = false;
|
|
schedule.run(&mut world);
|
|
assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
|
|
|
|
// internal state for the bool2 run criteria was updated in the
|
|
// previous run, so system still does not run
|
|
world.get_resource_mut::<Bool2>().unwrap().0 = false;
|
|
schedule.run(&mut world);
|
|
assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
|
|
|
|
// internal state for bool2 was updated, so system still does not run
|
|
world.get_resource_mut::<RunConditionBool>().unwrap().0 = false;
|
|
schedule.run(&mut world);
|
|
assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
|
|
|
|
// now check that it works correctly changing Bool2 first and then RunConditionBool
|
|
world.get_resource_mut::<Bool2>().unwrap().0 = false;
|
|
world.get_resource_mut::<RunConditionBool>().unwrap().0 = false;
|
|
schedule.run(&mut world);
|
|
assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn system_set_conditions_and_change_detection() {
|
|
#[derive(Resource, Default)]
|
|
struct Bool2(pub bool);
|
|
|
|
let mut world = World::default();
|
|
world.init_resource::<Counter>();
|
|
world.init_resource::<RunConditionBool>();
|
|
world.init_resource::<Bool2>();
|
|
let mut schedule = Schedule::default();
|
|
|
|
schedule.configure_set(
|
|
TestSet::A
|
|
.run_if(|res1: Res<RunConditionBool>| res1.is_changed())
|
|
.run_if(|res2: Res<Bool2>| res2.is_changed()),
|
|
);
|
|
|
|
schedule.add_system(counting_system.in_set(TestSet::A));
|
|
|
|
// both resource were just added.
|
|
schedule.run(&mut world);
|
|
assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
|
|
|
|
// nothing has changed
|
|
schedule.run(&mut world);
|
|
assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
|
|
|
|
// RunConditionBool has changed, but counting_system did not run
|
|
world.get_resource_mut::<RunConditionBool>().unwrap().0 = false;
|
|
schedule.run(&mut world);
|
|
assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
|
|
|
|
// internal state for the bool2 run criteria was updated in the
|
|
// previous run, so system still does not run
|
|
world.get_resource_mut::<Bool2>().unwrap().0 = false;
|
|
schedule.run(&mut world);
|
|
assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
|
|
|
|
// internal state for bool2 was updated, so system still does not run
|
|
world.get_resource_mut::<RunConditionBool>().unwrap().0 = false;
|
|
schedule.run(&mut world);
|
|
assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
|
|
|
|
// the system only runs when both are changed on the same run
|
|
world.get_resource_mut::<Bool2>().unwrap().0 = false;
|
|
world.get_resource_mut::<RunConditionBool>().unwrap().0 = false;
|
|
schedule.run(&mut world);
|
|
assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn mixed_conditions_and_change_detection() {
|
|
#[derive(Resource, Default)]
|
|
struct Bool2(pub bool);
|
|
|
|
let mut world = World::default();
|
|
world.init_resource::<Counter>();
|
|
world.init_resource::<RunConditionBool>();
|
|
world.init_resource::<Bool2>();
|
|
let mut schedule = Schedule::default();
|
|
|
|
schedule
|
|
.configure_set(TestSet::A.run_if(|res1: Res<RunConditionBool>| res1.is_changed()));
|
|
|
|
schedule.add_system(
|
|
counting_system
|
|
.run_if(|res2: Res<Bool2>| res2.is_changed())
|
|
.in_set(TestSet::A),
|
|
);
|
|
|
|
// both resource were just added.
|
|
schedule.run(&mut world);
|
|
assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
|
|
|
|
// nothing has changed
|
|
schedule.run(&mut world);
|
|
assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
|
|
|
|
// RunConditionBool has changed, but counting_system did not run
|
|
world.get_resource_mut::<RunConditionBool>().unwrap().0 = false;
|
|
schedule.run(&mut world);
|
|
assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
|
|
|
|
// we now only change bool2 and the system also should not run
|
|
world.get_resource_mut::<Bool2>().unwrap().0 = false;
|
|
schedule.run(&mut world);
|
|
assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
|
|
|
|
// internal state for the bool2 run criteria was updated in the
|
|
// previous run, so system still does not run
|
|
world.get_resource_mut::<RunConditionBool>().unwrap().0 = false;
|
|
schedule.run(&mut world);
|
|
assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
|
|
|
|
// the system only runs when both are changed on the same run
|
|
world.get_resource_mut::<Bool2>().unwrap().0 = false;
|
|
world.get_resource_mut::<RunConditionBool>().unwrap().0 = false;
|
|
schedule.run(&mut world);
|
|
assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 2);
|
|
}
|
|
}
|
|
|
|
mod schedule_build_errors {
|
|
use super::*;
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn dependency_loop() {
|
|
let mut schedule = Schedule::new();
|
|
schedule.configure_set(TestSet::X.after(TestSet::X));
|
|
}
|
|
|
|
#[test]
|
|
fn dependency_cycle() {
|
|
let mut world = World::new();
|
|
let mut schedule = Schedule::new();
|
|
|
|
schedule.configure_set(TestSet::A.after(TestSet::B));
|
|
schedule.configure_set(TestSet::B.after(TestSet::A));
|
|
|
|
let result = schedule.initialize(&mut world);
|
|
assert!(matches!(result, Err(ScheduleBuildError::DependencyCycle)));
|
|
|
|
fn foo() {}
|
|
fn bar() {}
|
|
|
|
let mut world = World::new();
|
|
let mut schedule = Schedule::new();
|
|
|
|
schedule.add_systems((foo.after(bar), bar.after(foo)));
|
|
let result = schedule.initialize(&mut world);
|
|
assert!(matches!(result, Err(ScheduleBuildError::DependencyCycle)));
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn hierarchy_loop() {
|
|
let mut schedule = Schedule::new();
|
|
schedule.configure_set(TestSet::X.in_set(TestSet::X));
|
|
}
|
|
|
|
#[test]
|
|
fn hierarchy_cycle() {
|
|
let mut world = World::new();
|
|
let mut schedule = Schedule::new();
|
|
|
|
schedule.configure_set(TestSet::A.in_set(TestSet::B));
|
|
schedule.configure_set(TestSet::B.in_set(TestSet::A));
|
|
|
|
let result = schedule.initialize(&mut world);
|
|
assert!(matches!(result, Err(ScheduleBuildError::HierarchyCycle)));
|
|
}
|
|
|
|
#[test]
|
|
fn system_type_set_ambiguity() {
|
|
// Define some systems.
|
|
fn foo() {}
|
|
fn bar() {}
|
|
|
|
let mut world = World::new();
|
|
let mut schedule = Schedule::new();
|
|
|
|
// Schedule `bar` to run after `foo`.
|
|
schedule.add_system(foo);
|
|
schedule.add_system(bar.after(foo));
|
|
|
|
// There's only one `foo`, so it's fine.
|
|
let result = schedule.initialize(&mut world);
|
|
assert!(result.is_ok());
|
|
|
|
// Schedule another `foo`.
|
|
schedule.add_system(foo);
|
|
|
|
// When there are multiple instances of `foo`, dependencies on
|
|
// `foo` are no longer allowed. Too much ambiguity.
|
|
let result = schedule.initialize(&mut world);
|
|
assert!(matches!(
|
|
result,
|
|
Err(ScheduleBuildError::SystemTypeSetAmbiguity(_))
|
|
));
|
|
|
|
// same goes for `ambiguous_with`
|
|
let mut schedule = Schedule::new();
|
|
schedule.add_system(foo);
|
|
schedule.add_system(bar.ambiguous_with(foo));
|
|
let result = schedule.initialize(&mut world);
|
|
assert!(result.is_ok());
|
|
schedule.add_system(foo);
|
|
let result = schedule.initialize(&mut world);
|
|
assert!(matches!(
|
|
result,
|
|
Err(ScheduleBuildError::SystemTypeSetAmbiguity(_))
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn configure_system_type_set() {
|
|
fn foo() {}
|
|
let mut schedule = Schedule::new();
|
|
schedule.configure_set(foo.into_system_set());
|
|
}
|
|
|
|
#[test]
|
|
fn hierarchy_redundancy() {
|
|
let mut world = World::new();
|
|
let mut schedule = Schedule::new();
|
|
|
|
schedule.set_build_settings(ScheduleBuildSettings {
|
|
hierarchy_detection: LogLevel::Error,
|
|
..Default::default()
|
|
});
|
|
|
|
// Add `A`.
|
|
schedule.configure_set(TestSet::A);
|
|
|
|
// Add `B` as child of `A`.
|
|
schedule.configure_set(TestSet::B.in_set(TestSet::A));
|
|
|
|
// Add `X` as child of both `A` and `B`.
|
|
schedule.configure_set(TestSet::X.in_set(TestSet::A).in_set(TestSet::B));
|
|
|
|
// `X` cannot be the `A`'s child and grandchild at the same time.
|
|
let result = schedule.initialize(&mut world);
|
|
assert!(matches!(
|
|
result,
|
|
Err(ScheduleBuildError::HierarchyRedundancy)
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn cross_dependency() {
|
|
let mut world = World::new();
|
|
let mut schedule = Schedule::new();
|
|
|
|
// Add `B` and give it both kinds of relationships with `A`.
|
|
schedule.configure_set(TestSet::B.in_set(TestSet::A));
|
|
schedule.configure_set(TestSet::B.after(TestSet::A));
|
|
let result = schedule.initialize(&mut world);
|
|
assert!(matches!(
|
|
result,
|
|
Err(ScheduleBuildError::CrossDependency(_, _))
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn sets_have_order_but_intersect() {
|
|
let mut world = World::new();
|
|
let mut schedule = Schedule::new();
|
|
|
|
fn foo() {}
|
|
|
|
// Add `foo` to both `A` and `C`.
|
|
schedule.add_system(foo.in_set(TestSet::A).in_set(TestSet::C));
|
|
|
|
// Order `A -> B -> C`.
|
|
schedule.configure_sets((
|
|
TestSet::A,
|
|
TestSet::B.after(TestSet::A),
|
|
TestSet::C.after(TestSet::B),
|
|
));
|
|
|
|
let result = schedule.initialize(&mut world);
|
|
// `foo` can't be in both `A` and `C` because they can't run at the same time.
|
|
assert!(matches!(
|
|
result,
|
|
Err(ScheduleBuildError::SetsHaveOrderButIntersect(_, _))
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn ambiguity() {
|
|
#[derive(Resource)]
|
|
struct X;
|
|
|
|
fn res_ref(_x: Res<X>) {}
|
|
fn res_mut(_x: ResMut<X>) {}
|
|
|
|
let mut world = World::new();
|
|
let mut schedule = Schedule::new();
|
|
|
|
schedule.set_build_settings(ScheduleBuildSettings {
|
|
ambiguity_detection: LogLevel::Error,
|
|
..Default::default()
|
|
});
|
|
|
|
schedule.add_systems((res_ref, res_mut));
|
|
let result = schedule.initialize(&mut world);
|
|
assert!(matches!(result, Err(ScheduleBuildError::Ambiguity)));
|
|
}
|
|
}
|
|
|
|
mod base_sets {
|
|
use super::*;
|
|
|
|
#[derive(SystemSet, Hash, Debug, Eq, PartialEq, Clone)]
|
|
#[system_set(base)]
|
|
enum Base {
|
|
A,
|
|
B,
|
|
}
|
|
|
|
#[derive(SystemSet, Hash, Debug, Eq, PartialEq, Clone)]
|
|
enum Normal {
|
|
X,
|
|
Y,
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn disallow_adding_base_sets_to_sets() {
|
|
let mut schedule = Schedule::new();
|
|
schedule.configure_set(Base::A.in_set(Normal::X));
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn disallow_adding_base_sets_to_base_sets() {
|
|
let mut schedule = Schedule::new();
|
|
schedule.configure_set(Base::A.in_base_set(Base::B));
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn disallow_adding_set_to_multiple_base_sets() {
|
|
let mut schedule = Schedule::new();
|
|
schedule.configure_set(Normal::X.in_base_set(Base::A).in_base_set(Base::B));
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn disallow_adding_sets_to_multiple_base_sets() {
|
|
let mut schedule = Schedule::new();
|
|
schedule.configure_sets(
|
|
(Normal::X, Normal::Y)
|
|
.in_base_set(Base::A)
|
|
.in_base_set(Base::B),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn disallow_adding_system_to_multiple_base_sets() {
|
|
let mut schedule = Schedule::new();
|
|
schedule.add_system(named_system.in_base_set(Base::A).in_base_set(Base::B));
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn disallow_adding_systems_to_multiple_base_sets() {
|
|
let mut schedule = Schedule::new();
|
|
schedule.add_systems(
|
|
(make_function_system(0), make_function_system(1))
|
|
.in_base_set(Base::A)
|
|
.in_base_set(Base::B),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn disallow_multiple_base_sets() {
|
|
let mut world = World::new();
|
|
|
|
let mut schedule = Schedule::new();
|
|
schedule
|
|
.configure_set(Normal::X.in_base_set(Base::A))
|
|
.configure_set(Normal::Y.in_base_set(Base::B))
|
|
.add_system(named_system.in_set(Normal::X).in_set(Normal::Y));
|
|
|
|
let result = schedule.initialize(&mut world);
|
|
assert!(matches!(
|
|
result,
|
|
Err(ScheduleBuildError::SystemInMultipleBaseSets { .. })
|
|
));
|
|
|
|
let mut schedule = Schedule::new();
|
|
schedule
|
|
.configure_set(Normal::X.in_base_set(Base::A))
|
|
.configure_set(Normal::Y.in_base_set(Base::B).in_set(Normal::X));
|
|
|
|
let result = schedule.initialize(&mut world);
|
|
assert!(matches!(
|
|
result,
|
|
Err(ScheduleBuildError::SetInMultipleBaseSets { .. })
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn allow_same_base_sets() {
|
|
let mut world = World::new();
|
|
|
|
let mut schedule = Schedule::new();
|
|
schedule
|
|
.configure_set(Normal::X.in_base_set(Base::A))
|
|
.configure_set(Normal::Y.in_base_set(Base::A))
|
|
.add_system(named_system.in_set(Normal::X).in_set(Normal::Y));
|
|
|
|
let result = schedule.initialize(&mut world);
|
|
assert!(matches!(result, Ok(())));
|
|
|
|
let mut schedule = Schedule::new();
|
|
schedule
|
|
.configure_set(Normal::X.in_base_set(Base::A))
|
|
.configure_set(Normal::Y.in_base_set(Base::A).in_set(Normal::X));
|
|
|
|
let result = schedule.initialize(&mut world);
|
|
assert!(matches!(result, Ok(())));
|
|
}
|
|
|
|
#[test]
|
|
fn default_base_set_ordering() {
|
|
let mut world = World::default();
|
|
let mut schedule = Schedule::default();
|
|
|
|
world.init_resource::<SystemOrder>();
|
|
|
|
schedule
|
|
.set_default_base_set(Base::A)
|
|
.configure_set(Base::A.before(Base::B))
|
|
.add_system(make_function_system(0).in_base_set(Base::B))
|
|
.add_system(make_function_system(1));
|
|
schedule.run(&mut world);
|
|
|
|
assert_eq!(world.resource::<SystemOrder>().0, vec![1, 0]);
|
|
}
|
|
}
|
|
}
|