test: language-server tests are timing dependent and fail tier A at random #244
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
The
language-servertests incompiler/tests/language-server.test.trbfailed intermittently in tier A (linux-x64), and the failing test differed from run to run on the same code. Observed on 2026-10-08/09 (run numbers of the forge CI):fef9149):Diagnostics > the findings of the lint rules are hints that name their rule(diagnostics.length() == 2)Diagnostics > ...testQuick fixes > the fix of a lint finding is a code action on its range05564c7, onlyruns-oninimages.ymlchanged):A keystroke in a body keeps the sites and the index of every other module > the first keystroke builds them for every module, ...(runs.length() == 2) andHover > a function shows its signature and its doc comment(answers.length() == 1; the log showsexpected one message, got [publishDiagnostics, hover result])Cause: the check of an open document runs in slices of 40 ms (
stepBudgetincompiler/src/language-server/run.trb,docs/design/LANGUAGE-SERVER.md: "the diagnostics are published after the last slice", "a request about a document whose check runs waits for its last slice"). On a loaded runner a slice ran out in the middle of a batch, so thepublishDiagnosticsof a document and the answer to a request landed in different batches than on an idle machine. A test that counts the messages of a batch (only,length() == 1) then saw two messages or none.State on
mainThe time-budget half is fixed:
started()in the test file setsserver.checkBudget = Int.maximum(commit2990ac8f, 2026-10-09), so a check a batch begins ends in that batch. Tests that need several steps setcheckBudget = 0on purpose. Since then no failed tier A job has a failing language-server test (details in the decision comment).What is left of the issue's "Wanted": the helper
onlystill counts the messages of a batch, so any later change that lets a notification share a batch with a response (a new notification, a changed publish order) turns into a random red again, with the same misleading message.Goal
A test that asks for the answer to a request gets exactly that answer, found by its request id, whatever notifications the same batch carries.
Acceptance criteria
compiler/tests/language-server.test.trbhas a helperresponseTo(var server, messages, id)that returns the response with thatidfrom the batch, ignoring notifications (publishDiagnostics,logMessage, ...), and fails with the batch printed (asonlydoes) when there is not exactly one response with that id.only(the hover, definition, completion, rename, code action, resolve, semantic tokens andtorbscript/teststests; request ids 5 to 85 in the file) usesresponseTowith its id instead.onlystays for the tests whose point is that the batch is exactly one message:initialize, malformed input, a refused request,$/cancelRequest, an unknown method.publishDiagnosticsof an opened document and the answer to a hover request in the same batch is answered byresponseTowith the hover result.started()keepscheckBudget = Int.maximumand its comment; no test in the file reads an answer by position (answers[0]) or byanswers.length() == 1when its batch holds a request.sh tools/gates.sh ais green in the forge CI of the PR (the changed file has no other behavioural change; the test count rises by one).Design record, seed impact, size
docs/design/LANGUAGE-SERVER.md(the check in slices between the messages; behaviour under test, not changed). ROADMAP: Milestone 8, language server; no milestone slice changes.test(lsp): ...).Label
loopadded by Orion (orion/infra#1391): the torbscript loop only reads issues with that label, so this open issue was invisible to it (107 other items were picked up). No scope change intended; intake will triage it.[loop:torbscript] claim refine by session:loop-torbscript-20261013 until 2026-10-13T00:57Z
[loop:torbscript] decided by the loop: narrow the issue to hardening the request helper in the test file (
responseToby request id), keep the existingcheckBudget = Int.maximumfix, and do not add a clock or a retry. Why: the two causes were separated by measurement. (1) The flake came from the 40 ms slice budget (stepBudget): on a loaded runner a check straddled two batches, sopublishDiagnosticsand the response of a request shared or split a batch differently than on an idle machine. Commit2990ac8f(2026-10-09) removed the budget instarted(). (2) Evidence from forge CI since that fix reachedmain(runs from 2026-10-09 13:30Z to 2026-10-12 22:55Z, 396 runs): 178 tier A jobs, 105 success, 33 failure, 33 cancelled, 7 skipped; the logs of all failed jobs (run window from 12:56Z) show 11 distinct failing tests, none of them inlanguage-server.test.trbexcept one completion-item test in run 602 (2026-10-09 12:56Z), which ran before the fix reachedmain. Before the fix the issue lists 5 language-server failures in 4 runs within one day. (3) The slices stay (they are the design indocs/design/LANGUAGE-SERVER.md); a fake clock would change the server for a test only, andserver.now/checkBudget = 0already give the deterministic stepping the slice tests need. (4) What remains is cheap and mechanical: 30 uses ofonlyin the file, about 20 of them read the answer of a request; selecting it by id removes the whole class (a stray notification in the batch) rather than the one cause already fixed. Size: one test file, one commit, no seed impact.[loop:torbscript] refine: triaged in scope (author orion's issue under label
loop, no duplicate, no design-record collision), located the cause in the 40 ms slice budget of the check (already fixed onmainby2990ac8f, confirmed by tier A history), narrowed the remaining work to aresponseTohelper by request id in the language-server test file, rewrote the body with goal, five acceptance criteria, design record, seed impact (no) and size (one PR, one commit); about 96k tokens, next: implement[loop:torbscript] claim implement by session:loop-torbscript-20261013 until 2026-10-13T05:03Z
[loop:torbscript] release implement: the worker ended on a session limit before opening a PR, next: implement (respawn follows with a fresh claim)
[loop:torbscript] claim implement by session:loop-torbscript-20261013 until 2026-10-13T09:42Z
[loop:torbscript] release implement: the worker ended on a session limit after opening PR #356 (head
b0410e55); all required forge CI jobs are green and the merge gate's review is requested, next: waiting (merge gate review of PR #356)