Add screenshot helpers to iced_test
This commit is contained in:
parent
ea614387f4
commit
085c8fae8d
10 changed files with 183 additions and 78 deletions
|
|
@ -2,8 +2,10 @@
|
|||
use crate::core;
|
||||
use crate::core::mouse;
|
||||
use crate::core::renderer;
|
||||
use crate::core::time::Instant;
|
||||
use crate::core::widget;
|
||||
use crate::core::{Element, Point, Size};
|
||||
use crate::core::window;
|
||||
use crate::core::{Bytes, Element, Point, Size};
|
||||
use crate::instruction;
|
||||
use crate::program;
|
||||
use crate::program::Program;
|
||||
|
|
@ -15,7 +17,6 @@ use crate::runtime::futures::subscription;
|
|||
use crate::runtime::futures::{Executor, Runtime};
|
||||
use crate::runtime::task;
|
||||
use crate::runtime::user_interface;
|
||||
use crate::runtime::window;
|
||||
use crate::runtime::{Task, UserInterface};
|
||||
use crate::{Instruction, Selector};
|
||||
|
||||
|
|
@ -209,50 +210,55 @@ impl<P: Program + 'static> Emulator<P> {
|
|||
// TODO
|
||||
dbg!(action);
|
||||
}
|
||||
runtime::Action::Window(action) => match action {
|
||||
window::Action::Open(id, _settings, sender) => {
|
||||
self.window = id;
|
||||
runtime::Action::Window(action) => {
|
||||
use crate::runtime::window;
|
||||
|
||||
let _ = sender.send(self.window);
|
||||
}
|
||||
window::Action::GetOldest(sender)
|
||||
| window::Action::GetLatest(sender) => {
|
||||
let _ = sender.send(Some(self.window));
|
||||
}
|
||||
window::Action::GetSize(id, sender) => {
|
||||
if id == self.window {
|
||||
let _ = sender.send(self.size);
|
||||
match action {
|
||||
window::Action::Open(id, _settings, sender) => {
|
||||
self.window = id;
|
||||
|
||||
let _ = sender.send(self.window);
|
||||
}
|
||||
window::Action::GetOldest(sender)
|
||||
| window::Action::GetLatest(sender) => {
|
||||
let _ = sender.send(Some(self.window));
|
||||
}
|
||||
window::Action::GetSize(id, sender) => {
|
||||
if id == self.window {
|
||||
let _ = sender.send(self.size);
|
||||
}
|
||||
}
|
||||
window::Action::GetMaximized(id, sender) => {
|
||||
if id == self.window {
|
||||
let _ = sender.send(false);
|
||||
}
|
||||
}
|
||||
window::Action::GetMinimized(id, sender) => {
|
||||
if id == self.window {
|
||||
let _ = sender.send(None);
|
||||
}
|
||||
}
|
||||
window::Action::GetPosition(id, sender) => {
|
||||
if id == self.window {
|
||||
let _ = sender.send(Some(Point::ORIGIN));
|
||||
}
|
||||
}
|
||||
window::Action::GetScaleFactor(id, sender) => {
|
||||
if id == self.window {
|
||||
let _ = sender.send(1.0);
|
||||
}
|
||||
}
|
||||
window::Action::GetMode(id, sender) => {
|
||||
if id == self.window {
|
||||
let _ =
|
||||
sender.send(core::window::Mode::Windowed);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// Ignored
|
||||
}
|
||||
}
|
||||
window::Action::GetMaximized(id, sender) => {
|
||||
if id == self.window {
|
||||
let _ = sender.send(false);
|
||||
}
|
||||
}
|
||||
window::Action::GetMinimized(id, sender) => {
|
||||
if id == self.window {
|
||||
let _ = sender.send(None);
|
||||
}
|
||||
}
|
||||
window::Action::GetPosition(id, sender) => {
|
||||
if id == self.window {
|
||||
let _ = sender.send(Some(Point::ORIGIN));
|
||||
}
|
||||
}
|
||||
window::Action::GetScaleFactor(id, sender) => {
|
||||
if id == self.window {
|
||||
let _ = sender.send(1.0);
|
||||
}
|
||||
}
|
||||
window::Action::GetMode(id, sender) => {
|
||||
if id == self.window {
|
||||
let _ = sender.send(core::window::Mode::Windowed);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// Ignored
|
||||
}
|
||||
},
|
||||
}
|
||||
runtime::Action::System(action) => {
|
||||
// TODO
|
||||
dbg!(action);
|
||||
|
|
@ -435,6 +441,62 @@ impl<P: Program + 'static> Emulator<P> {
|
|||
program.theme(&self.state, self.window)
|
||||
}
|
||||
|
||||
/// Takes a [`window::Screenshot`] of the current state of the [`Emulator`].
|
||||
pub fn screenshot(
|
||||
&mut self,
|
||||
program: &P,
|
||||
theme: &P::Theme,
|
||||
scale_factor: f32,
|
||||
) -> window::Screenshot {
|
||||
use core::renderer::Headless;
|
||||
|
||||
let style = program.style(&self.state, theme);
|
||||
|
||||
let mut user_interface = UserInterface::build(
|
||||
program.view(&self.state, self.window),
|
||||
self.size,
|
||||
self.cache.take().unwrap(),
|
||||
&mut self.renderer,
|
||||
);
|
||||
|
||||
// TODO: Nested redraws!
|
||||
let _ = user_interface.update(
|
||||
&[core::Event::Window(window::Event::RedrawRequested(
|
||||
Instant::now(),
|
||||
))],
|
||||
mouse::Cursor::Unavailable,
|
||||
&mut self.renderer,
|
||||
&mut self.clipboard,
|
||||
&mut Vec::new(),
|
||||
);
|
||||
|
||||
user_interface.draw(
|
||||
&mut self.renderer,
|
||||
theme,
|
||||
&renderer::Style {
|
||||
text_color: style.text_color,
|
||||
},
|
||||
mouse::Cursor::Unavailable,
|
||||
);
|
||||
|
||||
let physical_size = Size::new(
|
||||
(self.size.width * scale_factor).round() as u32,
|
||||
(self.size.height * scale_factor).round() as u32,
|
||||
);
|
||||
|
||||
let rgba = self.renderer.screenshot(
|
||||
physical_size,
|
||||
scale_factor,
|
||||
style.background_color,
|
||||
);
|
||||
|
||||
window::Screenshot {
|
||||
rgba: Bytes::from(rgba),
|
||||
size: physical_size,
|
||||
scale_factor,
|
||||
}
|
||||
}
|
||||
|
||||
/// Turns the [`Emulator`] into its internal state.
|
||||
pub fn into_state(self) -> (P::State, core::window::Id) {
|
||||
(self.state, self.window)
|
||||
|
|
|
|||
|
|
@ -104,6 +104,10 @@ pub use instruction::Instruction;
|
|||
pub use selector::Selector;
|
||||
pub use simulator::{Simulator, simulator};
|
||||
|
||||
use crate::core::Size;
|
||||
use crate::core::time::{Duration, Instant};
|
||||
use crate::core::window;
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
/// Runs an [`Ice`] test suite for the given [`Program`](program::Program).
|
||||
|
|
@ -213,3 +217,50 @@ pub fn run(
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Takes a screenshot of the given [`Program`](program::Program) with the given theme, viewport,
|
||||
/// and scale factor after running it for the given [`Duration`].
|
||||
pub fn screenshot<P: program::Program + 'static>(
|
||||
program: P,
|
||||
theme: &P::Theme,
|
||||
viewport: impl Into<Size>,
|
||||
scale_factor: f32,
|
||||
duration: Duration,
|
||||
) -> window::Screenshot {
|
||||
use crate::runtime::futures::futures::channel::mpsc;
|
||||
|
||||
let (sender, mut receiver) = mpsc::channel(100);
|
||||
|
||||
let mut emulator = Emulator::new(
|
||||
sender,
|
||||
&program,
|
||||
emulator::Mode::Immediate,
|
||||
viewport.into(),
|
||||
);
|
||||
|
||||
let start = Instant::now();
|
||||
|
||||
loop {
|
||||
if let Some(event) = receiver.try_next().ok().flatten() {
|
||||
match event {
|
||||
emulator::Event::Action(action) => {
|
||||
emulator.perform(&program, action);
|
||||
}
|
||||
emulator::Event::Failed(_) => {
|
||||
unreachable!(
|
||||
"no instructions should be executed during a screenshot"
|
||||
);
|
||||
}
|
||||
emulator::Event::Ready => {}
|
||||
}
|
||||
}
|
||||
|
||||
if start.elapsed() >= duration {
|
||||
break;
|
||||
}
|
||||
|
||||
std::thread::sleep(Duration::from_millis(1));
|
||||
}
|
||||
|
||||
emulator.screenshot(&program, theme, scale_factor)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -276,7 +276,7 @@ impl Snapshot {
|
|||
let mut bytes = vec![0; n];
|
||||
let info = reader.next_frame(&mut bytes)?;
|
||||
|
||||
Ok(self.screenshot.bytes == bytes[..info.buffer_size()])
|
||||
Ok(self.screenshot.rgba == bytes[..info.buffer_size()])
|
||||
} else {
|
||||
if let Some(directory) = path.parent() {
|
||||
fs::create_dir_all(directory)?;
|
||||
|
|
@ -292,7 +292,7 @@ impl Snapshot {
|
|||
encoder.set_color(png::ColorType::Rgba);
|
||||
|
||||
let mut writer = encoder.write_header()?;
|
||||
writer.write_image_data(&self.screenshot.bytes)?;
|
||||
writer.write_image_data(&self.screenshot.rgba)?;
|
||||
writer.finish()?;
|
||||
|
||||
Ok(true)
|
||||
|
|
@ -311,7 +311,7 @@ impl Snapshot {
|
|||
|
||||
let hash = {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(&self.screenshot.bytes);
|
||||
hasher.update(&self.screenshot.rgba);
|
||||
format!("{:x}", hasher.finalize())
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue