1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-29 04:40:03 -04:00
Files
egui/crates/egui_kittest/tests/recording.rs
2026-08-11 15:59:34 +02:00

182 lines
5.2 KiB
Rust

#![cfg(all(feature = "recording", feature = "wgpu"))]
use egui_kittest::{Harness, RecordingOptions, RecordingPlugin, RecordingTrigger};
use kittest::Queryable as _;
use tempfile::tempdir;
fn counter_harness(value: &mut u32) -> Harness<'_, &mut u32> {
Harness::builder()
.with_size(egui::Vec2::new(120.0, 60.0))
.build_ui_state(
|ui, state| {
if ui.button(format!("count: {state}")).clicked() {
**state += 1;
}
},
value,
)
}
fn count_pngs(dir: &std::path::Path) -> usize {
std::fs::read_dir(dir)
.expect("png output dir exists")
.filter_map(Result::ok)
.filter(|entry| entry.path().extension().is_some_and(|ext| ext == "png"))
.count()
}
#[test]
fn records_a_gif() {
let dir = tempdir().expect("tempdir");
let gif_path = dir.path().join("counter.gif");
let mut value = 0;
let mut harness = counter_harness(&mut value);
harness.start_recording(RecordingOptions::gif(&gif_path, 12.0));
harness.run();
harness.get_by_label_contains("count").click();
harness.run();
assert!(harness.is_recording());
harness.finish_recording().expect("save gif");
assert!(!harness.is_recording());
let frames = decode_gif_frames(&gif_path);
assert!(
frames >= 2,
"the click should have produced at least two different frames, got {frames}"
);
}
fn decode_gif_frames(path: &std::path::Path) -> usize {
use image::AnimationDecoder as _;
let file = std::io::BufReader::new(std::fs::File::open(path).expect("gif exists"));
image::codecs::gif::GifDecoder::new(file)
.expect("decode gif")
.into_frames()
.count()
}
#[test]
fn records_a_png_sequence() {
let dir = tempdir().expect("tempdir");
let out = dir.path().join("frames");
let mut value = 0;
let mut harness = counter_harness(&mut value);
harness.start_recording(
RecordingOptions::png_sequence(&out).with_trigger(RecordingTrigger::EveryFrame),
);
harness.run();
harness.get_by_label_contains("count").click();
harness.run();
harness.finish_recording().expect("save png sequence");
assert!(count_pngs(&out) > 0, "expected at least one frame");
}
#[test]
fn changed_frames_drops_identical_frames() {
let dir = tempdir().expect("tempdir");
let out = dir.path().join("frames");
let mut value = 0;
let mut harness = counter_harness(&mut value);
harness.start_recording(
RecordingOptions::png_sequence(&out).with_trigger(RecordingTrigger::ChangedFrames),
);
for _ in 0..6 {
harness.run();
}
harness.finish_recording().expect("save png sequence");
assert_eq!(
count_pngs(&out),
1,
"nothing changed, so only the first frame should be kept"
);
}
#[test]
fn every_nth_frame_skips_frames() {
let mut value = 0;
let mut harness = counter_harness(&mut value);
harness.start_recording(
RecordingOptions::gif(std::path::PathBuf::new(), 10.0)
.with_trigger(RecordingTrigger::EveryNthFrame(2)),
);
harness.run_steps(4);
let frames = harness
.with_recording(|plugin| plugin.frames().len())
.expect("the plugin is registered");
assert_eq!(frames, 2, "every second of the 4 passes should be captured");
}
#[test]
fn finishing_without_recording_is_an_error() {
let mut value = 0;
let mut harness = counter_harness(&mut value);
let err = harness.finish_recording().expect_err("not recording");
assert!(matches!(
err,
egui_kittest::RecordingError::NotRecording | egui_kittest::RecordingError::NoFrames
));
}
#[test]
fn recording_without_frames_is_an_error() {
let dir = tempdir().expect("tempdir");
let mut value = 0;
let mut harness = counter_harness(&mut value);
harness.start_recording(RecordingOptions::gif(dir.path().join("empty.gif"), 10.0));
let err = harness.finish_recording().expect_err("no frames");
assert!(matches!(err, egui_kittest::RecordingError::NoFrames));
}
/// The recorder is a plain [`egui::Plugin`]: it records any [`egui::Context`],
/// with no harness and no renderer of your own.
#[test]
fn records_a_plain_context() {
let dir = tempdir().expect("tempdir");
let gif_path = dir.path().join("plain.gif");
let ctx = egui::Context::default();
ctx.add_plugin(RecordingPlugin::new(RecordingOptions::gif(&gif_path, 10.0)));
let input = egui::RawInput {
screen_rect: Some(egui::Rect::from_min_size(
egui::Pos2::ZERO,
egui::Vec2::new(120.0, 60.0),
)),
..Default::default()
};
for pass in 0..3 {
let output = ctx.run_ui(input.clone(), |ui| {
ui.label(format!("pass {pass}"));
});
// We have no renderer of our own; the plugin already rendered what it needed.
output.drop_without_applying_deltas();
}
let frames = ctx
.with_plugin::<RecordingPlugin, _>(|plugin| {
plugin.save().expect("save gif");
plugin.frames().len()
})
.expect("the plugin is registered");
assert_eq!(frames, 3, "each pass shows a different label");
assert!(gif_path.exists(), "the GIF should have been written");
}