Fix flaky TUI diff-pipeline test (await layout_complete instead of fixed-iteration poll)#13676
Draft
warp-dev-github-integration[bot] wants to merge 1 commit into
Draft
Fix flaky TUI diff-pipeline test (await layout_complete instead of fixed-iteration poll)#13676warp-dev-github-integration[bot] wants to merge 1 commit into
warp-dev-github-integration[bot] wants to merge 1 commit into
Conversation
`diff_pipeline_computes_added_lines_and_ghost_blocks` flaked in CI (e.g. PR #13486) with `assertion left == right failed: left: 0, right: 1` at the `ghosts.len() == 1` check. Root cause: after `expand_diffs`, the removed-line ghost blocks are not stored synchronously. `refresh_diff_state` calls `RenderState::add_temporary_blocks`, which only `try_send`s a `LayoutAction::LayoutTemporaryBlock` onto the render state's async layout channel. That action is applied by a spawned foreground handler that only runs after a background->foreground hop: `App::spawn_stream_local` polls `layout_rx` on the (tokio) background executor and forwards each item to a foreground task that calls `handle_layout_action` -> `set_temporary_blocks`. Until that handler runs, `CharCellState::temporary_blocks` is empty, so `display_lattice(&[]).ghosts()` returns no blocks. The test polled for the ghosts with a fixed `for _ in 0..100 { ...; yield_now().await }` loop. `yield_now` only yields on the foreground executor; it does not drive the background thread that must first forward the queued action. Under CI CPU contention the 100 fast foreground yield cycles can elapse before the background thread is scheduled and the foreground handler stores the blocks, so the loop exits with `ghosts` empty and the assertion fails. This is a timing race, not a logic bug -- under load it reproduces ~50% of the time (40 parallel runs: 20 pass / 20 fail on the unmodified test). Fix: replace the fixed-iteration poll with the render state's `layout_complete()` future, the deterministic sync primitive already used by the GUI editor tests (`app/src/code/editor/model_tests.rs`). `submit_layout_action` increments an `outstanding_layouts` counter for every queued action and `handle_layout_action` decrements it as each is applied; `layout_complete()` awaits until that counter reaches zero, i.e. until every in-flight layout action -- including the ghost-block one -- has been applied. It loops without a cap, so it waits however long the background->foreground hop takes instead of racing it. `layout_complete()` is gated behind the editor crate's `test-util` feature, so enable `warp_editor/test-util` in `warp_tui`'s `[dev-dependencies]` (mirroring how the `app` crate wires it). The feature is empty apart from gating test-only sync primitives. Verified: 160/160 passes under 2x80 parallel CPU-oversubscribed runs (versus 20/20 failures at 40 parallel before); full `warp_tui --lib` suite green (102 tests); `cargo clippy -p warp_tui --all-targets --tests` and `cargo fmt -p warp_tui --check` clean. Co-Authored-By: Oz <oz-agent@warp.dev>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Fixes the flaky TUI test
diff_pipeline_computes_added_lines_and_ghost_blocks(flaked in CI, e.g. on a run for PR #13486), failing with:at the
assert_eq!(ghosts.len(), 1)check.Root cause
After
expand_diffs, the removed-line ghost blocks are not stored synchronously.CodeEditorModel::refresh_diff_statecallsRenderState::add_temporary_blocks, which onlytry_sends aLayoutAction::LayoutTemporaryBlockonto the render state's async layout channel. That action is applied by a spawned foreground handler that only runs after a background→foreground hop:App::spawn_stream_localpollslayout_rxon the (tokio) background executor and forwards each item to a foreground task that callshandle_layout_action→CharCellState::set_temporary_blocks. Until that handler runs,temporary_blocksis empty, sodisplay_lattice(&[]).ghosts()returns no blocks.The test waited for the ghosts with a fixed
for _ in 0..100 { …; yield_now().await }loop.yield_nowonly yields on the foreground executor; it does not drive the background thread that must first forward the queued action. Under CI CPU contention the 100 fast foreground yield cycles can elapse before the background thread is scheduled and the foreground handler stores the blocks, so the loop exits withghostsempty and the assertion fails. This is a timing race, not a logic bug — under load it reproduces ~50% of the time (40 parallel runs of the unmodified test: 20 pass / 20 fail).Fix
Replace the fixed-iteration poll with the render state's
layout_complete()future — the deterministic sync primitive already used by the GUI editor tests (app/src/code/editor/model_tests.rs).submit_layout_actionincrements anoutstanding_layoutscounter for every queued action andhandle_layout_actiondecrements it as each is applied;layout_complete()awaits until that counter reaches zero, i.e. until every in-flight layout action — including the ghost-block one — has been applied. It loops without a cap, so it waits however long the background→foreground hop takes instead of racing it.RenderState::layout_complete()is gated behind the editor crate'stest-utilfeature, so this enableswarp_editor/test-utilinwarp_tui's[dev-dependencies](mirroring how theappcrate wires it). That feature is empty apart from gating test-only sync primitives, so it has no effect on non-test builds.Linked Issue
N/A — flaky-test fix, not tied to a spec/issue.
Testing
Reproduced the flake first: 40 parallel runs of the unmodified test (CPU oversubscription to emulate CI contention) → 20 pass / 20 fail, every failure at
tui_file_edits_view_tests.rs:127withleft: 0, right: 1(empty ghosts).After the fix: 160/160 pass across two rounds of 80 parallel runs under the same load.
Full
cargo test -p warp_tui --libsuite green (102 passed, 0 failed).cargo clippy -p warp_tui --all-targets --tests -- -D warningsandcargo fmt -p warp_tui -- --checkclean.I have manually tested my changes locally with
./script/run— N/A: test-only change to a headless TUI unit test; verified via
cargo test/cargo clippy/cargo fmt.Screenshots / Videos
N/A — no UI change.
Agent Mode
CHANGELOG-NONE
Conversation: https://staging.warp.dev/conversation/ff08fdaf-28c9-4593-bd89-d0cc11d2de95
Run: https://oz.staging.warp.dev/runs/019f5d5a-48e7-7930-8f52-158e9f67161a
This PR was generated with Oz.