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

egui_kittest: Allow passing state to the app closure #5313

Merged
merged 60 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
c50394a
Add egui testing library
lucasmerlin Sep 24, 2024
d5dedba
Improved usability
lucasmerlin Sep 25, 2024
608e4df
Rename and move kittest to external crate
lucasmerlin Sep 25, 2024
6f2691f
Enable git lfs for snapshots files
lucasmerlin Sep 25, 2024
e791a74
Add widget_gallery.png
lucasmerlin Sep 25, 2024
0853a52
Fix Cargo.toml formatting
lucasmerlin Sep 25, 2024
661bc3e
Add snapshot tests for all demos
lucasmerlin Sep 25, 2024
87cdb17
Move demo snapshots
lucasmerlin Sep 26, 2024
ee3e766
Update kittest
lucasmerlin Sep 26, 2024
e63ffa3
Add text_edit.rs test and fix ime input
lucasmerlin Sep 28, 2024
bba834b
Typo
lucasmerlin Sep 28, 2024
b2db13d
Run tests with gpu
lucasmerlin Sep 28, 2024
94d0b84
Improve snapshot error
lucasmerlin Sep 28, 2024
f26e98d
Test if snapshots fail in ci
lucasmerlin Sep 28, 2024
02f734c
Update dify
lucasmerlin Oct 4, 2024
80381ab
Fix lints
lucasmerlin Oct 4, 2024
7cbf557
Try if tests fail on style change
lucasmerlin Oct 4, 2024
d478162
Upload snapshots as artifacts
lucasmerlin Oct 4, 2024
10c0767
Fail tests on missing snapshot
lucasmerlin Oct 4, 2024
b4f7129
Checkout with lfs
lucasmerlin Oct 4, 2024
b00315b
Always upload artifacts
lucasmerlin Oct 4, 2024
2ac2d85
Update dify
lucasmerlin Oct 4, 2024
d7b874e
"un-break" tests
lucasmerlin Oct 4, 2024
66a6c00
Fix lint
lucasmerlin Oct 4, 2024
e0d8263
Always run egui_demo_lib tests with chrono feature
lucasmerlin Oct 5, 2024
e1a8196
Add Harness::builder and add README.md
lucasmerlin Oct 5, 2024
a5994d8
Add documentation
lucasmerlin Oct 5, 2024
dcdc601
Fixes after rebase
lucasmerlin Oct 5, 2024
76aa7ad
Fixes tests after rebase (where do those dots come from?)
lucasmerlin Oct 5, 2024
da32e8c
Checkout lfs
lucasmerlin Oct 5, 2024
d0a307d
Only run tests on macos
lucasmerlin Oct 9, 2024
0d4cebf
Set fixed date in widget_gallery test
lucasmerlin Oct 9, 2024
84977aa
Fix doc
lucasmerlin Oct 9, 2024
2d411a8
Fix deny
lucasmerlin Oct 9, 2024
cac6db8
Fix doc tests
lucasmerlin Oct 9, 2024
311d447
Refactor events and impl debug for Harness
lucasmerlin Oct 10, 2024
ddf79ce
Use kittest from main
lucasmerlin Oct 11, 2024
148e79a
Review changes
lucasmerlin Oct 12, 2024
24f29d6
Remember mouse position
lucasmerlin Oct 12, 2024
27414ea
Add comment about threshold and for the SnapshotError variants
lucasmerlin Oct 12, 2024
b2fe731
Improved snapshot error handling
lucasmerlin Oct 12, 2024
554fc57
Rename texture_to_bytes
lucasmerlin Oct 12, 2024
97ecb25
Round screen size
lucasmerlin Oct 12, 2024
c9d1bea
Add step function
lucasmerlin Oct 12, 2024
2b1fafa
Add more convenient snapshot api, allow reusing the TestRenderer and …
lucasmerlin Oct 12, 2024
df24f74
lints
lucasmerlin Oct 12, 2024
e824e01
Update example snapshot
lucasmerlin Oct 12, 2024
7cad2da
Add Harness::fit_contents and add rendering_test snapshot test
lucasmerlin Oct 16, 2024
54cf256
Merge branch 'master' into lucas/basic_tests
lucasmerlin Oct 23, 2024
7f2c2ff
Fix lints and test_shrink
lucasmerlin Oct 23, 2024
482921f
Add more doc comments
lucasmerlin Oct 23, 2024
aadf47f
Rename dpi to pixels per point
lucasmerlin Oct 29, 2024
3d59719
Make AppKind private
lucasmerlin Oct 29, 2024
703c702
Add 8px transparent border around ui tests to visualize widgets paint…
lucasmerlin Oct 29, 2024
048c69a
Fix docs
lucasmerlin Oct 29, 2024
c93846c
Fix cargo test in check.sh (#5299)
lucasmerlin Oct 24, 2024
1c60bb5
egui_kittest: Allow passing state to the app closure
lucasmerlin Oct 27, 2024
e38fe1d
Store state in the harness
lucasmerlin Oct 29, 2024
2b59ce9
Merge branch 'master' into lucas/kittest_state
lucasmerlin Nov 1, 2024
cd23048
Rename S to State
lucasmerlin Nov 6, 2024
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
16 changes: 10 additions & 6 deletions crates/egui_demo_lib/src/demo/text_edit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,15 @@ mod tests {

#[test]
pub fn should_type() {
let mut text = "Hello, world!".to_owned();
let mut harness = Harness::new(move |ctx| {
CentralPanel::default().show(ctx, |ui| {
ui.text_edit_singleline(&mut text);
});
});
let text = "Hello, world!".to_owned();
let mut harness = Harness::new_state(
move |ctx, text| {
CentralPanel::default().show(ctx, |ui| {
ui.text_edit_singleline(text);
});
},
text,
);

harness.run();

Expand All @@ -144,5 +147,6 @@ mod tests {
harness.run();
let text_edit = harness.get_by_role(accesskit::Role::TextInput);
assert_eq!(text_edit.value().as_deref(), Some("Hi there!"));
assert_eq!(harness.state(), "Hi there!");
}
}
42 changes: 28 additions & 14 deletions crates/egui_kittest/src/app_kind.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
use egui::Frame;

type AppKindContextState<'a, State> = Box<dyn FnMut(&egui::Context, &mut State) + 'a>;
type AppKindUiState<'a, State> = Box<dyn FnMut(&mut egui::Ui, &mut State) + 'a>;
type AppKindContext<'a> = Box<dyn FnMut(&egui::Context) + 'a>;
type AppKindUi<'a> = Box<dyn FnMut(&mut egui::Ui) + 'a>;

pub(crate) enum AppKind<'a> {
pub(crate) enum AppKind<'a, State> {
Context(AppKindContext<'a>),
Ui(AppKindUi<'a>),
ContextState(AppKindContextState<'a, State>),
UiState(AppKindUiState<'a, State>),
}

// TODO(lucasmerlin): These aren't working unfortunately :(
Expand All @@ -32,28 +36,34 @@ pub(crate) enum AppKind<'a> {
// }
// }

impl<'a> AppKind<'a> {
pub fn run(&mut self, ctx: &egui::Context) -> Option<egui::Response> {
impl<'a, State> AppKind<'a, State> {
pub fn run(
&mut self,
ctx: &egui::Context,
state: &mut State,
sizing_pass: bool,
) -> Option<egui::Response> {
match self {
AppKind::Context(f) => {
debug_assert!(!sizing_pass, "Context closures cannot do a sizing pass");
f(ctx);
None
}
AppKind::Ui(f) => Some(Self::run_ui(f, ctx, false)),
}
}

pub(crate) fn run_sizing_pass(&mut self, ctx: &egui::Context) -> Option<egui::Response> {
match self {
AppKind::Context(f) => {
f(ctx);
AppKind::ContextState(f) => {
debug_assert!(!sizing_pass, "Context closures cannot do a sizing pass");
f(ctx, state);
None
}
AppKind::Ui(f) => Some(Self::run_ui(f, ctx, true)),
kind_ui => Some(kind_ui.run_ui(ctx, state, sizing_pass)),
}
}

fn run_ui(f: &mut AppKindUi<'a>, ctx: &egui::Context, sizing_pass: bool) -> egui::Response {
fn run_ui(
&mut self,
ctx: &egui::Context,
state: &mut State,
sizing_pass: bool,
) -> egui::Response {
egui::CentralPanel::default()
.frame(Frame::none())
.show(ctx, |ui| {
Expand All @@ -65,7 +75,11 @@ impl<'a> AppKind<'a> {
Frame::central_panel(ui.style())
.outer_margin(8.0)
.inner_margin(0.0)
.show(ui, |ui| f(ui));
.show(ui, |ui| match self {
AppKind::Ui(f) => f(ui),
AppKind::UiState(f) => f(ui, state),
_ => unreachable!(),
});
})
.response
})
Expand Down
80 changes: 73 additions & 7 deletions crates/egui_kittest/src/builder.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
use crate::app_kind::AppKind;
use crate::Harness;
use egui::{Pos2, Rect, Vec2};
use std::marker::PhantomData;

/// Builder for [`Harness`].
pub struct HarnessBuilder {
pub struct HarnessBuilder<State = ()> {
pub(crate) screen_rect: Rect,
pub(crate) pixels_per_point: f32,
pub(crate) state: PhantomData<State>,
}

impl Default for HarnessBuilder {
impl<State> Default for HarnessBuilder<State> {
fn default() -> Self {
Self {
screen_rect: Rect::from_min_size(Pos2::ZERO, Vec2::new(800.0, 600.0)),
pixels_per_point: 1.0,
state: PhantomData,
}
}
}

impl HarnessBuilder {
impl<State> HarnessBuilder<State> {
/// Set the size of the window.
#[inline]
pub fn with_size(mut self, size: impl Into<Vec2>) -> Self {
Expand All @@ -34,6 +37,69 @@ impl HarnessBuilder {
self
}

/// Create a new Harness with the given app closure and a state.
///
/// The app closure will immediately be called once to create the initial ui.
///
/// If you don't need to create Windows / Panels, you can use [`HarnessBuilder::build_ui`] instead.
///
/// # Example
/// ```rust
/// # use egui::CentralPanel;
/// # use egui_kittest::{Harness, kittest::Queryable};
/// let checked = false;
/// let mut harness = Harness::builder()
/// .with_size(egui::Vec2::new(300.0, 200.0))
/// .build_state(|ctx, checked| {
/// CentralPanel::default().show(ctx, |ui| {
/// ui.checkbox(checked, "Check me!");
/// });
/// }, checked);
///
/// harness.get_by_name("Check me!").click();
/// harness.run();
///
/// assert_eq!(*harness.state(), true);
/// ```
pub fn build_state<'a>(
self,
app: impl FnMut(&egui::Context, &mut State) + 'a,
state: State,
) -> Harness<'a, State> {
Harness::from_builder(&self, AppKind::ContextState(Box::new(app)), state)
}

/// Create a new Harness with the given ui closure and a state.
///
/// The ui closure will immediately be called once to create the initial ui.
///
/// If you need to create Windows / Panels, you can use [`HarnessBuilder::build`] instead.
///
/// # Example
/// ```rust
/// # use egui_kittest::{Harness, kittest::Queryable};
/// let mut checked = false;
/// let mut harness = Harness::builder()
/// .with_size(egui::Vec2::new(300.0, 200.0))
/// .build_ui_state(|ui, checked| {
/// ui.checkbox(checked, "Check me!");
/// }, checked);
///
/// harness.get_by_name("Check me!").click();
/// harness.run();
///
/// assert_eq!(*harness.state(), true);
/// ```
pub fn build_ui_state<'a>(
self,
app: impl FnMut(&mut egui::Ui, &mut State) + 'a,
state: State,
) -> Harness<'a, State> {
Harness::from_builder(&self, AppKind::UiState(Box::new(app)), state)
}
}

impl HarnessBuilder {
/// Create a new Harness with the given app closure.
///
/// The app closure will immediately be called once to create the initial ui.
Expand All @@ -43,7 +109,7 @@ impl HarnessBuilder {
/// # Example
/// ```rust
/// # use egui::CentralPanel;
/// # use egui_kittest::Harness;
/// # use egui_kittest::{Harness, kittest::Queryable};
/// let mut harness = Harness::builder()
/// .with_size(egui::Vec2::new(300.0, 200.0))
/// .build(|ctx| {
Expand All @@ -53,7 +119,7 @@ impl HarnessBuilder {
/// });
/// ```
pub fn build<'a>(self, app: impl FnMut(&egui::Context) + 'a) -> Harness<'a> {
Harness::from_builder(&self, AppKind::Context(Box::new(app)))
Harness::from_builder(&self, AppKind::Context(Box::new(app)), ())
}

/// Create a new Harness with the given ui closure.
Expand All @@ -64,14 +130,14 @@ impl HarnessBuilder {
///
/// # Example
/// ```rust
/// # use egui_kittest::Harness;
/// # use egui_kittest::{Harness, kittest::Queryable};
/// let mut harness = Harness::builder()
/// .with_size(egui::Vec2::new(300.0, 200.0))
/// .build_ui(|ui| {
/// ui.label("Hello, world!");
/// });
/// ```
pub fn build_ui<'a>(self, app: impl FnMut(&mut egui::Ui) + 'a) -> Harness<'a> {
Harness::from_builder(&self, AppKind::Ui(Box::new(app)))
Harness::from_builder(&self, AppKind::Ui(Box::new(app)), ())
}
}
Loading
Loading