Skip to content

Commit

Permalink
Add image and hash snapshot-based testing to iced_test
Browse files Browse the repository at this point in the history
  • Loading branch information
hecrj committed Dec 10, 2024
1 parent 8e3636d commit 1aeb317
Show file tree
Hide file tree
Showing 17 changed files with 280 additions and 105 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,13 @@ num-traits = "0.2"
once_cell = "1.0"
ouroboros = "0.18"
palette = "0.7"
png = "0.17"
pulldown-cmark = "0.11"
qrcode = { version = "0.13", default-features = false }
raw-window-handle = "0.6"
resvg = "0.42"
rustc-hash = "2.0"
sha2 = "0.10"
smol = "1.0"
smol_str = "0.2"
softbuffer = "0.4"
Expand Down
34 changes: 34 additions & 0 deletions core/src/theme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ pub mod palette;

pub use palette::Palette;

use crate::Color;

use std::fmt;
use std::sync::Arc;

Expand Down Expand Up @@ -246,3 +248,35 @@ impl fmt::Display for Custom {
write!(f, "{}", self.name)
}
}

/// The base style of a [`Theme`].
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Style {
/// The background [`Color`] of the application.
pub background_color: Color,

/// The default text [`Color`] of the application.
pub text_color: Color,
}

/// The default blank style of a [`Theme`].
pub trait Base {
/// Returns the default base [`Style`] of a [`Theme`].
fn base(&self) -> Style;
}

impl Base for Theme {
fn base(&self) -> Style {
default(self)
}
}

/// The default [`Style`] of a built-in [`Theme`].
pub fn default(theme: &Theme) -> Style {
let palette = theme.extended_palette();

Style {
background_color: palette.background.base.color,
text_color: palette.background.base.text,
}
}
2 changes: 2 additions & 0 deletions core/src/window.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Build window-based GUI applications.
pub mod icon;
pub mod screenshot;
pub mod settings;

mod event;
Expand All @@ -17,5 +18,6 @@ pub use level::Level;
pub use mode::Mode;
pub use position::Position;
pub use redraw_request::RedrawRequest;
pub use screenshot::Screenshot;
pub use settings::Settings;
pub use user_attention::UserAttention;
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! Take screenshots of a window.
use crate::core::{Rectangle, Size};
use crate::{Rectangle, Size};

use bytes::Bytes;
use std::fmt::{Debug, Formatter};
Expand Down
10 changes: 4 additions & 6 deletions examples/gradient/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use iced::application;
use iced::gradient;
use iced::theme;
use iced::widget::{
checkbox, column, container, horizontal_space, row, slider, text,
};
Expand Down Expand Up @@ -95,16 +95,14 @@ impl Gradient {
.into()
}

