Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add RunSystem #9366

Merged
merged 3 commits into from
Aug 11, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
11 changes: 11 additions & 0 deletions crates/bevy_app/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use bevy_ecs::{
run_enter_schedule, BoxedScheduleLabel, IntoSystemConfigs, IntoSystemSetConfigs,
ScheduleLabel,
},
system::RunSystem,
};
use bevy_utils::{tracing::debug, HashMap, HashSet};
use std::{
Expand Down Expand Up @@ -874,6 +875,16 @@ fn run_once(mut app: App) {
#[derive(Event, Debug, Clone, Default)]
pub struct AppExit;

impl RunSystem for &mut App {
fn run_system_with<T: IntoSystem<In, Out, Marker>, In, Out, Marker>(
self,
input: In,
system: T,
) -> Out {
self.world.run_system_with(input, system)
}
}

#[cfg(test)]
mod tests {
use bevy_ecs::{
Expand Down
102 changes: 102 additions & 0 deletions crates/bevy_ecs/src/system/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -164,3 +166,103 @@ 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);
/// ```
Zeenobit marked this conversation as resolved.
Show resolved Hide resolved
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);
}
}