test(gui-1b): the split row now guarantees its own setup

The row claimed to target the inactive pane and did not. It hit-tested
a hardcoded column, took whichever pane that resolved to, and then
branched its assertions to match. If the layout ever put the active pane
under that column, an active-window-routing mutant would pass and the
focus assertion would be a tautology --- the row would look like
evidence while proving nothing.

It now SCANS for a cell the hit-test actually resolves to the inactive
pane, asserts `under_pointer == other` before dispatching, and asserts
directly with no adaptive branch:

  the inactive pane moves by three
  the active pane's origin is unchanged
  the active window id is unchanged

The setup guard is itself falsifiable: pointing the scan at the active
pane makes the row fail on "setup: the pointer must be over the INACTIVE
pane" rather than passing quietly. That check matters more than the
mutation --- a setup assertion nothing can break is the same defect one
level up.

The routing mutant still fires the row, and only it.
This commit is contained in:
Levi Neuwirth 2026-09-01 11:32:48 +02:00
parent 951c6447b3
commit 5371229b9a
No known key found for this signature in database
1 changed files with 37 additions and 26 deletions

View File

@ -10660,46 +10660,57 @@ mod tests {
let active = s.core.borrow().active_window_id(); let active = s.core.borrow().active_window_id();
let other = *ids.iter().find(|id| **id != active).expect("a second pane"); let other = *ids.iter().find(|id| **id != active).expect("a second pane");
// A 50/50 vertical split: column 60 is inside the right pane.
// Whichever pane that is, it must be the one that moves. // **Find a cell the hit-test actually resolves to the INACTIVE
let target_col: u16 = 60; // pane**, rather than assuming a column lands there. A row that
let under_pointer = { // adapts to whichever pane it hits cannot fail an
// active-window-routing mutant, and its focus assertion becomes
// a tautology.
let size = term_size_24x80();
let target = {
let core = s.core.borrow(); let core = s.core.borrow();
window_at_cell( (0..size.rows.saturating_sub(1))
&core, .flat_map(|row| (0..size.cols).map(move |col| (row, col)))
FrontendId::LOCAL, .find(|(row, col)| {
term_size_24x80(), window_at_cell(&core, FrontendId::LOCAL, size, *row, *col)
5, .is_some_and(|(id, _)| id == other)
u32::from(target_col), })
)
.map_or(other, |(id, _)| id)
}; };
let (target_row, target_col) = target.expect("some cell must resolve to the inactive pane");
{
let core = s.core.borrow();
let (under_pointer, _) =
window_at_cell(&core, FrontendId::LOCAL, size, target_row, target_col)
.expect("the cell resolves to a pane");
assert_eq!(
under_pointer, other,
"setup: the pointer must be over the INACTIVE pane"
);
}
let before_active = s.core.borrow().windows[&active].view_left; let before_active = s.core.borrow().windows[&active].view_left;
let before_other = s.core.borrow().windows[&other].view_left; let before_other = s.core.borrow().windows[&other].view_left;
s.dispatch_mouse( s.dispatch_mouse(
FrontendId::LOCAL, FrontendId::LOCAL,
mouse(MouseEventKind::ScrollRight, 5, target_col), mouse(
term_size_24x80(), MouseEventKind::ScrollRight,
u16::try_from(target_row).unwrap_or(0),
u16::try_from(target_col).unwrap_or(0),
),
size,
); );
let after_active = s.core.borrow().windows[&active].view_left;
let after_other = s.core.borrow().windows[&other].view_left;
let (moved, still) = if under_pointer == active {
((after_active, before_active), (after_other, before_other))
} else {
((after_other, before_other), (after_active, before_active))
};
assert_eq!( assert_eq!(
moved.0 - moved.1, s.core.borrow().windows[&other].view_left - before_other,
SCROLL_COLUMNS as u32, SCROLL_COLUMNS as u32,
"the pane under the pointer moves by one notch" "the INACTIVE pane, under the pointer, moves by one notch"
); );
assert_eq!( assert_eq!(
still.0, still.1, s.core.borrow().windows[&active].view_left,
"and the other pane's origin is untouched — horizontal state \ before_active,
is per-window" "the ACTIVE pane's origin is untouched — horizontal state is \
per-window, and routing to the active pane would move this"
); );
assert_eq!( assert_eq!(
s.core.borrow().active_window_id(), s.core.borrow().active_window_id(),