From 97dcb279fb2a96902bc04286238326b0fa6081ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Wed, 25 Jun 2025 00:44:30 +0200 Subject: [PATCH] CI tests can exit directly after taking a screenshot (#19806) # Objective - Currently, CI tests take a screenshot at frame X and exits at frame Y with X < Y, and both number fixed - This means tests can take longer than they actually need when taking the screenshot is fast, and can fail to take the screenshot when it's taking too long ## Solution - Add a new event `ScreenshotAndExit` that exit directly after the screenshot is saved --- .../bevy_dev_tools/src/ci_testing/config.rs | 3 ++ .../bevy_dev_tools/src/ci_testing/systems.rs | 13 +++++++ tools/example-showcase/src/main.rs | 37 ++++++++++++++++--- 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/crates/bevy_dev_tools/src/ci_testing/config.rs b/crates/bevy_dev_tools/src/ci_testing/config.rs index a2419dfaa5..01ab4f26cd 100644 --- a/crates/bevy_dev_tools/src/ci_testing/config.rs +++ b/crates/bevy_dev_tools/src/ci_testing/config.rs @@ -37,6 +37,9 @@ pub enum CiTestingEvent { /// Takes a screenshot of the entire screen, and saves the results to /// `screenshot-{current_frame}.png`. Screenshot, + /// Takes a screenshot of the entire screen, saves the results to + /// `screenshot-{current_frame}.png`, and exits once the screenshot is taken. + ScreenshotAndExit, /// Takes a screenshot of the entire screen, and saves the results to /// `screenshot-{name}.png`. NamedScreenshot(String), diff --git a/crates/bevy_dev_tools/src/ci_testing/systems.rs b/crates/bevy_dev_tools/src/ci_testing/systems.rs index f9570133c0..68a8615fea 100644 --- a/crates/bevy_dev_tools/src/ci_testing/systems.rs +++ b/crates/bevy_dev_tools/src/ci_testing/systems.rs @@ -21,6 +21,19 @@ pub(crate) fn send_events(world: &mut World, mut current_frame: Local) { world.send_event(AppExit::Success); info!("Exiting after {} frames. Test successful!", *current_frame); } + CiTestingEvent::ScreenshotAndExit => { + let this_frame = *current_frame; + world.spawn(Screenshot::primary_window()).observe( + move |captured: On, + mut exit_event: EventWriter| { + let path = format!("./screenshot-{}.png", this_frame); + save_to_disk(path)(captured); + info!("Exiting. Test successful!"); + exit_event.write(AppExit::Success); + }, + ); + info!("Took a screenshot at frame {}.", *current_frame); + } CiTestingEvent::Screenshot => { let path = format!("./screenshot-{}.png", *current_frame); world diff --git a/tools/example-showcase/src/main.rs b/tools/example-showcase/src/main.rs index 9ea4ad2c7e..f6d99f7331 100644 --- a/tools/example-showcase/src/main.rs +++ b/tools/example-showcase/src/main.rs @@ -55,6 +55,12 @@ enum Action { /// This defaults to frame 250. Set it to 0 to not stop the example automatically. stop_frame: u32, + #[arg(long, default_value = "false")] + /// Automatically ends after taking a screenshot + /// + /// Only works if `screenshot-frame` is set to non-0, and overrides `stop-frame`. + auto_stop_frame: bool, + #[arg(long)] /// Which frame to take a screenshot at. Set to 0 for no screenshot. screenshot_frame: u32, @@ -150,6 +156,7 @@ fn main() { Action::Run { wgpu_backend, stop_frame, + auto_stop_frame, screenshot_frame, fixed_frame_time, in_ci, @@ -183,11 +190,21 @@ fn main() { let mut extra_parameters = vec![]; - match (stop_frame, screenshot_frame) { + match (stop_frame, screenshot_frame, auto_stop_frame) { // When the example does not automatically stop nor take a screenshot. - (0, 0) => (), + (0, 0, _) => (), + // When the example automatically stops at an automatic frame. + (0, _, true) => { + let mut file = File::create("example_showcase_config.ron").unwrap(); + file.write_all( + format!("(setup: (fixed_frame_time: Some({fixed_frame_time})), events: [({screenshot_frame}, ScreenshotAndExit)])").as_bytes(), + ) + .unwrap(); + extra_parameters.push("--features"); + extra_parameters.push("bevy_ci_testing"); + } // When the example does not automatically stop. - (0, _) => { + (0, _, false) => { let mut file = File::create("example_showcase_config.ron").unwrap(); file.write_all( format!("(setup: (fixed_frame_time: Some({fixed_frame_time})), events: [({screenshot_frame}, Screenshot)])").as_bytes(), @@ -197,15 +214,25 @@ fn main() { extra_parameters.push("bevy_ci_testing"); } // When the example does not take a screenshot. - (_, 0) => { + (_, 0, _) => { let mut file = File::create("example_showcase_config.ron").unwrap(); file.write_all(format!("(events: [({stop_frame}, AppExit)])").as_bytes()) .unwrap(); extra_parameters.push("--features"); extra_parameters.push("bevy_ci_testing"); } + // When the example both automatically stops at an automatic frame and takes a screenshot. + (_, _, true) => { + let mut file = File::create("example_showcase_config.ron").unwrap(); + file.write_all( + format!("(setup: (fixed_frame_time: Some({fixed_frame_time})), events: [({screenshot_frame}, ScreenshotAndExit)])").as_bytes(), + ) + .unwrap(); + extra_parameters.push("--features"); + extra_parameters.push("bevy_ci_testing"); + } // When the example both automatically stops and takes a screenshot. - (_, _) => { + (_, _, false) => { let mut file = File::create("example_showcase_config.ron").unwrap(); file.write_all( format!("(setup: (fixed_frame_time: Some({fixed_frame_time})), events: [({screenshot_frame}, Screenshot), ({stop_frame}, AppExit)])").as_bytes(),