Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 161 additions & 1 deletion crates/bevy_ecs/src/schedule/executor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ mod __rust_begin_short_backtrace {
#[cfg(test)]
mod tests {
use crate::{
prelude::{Component, Resource, Schedule},
prelude::{Component, In, IntoSystem, Resource, Schedule},
schedule::ExecutorKind,
system::{Populated, Res, ResMut, Single},
world::World,
Expand All @@ -336,6 +336,9 @@ mod tests {
single_ran: bool,
}

#[derive(Resource, Default)]
struct Counter(u8);

fn set_single_state(mut _single: Single<&TestComponent>, mut state: ResMut<TestState>) {
state.single_ran = true;
}
Expand Down Expand Up @@ -408,4 +411,161 @@ mod tests {
schedule.add_systems(look_for_missing_resource);
schedule.run(&mut world);
}

#[test]
fn piped_systems_first_system_skipped() {
// This system should be skipped when run due to no matching entity
fn pipe_out(_single: Single<&TestComponent>) -> u8 {
42
}

fn pipe_in(_input: In<u8>, mut counter: ResMut<Counter>) {
counter.0 += 1;
}

let mut world = World::new();
world.init_resource::<Counter>();
let mut schedule = Schedule::default();

schedule.add_systems(pipe_out.pipe(pipe_in));
schedule.run(&mut world);

let counter = world.resource::<Counter>();
assert_eq!(counter.0, 0);
}

#[test]
fn piped_system_second_system_skipped() {
fn pipe_out(mut counter: ResMut<Counter>) -> u8 {
counter.0 += 1;
42
}

// This system should be skipped when run due to no matching entity
fn pipe_in(_input: In<u8>, _single: Single<&TestComponent>) {}

let mut world = World::new();
world.init_resource::<Counter>();
let mut schedule = Schedule::default();

schedule.add_systems(pipe_out.pipe(pipe_in));
schedule.run(&mut world);
let counter = world.resource::<Counter>();
assert_eq!(counter.0, 0);
}

#[test]
#[should_panic]
fn piped_system_first_system_panics() {
// This system should panic when run because the resource is missing
fn pipe_out(_res: Res<TestState>) -> u8 {
42
}

fn pipe_in(_input: In<u8>) {}

let mut world = World::new();
let mut schedule = Schedule::default();

schedule.add_systems(pipe_out.pipe(pipe_in));
schedule.run(&mut world);
}

#[test]
#[should_panic]
fn piped_system_second_system_panics() {
fn pipe_out() -> u8 {
42
}

// This system should panic when run because the resource is missing
fn pipe_in(_input: In<u8>, _res: Res<TestState>) {}

let mut world = World::new();
let mut schedule = Schedule::default();

schedule.add_systems(pipe_out.pipe(pipe_in));
schedule.run(&mut world);
}

#[test]
#[should_panic]
fn piped_system_skip_and_panic() {
// This system should be skipped when run due to no matching entity
fn pipe_out(_single: Single<&TestComponent>) -> u8 {
42
}

// This system should panic when run because the resource is missing
fn pipe_in(_input: In<u8>, _res: Res<TestState>) {}

let mut world = World::new();
let mut schedule = Schedule::default();

schedule.add_systems(pipe_out.pipe(pipe_in));
schedule.run(&mut world);
}

#[test]
#[should_panic]
fn piped_system_panic_and_skip() {
// This system should panic when run because the resource is missing

fn pipe_out(_res: Res<TestState>) -> u8 {
42
}

// This system should be skipped when run due to no matching entity
fn pipe_in(_input: In<u8>, _single: Single<&TestComponent>) {}

let mut world = World::new();
let mut schedule = Schedule::default();

schedule.add_systems(pipe_out.pipe(pipe_in));
schedule.run(&mut world);
}

#[test]
#[should_panic]
fn piped_system_panic_and_panic() {
// This system should panic when run because the resource is missing

fn pipe_out(_res: Res<TestState>) -> u8 {
42
}

// This system should panic when run because the resource is missing
fn pipe_in(_input: In<u8>, _res: Res<TestState>) {}

let mut world = World::new();
let mut schedule = Schedule::default();

schedule.add_systems(pipe_out.pipe(pipe_in));
schedule.run(&mut world);
}

#[test]
fn piped_system_skip_and_skip() {
// This system should be skipped when run due to no matching entity

fn pipe_out(_single: Single<&TestComponent>, mut counter: ResMut<Counter>) -> u8 {
counter.0 += 1;
42
}

// This system should be skipped when run due to no matching entity
fn pipe_in(_input: In<u8>, _single: Single<&TestComponent>, mut counter: ResMut<Counter>) {
counter.0 += 1;
}

let mut world = World::new();
world.init_resource::<Counter>();
let mut schedule = Schedule::default();

schedule.add_systems(pipe_out.pipe(pipe_in));
schedule.run(&mut world);

let counter = world.resource::<Counter>();
assert_eq!(counter.0, 0);
}
}
26 changes: 24 additions & 2 deletions crates/bevy_ecs/src/system/combinator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -421,8 +421,30 @@ where
&mut self,
world: UnsafeWorldCell,
) -> Result<(), SystemParamValidationError> {
// SAFETY: Delegate to other `System` implementations.
unsafe { self.a.validate_param_unsafe(world) }
// SAFETY: Delegate to the `System` implementation for `a`.
let a_validation = unsafe { self.a.validate_param_unsafe(world) };

// SAFETY: Delegate to the `System` implementation for `b`.
let b_validation = unsafe { self.b.validate_param_unsafe(world) };

match (a_validation, b_validation) {
(Ok(()), Ok(())) => Ok(()),
(Ok(_), Err(err_b)) => Err(err_b),
(Err(err_a), Ok(_)) => Err(err_a),
(Err(err_a), Err(err_b)) => {
// Combine the errors from both systems, using the most severe outcome.
// TODO: consider combining the error strings in a more intelligible way
// but be mindful of the possibility of multiple piped systems!
Err(SystemParamValidationError {
// Only skip if both systems should be skipped.
// Panicking systems should not be skipped!
skipped: err_a.skipped && err_b.skipped,
message: err_a.message + err_b.message,
param: err_a.param + err_b.param,
field: err_a.field + err_b.field,
})
}
}
}

fn validate_param(&mut self, world: &World) -> Result<(), SystemParamValidationError> {
Expand Down