Add RunSystem
(#9366)
Add a `RunSystem` extension trait to allow for immediate execution of systems on a `World` for debugging and/or testing purposes. # Objective Fixes #6184 Initially, I made this CL as `ApplyCommands`. After a discussion with @cart , we decided a more generic implementation would be better to support all systems. This is the new revised CL. Sorry for the long delay! 😅 This CL allows users to do this: ```rust use bevy::prelude::*; use bevy::ecs::system::RunSystem; struct T(usize); impl Resource for T {} fn system(In(n): In<usize>, mut commands: Commands) -> usize { commands.insert_resource(T(n)); n + 1 } let mut world = World::default(); let n = world.run_system_with(1, system); assert_eq!(n, 2); assert_eq!(world.resource::<T>().0, 1); ``` ## Solution This is implemented as a trait extension and not included in any preludes to ensure it's being used consciously. Internally, it just initializes and runs a systems, and applies any deferred parameters all "in place". The trait has 2 functions (one of which calls the other by default): - `run_system_with` is the general implementation, which allows user to pass system input parameters - `run_system` is the ergonomic wrapper for systems with no input parameter (to avoid having the user pass `()` as input). ~~Additionally, this trait is also implemented for `&mut App`. I added this mainly for ergonomics (`app.run_system` vs. `app.world.run_system`).~~ (Removed based on feedback) --------- Co-authored-by: Pascal Hertleif <killercup@gmail.com>
This commit is contained in:
parent
37915e1d93
commit
1e170d2e90
@ -8,6 +8,8 @@ use crate::{archetype::ArchetypeComponentId, component::ComponentId, query::Acce
|
||||
use std::any::TypeId;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use super::IntoSystem;
|
||||
|
||||
/// An ECS system that can be added to a [`Schedule`](crate::schedule::Schedule)
|
||||
///
|
||||
/// Systems are functions with all arguments implementing
|
||||
@ -164,3 +166,125 @@ impl Debug for dyn System<In = (), Out = ()> {
|
||||
},)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait used to run a system immediately on a [`World`].
|
||||
///
|
||||
/// # Warning
|
||||
/// This function is not an efficient method of running systems and its meant to be used as a utility
|
||||
/// for testing and/or diagnostics.
|
||||
///
|
||||
/// # Usage
|
||||
/// Typically, to test a system, or to extract specific diagnostics information from a world,
|
||||
/// you'd need a [`Schedule`](crate::schedule::Schedule) to run the system. This can create redundant boilerplate code
|
||||
/// when writing tests or trying to quickly iterate on debug specific systems.
|
||||
///
|
||||
/// For these situations, this function can be useful because it allows you to execute a system
|
||||
/// immediately with some custom input and retrieve its output without requiring the necessary boilerplate.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ## Immediate Command Execution
|
||||
///
|
||||
/// This usage is helpful when trying to test systems or functions that operate on [`Commands`](crate::system::Commands):
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// # use bevy_ecs::system::RunSystem;
|
||||
/// let mut world = World::default();
|
||||
/// let entity = world.run_system(|mut commands: Commands| {
|
||||
/// commands.spawn_empty().id()
|
||||
/// });
|
||||
/// # assert!(world.get_entity(entity).is_some());
|
||||
/// ```
|
||||
///
|
||||
/// ## Immediate Queries
|
||||
///
|
||||
/// This usage is helpful when trying to run an arbitrary query on a world for testing or debugging purposes:
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// # use bevy_ecs::system::RunSystem;
|
||||
///
|
||||
/// #[derive(Component)]
|
||||
/// struct T(usize);
|
||||
///
|
||||
/// let mut world = World::default();
|
||||
/// world.spawn(T(0));
|
||||
/// world.spawn(T(1));
|
||||
/// world.spawn(T(1));
|
||||
/// let count = world.run_system(|query: Query<&T>| {
|
||||
/// query.iter().filter(|t| t.0 == 1).count()
|
||||
/// });
|
||||
///
|
||||
/// # assert_eq!(count, 2);
|
||||
/// ```
|
||||
///
|
||||
/// Note that instead of closures you can also pass in regular functions as systems:
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// # use bevy_ecs::system::RunSystem;
|
||||
///
|
||||
/// #[derive(Component)]
|
||||
/// struct T(usize);
|
||||
///
|
||||
/// fn count(query: Query<&T>) -> usize {
|
||||
/// query.iter().filter(|t| t.0 == 1).count()
|
||||
/// }
|
||||
///
|
||||
/// let mut world = World::default();
|
||||
/// world.spawn(T(0));
|
||||
/// world.spawn(T(1));
|
||||
/// world.spawn(T(1));
|
||||
/// let count = world.run_system(count);
|
||||
///
|
||||
/// # assert_eq!(count, 2);
|
||||
/// ```
|
||||
pub trait RunSystem: Sized {
|
||||
/// Runs a system and applies its deferred parameters.
|
||||
fn run_system<T: IntoSystem<(), Out, Marker>, Out, Marker>(self, system: T) -> Out {
|
||||
self.run_system_with((), system)
|
||||
}
|
||||
|
||||
/// Runs a system with given input and applies its deferred parameters.
|
||||
fn run_system_with<T: IntoSystem<In, Out, Marker>, In, Out, Marker>(
|
||||
self,
|
||||
input: In,
|
||||
system: T,
|
||||
) -> Out;
|
||||
}
|
||||
|
||||
impl RunSystem for &mut World {
|
||||
fn run_system_with<T: IntoSystem<In, Out, Marker>, In, Out, Marker>(
|
||||
self,
|
||||
input: In,
|
||||
system: T,
|
||||
) -> Out {
|
||||
let mut system: T::System = IntoSystem::into_system(system);
|
||||
system.initialize(self);
|
||||
let out = system.run(input, self);
|
||||
system.apply_deferred(self);
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn run_system() {
|
||||
struct T(usize);
|
||||
|
||||
impl Resource for T {}
|
||||
|
||||
fn system(In(n): In<usize>, mut commands: Commands) -> usize {
|
||||
commands.insert_resource(T(n));
|
||||
n + 1
|
||||
}
|
||||
|
||||
let mut world = World::default();
|
||||
let n = world.run_system_with(1, system);
|
||||
assert_eq!(n, 2);
|
||||
assert_eq!(world.resource::<T>().0, 1);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user