Skip to content

Fix the races behind flaky Review-pane and language server tests - #511

Draft
jamescrosswell wants to merge 4 commits into
mainfrom
fix/flaky-host-tests
Draft

jamescrosswell wants to merge 4 commits into
mainfrom
fix/flaky-host-tests

Conversation

@jamescrosswell

@jamescrosswell jamescrosswell commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes the races behind the CI flakes that keep hitting PRs: host tests timing out (RunAsync timed out) once they open the Review pane, the language server smoke saying server never became ready (Loading), and LanguageServerTests failing now and then. Three are app bugs and two are test bugs.

Review tab (app fix). Showing the Review tab starts its load. When the fake git call finished before the await checked it, the load carried on synchronously on the main thread, and App.Invoke showed the result inside the tab switch, before FocusReview had moved focus. The refresh's focus settling then put focus back on the Explorer, the sidebar's Tabs followed it, and any test waiting on the Review pane timed out. ReviewView.Refresh now starts the load off the main thread when hosted, so its results always arrive on a later iteration. Real git is slow enough that this practically never happened in the app, but it was a re-entrancy bug all the same.

Language server readiness (app fix). The client marked itself initialized only after sending initialized, and worked out readiness and set it in two separate steps. A server that reported its loading finished before the client had marked itself initialized, as the smoke's fake does in reply to initialized, left it Loading until the quiet-load timer. In the AOT smoke that timer is the whole 10 s timeout, hence server never became ready (Loading); in the app, the status bar could say loading for longer than it should. Readiness is now decided and set under one lock, and checked again once the client is initialized.

Language server stop reason (app fix). Stopped recorded why the server stopped, killed the process, and only then set the state. The kill completes the connection, which calls Stopped(null) from another thread, and when that landed in between it replaced the reason with null, so A_server_that_fails_to_initialize_stops_and_says_why saw no failure message. The state is now set with the reason, under the same lock. Under stress that test failed 2 times in 400 runs before, and 0 in 600 after.

Language server tests (test fixes).

  • Edits_made_before_the_server_has_started_open_it_at_their_version assumed the fake hadn't answered initialize before the test's edits. The fake can now hold that answer (HoldInitialize), and the test holds it until it has made them.
  • The fake recorded a notification before handling it. A test that waited to see one could act before the fake had, so FinishLoading's "end" could overtake the "begin" that initialized triggers, which is what made It_loads_until_the_server_says_it_has_finished_then_is_ready time out. It now records a notification once it has handled it.

Notes

  • The Review race reproduces locally under load. Running FocusHostTests and ReviewOverviewHostTests in 10 parallel processes, the old code timed out 6 times in about 224 runs; with the fix, 0 in 240. A temporary stack-trace hook showed the chain: FocusReview → SidebarPart.ShowTab → ReviewView.Refresh → synchronous Show → SettleFocusAfterRedraw → focus to the Explorer.
  • A_server_that_finishes_loading_before_it_answers_initialize_is_ready_once_it_has is new. It times out every time against the old client and passes with the fix. The language server tests (LanguageServerTests, LanguageServersTests, GoToDefinitionHostTests, FindUsagesHostTests) then ran 400 times in parallel with no failures; before the fake's fix, It_loads_until… failed 2 times in 300.
  • The narrow-terminal sidebar drag test, the other Windows timeout this branch started with, was fixed on main by Ctrl+\ puts the current file beside the others, in a second editor group #500 in the meantime with the same change (pinning the screen size), so it's no longer in this diff.

🤖 Generated with Claude Code

jamescrosswell and others added 4 commits October 11, 2026 21:56
Showing the Review tab starts its load, and when the fake git call
finished before the await checked, the load carried on synchronously
on the main thread and showed its result inside the tab switch, before
FocusReview had moved focus. The refresh then put focus back on the
Explorer, the sidebar followed it, and tests waiting on the Review pane
timed out. Hosted, the load now starts off the main thread, so its
results always arrive on a later iteration.

The narrow-terminal sidebar drag test set the screen size once, but the
Windows console reports its own size periodically, which undid it while
the test waited for the sidebar to shrink. It now pins the size, as
other host tests do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	tests/TuiCode.Tests/SidebarDragHostTests.cs
The client marked itself initialized only after sending `initialized`,
and decided readiness and set it in two steps. A server that reported
its loading done before the client had marked itself initialized, as
the smoke test's fake does in reply to `initialized`, left it Loading
until the quiet-load timer, which in the AOT smoke is its whole 10 s
timeout. Readiness is now decided and set under one lock, and checked
again once the client is initialized.

Two test races go with it: the "edits before the server has started"
test now holds the fake's initialize answer until it has made its edits,
and the fake records a notification only once it has handled it, so a
test that waits for one can rely on its effect.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Stopped recorded the reason, killed the process and only then set the
state. The kill completes the connection, which calls Stopped(null) from
another thread, and when that landed in between it replaced the reason
with null. The state is now set with the reason, under the same lock.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jamescrosswell jamescrosswell changed the title Fix two causes of host tests timing out on Windows CI Fix the races behind flaky Review-pane and language server tests Oct 11, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant