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

Smarter testbeds #17573

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
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
8 changes: 0 additions & 8 deletions .github/example-run/testbed_2d.ron
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
(
events: [
(100, Screenshot),
(200, Custom("switch_scene")),
(300, Screenshot),
(400, Custom("switch_scene")),
(500, Screenshot),
(600, Custom("switch_scene")),
(700, Screenshot),
(800, AppExit),
]
)
8 changes: 0 additions & 8 deletions .github/example-run/testbed_3d.ron
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
(
events: [
(100, Screenshot),
(200, Custom("switch_scene")),
(300, Screenshot),
(400, Custom("switch_scene")),
(500, Screenshot),
(600, Custom("switch_scene")),
(700, Screenshot),
(800, AppExit),
]
)
5 changes: 4 additions & 1 deletion crates/bevy_dev_tools/src/ci_testing/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use serde::Deserialize;
/// 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)]
#[derive(Deserialize, Resource, PartialEq, Debug, Default)]
pub struct CiTestingConfig {
/// The setup for this test.
#[serde(default)]
Expand Down Expand Up @@ -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, and saves the results to
/// `screenshot-{name}.png`.
NamedScreenshot(String),
/// Stops the program by sending [`AppExit::Success`].
///
/// [`AppExit::Success`]: bevy_app::AppExit::Success
Expand Down
11 changes: 6 additions & 5 deletions crates/bevy_dev_tools/src/ci_testing/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@ impl Plugin for CiTestingPlugin {
let config: CiTestingConfig = {
let filename = std::env::var("CI_TESTING_CONFIG")
.unwrap_or_else(|_| "ci_testing_config.ron".to_string());
ron::from_str(
&std::fs::read_to_string(filename)
.expect("error reading CI testing configuration file"),
)
.expect("error deserializing CI testing configuration file")
std::fs::read_to_string(filename)
.map(|content| {
ron::from_str(&content)
.expect("error deserializing CI testing configuration file")
})
.unwrap_or_default()
};

#[cfg(target_arch = "wasm32")]
Expand Down
10 changes: 10 additions & 0 deletions crates/bevy_dev_tools/src/ci_testing/systems.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ pub(crate) fn send_events(world: &mut World, mut current_frame: Local<u32>) {
.observe(save_to_disk(path));
info!("Took a screenshot at frame {}.", *current_frame);
}
CiTestingEvent::NamedScreenshot(name) => {
let path = format!("./screenshot-{}.png", name);
world
.spawn(Screenshot::primary_window())
.observe(save_to_disk(path));
info!(
"Took a screenshot at frame {} for {}.",
*current_frame, name
);
}
// Custom events are forwarded to the world.
CiTestingEvent::Custom(event_string) => {
world.send_event(CiTestingCustomEvent(event_string));
Expand Down
56 changes: 44 additions & 12 deletions examples/testbed/2d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@
//!
//! You can switch scene by pressing the spacebar

#[cfg(feature = "bevy_ci_testing")]
use bevy::dev_tools::ci_testing::CiTestingCustomEvent;
use bevy::prelude::*;
#[cfg(feature = "bevy_ci_testing")]
use bevy::{
dev_tools::ci_testing::{CiTestingConfig, CiTestingEvent, CiTestingEventOnFrame},
diagnostic::FrameCount,
platform_support::collections::HashSet,
render::view::screenshot::Captured,
};

fn main() {
let mut app = App::new();
Expand All @@ -15,6 +20,10 @@ fn main() {
.add_systems(OnEnter(Scene::Text), text::setup)
.add_systems(OnEnter(Scene::Sprite), sprite::setup)
.add_systems(Update, switch_scene);

#[cfg(feature = "bevy_ci_testing")]
app.add_systems(Update, switch_scene_in_ci.before(switch_scene));

app.run();
}

Expand All @@ -28,21 +37,44 @@ enum Scene {
Sprite,
}

#[cfg(feature = "bevy_ci_testing")]
fn switch_scene_in_ci(
mut ci_config: ResMut<CiTestingConfig>,
scene: Res<State<Scene>>,
mut scenes_visited: Local<HashSet<Scene>>,
mut keyboard: ResMut<ButtonInput<KeyCode>>,
frame_count: Res<FrameCount>,
captured: RemovedComponents<Captured>,
) {
if scene.is_changed() {
// Changed scene! trigger a screenshot in 100 frames, and reset keyboard state
ci_config.events.push(CiTestingEventOnFrame(
frame_count.0 + 100,
CiTestingEvent::NamedScreenshot(format!("{:?}", scene.get())),
));
keyboard.release(KeyCode::Space);
mockersf marked this conversation as resolved.
Show resolved Hide resolved
return;
}

if !captured.is_empty() {
// Screenshot taken! Switch to the next scene
if scenes_visited.insert(scene.get().clone()) {
keyboard.press(KeyCode::Space);
} else {
ci_config.events.push(CiTestingEventOnFrame(
frame_count.0 + 1,
CiTestingEvent::AppExit,
));
}
}
}

fn switch_scene(
keyboard: Res<ButtonInput<KeyCode>>,
#[cfg(feature = "bevy_ci_testing")] mut ci_events: EventReader<CiTestingCustomEvent>,
scene: Res<State<Scene>>,
mut next_scene: ResMut<NextState<Scene>>,
) {
let mut should_switch = false;
should_switch |= keyboard.just_pressed(KeyCode::Space);
#[cfg(feature = "bevy_ci_testing")]
{
should_switch |= ci_events.read().any(|event| match event {
CiTestingCustomEvent(event) => event == "switch_scene",
});
}
if should_switch {
if keyboard.just_pressed(KeyCode::Space) {
info!("Switching scene");
next_scene.set(match scene.get() {
Scene::Shapes => Scene::Bloom,
Expand Down
60 changes: 44 additions & 16 deletions examples/testbed/3d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,27 @@
//!
//! You can switch scene by pressing the spacebar

#[cfg(feature = "bevy_ci_testing")]
use bevy::dev_tools::ci_testing::CiTestingCustomEvent;
use bevy::prelude::*;
#[cfg(feature = "bevy_ci_testing")]
use bevy::{
dev_tools::ci_testing::{CiTestingConfig, CiTestingEvent, CiTestingEventOnFrame},
diagnostic::FrameCount,
platform_support::collections::HashSet,
render::view::screenshot::Captured,
};

fn main() {
let mut app = App::new();
app.add_plugins((DefaultPlugins,))
.init_state::<Scene>()
.add_systems(OnEnter(Scene::Light), light::setup)
.add_systems(OnEnter(Scene::Animation), animation::setup)
.add_systems(OnEnter(Scene::Bloom), bloom::setup)
.add_systems(OnEnter(Scene::Gltf), gltf::setup)
.add_systems(Update, switch_scene);

// Those scenes don't work in CI on Windows runners
#[cfg(not(all(feature = "bevy_ci_testing", target_os = "windows")))]
app.add_systems(OnEnter(Scene::Bloom), bloom::setup)
.add_systems(OnEnter(Scene::Gltf), gltf::setup);
#[cfg(feature = "bevy_ci_testing")]
app.add_systems(Update, switch_scene_in_ci.before(switch_scene));

app.run();
}
Expand All @@ -32,21 +37,44 @@ enum Scene {
Animation,
}

#[cfg(feature = "bevy_ci_testing")]
fn switch_scene_in_ci(
mut ci_config: ResMut<CiTestingConfig>,
scene: Res<State<Scene>>,
mut scenes_visited: Local<HashSet<Scene>>,
mut keyboard: ResMut<ButtonInput<KeyCode>>,
frame_count: Res<FrameCount>,
captured: RemovedComponents<Captured>,
) {
if scene.is_changed() {
// Changed scene! trigger a screenshot in 100 frames, and reset keyboard state
ci_config.events.push(CiTestingEventOnFrame(
frame_count.0 + 100,
CiTestingEvent::NamedScreenshot(format!("{:?}", scene.get())),
));
keyboard.release(KeyCode::Space);
return;
}

if !captured.is_empty() {
// Screenshot taken! Switch to the next scene
if scenes_visited.insert(scene.get().clone()) {
keyboard.press(KeyCode::Space);
} else {
ci_config.events.push(CiTestingEventOnFrame(
frame_count.0 + 1,
CiTestingEvent::AppExit,
));
}
}
}

fn switch_scene(
keyboard: Res<ButtonInput<KeyCode>>,
#[cfg(feature = "bevy_ci_testing")] mut ci_events: EventReader<CiTestingCustomEvent>,
scene: Res<State<Scene>>,
mut next_scene: ResMut<NextState<Scene>>,
) {
let mut should_switch = false;
should_switch |= keyboard.just_pressed(KeyCode::Space);
#[cfg(feature = "bevy_ci_testing")]
{
should_switch |= ci_events.read().any(|event| match event {
CiTestingCustomEvent(event) => event == "switch_scene",
});
}
if should_switch {
if keyboard.just_pressed(KeyCode::Space) {
info!("Switching scene");
next_scene.set(match scene.get() {
Scene::Light => Scene::Bloom,
Expand Down
Loading