Skip to content

Commit

Permalink
Add ability to inspect all widgets' BaseState during tests
Browse files Browse the repository at this point in the history
  • Loading branch information
cmyr committed Feb 5, 2020
1 parent 2eb06f4 commit 489aee1
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 10 deletions.
5 changes: 5 additions & 0 deletions druid/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,11 @@ impl<T: Data, W: Widget<T>> WidgetPod<T, W> {
self.state.children.contains(&widget)
}
}
#[cfg(test)]
LifeCycle::DebugInspectState(f) => {
f.call(&self.state);
true
}
_ => true,
};

Expand Down
53 changes: 44 additions & 9 deletions druid/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ pub enum LifeCycle {
widget: WidgetId,
state_cell: StateCell,
},
#[cfg(test)]
DebugInspectState(StateCheckFn),
}

/// A mouse wheel event.
Expand Down Expand Up @@ -266,25 +268,29 @@ impl Event {
}

#[cfg(test)]
pub(crate) use state_cell::StateCell;
pub(crate) use state_cell::{StateCell, StateCheckFn};

#[cfg(test)]
mod state_cell {
use crate::core::BaseState;
use crate::WidgetId;
use std::{cell::RefCell, rc::Rc};

/// An interior-mutable struct for fetching BasteState.
#[derive(Clone, Default)]
pub struct StateCell(Rc<RefCell<Option<BaseState>>>);

impl std::fmt::Debug for StateCell {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let inner = if self.0.borrow().is_some() {
"Some"
} else {
"None"
};
write!(f, "StateCell({})", inner)
#[derive(Clone)]
pub struct StateCheckFn(Rc<dyn Fn(&BaseState)>);

/// a hacky way of printing the widget id if we panic
struct WidgetDrop(bool, WidgetId);

impl Drop for WidgetDrop {
fn drop(&mut self) {
if self.0 {
eprintln!("panic in {:?}", self.1);
}
}
}

Expand All @@ -302,4 +308,33 @@ mod state_cell {
self.0.borrow_mut().take()
}
}

impl StateCheckFn {
pub(crate) fn new(f: impl Fn(&BaseState) + 'static) -> Self {
StateCheckFn(Rc::new(f))
}

pub(crate) fn call(&self, state: &BaseState) {
let mut panic_reporter = WidgetDrop(true, state.id);
(self.0)(&state);
panic_reporter.0 = false;
}
}

impl std::fmt::Debug for StateCell {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let inner = if self.0.borrow().is_some() {
"Some"
} else {
"None"
};
write!(f, "StateCell({})", inner)
}
}

impl std::fmt::Debug for StateCheckFn {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "StateCheckFn")
}
}
}
2 changes: 1 addition & 1 deletion druid/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,4 @@ pub use win_handler::DruidHandler;
pub use window::{Window, WindowId};

#[cfg(test)]
pub(crate) use event::StateCell;
pub(crate) use event::{StateCell, StateCheckFn};
8 changes: 8 additions & 0 deletions druid/src/tests/harness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,14 @@ impl<T: Data> Harness<'_, T> {
cell.take()
}

/// Inspect the `BaseState` of each widget in the tree.
///
/// The provided closure will be called on each widget.
pub(crate) fn inspect_state(&mut self, f: impl Fn(&BaseState) + 'static) {
let checkfn = StateCheckFn::new(f);
self.lifecycle(LifeCycle::DebugInspectState(checkfn))
}

/// Send a command to a target.
pub fn submit_command(&mut self, cmd: impl Into<Command>, target: impl Into<Option<Target>>) {
let target = target.into().unwrap_or_else(|| self.inner.window.id.into());
Expand Down
7 changes: 7 additions & 0 deletions druid/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ fn participate_in_autofocus() {
);

Harness::create("my test text".to_string(), widget, |harness| {
// verify that all widgets are marked as having children_changed
// (this should always be true for a new widget)
harness.inspect_state(|state| assert!(state.children_changed));

harness.send_initial_events();
// verify that we start out with four widgets registered for focus
assert_eq!(harness.window().focus_widgets, vec![id_1, id_2, id_3, id_4]);
Expand All @@ -135,6 +139,9 @@ fn participate_in_autofocus() {
harness.window().focus_widgets,
vec![id_1, id_2, id_3, id_5, id_6]
);

// verify that no widgets still report that their children changed:
harness.inspect_state(|state| assert!(!state.children_changed))
})
}

Expand Down

0 comments on commit 489aee1

Please # to comment.