use std::cell::Cell;
use std::rc::Rc;
use teksilo_canvas::{EdgeInsets, Point, Rect, Size, SizeProposal};
use teksilo_tokens::{InputTokens, PointerKind, TargetDensity};
use super::*;
use crate::accessibility::target_audit::PinnedDp::{ClearsFloor, Is};
use crate::partition::TargetRegion;
use crate::widget::{LayoutContext, LayoutResponse, Widget, WidgetPlacement};
use crate::widget_builder::WidgetBuilder;
#[derive(Debug)]
struct Leaf {
size: Size,
outset: f32,
regions: Vec<(u16, f32, f32)>,
}
impl Leaf {
fn new(w: f32, h: f32) -> Self {
Self {
size: Size::new(w, h),
outset: 0.0,
regions: Vec::new(),
}
}
fn outset(mut self, dp: f32) -> Self {
self.outset = dp;
self
}
fn region(mut self, part: u16, start: f32, width: f32) -> Self {
self.regions.push((part, start, width));
self
}
}
impl Widget for Leaf {
fn layout_response(&self, _p: SizeProposal, _ctx: &LayoutContext) -> LayoutResponse {
self.size.into()
}
fn hit_outset(&self, kind: PointerKind, _tokens: &InputTokens) -> EdgeInsets {
if self.outset > 0.0 && kind.is_direct() {
EdgeInsets {
top: self.outset,
bottom: self.outset,
leading: self.outset,
trailing: self.outset,
}
} else {
EdgeInsets::ZERO
}
}
fn target_regions(&self, bounds: Rect) -> Vec<TargetRegion> {
self.regions
.iter()
.map(|&(part, start, width)| {
TargetRegion::target(
Rect::new(bounds.x + start, bounds.y, width, bounds.height),
part,
)
})
.collect()
}
}
#[derive(Debug)]
struct Row {
size: Size,
gap: f32,
pad: f32,
stack: bool,
clip: bool,
children: Vec<crate::widget_id::WidgetId>,
}
impl Row {
fn new(w: f32, h: f32) -> Self {
Self {
size: Size::new(w, h),
gap: 0.0,
pad: 0.0,
stack: false,
clip: false,
children: Vec::new(),
}
}
fn gap(mut self, gap: f32) -> Self {
self.gap = gap;
self
}
fn pad(mut self, pad: f32) -> Self {
self.pad = pad;
self
}
fn stack(mut self) -> Self {
self.stack = true;
self
}
fn clip(mut self) -> Self {
self.clip = true;
self
}
fn child(mut self, id: crate::widget_id::WidgetId) -> Self {
self.children.push(id);
self
}
}
impl Widget for Row {
fn build(
&mut self,
_ctx: &mut crate::build_context::BuildContext,
) -> Vec<crate::widget_id::WidgetId> {
self.children.clone()
}
fn preserves_children_on_rebuild(&self) -> bool {
true
}
fn layout_response(&self, _p: SizeProposal, _ctx: &LayoutContext) -> LayoutResponse {
self.size.into()
}
fn place_children(
&self,
bounds: Rect,
_p: SizeProposal,
children: &mut [WidgetPlacement],
ctx: &LayoutContext,
) {
let mut x = bounds.x + self.pad;
for child in children.iter_mut() {
let size = ctx
.child_size(child.id, SizeProposal::unspecified())
.unwrap_or(Size::new(0.0, 0.0));
child.origin = Point::new(x, bounds.y + (bounds.height - size.height) / 2.0);
child.size = size;
if !self.stack {
x += size.width + self.gap;
}
}
}
fn children(&self) -> Vec<crate::widget_id::WidgetId> {
self.children.clone()
}
fn clips_children(&self) -> bool {
self.clip
}
}
fn tree_at(density: TargetDensity) -> WidgetTree {
let mut tree = WidgetTree::new().with_theme(crate::presets::intui::light());
tree.set_input_density(density);
tree
}
fn tap() -> Rc<Cell<u32>> {
Rc::new(Cell::new(0))
}
fn find(measurements: &[TargetMeasurement], widget: &str) -> TargetMeasurement {
measurements
.iter()
.find(|m| m.path.ends_with(widget) && m.part.is_none())
.unwrap_or_else(|| panic!("no measurement for {widget} in {measurements:#?}"))
.clone()
}
#[test]
fn a_grip_its_parent_hugs_reaches_nothing() {
let counter = tap();
let c = counter.clone();
let mut tree = tree_at(TargetDensity::Compact);
let grip = tree.add(Leaf::new(6.0, 6.0).outset(9.0).on_tap(move |_, _| {
c.set(c.get() + 1);
}));
let hug = tree.add(Row::new(6.0, 6.0).clip().child(grip));
let n = tap();
let cn = n.clone();
let pane = tree.add(Leaf::new(200.0, 40.0).on_tap(move |_, _| cn.set(cn.get() + 1)));
let _root = tree.add(
Row::new(400.0, 300.0)
.pad(40.0)
.gap(1.0)
.child(hug)
.child(pane),
);
tree.layout(SizeProposal::exact(400.0, 300.0));
let m = find(&measure_targets(&tree, TargetDensity::Compact), "Leaf");
assert_eq!(m.size, Size::new(6.0, 6.0));
assert_eq!(
m.expanded,
Size::new(6.0, 6.0),
"an outset its ancestors cannot offer a point to must be credited nothing",
);
assert!(!m.sources.any(), "no mechanism reached it: {m:?}");
assert_eq!(m.rule, Some(TargetRule::MinTargetConformance));
}
#[test]
fn a_grip_in_a_wide_parent_reaches_the_floor_through_its_outset() {
let counter = tap();
let c = counter.clone();
let mut tree = tree_at(TargetDensity::Compact);
let grip = tree.add(Leaf::new(6.0, 6.0).outset(9.0).on_tap(move |_, _| {
c.set(c.get() + 1);
}));
let _root = tree.add(Row::new(400.0, 300.0).pad(40.0).child(grip));
tree.layout(SizeProposal::exact(400.0, 300.0));
let m = find(&measure_targets(&tree, TargetDensity::Compact), "Leaf");
assert_eq!(m.size, Size::new(6.0, 6.0));
assert!(
(m.expanded.width - 24.0).abs() < 0.1 && (m.expanded.height - 24.0).abs() < 0.1,
"6 dp grip + 9 dp ring per edge = 24 dp, got {:?}",
m.expanded,
);
assert_eq!(m.rule, None, "24 dp clears the AA floor at Compact");
}
#[test]
fn the_audit_attributes_a_grips_reach_to_its_outset_not_to_slop() {
let counter = tap();
let c = counter.clone();
let mut tree = tree_at(TargetDensity::Compact);
let grip = tree.add(Leaf::new(6.0, 6.0).outset(9.0).on_tap(move |_, _| {
c.set(c.get() + 1);
}));
let _root = tree.add(Row::new(400.0, 300.0).pad(40.0).child(grip));
tree.layout(SizeProposal::exact(400.0, 300.0));
let m = find(&measure_targets(&tree, TargetDensity::Compact), "Leaf");
assert!(m.sources.outset, "the outset delivers the ring: {m:?}");
assert!(
!m.sources.slop,
"the slop pass is never needed where the outset already reaches: {m:?}",
);
}
#[test]
fn an_undersized_control_beside_an_eligible_row_fails() {
let a = tap();
let b = tap();
let (ca, cb) = (a.clone(), b.clone());
let mut tree = tree_at(TargetDensity::Compact);
let small = tree.add(Leaf::new(10.0, 10.0).on_tap(move |_, _| ca.set(ca.get() + 1)));
let _row = tree.add(
Row::new(400.0, 40.0)
.pad(40.0)
.child(small)
.on_tap(move |_, _| cb.set(cb.get() + 1)),
);
tree.layout(SizeProposal::exact(400.0, 300.0));
let m = find(&measure_targets(&tree, TargetDensity::Compact), "Leaf");
assert_eq!(m.expanded, Size::new(10.0, 10.0), "{m:?}");
assert_eq!(m.rule, Some(TargetRule::MinTargetConformance));
let violations = target_audit(&tree, TargetDensity::Compact);
assert!(
violations
.iter()
.any(|v| v.path.ends_with("Leaf") && v.rule.is_conformance_failure()),
"the audit must fail an undersized control: {violations:#?}",
);
}
#[test]
fn the_same_control_alone_is_reached_by_the_slop_pass() {
let a = tap();
let ca = a.clone();
let mut tree = tree_at(TargetDensity::Compact);
let small = tree.add(Leaf::new(10.0, 10.0).on_tap(move |_, _| ca.set(ca.get() + 1)));
let _row = tree.add(Row::new(400.0, 40.0).pad(40.0).child(small));
tree.layout(SizeProposal::exact(400.0, 300.0));
let m = find(&measure_targets(&tree, TargetDensity::Compact), "Leaf");
assert!(
(m.expanded.width - 24.0).abs() < 0.1,
"10 dp + 2 × ((24 − 10) / 2) = 24 dp, got {:?}",
m.expanded,
);
assert!(m.sources.slop, "{m:?}");
assert!(!m.sources.outset, "{m:?}");
assert_eq!(m.rule, None);
}
#[test]
fn a_control_already_at_the_target_size_earns_no_slop() {
let a = tap();
let ca = a.clone();
let mut tree = tree_at(TargetDensity::Compact);
let control = tree.add(Leaf::new(24.0, 24.0).on_tap(move |_, _| ca.set(ca.get() + 1)));
let _row = tree.add(Row::new(400.0, 40.0).pad(40.0).child(control));
tree.layout(SizeProposal::exact(400.0, 300.0));
let m = find(&measure_targets(&tree, TargetDensity::Compact), "Leaf");
assert_eq!(m.expanded, Size::new(24.0, 24.0), "{m:?}");
assert!(!m.sources.any(), "{m:?}");
assert_eq!(m.rule, None);
}
#[test]
fn a_reported_region_is_measured_as_its_own_target() {
let a = tap();
let ca = a.clone();
let mut tree = tree_at(TargetDensity::Compact);
let node = tree.add(
Leaf::new(200.0, 30.0)
.region(0, 0.0, 192.0)
.region(1, 192.0, 8.0)
.on_tap(move |_, _| ca.set(ca.get() + 1)),
);
let _root = tree.add(Row::new(400.0, 300.0).pad(40.0).child(node));
tree.layout(SizeProposal::exact(400.0, 300.0));
let all = measure_targets(&tree, TargetDensity::Compact);
let label = all
.iter()
.find(|m| m.part == Some(0))
.expect("the wide zone is reported");
let filter = all
.iter()
.find(|m| m.part == Some(1))
.expect("the narrow zone is reported");
assert_eq!(label.rule, None, "{label:?}");
assert_eq!(
filter.rule,
Some(TargetRule::MinTargetConformance),
"an 8 dp zone inside a 200 dp node is still an 8 dp target: {filter:?}",
);
let own = find(&all, "Leaf");
assert_eq!(own.rule, None, "{own:?}");
}
#[test]
fn a_region_sharing_its_nodes_edge_inherits_the_growth_confirmed_there() {
let counter = tap();
let c = counter.clone();
let mut tree = tree_at(TargetDensity::Compact);
let bar = tree.add(
Leaf::new(12.0, 200.0)
.outset(6.0)
.region(0, 0.0, 12.0)
.region(1, 3.0, 6.0)
.on_tap(move |_, _| c.set(c.get() + 1)),
);
let _root = tree.add(Row::new(400.0, 300.0).pad(40.0).child(bar));
tree.layout(SizeProposal::exact(400.0, 300.0));
let all = measure_targets(&tree, TargetDensity::Compact);
let node = find(&all, "Leaf");
assert!(
(node.expanded.width - 24.0).abs() <= 0.05,
"the node's own ring must reach 24 dp for there to be anything to \
inherit; if this is the line that failed, the outset is broken and the \
region credit is not: {node:?}",
);
let full = all
.iter()
.find(|m| m.part == Some(0))
.expect("the full-width part is reported");
let interior = all
.iter()
.find(|m| m.part == Some(1))
.expect("the interior part is reported");
assert!(
(full.expanded.width - 24.0).abs() <= 0.05,
"a part spanning its node's whole width shares both side edges, so it \
inherits both confirmed growths: {full:?}",
);
assert!(
(interior.expanded.width - 6.0).abs() <= 0.05,
"a part touching neither side edge inherits nothing horizontally — a \
region is measured, never probed: {interior:?}",
);
assert_eq!(
interior.rule,
Some(TargetRule::MinTargetConformance),
"and 6 dp is still 6 dp: {interior:?}",
);
}
#[test]
fn a_grip_that_won_through_its_outset_keeps_its_point_against_the_slop_pass() {
for density in [
TargetDensity::Compact,
TargetDensity::Comfortable,
TargetDensity::Touch,
] {
let c = tap();
let cc = c.clone();
let mut tree = tree_at(density);
let bands: Vec<_> = (0..2)
.map(|_| {
let cr = tap();
tree.add(Leaf::new(150.0, 28.0).on_tap(move |_, _| cr.set(cr.get() + 1)))
})
.collect();
let grip = tree.add(
Leaf::new(12.0, 200.0)
.outset(6.0)
.on_tap(move |_, _| cc.set(cc.get() + 1)),
);
let _root = tree.add(
Row::new(400.0, 300.0)
.pad(40.0)
.gap(4.0)
.child(bands[0])
.child(grip)
.child(bands[1]),
);
tree.layout(SizeProposal::exact(400.0, 300.0));
let all = measure_targets(&tree, density);
let m = all
.iter()
.find(|x| x.part.is_none() && (x.size.width - 12.0).abs() <= 0.05)
.unwrap_or_else(|| panic!("the grip is measured at {density:?}: {all:#?}"))
.clone();
assert!(
(m.expanded.width - 24.0).abs() <= 0.05,
"the grip keeps both halves of its own ring at {density:?}: \
measured {:.4}, expected 24.00 — a reach short of that means a band \
beside it took part of the ring back through the miss-only pass",
m.expanded.width,
);
assert!(
m.sources.outset,
"and the reach is attributed to the exact pass at {density:?}: {:?}",
m.sources,
);
assert!(
!m.rule.is_some_and(|r| r.is_conformance_failure()),
"24 dp clears the AA floor at {density:?}: {:?}",
m.rule,
);
}
}
#[test]
fn a_target_a_sibling_covers_is_a_failure_not_a_skip() {
let a = tap();
let b = tap();
let (ca, cb) = (a.clone(), b.clone());
let mut tree = tree_at(TargetDensity::Compact);
let under = tree.add(Leaf::new(40.0, 40.0).on_tap(move |_, _| ca.set(ca.get() + 1)));
let over = tree.add(Leaf::new(40.0, 40.0).on_tap(move |_, _| cb.set(cb.get() + 1)));
let _root = tree.add(
Row::new(400.0, 300.0)
.pad(40.0)
.stack()
.child(under)
.child(over),
);
tree.layout(SizeProposal::exact(400.0, 300.0));
let all = measure_targets(&tree, TargetDensity::Compact);
let covered = all
.iter()
.filter(|m| m.part.is_none() && m.path.ends_with("Leaf"))
.find(|m| m.expanded.width == 0.0)
.expect("one of the two leaves is unreachable");
assert_eq!(covered.rule, Some(TargetRule::MinTargetConformance));
assert_eq!(covered.skipped, None, "occlusion is judged, not skipped");
}
#[test]
fn a_wrapper_its_own_child_covers_delegates_rather_than_failing() {
let a = tap();
let b = tap();
let (ca, cb) = (a.clone(), b.clone());
let mut tree = tree_at(TargetDensity::Compact);
let inner = tree.add(Leaf::new(40.0, 40.0).on_tap(move |_, _| ca.set(ca.get() + 1)));
let outer = tree.add(
Row::new(40.0, 40.0)
.child(inner)
.on_tap(move |_, _| cb.set(cb.get() + 1)),
);
let _root = tree.add(Row::new(400.0, 300.0).pad(40.0).child(outer));
tree.layout(SizeProposal::exact(400.0, 300.0));
let all = measure_targets(&tree, TargetDensity::Compact);
let wrapper = all
.iter()
.find(|m| m.part.is_none() && m.path.ends_with("Row") && m.node == outer)
.expect("the wrapper is collected");
assert_eq!(wrapper.skipped, Some(SkipReason::DelegatesToDescendant));
assert_eq!(wrapper.rule, None);
let child = find(all.as_slice(), "Leaf");
assert_eq!(child.rule, None, "{child:?}");
}
#[test]
fn the_conformance_floor_never_scales_and_the_recommendation_always_does() {
for density in [
TargetDensity::Compact,
TargetDensity::Comfortable,
TargetDensity::Touch,
] {
let tokens = InputTokens::for_density(density);
assert_eq!(tokens.min_target_conformance, 24.0);
assert!(tokens.target_size >= tokens.min_target_conformance);
}
assert_eq!(
InputTokens::for_density(TargetDensity::Touch).target_size,
44.0,
"the Touch ladder is SC 2.5.5 AAA / Apple HIG, and the rule that names \
it must be TouchTargetRecommendation, not MinTargetConformance",
);
assert!(TargetRule::MinTargetConformance.is_conformance_failure());
assert!(!TargetRule::TouchTargetRecommendation.is_conformance_failure());
assert!(!TargetRule::SpacingException.is_conformance_failure());
}
#[test]
fn a_conformant_control_still_reports_the_recommendation_above_compact() {
for (density, expect) in [
(TargetDensity::Compact, None),
(
TargetDensity::Comfortable,
Some(TargetRule::TouchTargetRecommendation),
),
(
TargetDensity::Touch,
Some(TargetRule::TouchTargetRecommendation),
),
] {
let a = tap();
let b = tap();
let (ca, cb) = (a.clone(), b.clone());
let mut tree = tree_at(density);
let control = tree.add(Leaf::new(24.0, 24.0).on_tap(move |_, _| ca.set(ca.get() + 1)));
let _row = tree.add(
Row::new(400.0, 60.0)
.pad(40.0)
.child(control)
.on_tap(move |_, _| cb.set(cb.get() + 1)),
);
tree.layout(SizeProposal::exact(400.0, 300.0));
let m = find(&measure_targets(&tree, density), "Leaf");
assert_eq!(m.expanded, Size::new(24.0, 24.0), "at {density:?}: {m:?}");
assert_eq!(m.rule, expect, "at {density:?}: {m:?}");
assert!(
!m.rule.is_some_and(|r| r.is_conformance_failure()),
"24 dp conforms at every density: {m:?}",
);
}
}
#[test]
fn an_isolated_undersized_target_records_the_spacing_exception() {
let a = tap();
let ca = a.clone();
let mut tree = tree_at(TargetDensity::Compact);
let small = tree.add(Leaf::new(6.0, 6.0).on_tap(move |_, _| ca.set(ca.get() + 1)));
let hug = tree.add(Row::new(6.0, 6.0).clip().child(small));
let _root = tree.add(Row::new(400.0, 300.0).pad(40.0).child(hug));
tree.layout(SizeProposal::exact(400.0, 300.0));
let m = find(&measure_targets(&tree, TargetDensity::Compact), "Leaf");
assert_eq!(m.expanded, Size::new(6.0, 6.0), "{m:?}");
assert_eq!(m.rule, Some(TargetRule::SpacingException), "{m:?}");
assert!(!m.rule.unwrap().is_conformance_failure());
}
#[test]
fn two_undersized_targets_close_together_lose_the_exception() {
let a = tap();
let b = tap();
let (ca, cb) = (a.clone(), b.clone());
let mut tree = tree_at(TargetDensity::Compact);
let one = tree.add(Leaf::new(6.0, 6.0).on_tap(move |_, _| ca.set(ca.get() + 1)));
let two = tree.add(Leaf::new(6.0, 6.0).on_tap(move |_, _| cb.set(cb.get() + 1)));
let hug = tree.add(Row::new(16.0, 6.0).gap(4.0).clip().child(one).child(two));
let _root = tree.add(Row::new(400.0, 300.0).pad(40.0).child(hug));
tree.layout(SizeProposal::exact(400.0, 300.0));
let all = measure_targets(&tree, TargetDensity::Compact);
let rules: Vec<_> = all
.iter()
.filter(|m| m.part.is_none() && m.path.ends_with("Leaf"))
.map(|m| m.rule)
.collect();
assert_eq!(
rules,
vec![
Some(TargetRule::MinTargetConformance),
Some(TargetRule::MinTargetConformance)
],
"{all:#?}",
);
}
#[test]
fn audit_at_density_switches_the_tree_before_it_measures() {
let a = tap();
let b = tap();
let (ca, cb) = (a.clone(), b.clone());
let mut tree = tree_at(TargetDensity::Compact);
let control = tree.add(Leaf::new(24.0, 24.0).on_tap(move |_, _| ca.set(ca.get() + 1)));
let _row = tree.add(
Row::new(400.0, 60.0)
.pad(40.0)
.child(control)
.on_tap(move |_, _| cb.set(cb.get() + 1)),
);
let compact = audit_at_density(&mut tree, TargetDensity::Compact, Size::new(400.0, 300.0));
assert!(compact.is_empty(), "{compact:#?}");
let touch = audit_at_density(&mut tree, TargetDensity::Touch, Size::new(400.0, 300.0));
let leaf = touch
.iter()
.find(|v| v.path.ends_with("Leaf"))
.expect("a fixed 24 dp leaf cannot follow the Touch ladder");
assert_eq!(leaf.density, TargetDensity::Touch);
assert_eq!(leaf.rule, TargetRule::TouchTargetRecommendation);
assert!(
touch.iter().all(|v| !v.rule.is_conformance_failure()),
"and it still conforms at AA: {touch:#?}",
);
}
#[test]
fn an_app_installed_style_slot_is_reported_as_unprojected() {
let plain = crate::presets::intui::light();
assert!(
unprojected_style_slots(&plain).is_empty(),
"a preset with no slots installed has nothing to report",
);
let mut custom = crate::presets::intui::light();
custom.style_slots.button = Some(std::rc::Rc::new(StubButtonStyle));
assert_eq!(unprojected_style_slots(&custom), vec!["button"]);
let mut tokens_only =
crate::presets::intui::light().with_density_projection(|theme, density| {
crate::styles::Theme {
input: InputTokens::for_density(density),
..theme.clone()
}
});
tokens_only.style_slots.button = Some(std::rc::Rc::new(StubButtonStyle));
assert_eq!(
unprojected_style_slots(&tokens_only),
vec!["button"],
"a projection that does not rebuild this slot leaves it frozen, \
whatever else it re-derives",
);
let mut rebuilding =
crate::presets::intui::light().with_density_projection(|theme, density| {
let mut out = crate::styles::Theme {
input: InputTokens::for_density(density),
..theme.clone()
};
out.style_slots.button = Some(std::rc::Rc::new(StubButtonStyle));
out
});
rebuilding.style_slots.button = Some(std::rc::Rc::new(StubButtonStyle));
assert!(
unprojected_style_slots(&rebuilding).is_empty(),
"a theme that re-derives this slot reports none",
);
}
#[derive(Debug)]
struct StubButtonStyle;
impl crate::styles::ButtonStyle for StubButtonStyle {
fn make_body(
&self,
_cfg: &crate::styles::ButtonStyleConfig,
ctx: &mut crate::build_context::BuildContext,
) -> crate::widget_id::WidgetId {
ctx.add(Leaf::new(12.0, 12.0))
}
}
#[test]
fn a_target_wider_than_the_probe_budget_is_still_judged() {
let a = tap();
let ca = a.clone();
let mut tree = tree_at(TargetDensity::Compact);
let node = tree.add(Leaf::new(200.0, 30.0).on_tap(move |_, _| ca.set(ca.get() + 1)));
let _root = tree.add(Row::new(400.0, 300.0).pad(40.0).child(node));
tree.layout(SizeProposal::exact(400.0, 300.0));
let m = find(&measure_targets(&tree, TargetDensity::Compact), "Leaf");
assert_eq!(
m.skipped, None,
"nothing covers this node, so it is not shadowed by anything: {m:#?}",
);
assert!(m.capped, "and its horizontal axis spent the budget: {m:#?}");
assert_eq!(
m.rule, None,
"a 200 x 30 dp target clears every floor: {m:#?}"
);
}
#[test]
fn a_violation_can_have_a_capped_axis_and_the_verdict_is_the_other_axis() {
let a = tap();
let b = tap();
let (ca, cb) = (a.clone(), b.clone());
let mut tree = tree_at(TargetDensity::Compact);
let wide = tree.add(Leaf::new(200.0, 10.0).on_tap(move |_, _| ca.set(ca.get() + 1)));
let _row = tree.add(
Row::new(400.0, 40.0)
.pad(40.0)
.child(wide)
.on_tap(move |_, _| cb.set(cb.get() + 1)),
);
tree.layout(SizeProposal::exact(400.0, 300.0));
let tokens = InputTokens::for_density(TargetDensity::Compact);
let m = find(&measure_targets(&tree, TargetDensity::Compact), "Leaf");
assert_eq!(
m.rule,
Some(TargetRule::MinTargetConformance),
"the height is 10 dp and the row beside it denies the slop pass: {m:#?}",
);
assert!(
m.capped,
"and the width axis spent the whole probe budget: {m:#?}",
);
assert!(
(m.expanded.width - probe_limit(&tokens)).abs() <= 0.05,
"so the width in `expanded` is the budget ({}), not a boundary — measured {:.4}",
probe_limit(&tokens),
m.expanded.width,
);
assert!(
(m.expanded.height - 10.0).abs() <= 0.05,
"while the axis that decides the verdict is measured exactly: {m:#?}",
);
assert!(
target_audit(&tree, TargetDensity::Compact)
.iter()
.any(|v| v.path.ends_with("Leaf") && v.rule.is_conformance_failure()),
"and it reaches the gate as a failure",
);
}
#[test]
fn a_target_another_target_covers_part_of_is_not_judged_on_its_size() {
let a = tap();
let ca = a.clone();
let b = tap();
let cb = b.clone();
let mut tree = tree_at(TargetDensity::Compact);
let under = tree.add(Leaf::new(20.0, 30.0).on_tap(move |_, _| ca.set(ca.get() + 1)));
let over = tree.add(Leaf::new(8.0, 30.0).on_tap(move |_, _| cb.set(cb.get() + 1)));
let _root = tree.add(
Row::new(400.0, 300.0)
.pad(40.0)
.stack()
.child(under)
.child(over),
);
tree.layout(SizeProposal::exact(400.0, 300.0));
let all = measure_targets(&tree, TargetDensity::Compact);
let m = all
.iter()
.find(|x| x.node == under && x.part.is_none())
.expect("the covered leaf is measured");
assert!(
m.expanded.width + 0.05 < m.size.width,
"the sibling really does take part of its width: reach {:?}, paint {:?}",
m.expanded,
m.size,
);
assert_eq!(
m.skipped,
Some(SkipReason::ShadowedByAnotherTarget),
"what limits it is the sibling on top, not its size: {m:#?}",
);
assert_eq!(m.rule, None, "so it carries no verdict: {m:#?}");
}
fn theme_with_target_size(density: TargetDensity, target_size: f32) -> crate::styles::Theme {
let mut theme = crate::presets::intui::light();
theme.input = InputTokens {
target_size,
..InputTokens::for_density(density)
};
theme
}
#[test]
fn the_probe_reads_the_trees_own_token_ladder() {
let mut tree = WidgetTree::new().with_theme(theme_with_target_size(TargetDensity::Touch, 48.0));
let leaf_tap = tap();
let lt = leaf_tap.clone();
let leaf = tree.add(Leaf::new(46.0, 46.0).on_tap(move |_, _| lt.set(lt.get() + 1)));
let row_tap = tap();
let rt = row_tap.clone();
let row = tree.add(
Row::new(300.0, 120.0)
.pad(40.0)
.child(leaf)
.on_tap(move |_, _| rt.set(rt.get() + 1)),
);
let _root = tree.add(Row::new(400.0, 300.0).pad(20.0).child(row));
tree.layout(SizeProposal::exact(400.0, 300.0));
let m = find(&measure_targets(&tree, TargetDensity::Touch), "Leaf");
assert!(
(m.expanded.width - 46.0).abs() < 0.1 && (m.expanded.height - 46.0).abs() < 0.1,
"the leaf's own 46 dp, measured to a boundary: {:?}",
m.expanded,
);
assert!(
!m.capped,
"a budget one dp past this tree's own 48 dp floor reaches the boundary: {m:?}",
);
assert_eq!(
m.rule,
Some(TargetRule::TouchTargetRecommendation),
"46 dp clears the 24 dp AA floor and falls short of this theme's 48 dp \
recommendation: {m:?}",
);
}
#[test]
fn an_int_ui_trees_ladder_is_the_generic_table() {
for density in [
TargetDensity::Compact,
TargetDensity::Comfortable,
TargetDensity::Touch,
] {
let tree = tree_at(density);
assert_eq!(
tree.theme().input,
InputTokens::for_density(density),
"an Int UI tree at {density:?} must carry the generic ladder, or \
every measurement taken through the generic table before this \
change silently moved",
);
}
}
fn theme_with_conformance_floor(density: TargetDensity, floor: f32) -> crate::styles::Theme {
let mut theme = crate::presets::intui::light();
theme.input = InputTokens {
min_target_conformance: floor,
..InputTokens::for_density(density)
};
theme
}
#[test]
fn an_entry_is_judged_against_the_floor_the_walker_measured_not_the_generic_one() {
const RAISED: f32 = 32.0;
let generic = InputTokens::for_density(TargetDensity::Touch).min_target_conformance;
assert_eq!(
generic, 24.0,
"the generic table's floor is what this test contrasts against",
);
let a = tap();
let b = tap();
let (ca, cb) = (a.clone(), b.clone());
let mut tree =
WidgetTree::new().with_theme(theme_with_conformance_floor(TargetDensity::Touch, RAISED));
let small = tree.add(Leaf::new(30.0, 16.0).on_tap(move |_, _| ca.set(ca.get() + 1)));
let _row = tree.add(
Row::new(400.0, 60.0)
.pad(40.0)
.child(small)
.on_tap(move |_, _| cb.set(cb.get() + 1)),
);
tree.layout(SizeProposal::exact(400.0, 300.0));
let violation = target_audit(&tree, TargetDensity::Touch)
.into_iter()
.find(|v| v.path.ends_with("Leaf"))
.expect("the leaf fails the raised floor");
assert_eq!(
violation.rule,
TargetRule::MinTargetConformance,
"16 dp is under 32: {violation:?}",
);
assert!(
(violation.expanded.width - 30.0).abs() < 0.1
&& (violation.expanded.height - 16.0).abs() < 0.1,
"the tappable row denies the slop pass, so the reach is the leaf's own \
rectangle: {violation:?}",
);
assert_eq!(
violation.conformance_floor, RAISED,
"the row must carry the floor its verdict was taken against, not the \
generic {generic}: {violation:?}",
);
let as_generic = TargetViolation {
conformance_floor: generic,
..violation.clone()
};
assert!(
ClearsFloor.matches(violation.expanded.width, &as_generic),
"read against the generic table, 30 dp clears the floor — which is what \
would excuse this failure. The floor can no longer be handed to the pin \
as a bare number, so saying this costs a falsified row, which is the \
point: nothing reaches that comparison by accident any more",
);
let entry = AllowedViolation {
path: "Leaf",
measured: &[PinnedGeometry {
densities: &[TargetDensity::Touch],
themes: &["intui.light"],
paints: (ClearsFloor, Is(16.0)),
reaches: (ClearsFloor, Is(16.0)),
}],
owner: Owner::Named("this test"),
exception: None,
why: "a fixture entry",
};
assert!(
!entry.matches(&violation),
"30 dp is under this tree's {RAISED} dp floor, so a `ClearsFloor` axis \
does not clear it and the entry must not excuse the failure the walker \
just reported: {violation}",
);
let exact = AllowedViolation {
measured: &[PinnedGeometry {
densities: &[TargetDensity::Touch],
themes: &["intui.light"],
paints: (Is(30.0), Is(16.0)),
reaches: (Is(30.0), Is(16.0)),
}],
..entry
};
assert!(
exact.matches(&violation),
"the path, density, theme and both exact axes all match — only the \
`ClearsFloor` reading separates the two entries: {violation}",
);
}
#[test]
fn the_shadow_slack_covers_an_extent_not_a_direction() {
let quantum = PROBE_STEP / (1 << PROBE_REFINE) as f32;
assert!(
PROBE_EPSILON >= 2.0 * quantum,
"an extent carries two directions' refinement error ({} dp), and the \
slack is {} dp",
2.0 * quantum,
PROBE_EPSILON,
);
assert!(
PROBE_EPSILON <= 4.0 * quantum,
"a slack more than twice the noise band ({} dp) is no longer a bound on \
the probe's error but a tolerance on the finding, and the same constant \
decides what `classify` reports against the 24 dp floor; it is {} dp",
4.0 * quantum,
PROBE_EPSILON,
);
}
#[test]
fn a_target_short_of_its_paint_only_by_measurement_noise_is_still_judged() {
let quantum = PROBE_STEP / (1 << PROBE_REFINE) as f32;
let mut tree = tree_at(TargetDensity::Compact);
let leaf_tap = tap();
let lt = leaf_tap.clone();
let leaf = tree.add(Leaf::new(21.748, 21.748).on_tap(move |_, _| lt.set(lt.get() + 1)));
let row_tap = tap();
let rt = row_tap.clone();
let row = tree.add(
Row::new(300.0, 120.0)
.pad(40.0)
.child(leaf)
.on_tap(move |_, _| rt.set(rt.get() + 1)),
);
let _root = tree.add(Row::new(400.0, 300.0).pad(20.0).child(row));
tree.layout(SizeProposal::exact(400.0, 300.0));
let m = find(&measure_targets(&tree, TargetDensity::Compact), "Leaf");
let shortfall = m.size.height - m.expanded.height;
assert!(
shortfall > 1.9 * quantum && shortfall < 2.0 * quantum,
"the fixture must sit near the top of the two-quantum noise band, or a \
slack picked from inside the band would still cover it: shortfall {} \
dp, band ({}, {})",
shortfall,
quantum,
2.0 * quantum,
);
assert_eq!(
m.skipped, None,
"nothing overlaps this leaf, so the only gap between its paint and its \
reach is the probe's own: {m:#?}",
);
assert_eq!(
m.rule,
Some(TargetRule::MinTargetConformance),
"and a 21.7 dp target under the 24 dp floor is the failure it looks \
like: {m:#?}",
);
}