# Objective - Improve CI when testing rendering by having smarter testbeds ## Solution - CI testing no longer need a config file and will run with a default config if not found - It is now possible to give a name to a screenshot instead of just a frame number - 2d and 3d testbeds are now driven from code - a new system in testbed will watch for state changed - on state changed, trigger a screenshot 100 frames after (so that the scene has time to render) with the name of the scene - when the screenshot is taken (`Captured` component has been removed), switch scene - this means less setup to run a testbed (no need for a config file), screenshots have better names, and it's faster as we don't wait 100 frames for the screenshot to be taken ## Testing - `cargo run --example testbed_2d --features bevy_ci_testing`
89 lines
2.7 KiB
Rust
89 lines
2.7 KiB
Rust
use bevy_ecs::prelude::*;
|
|
use serde::Deserialize;
|
|
|
|
/// A configuration struct for automated CI testing.
|
|
///
|
|
/// It gets used when the `bevy_ci_testing` feature is enabled to automatically
|
|
/// exit a Bevy app when run through the CI. This is needed because otherwise
|
|
/// Bevy apps would be stuck in the game loop and wouldn't allow the CI to progress.
|
|
#[derive(Deserialize, Resource, PartialEq, Debug, Default)]
|
|
pub struct CiTestingConfig {
|
|
/// The setup for this test.
|
|
#[serde(default)]
|
|
pub setup: CiTestingSetup,
|
|
/// Events to send, with their associated frame.
|
|
#[serde(default)]
|
|
pub events: Vec<CiTestingEventOnFrame>,
|
|
}
|
|
|
|
/// Setup for a test.
|
|
#[derive(Deserialize, Default, PartialEq, Debug)]
|
|
pub struct CiTestingSetup {
|
|
/// The amount of time in seconds between frame updates.
|
|
///
|
|
/// This is set through the [`TimeUpdateStrategy::ManualDuration`] resource.
|
|
///
|
|
/// [`TimeUpdateStrategy::ManualDuration`]: bevy_time::TimeUpdateStrategy::ManualDuration
|
|
pub fixed_frame_time: Option<f32>,
|
|
}
|
|
|
|
/// An event to send at a given frame, used for CI testing.
|
|
#[derive(Deserialize, PartialEq, Debug)]
|
|
pub struct CiTestingEventOnFrame(pub u32, pub CiTestingEvent);
|
|
|
|
/// An event to send, used for CI testing.
|
|
#[derive(Deserialize, PartialEq, Debug)]
|
|
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, and saves the results to
|
|
/// `screenshot-{name}.png`.
|
|
NamedScreenshot(String),
|
|
/// Stops the program by sending [`AppExit::Success`].
|
|
///
|
|
/// [`AppExit::Success`]: bevy_app::AppExit::Success
|
|
AppExit,
|
|
/// Sends a [`CiTestingCustomEvent`] using the given [`String`].
|
|
Custom(String),
|
|
}
|
|
|
|
/// A custom event that can be configured from a configuration file for CI testing.
|
|
#[derive(Event)]
|
|
pub struct CiTestingCustomEvent(pub String);
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn deserialize() {
|
|
const INPUT: &str = r#"
|
|
(
|
|
setup: (
|
|
fixed_frame_time: Some(0.03),
|
|
),
|
|
events: [
|
|
(100, Custom("Hello, world!")),
|
|
(200, Screenshot),
|
|
(300, AppExit),
|
|
],
|
|
)"#;
|
|
|
|
let expected = CiTestingConfig {
|
|
setup: CiTestingSetup {
|
|
fixed_frame_time: Some(0.03),
|
|
},
|
|
events: vec![
|
|
CiTestingEventOnFrame(100, CiTestingEvent::Custom("Hello, world!".into())),
|
|
CiTestingEventOnFrame(200, CiTestingEvent::Screenshot),
|
|
CiTestingEventOnFrame(300, CiTestingEvent::AppExit),
|
|
],
|
|
};
|
|
|
|
let config: CiTestingConfig = ron::from_str(INPUT).unwrap();
|
|
|
|
assert_eq!(config, expected);
|
|
}
|
|
}
|