fn style(&self, theme: &Theme) -> application::Appearance {
use application::DefaultStyle;

fn style(&self, theme: &Theme) -> theme::Style {
if self.transparent {
application::Appearance {
theme::Style {
background_color: Color::TRANSPARENT,
text_color: theme.palette().text,
}
} else {
Theme::default_style(theme)
theme::default(theme)
}
}
}
Expand Down
1 change: 1 addition & 0 deletions examples/todos/snapshots/creates_a_new_task.sha256
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
5047c10c4ea050393b686185b8d03b3c3738b47182f9892fb68df589ec46bd4b
15 changes: 12 additions & 3 deletions examples/todos/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub fn main() -> iced::Result {

iced::application(Todos::title, Todos::update, Todos::view)
.subscription(Todos::subscription)
.font(include_bytes!("../fonts/icons.ttf").as_slice())
.font(Todos::ICON_FONT)
.window_size((500.0, 800.0))
.run_with(Todos::new)
}
Expand Down Expand Up @@ -48,6 +48,8 @@ enum Message {
}

impl Todos {
const ICON_FONT: &[u8] = include_bytes!("../fonts/icons.ttf");

Check failure on line 51 in examples/todos/src/main.rs

View workflow job for this annotation

GitHub Actions / all (macOS-latest, 1.80)

`&` without an explicit lifetime name cannot be used here

fn new() -> (Self, Command<Message>) {
(
Self::Loading,
Expand Down Expand Up @@ -449,11 +451,10 @@ fn empty_message(message: &str) -> Element<'_, Message> {
}

// Fonts
const ICONS: Font = Font::with_name("Iced-Todos-Icons");

fn icon(unicode: char) -> Text<'static> {
text(unicode.to_string())
.font(ICONS)
.font(Font::with_name("Iced-Todos-Icons"))
.width(20)
.align_x(Center)
}
Expand Down Expand Up @@ -594,6 +595,8 @@ mod tests {

#[test]
fn it_creates_a_new_task() -> Result<(), test::Error> {
test::load_font(Todos::ICON_FONT)?;

let (mut todos, _command) = Todos::new();
let _command = todos.update(Message::Loaded(Err(LoadError::File)));

Expand All @@ -610,6 +613,12 @@ mod tests {
let mut interface = test::interface(todos.view());
let _ = interface.find(selector::text("Create the universe"))?;

let snapshot = interface.snapshot()?;
assert!(
snapshot.matches_hash("snapshots/creates_a_new_task")?,
"snapshots should match!"
);

Ok(())
}
}
6 changes: 1 addition & 5 deletions runtime/src/window.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
//! Build window-based GUI applications.
pub mod screenshot;

pub use screenshot::Screenshot;

use crate::core::time::Instant;
use crate::core::window::{
Event, Icon, Id, Level, Mode, Settings, UserAttention,
Event, Icon, Id, Level, Mode, Screenshot, Settings, UserAttention,
};
use crate::core::{Point, Size};
use crate::futures::event;
Expand Down
9 changes: 4 additions & 5 deletions src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,14 @@
//! }
//! ```
use crate::program::{self, Program};
use crate::theme;
use crate::window;
use crate::{
Element, Executor, Font, Result, Settings, Size, Subscription, Task,
};

use std::borrow::Cow;

pub use crate::shell::program::{Appearance, DefaultStyle};

/// Creates an iced [`Application`] given its title, update, and view logic.
///
/// # Example
Expand Down Expand Up @@ -76,7 +75,7 @@ pub fn application<State, Message, Theme, Renderer>(
where
State: 'static,
Message: Send + std::fmt::Debug + 'static,
Theme: Default + DefaultStyle,
Theme: Default + theme::Base,
Renderer: program::Renderer,
{
use std::marker::PhantomData;
Expand All @@ -94,7 +93,7 @@ where
for Instance<State, Message, Theme, Renderer, Update, View>
where
Message: Send + std::fmt::Debug + 'static,
Theme: Default + DefaultStyle,
Theme: Default + theme::Base,
Renderer: program::Renderer,
Update: self::Update<State, Message>,
View: for<'a> self::View<'a, State, Message, Theme, Renderer>,
Expand Down Expand Up @@ -352,7 +351,7 @@ impl<P: Program> Application<P> {
/// Sets the style logic of the [`Application`].
pub fn style(
self,
f: impl Fn(&P::State, &P::Theme) -> Appearance,
f: impl Fn(&P::State, &P::Theme) -> theme::Style,
) -> Application<
impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
> {
Expand Down
9 changes: 4 additions & 5 deletions src/daemon.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
//! Create and run daemons that run in the background.
use crate::application;
use crate::program::{self, Program};
use crate::theme;
use crate::window;
use crate::{Element, Executor, Font, Result, Settings, Subscription, Task};

use std::borrow::Cow;

pub use crate::shell::program::{Appearance, DefaultStyle};

/// Creates an iced [`Daemon`] given its title, update, and view logic.
///
/// A [`Daemon`] will not open a window by default, but will run silently
Expand All @@ -26,7 +25,7 @@ pub fn daemon<State, Message, Theme, Renderer>(
where
State: 'static,
Message: Send + std::fmt::Debug + 'static,
Theme: Default + DefaultStyle,
Theme: Default + theme::Base,
Renderer: program::Renderer,
{
use std::marker::PhantomData;
Expand All @@ -44,7 +43,7 @@ where
for Instance<State, Message, Theme, Renderer, Update, View>
where
Message: Send + std::fmt::Debug + 'static,
Theme: Default + DefaultStyle,
Theme: Default + theme::Base,
Renderer: program::Renderer,
Update: application::Update<State, Message>,
View: for<'a> self::View<'a, State, Message, Theme, Renderer>,
Expand Down Expand Up @@ -201,7 +200,7 @@ impl<P: Program> Daemon<P> {
/// Sets the style logic of the [`Daemon`].
pub fn style(
self,
f: impl Fn(&P::State, &P::Theme) -> Appearance,
f: impl Fn(&P::State, &P::Theme) -> theme::Style,
) -> Daemon<
impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
> {
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,7 @@ pub fn run<State, Message, Theme, Renderer>(
where
State: Default + 'static,
Message: std::fmt::Debug + Send + 'static,
Theme: Default + program::DefaultStyle + 'static,
Theme: Default + theme::Base + 'static,
Renderer: program::Renderer + 'static,
{
application(title, update, view).run()
Expand Down
29 changes: 14 additions & 15 deletions src/program.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use crate::core::text;
use crate::graphics::compositor;
use crate::shell;
use crate::theme;
use crate::window;
use crate::{Element, Executor, Result, Settings, Subscription, Task};

pub use crate::shell::program::{Appearance, DefaultStyle};

/// The internal definition of a [`Program`].
///
/// You should not need to implement this trait directly. Instead, use the
Expand All @@ -19,7 +18,7 @@ pub trait Program: Sized {
type Message: Send + std::fmt::Debug + 'static;

/// The theme of the program.
type Theme: Default + DefaultStyle;
type Theme: Default + theme::Base;

/// The renderer of the program.
type Renderer: Renderer;
Expand Down Expand Up @@ -51,11 +50,11 @@ pub trait Program: Sized {
}

fn theme(&self, _state: &Self::State, _window: window::Id) -> Self::Theme {
Self::Theme::default()
<Self::Theme as Default>::default()
}

fn style(&self, _state: &Self::State, theme: &Self::Theme) -> Appearance {
DefaultStyle::default_style(theme)
fn style(&self, _state: &Self::State, theme: &Self::Theme) -> theme::Style {
theme::Base::base(theme)
}

fn scale_factor(&self, _state: &Self::State, _window: window::Id) -> f64 {
Expand Down Expand Up @@ -153,7 +152,7 @@ pub trait Program: Sized {
self.program.theme(&self.state, window)
}

fn style(&self, theme: &Self::Theme) -> Appearance {
fn style(&self, theme: &Self::Theme) -> theme::Style {
self.program.style(&self.state, theme)
}

Expand Down Expand Up @@ -252,7 +251,7 @@ pub fn with_title<P: Program>(
&self,
state: &Self::State,
theme: &Self::Theme,
) -> Appearance {
) -> theme::Style {
self.program.style(state, theme)
}

Expand Down Expand Up @@ -322,7 +321,7 @@ pub fn with_subscription<P: Program>(
&self,
state: &Self::State,
theme: &Self::Theme,
) -> Appearance {
) -> theme::Style {
self.program.style(state, theme)
}

Expand Down Expand Up @@ -395,7 +394,7 @@ pub fn with_theme<P: Program>(
&self,
state: &Self::State,
theme: &Self::Theme,
) -> Appearance {
) -> theme::Style {
self.program.style(state, theme)
}

Expand All @@ -409,7 +408,7 @@ pub fn with_theme<P: Program>(

pub fn with_style<P: Program>(
program: P,
f: impl Fn(&P::State, &P::Theme) -> Appearance,
f: impl Fn(&P::State, &P::Theme) -> theme::Style,
) -> impl Program<State = P::State, Message = P::Message, Theme = P::Theme> {
struct WithStyle<P, F> {
program: P,
Expand All @@ -418,7 +417,7 @@ pub fn with_style<P: Program>(

impl<P: Program, F> Program for WithStyle<P, F>
where
F: Fn(&P::State, &P::Theme) -> Appearance,
F: Fn(&P::State, &P::Theme) -> theme::Style,
{
type State = P::State;
type Message = P::Message;
Expand All @@ -430,7 +429,7 @@ pub fn with_style<P: Program>(
&self,
state: &Self::State,
theme: &Self::Theme,
) -> Appearance {
) -> theme::Style {
(self.style)(state, theme)
}

Expand Down Expand Up @@ -535,7 +534,7 @@ pub fn with_scale_factor<P: Program>(
&self,
state: &Self::State,
theme: &Self::Theme,
) -> Appearance {
) -> theme::Style {
self.program.style(state, theme)
}

Expand Down Expand Up @@ -609,7 +608,7 @@ pub fn with_executor<P: Program, E: Executor>(
&self,
state: &Self::State,
theme: &Self::Theme,
) -> Appearance {
) -> theme::Style {
self.program.style(state, theme)
}

Expand Down
3 changes: 3 additions & 0 deletions test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@ iced_tiny_skia.workspace = true

iced_renderer.workspace = true
iced_renderer.features = ["tiny-skia"]

png.workspace = true
sha2.workspace = true
Loading

0 comments on commit 1aeb317

Please sign in to comment.