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:
parent
951c6447b3
commit
5371229b9a
|
|
@ -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(),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue