use std::time::Duration;
use bevy_log::tracing::{info_span, subscriber};
use bevy_log::tracing_subscriber::Registry;
use bevy_log::tracing_subscriber::layer::SubscriberExt;
use super::{SystemTimings, TimeSystems};
#[test]
fn each_run_of_a_system_is_timed() {
let timings = SystemTimings::default();
let logged = Registry::default().with(TimeSystems(timings.clone()));
subscriber::with_default(logged, || {
let system = info_span!(parent: None, "system", name = "corpus_profiled::busy");
for _ in 0..2 {
let _running = system.enter();
std::thread::sleep(Duration::from_millis(2));
}
let _other = info_span!("calculate conflicting systems").entered();
});
let systems = timings.systems();
let busy = systems.get(&("corpus_profiled::busy".to_owned(), None));
assert_eq!(busy.map(|busy| busy.runs), Some(2), "{systems:?}");
assert!(busy.is_some_and(|busy| busy.max >= Duration::from_millis(2)));
assert_eq!(systems.len(), 1, "{systems:?}");
timings.clear();
assert!(
timings.systems().is_empty(),
"clearing forgets every timing"
);
}
#[cfg(feature = "trace")]
mod trace {
use bevy_app::prelude::*;
use bevy_ecs::prelude::*;
use bevy_ecs::schedule::{ScheduleLabel, SingleThreadedExecutor};
use bevy_ecs::system::RunSystemOnce;
use bevy_log::tracing::subscriber;
use bevy_log::tracing_subscriber::Registry;
use bevy_log::tracing_subscriber::layer::SubscriberExt;
use crate::timings::{PluginTimings, SystemRun, SystemTimings, TimeSystems};
use crate::{PluginEntity, load};
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
struct Pass;
fn busy() {}
fn idle() {}
#[derive(Default)]
struct Busy;
impl Plugin for Busy {
fn build(&self, app: &mut App) {
app.add_systems(Update, busy);
}
}
#[derive(Default)]
struct Passes;
impl Plugin for Passes {
fn build(&self, app: &mut App) {
app.add_systems(Pass, idle)
.add_systems(Update, |world: &mut World| {
world.run_schedule(Pass);
});
}
}
fn timed(timings: &SystemTimings) -> App {
let logged = Registry::default().with(TimeSystems(timings.clone()));
let app = subscriber::with_default(logged, || {
let mut app = App::new();
load::<Busy>(&mut app, "corpus-two", "path corpus/two");
load::<Passes>(&mut app, "corpus-two", "path corpus/two");
for label in [Update.intern(), Pass.intern()] {
app.edit_schedule(label, |schedule| {
schedule.set_executor(SingleThreadedExecutor::new());
});
}
app.world_mut().run_schedule(Update);
app
});
timings.next_frame();
app
}
#[test]
fn each_plugin_of_a_package_is_timed_apart_by_schedule() {
let timings = SystemTimings::default();
let mut app = timed(&timings);
app.insert_resource(timings.clone());
let world = app.world_mut();
let busy = world.resource::<PluginEntity<Busy>>().entity();
let passes = world.resource::<PluginEntity<Passes>>().entity();
let totals = world.run_system_once(|timings: PluginTimings| timings.totals());
let totals = totals.unwrap_or_default();
let runs = |plugin, schedule: &str| {
let key = (Some(plugin), Some(schedule.to_owned()));
totals.get(&key).map(|timing| timing.runs)
};
assert_eq!(runs(busy, "Update"), Some(1), "{totals:?}");
assert_eq!(runs(passes, "Update"), Some(1), "{totals:?}");
assert_eq!(runs(passes, "Pass"), Some(1), "{totals:?}");
}
#[test]
fn the_last_frame_lists_each_run() {
let timings = SystemTimings::default();
let mut app = timed(&timings);
app.insert_resource(timings.clone());
let world = app.world_mut();
let passes = world.resource::<PluginEntity<Passes>>().entity();
let last = world.run_system_once(|timings: PluginTimings| timings.last_frame());
let last = last.unwrap_or_default();
let idle: Vec<&SystemRun> = last
.iter()
.filter(|run| run.system.ends_with("::idle"))
.collect();
assert_eq!(idle.len(), 1, "{last:?}");
let run = idle
.first()
.map(|run| (run.plugin, run.schedule.as_deref()));
assert_eq!(run, Some((Some(passes), Some("Pass"))));
}
}