From 42a0a91f5886abf3b3c7ddf82bb05ee6efd949d3 Mon Sep 17 00:00:00 2001 From: Levi Neuwirth Date: Wed, 12 Aug 2026 22:38:45 +0200 Subject: [PATCH] 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 Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai --- tests/gui_stage1a_acceptance.rs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/tests/gui_stage1a_acceptance.rs b/tests/gui_stage1a_acceptance.rs index b0ca30b..dad89e5 100644 --- a/tests/gui_stage1a_acceptance.rs +++ b/tests/gui_stage1a_acceptance.rs @@ -324,6 +324,12 @@ fn a8_a_terminal_receives_raw_utf8_with_bracketed_paste_enabled() { let typed = "h\u{e9}llo\u{301}"; 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 got = loop { s.tick_processes(); @@ -356,20 +362,27 @@ fn a8_a_terminal_receives_raw_utf8_with_bracketed_paste_enabled() { s.dispatch_paste(FID, b"pasted"), "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); loop { s.tick_processes(); let all = String::from_utf8_lossy(&std::fs::read(&sink).unwrap_or_default()).into_owned(); - if all.contains("\u{1b}[200~") { - assert!( - all.contains("pasted") && all.contains("\u{1b}[201~"), - "a paste is bracketed on both sides: {all:?}" - ); + if all.contains(want) { break; } assert!( 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)); }