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

Add unit tests to passes #711

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
10 changes: 5 additions & 5 deletions masonry/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ impl From<PointerButton> for PointerButtons {
// TODO - Touchpad, Touch, AxisMotion
// TODO - How to handle CursorEntered?
// Note to self: Events like "pointerenter", "pointerleave" are handled differently at the Widget level. But that's weird because WidgetPod can distribute them. Need to think about this again.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub enum PointerEvent {
PointerDown(PointerButton, PointerState),
PointerUp(PointerButton, PointerState),
Expand All @@ -198,7 +198,7 @@ pub enum PointerEvent {

// TODO - Clipboard Paste?
// TODO skip is_synthetic=true events
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub enum TextEvent {
KeyboardKey(KeyEvent, ModifiersState),
Ime(Ime),
Expand All @@ -207,13 +207,13 @@ pub enum TextEvent {
FocusChange(bool),
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub struct AccessEvent {
pub action: accesskit::Action,
pub data: Option<accesskit::ActionData>,
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub struct PointerState {
// TODO
// pub device_id: DeviceId,
Expand Down Expand Up @@ -242,7 +242,7 @@ pub enum WindowTheme {
/// may occur during an [`on_event`](crate::Widget::on_event) pass, if some
/// widget has been added then.
#[non_exhaustive]
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
#[allow(variant_size_differences)]
pub enum Update {
/// Sent to a `Widget` when it is added to the widget tree. This should be
Expand Down
27 changes: 27 additions & 0 deletions masonry/src/passes/anim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,30 @@ pub(crate) fn run_update_anim_pass(root: &mut RenderRoot, elapsed_ns: u64) {
elapsed_ns,
);
}

// --- MARK: TESTS ---
#[cfg(test)]
mod tests {
use crate::testing::{Record, Recording, TestHarness, TestWidgetExt as _};
use crate::widget::SizedBox;
use crate::WidgetId;

#[test]
fn test_update_anim_pass() {
let record = Recording::default();

let id = WidgetId::next();
let widget = SizedBox::new_with_id(SizedBox::empty().record(&record), id);

let mut harness = TestHarness::create(widget);

record.clear();

harness.edit_widget(id, |mut widget| {
widget.ctx.request_anim_frame();
});
harness.animate_ms(20);

assert_eq!(record.next(), Some(Record::AnimFrame(20_000_000)));
}
}
117 changes: 117 additions & 0 deletions masonry/src/passes/compose.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,120 @@ pub(crate) fn run_compose_pass(root: &mut RenderRoot) {
Vec2::ZERO,
);
}

// --- MARK: TESTS ---
#[cfg(test)]
mod tests {
use smallvec::smallvec;
use vello::kurbo::{Point, Size};

use crate::testing::{
widget_ids, ModularWidget, Record, Recording, TestHarness, TestWidgetExt as _,
};
use crate::widget::SizedBox;
use crate::WidgetPod;

use super::*;

#[test]
fn test_compose_pass() {
let record = Recording::default();
let [parent_id, recorder_id] = widget_ids();
let inner = SizedBox::new_with_id(SizedBox::empty().record(&record), recorder_id);
let parent = ModularWidget::new((WidgetPod::new(inner), Point::ZERO, Vec2::ZERO))
.layout_fn(|state, ctx, bc| {
let (child, pos, _) = state;
ctx.run_layout(child, bc);
ctx.place_child(child, *pos);
Size::ZERO
})
.compose_fn(|state, ctx| {
let (child, _, translation) = state;
ctx.set_child_translation(child, *translation);
})
.register_children_fn(move |state, ctx| {
let (child, _, _) = state;
ctx.register_child(child);
})
.children_fn(|(child, _, _)| smallvec![child.id()]);
let root = SizedBox::new_with_id(parent, parent_id);

let mut harness = TestHarness::create(root);
record.clear();

harness.edit_widget(parent_id, |mut widget| {
// TODO - Find better way to express this
let mut widget = widget.downcast::<ModularWidget<(WidgetPod<SizedBox>, Point, Vec2)>>();
widget.widget.state.1 = Point::new(30., 30.);
widget.ctx.request_layout();
});
assert_eq!(
record.drain(),
vec![
Record::Layout(Size::new(400., 400.)),
Record::Compose(Point::new(30., 30.)),
]
);

harness.edit_widget(parent_id, |mut widget| {
// TODO - Find better way to express this
let mut widget = widget.downcast::<ModularWidget<(WidgetPod<SizedBox>, Point, Vec2)>>();
widget.widget.state.2 = Vec2::new(8., 8.);
widget.ctx.request_compose();
});

// TODO - Should changing a parent transform call the child's compose method?
assert_eq!(record.drain(), vec![]);
}

#[test]
fn test_move_text_input() {
let record = Recording::default();
let [parent_id, recorder_id] = widget_ids();
let inner = SizedBox::new_with_id(SizedBox::empty().record(&record), recorder_id);
let parent = ModularWidget::new((WidgetPod::new(inner), Point::ZERO, Vec2::ZERO))
.layout_fn(|state, ctx, bc| {
let (child, pos, _) = state;
ctx.run_layout(child, bc);
ctx.place_child(child, *pos);
Size::ZERO
})
.compose_fn(|state, ctx| {
let (child, _, translation) = state;
ctx.set_child_translation(child, *translation);
})
.register_children_fn(move |state, ctx| {
let (child, _, _) = state;
ctx.register_child(child);
})
.children_fn(|(child, _, _)| smallvec![child.id()]);
let root = SizedBox::new_with_id(parent, parent_id);

let mut harness = TestHarness::create(root);
record.clear();

harness.edit_widget(parent_id, |mut widget| {
// TODO - Find better way to express this
let mut widget = widget.downcast::<ModularWidget<(WidgetPod<SizedBox>, Point, Vec2)>>();
widget.widget.state.1 = Point::new(30., 30.);
widget.ctx.request_layout();
});
assert_eq!(
record.drain(),
vec![
Record::Layout(Size::new(400., 400.)),
Record::Compose(Point::new(30., 30.)),
]
);

harness.edit_widget(parent_id, |mut widget| {
// TODO - Find better way to express this
let mut widget = widget.downcast::<ModularWidget<(WidgetPod<SizedBox>, Point, Vec2)>>();
widget.widget.state.2 = Vec2::new(8., 8.);
widget.ctx.request_compose();
});

// TODO - Should changing a parent transform call the child's compose method?
assert_eq!(record.drain(), vec![]);
}
}
54 changes: 35 additions & 19 deletions masonry/src/testing/helper_widgets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
use std::rc::Rc;

use accesskit::{NodeBuilder, Role};
use smallvec::SmallVec;
use smallvec::{smallvec, SmallVec};
use tracing::trace_span;
use vello::Scene;
use widget::widget::get_child_at_pos;
Expand Down Expand Up @@ -43,7 +43,7 @@
///
/// This widget is generic over its state, which is passed in at construction time.
pub struct ModularWidget<S> {
state: S,
pub state: S,
accepts_pointer_interaction: bool,
accepts_focus: bool,
accepts_text_input: bool,
Expand Down Expand Up @@ -102,18 +102,18 @@
/// A recording of a method call on a widget.
///
/// Each member of the enum corresponds to one of the methods on `Widget`.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub enum Record {
PE(PointerEvent),
TE(TextEvent),
AE(AccessEvent),
AF(u64),
RC,
U(Update),
PointerEvent(PointerEvent),
TextEvent(TextEvent),
AccessEvent(AccessEvent),
AnimFrame(u64),
RegisterChildren,
Update(Update),
Layout(Size),
Compose,
Compose(Point),
Paint,
Access,
Accessibilty,

Check warning on line 116 in masonry/src/testing/helper_widgets.rs

View workflow job for this annotation

GitHub Actions / typos

"Accessibilty" should be "Accessibility".
}

/// External trait implemented for all widgets.
Expand Down Expand Up @@ -161,6 +161,22 @@
}
}

impl<W: Widget> ModularWidget<WidgetPod<W>> {
pub fn new_parent(child: W) -> ModularWidget<WidgetPod<W>> {
let child = WidgetPod::new(child);
ModularWidget::new(child)
.register_children_fn(move |child, ctx| {
ctx.register_child(child);
})
.layout_fn(move |child, ctx, bc| {
let size = ctx.run_layout(child, bc);
ctx.place_child(child, Point::ZERO);
size
})
.children_fn(|child| smallvec![child.id()])
}
}

/// Builder methods.
///
/// Each method takes a flag which is then returned by the matching Widget method.
Expand Down Expand Up @@ -508,32 +524,32 @@
#[warn(clippy::missing_trait_methods)]
impl<W: Widget> Widget for Recorder<W> {
fn on_pointer_event(&mut self, ctx: &mut EventCtx, event: &event::PointerEvent) {
self.recording.push(Record::PE(event.clone()));
self.recording.push(Record::PointerEvent(event.clone()));
self.child.on_pointer_event(ctx, event);
}

fn on_text_event(&mut self, ctx: &mut EventCtx, event: &event::TextEvent) {
self.recording.push(Record::TE(event.clone()));
self.recording.push(Record::TextEvent(event.clone()));
self.child.on_text_event(ctx, event);
}

fn on_access_event(&mut self, ctx: &mut EventCtx, event: &AccessEvent) {
self.recording.push(Record::AE(event.clone()));
self.recording.push(Record::AccessEvent(event.clone()));
self.child.on_access_event(ctx, event);
}

fn on_anim_frame(&mut self, ctx: &mut UpdateCtx, interval: u64) {
self.recording.push(Record::AF(interval));
self.recording.push(Record::AnimFrame(interval));
self.child.on_anim_frame(ctx, interval);
}

fn register_children(&mut self, ctx: &mut RegisterCtx) {
self.recording.push(Record::RC);
self.recording.push(Record::RegisterChildren);
self.child.register_children(ctx);
}

fn update(&mut self, ctx: &mut UpdateCtx, event: &Update) {
self.recording.push(Record::U(event.clone()));
self.recording.push(Record::Update(event.clone()));
self.child.update(ctx, event);
}

Expand All @@ -544,7 +560,7 @@
}

fn compose(&mut self, ctx: &mut ComposeCtx) {
self.recording.push(Record::Compose);
self.recording.push(Record::Compose(ctx.window_origin()));
self.child.compose(ctx);
}

Expand All @@ -558,7 +574,7 @@
}

fn accessibility(&mut self, ctx: &mut AccessCtx, node: &mut NodeBuilder) {
self.recording.push(Record::Access);
self.recording.push(Record::Accessibilty);

Check warning on line 577 in masonry/src/testing/helper_widgets.rs

View workflow job for this annotation

GitHub Actions / typos

"Accessibilty" should be "Accessibility".
self.child.accessibility(ctx, node);
}

Expand Down
14 changes: 1 addition & 13 deletions masonry/src/widget/tests/safety_rails.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,12 @@
// Copyright 2022 the Xilem Authors
// SPDX-License-Identifier: Apache-2.0

use smallvec::smallvec;

use crate::testing::{ModularWidget, TestHarness, TestWidgetExt};
use crate::widget::Flex;
use crate::{Point, PointerButton, Size, Update, Widget, WidgetId, WidgetPod};

fn make_parent_widget<W: Widget>(child: W) -> ModularWidget<WidgetPod<W>> {
let child = WidgetPod::new(child);
ModularWidget::new(child)
.register_children_fn(move |child, ctx| {
ctx.register_child(child);
})
.layout_fn(move |child, ctx, bc| {
let size = ctx.run_layout(child, bc);
ctx.place_child(child, Point::ZERO);
size
})
.children_fn(|child| smallvec![child.id()])
ModularWidget::new_parent(child)
}

#[cfg(FALSE)]
Expand Down
4 changes: 2 additions & 2 deletions masonry/src/widget/tests/status_change.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::*;
fn next_pointer_event(recording: &Recording) -> Option<PointerEvent> {
while let Some(event) = recording.next() {
match event {
Record::PE(event) => {
Record::PointerEvent(event) => {
return Some(event);
}
_ => {}
Expand All @@ -27,7 +27,7 @@ fn is_hovered(harness: &TestHarness, id: WidgetId) -> bool {
fn next_hovered_changed(recording: &Recording) -> Option<bool> {
while let Some(event) = recording.next() {
match event {
Record::U(Update::HoveredChanged(hovered)) => return Some(hovered),
Record::Update(Update::HoveredChanged(hovered)) => return Some(hovered),
_ => {}
}
}
Expand Down
Loading