test: A8's contrast waited for a prefix, not the sequence

**A partial write could have failed correct code.** The contrast broke
out of its poll as soon as `ESC[200~` appeared and then required
`pasted` and `ESC[201~` in the same breath --- but PTY delivery and the
child's writes split anywhere, so the closer may simply not have
arrived. A flake that only ever fires on a loaded machine, and one that
would have been read as an A8 regression.

It now polls for the COMPLETE `ESC[200~pastedESC[201~`, which makes a
partial write indistinguishable from "not yet" --- which is what it is.
Same rule the vterm suite already follows when it waits for `row19`
rather than for a prefix of it.

**The typed-text read above it is sound and is now documented as such**,
because the two loops look alike and are not. Its gate is a LOWER BOUND
ON LENGTH: a split delivery keeps waiting rather than being mistaken for
a wrong answer, and the exact-equality assertion afterwards can still
fail for the real reason. A wait-for-exact-content loop there would have
been tautological. Stating the difference beside the two loops is worth
more than making them superficially uniform.

M-1a-4 re-run after the change: still fails the row, so the fix removed
a race without removing the discrimination.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
This commit is contained in:
Levi Neuwirth 2026-08-12 22:38:45 +02:00
parent e3a19a4e63
commit 42a0a91f58
No known key found for this signature in database
1 changed files with 19 additions and 6 deletions

View File

@ -324,6 +324,12 @@ fn a8_a_terminal_receives_raw_utf8_with_bracketed_paste_enabled() {
let typed = "h\u{e9}llo\u{301}"; let typed = "h\u{e9}llo\u{301}";
s.dispatch_text_input(FID, typed); s.dispatch_text_input(FID, typed);
// Waits for AT LEAST the full payload, then asserts exact equality.
// Sound against a split write for a reason the contrast below does
// not share: the gate is a lower bound on length, so a partial
// delivery keeps waiting rather than being mistaken for a wrong
// answer — and the equality can still fail for the real reason,
// which a wait-for-exact-content loop could not.
let deadline = Instant::now() + Duration::from_secs(10); let deadline = Instant::now() + Duration::from_secs(10);
let got = loop { let got = loop {
s.tick_processes(); s.tick_processes();
@ -356,20 +362,27 @@ fn a8_a_terminal_receives_raw_utf8_with_bracketed_paste_enabled() {
s.dispatch_paste(FID, b"pasted"), s.dispatch_paste(FID, b"pasted"),
"the terminal claims the paste" "the terminal claims the paste"
); );
//
// **Wait for the COMPLETE sequence, not the opening marker.** PTY
// delivery and the child's writes can split anywhere, so breaking
// as soon as `ESC[200~` appears and then requiring the payload and
// the closer is a race that fails on correct code — the closer may
// simply not have arrived yet. Polling for the whole string makes a
// partial write indistinguishable from "not yet", which is what it
// is. Same rule the vterm suite follows when it waits for `row19`
// rather than for a prefix of it.
let want = "\u{1b}[200~pasted\u{1b}[201~";
let deadline = Instant::now() + Duration::from_secs(10); let deadline = Instant::now() + Duration::from_secs(10);
loop { loop {
s.tick_processes(); s.tick_processes();
let all = String::from_utf8_lossy(&std::fs::read(&sink).unwrap_or_default()).into_owned(); let all = String::from_utf8_lossy(&std::fs::read(&sink).unwrap_or_default()).into_owned();
if all.contains("\u{1b}[200~") { if all.contains(want) {
assert!(
all.contains("pasted") && all.contains("\u{1b}[201~"),
"a paste is bracketed on both sides: {all:?}"
);
break; break;
} }
assert!( assert!(
Instant::now() < deadline, Instant::now() < deadline,
"a paste through the same terminal must be bracketed; got {all:?}" "a paste through the same terminal must be bracketed on both \
sides; waited for {want:?}, saw {all:?}"
); );
std::thread::sleep(Duration::from_millis(10)); std::thread::sleep(Duration::from_millis(10));
} }