Link the ordinary tests once, and keep their debug info to line tables
Eleven `tests/*.rs` were eleven binaries, each linking the whole graph -- `wgpu` and all -- to run a handful of cases. They are modules of one target now, under `tests/cases/`, and `cargo test --test suite layout::` still picks one out. The fuzzers and the `*_cost` measurements stay their own targets: they are run on their own and want to be selectable without building the rest. `profile.test` takes `debug = "line-tables-only"`, which is what a backtrace here actually reads; the type and variable information was the bulk of what the linker was writing. Measured on this machine, rebuilding `iris`'s test targets after a change to the crate: 14.3 s before, 9.8 s with one target, 7.7 s with both. `target/` went from 45 GB to 13 GB. The suite still passes 102 tests, and the binary still carries `.debug_line`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
39e4ca20e6
commit
cb955f1023
13 files changed
+35
No files matched your search
@@ -0,0 +1,225 @@
|
||||
//! Input across layers: what stops at a layer, what passes through it, and
|
||||
//! where hovering stops.
|
||||
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use iris::harness::Harness;
|
||||
use iris::prelude::*;
|
||||
|
||||
const WINDOW: f32 = 100.0;
|
||||
|
||||
/// Every sense that has fired on one widget since it was last read.
|
||||
#[derive(Default, Clone)]
|
||||
struct Fired(Rc<RefCell<Vec<CursorSense>>>);
|
||||
|
||||
impl Fired {
|
||||
fn take(&self) -> Vec<CursorSense> {
|
||||
std::mem::take(&mut self.0.borrow_mut())
|
||||
}
|
||||
}
|
||||
|
||||
/// A widget filling whatever it is given, recording the senses it is sent.
|
||||
fn listener(h: &mut Harness, senses: impl Into<CursorSenses>) -> (WeakWidget<Rect>, Fired) {
|
||||
let fired = Fired::default();
|
||||
let record = fired.clone();
|
||||
let id = rect(Color::WHITE)
|
||||
.on(senses.into(), move |ctx, _| {
|
||||
record.0.borrow_mut().push(ctx.data.sense)
|
||||
})
|
||||
.add(&mut h.rsc);
|
||||
(id, fired)
|
||||
}
|
||||
|
||||
/// A widget with no senses of its own, to leave a gap beside one that has.
|
||||
fn blank(h: &mut Harness) -> WeakWidget<Rect> {
|
||||
rect(Color::WHITE).add(&mut h.rsc)
|
||||
}
|
||||
|
||||
fn harness() -> Harness {
|
||||
Harness::new((WINDOW, WINDOW))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_stops_at_the_topmost_widget() {
|
||||
let mut h = harness();
|
||||
let (bottom, bottom_hover) = listener(&mut h, CursorSense::HoverStart);
|
||||
let (middle, middle_hover) = listener(&mut h, CursorSense::HoverStart);
|
||||
let (top, top_hover) = listener(&mut h, CursorSense::HoverStart);
|
||||
h.set_root((bottom, middle, top).stack());
|
||||
|
||||
h.move_to((50, 50));
|
||||
|
||||
assert_eq!(top_hover.take(), [CursorSense::HoverStart]);
|
||||
assert_eq!(
|
||||
middle_hover.take(),
|
||||
[],
|
||||
"hover is not shared with a layer below"
|
||||
);
|
||||
assert_eq!(bottom_hover.take(), []);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_scroll_passes_through_every_widget_that_does_not_want_it() {
|
||||
let mut h = harness();
|
||||
let (list, scrolled) = listener(&mut h, CursorSense::Scroll);
|
||||
let (button, clicked) = listener(&mut h, CursorSense::click());
|
||||
let (overlay, overlay_clicked) = listener(&mut h, CursorSense::click());
|
||||
h.set_root((list, button, overlay).stack());
|
||||
|
||||
h.move_to((50, 50));
|
||||
h.scroll((0, 10));
|
||||
|
||||
assert_eq!(
|
||||
scrolled.take(),
|
||||
[CursorSense::Scroll],
|
||||
"two layers of click-only widgets do not stop a scroll"
|
||||
);
|
||||
assert_eq!(clicked.take(), []);
|
||||
assert_eq!(overlay_clicked.take(), []);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hovering_a_button_above_does_not_stop_a_later_scroll() {
|
||||
let mut h = harness();
|
||||
let (list, scrolled) = listener(&mut h, CursorSense::Scroll);
|
||||
let (button, _clicked) = listener(&mut h, CursorSense::click());
|
||||
h.set_root((list, button).stack());
|
||||
|
||||
// The hover arrives in its own frame, as a window delivers it.
|
||||
h.move_to((50, 50));
|
||||
assert_eq!(scrolled.take(), []);
|
||||
|
||||
h.scroll((0, 10));
|
||||
assert_eq!(
|
||||
scrolled.take(),
|
||||
[CursorSense::Scroll],
|
||||
"a hover already resting on the button must not consume the wheel"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_the_topmost_listener_takes_a_press() {
|
||||
let mut h = harness();
|
||||
let (below, below_clicked) = listener(&mut h, CursorSense::click());
|
||||
let (above, above_clicked) = listener(&mut h, CursorSense::click());
|
||||
h.set_root((below, above).stack());
|
||||
|
||||
h.click((50, 50));
|
||||
|
||||
assert_eq!(above_clicked.take(), [CursorSense::click()]);
|
||||
assert_eq!(below_clicked.take(), [], "one press goes to one widget");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_press_beside_the_button_reaches_the_layer_below() {
|
||||
let mut h = harness();
|
||||
let (list, list_clicked) = listener(&mut h, CursorSense::click());
|
||||
// The row above the list covers it, but only its left half is the button.
|
||||
let (button, button_clicked) = listener(&mut h, CursorSense::click());
|
||||
let row = (button, blank(&mut h)).span(Dir::RIGHT).add(&mut h.rsc);
|
||||
h.set_root((list, row).stack());
|
||||
|
||||
h.click((20, 50));
|
||||
assert_eq!(button_clicked.take(), [CursorSense::click()]);
|
||||
assert_eq!(list_clicked.take(), []);
|
||||
|
||||
h.click((80, 50));
|
||||
assert_eq!(button_clicked.take(), [], "the cursor is not on the button");
|
||||
assert_eq!(
|
||||
list_clicked.take(),
|
||||
[CursorSense::click()],
|
||||
"a press beside the button belongs to what is under it"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leaving_a_widget_still_ends_its_hover() {
|
||||
let mut h = harness();
|
||||
let (widget, hover) = listener(&mut h, CursorSense::HoverStart | CursorSense::HoverEnd);
|
||||
h.set_root(widget);
|
||||
|
||||
h.move_to((50, 50));
|
||||
assert_eq!(hover.take(), [CursorSense::HoverStart]);
|
||||
|
||||
h.leave();
|
||||
assert_eq!(hover.take(), [CursorSense::HoverEnd]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leaving_a_widget_does_not_block_the_layer_below() {
|
||||
let mut h = harness();
|
||||
let (below, below_hover) = listener(&mut h, CursorSense::HoverStart);
|
||||
// Only the left half of the layer above is a widget, so the cursor can
|
||||
// leave it without leaving the one underneath.
|
||||
let (above, above_hover) = listener(&mut h, CursorSense::HoverStart | CursorSense::HoverEnd);
|
||||
let row = (above, blank(&mut h)).span(Dir::RIGHT).add(&mut h.rsc);
|
||||
h.set_root((below, row).stack());
|
||||
|
||||
h.move_to((20, 50));
|
||||
assert_eq!(above_hover.take(), [CursorSense::HoverStart]);
|
||||
assert_eq!(below_hover.take(), [], "the layer above is over it");
|
||||
|
||||
h.move_to((80, 50));
|
||||
assert_eq!(above_hover.take(), [CursorSense::HoverEnd]);
|
||||
assert_eq!(
|
||||
below_hover.take(),
|
||||
[CursorSense::HoverStart],
|
||||
"ending a hover above must not stop the hover below"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn covering_a_widget_ends_its_hover() {
|
||||
let mut h = harness();
|
||||
let (below, below_hover) = listener(&mut h, CursorSense::HoverStart | CursorSense::HoverEnd);
|
||||
let (above, above_hover) = listener(&mut h, CursorSense::HoverStart);
|
||||
let row = (above, blank(&mut h)).span(Dir::RIGHT).add(&mut h.rsc);
|
||||
h.set_root((below, row).stack());
|
||||
|
||||
h.move_to((80, 50));
|
||||
assert_eq!(below_hover.take(), [CursorSense::HoverStart]);
|
||||
|
||||
h.move_to((20, 50));
|
||||
assert_eq!(above_hover.take(), [CursorSense::HoverStart]);
|
||||
assert_eq!(
|
||||
below_hover.take(),
|
||||
[CursorSense::HoverEnd],
|
||||
"a widget covered by one that took the input is no longer hovered"
|
||||
);
|
||||
|
||||
h.move_to((80, 50));
|
||||
assert_eq!(
|
||||
below_hover.take(),
|
||||
[CursorSense::HoverStart],
|
||||
"uncovering it hovers it again"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_starts_and_ends_once_each() {
|
||||
let mut h = harness();
|
||||
// Only the left half is the widget, so the cursor can leave it without
|
||||
// leaving the window.
|
||||
let (widget, hover) = listener(&mut h, CursorSense::HoverStart | CursorSense::HoverEnd);
|
||||
let row = (widget, blank(&mut h)).span(Dir::RIGHT).add(&mut h.rsc);
|
||||
h.set_root(row);
|
||||
|
||||
h.move_to((20, 50));
|
||||
assert_eq!(hover.take(), [CursorSense::HoverStart]);
|
||||
|
||||
h.move_to((30, 50));
|
||||
assert_eq!(hover.take(), [], "staying inside is not a second start");
|
||||
|
||||
h.move_to((80, 50));
|
||||
assert_eq!(hover.take(), [CursorSense::HoverEnd]);
|
||||
|
||||
h.move_to((90, 50));
|
||||
assert_eq!(hover.take(), [], "an ended hover does not end again");
|
||||
|
||||
h.move_to((20, 50));
|
||||
assert_eq!(
|
||||
hover.take(),
|
||||
[CursorSense::HoverStart],
|
||||
"re-entering starts it"
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user