Enable VS integration tests on CI#19930
Conversation
❗ Release notes requiredYou can open this PR in browser to add release notes: open in github.dev Caution No release notes found for the changed paths (see table below). Please make sure to add an entry with an informative description of the change as well as link to this pull request, issue and language suggestion if applicable. Release notes for this repository are based on Keep A Changelog format. The following format is recommended for this repository:
If you believe that release notes are not necessary for this PR, please add NO_RELEASE_NOTES label to the pull request.
|
Can't use `dotnet` due to a conflict with mtp/xunit3
This comment has been minimized.
This comment has been minimized.
This reverts commit 2458906.
This reverts commit 28ae1d6.
This reverts commit b3d0b5b.
The two GoToDefinitionTests [IdeFact]s were no-ops: after Shell.ExecuteCommandAsync(GotoDefn) the caret line was sampled immediately, but FSharpNavigation.NavigateToItem schedules the actual caret move asynchronously (via JoinableTaskFactory). The previous fixed Task.Delay then re-anchored the caret before navigation landed, making it bounce between the call site and the definition without ever being detected. Replace the fixed delay with GoToDefinitionWithRetryAsync: wait for the project system, anchor the caret on the call site, invoke GotoDefn, then poll for the caret line to change (letting the async navigation land) before treating the attempt as a no-op and retrying. The caret is only re-anchored at the start of each attempt, never while a navigation may be in flight. Add EditorInProcess.ActivateAsync to keep the editor as the active command context after PlaceCaretAsync's dte.Find shifts VS's active selection (otherwise GotoDefn routes to the wrong target and returns E_FAIL). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CI showed FsiAndFsFilesGoToCorrespondentDefinitions now passes but GoesToDefinition still no-ops (GotoDefn never resolves the symbol after 20 attempts, no command error). The only structural difference is that the passing test builds the solution while GoesToDefinition only edited the buffer via SetTextAsync. Building gives the F# checker the project's full options and on-disk source, so the symbol resolves. Mirror the sibling test by building before navigating. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The previous build step left the Build Output pane as the active text view, so PlaceCaretAsync
searched the build log ('Marker add 1 not found in text: Build started...') instead of the
source. Re-open Library.fs after building to make it the active document again, mirroring
FsiAndFsFilesGoToCorrespondentDefinitions which opens the file it navigates after building.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…sion A future-proofing requirement: when F# code actions move to LSP, the fixes will be served by the VS LSP client's CodeActionSource rather than Roslyn's SuggestedActionsSource, so querying Roslyn's IAsyncSuggestedActionsSource directly would stop finding them. The lightbulb broker session aggregates all suggested-action sources, so it stays valid across that migration. Use broker.CreateSession(Any) (a session always exists, even for a push-model analyzer whose diagnostic isn't published yet - unlike ShowQuickFixes) and wait for the terminal SuggestedActionsUpdated event rather than PopulateWithDataAsync's fast/empty initial snapshot. Retry with a fresh session on dismissal or per-attempt timeout to cover background-analyzer timing. PopulateWithDataAsync and SuggestedActionsUpdatedArgs.ActionSets are ImmutableArray-typed (System.Collections.Immutable skews between the NuGet ref and the in-proc VS runtime), so they're invoked/read via reflection through the non-generic IEnumerable; these are VS platform APIs unaffected by the LSP move. A per-attempt diagnostic dump is thrown on timeout since the CI log is the only signal. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Temporary diagnostic instrumentation to determine, headless, whether the unused-opens semantic analyzer diagnostic is actually published to the Error List (the producer-agnostic lightbulb session finds nothing and dismisses every attempt for UnusedOpenDeclarations). InvokeCodeActionListAsync now catches the no-actions failure and appends a dump of every Error List entry (all sources/severities) so the CI log shows whether the diagnostic exists. Passing tests are unaffected. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CI evidence (build 1466711): code-action failures coincide with an EMPTY error list - the F# checker hadn't produced any diagnostic for the document, so no code fix could exist - and the failing set rotated run-to-run, i.e. flaky. The prior 200+-attempt lightbulb churn appears to starve the checker. Before invoking the lightbulb, poll the error list lightly (500ms, no lightbulb activity) until the document has at least one diagnostic of any severity, then invoke once. This targets the actual root cause (missing diagnostic) rather than lightbulb session mechanics. Best-effort with a 60s budget; the error-list dump on failure is retained so a genuinely diagnostic-free state is still visible. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Decisive probe of the warm-up/latency hypothesis. Last run proved the diagnostic IS published (FS0760 in the error list) yet the session still auto-dismissed on its empty initial snapshot. After confirming the diagnostic is present, wait 2 minutes with NO lightbulb activity so the checker, analyzers and the editor's lightbulb tagger fully settle, then do a short bounded invoke (30s). If it passes, the failures are a warm-up race and the approach is salvageable with an adaptive wait; if it still 'session dismissed', settling is irrelevant and the session path is structurally unfit headless. Temporary - to be reverted once we learn which. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…oking
The Error List barrier was hidden-blind: F# unused-opens is a DiagnosticSeverity.Hidden ('Unnecessary'
fade) diagnostic (Roslyn FSharpUnusedOpensDeclarationsDiagnosticAnalyzer), so it never appears in the
Error List - the '0 entries' readings were a measurement artifact, not proof the analyzer didn't run.
Replace the error-list barrier and the temporary 2-minute settle with a producer-agnostic poll of
ILightBulbBroker.IsLightBulbSessionActive: it reflects the editor's own lightbulb tagger detecting an
available fix, including ones from Hidden diagnostics. The 120s cap doubles as a quiet settle (no
lightbulb churn) for fixes whose computation lags. The barrier outcome is included in the failure dump.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Bring back the 2-minute quiet settle before invoking the lightbulb (the best producer-agnostic result so far: AddNew/AddMissingFun passed under it) so we can re-run a few times and measure how flaky it is. For UnusedOpenDeclarations, skip the error-list barrier entirely: its diagnostic is Hidden and never appears in the error list, so that wait can only ever time out. Add an InvokeCodeActionListAsync overload taking waitForErrorListDiagnostics and pass false from that test; the 2-minute settle still applies. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ation listeners Drain Roslyn's IAsynchronousOperationListener (the mechanism Apex/Roslyn integration tests use) instead of time/heuristic barriers: enable tracking via RoslynWaiterEnabled env + ctor Enable; pre-drain Workspace/SolutionCrawlerLegacy/DiagnosticService so the fix exists before opening the lightbulb session; LightBulb post-drain. Keep the producer-agnostic broker-session reader. Remove the 2-minute sleep and error-list barrier. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Build 1468525 proved the broker.CreateSession session is superseded/dismissed by the editor's real lightbulb in ~20ms headless (the persistent 'session dismissed'). TypeScript-VS Apex and Roslyn never create an owned session - they trigger the editor's real lightbulb (ShowQuickFixes) and read broker.GetSession(view). Switch to that: PostExecCommand(VSStd14 ShowQuickFixes) after activating the document (Find leaves the wrong command target), then read the editor-owned IAsyncLightBulbSession. Producer-agnostic; keeps the best-effort diagnostic pre-drain and tracking enablement. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
On a code-action failure, query ILightBulbBroker.HasSuggestedActionsAsync and ILightBulbBroker2.GetSuggestedActionCategoriesAsync at the caret to record whether any fix is offered there at all. This distinguishes 'fix never offered' (diagnostic not produced) from 'fix offered but no session' for the remaining UnusedOpenDeclarations failure. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…nused-opens Build 1472364 probe (HasSuggestedActions=False; categories=[]; Error List 0) proved failures are upstream of the lightbulb: the F# diagnostic isn't computed for the freshly-opened document, so the fix is never offered. AddMissingFun is flaky this way; UnusedOpens (Hidden background-analyzer diagnostic) never produces. - Quarantine UnusedOpenDeclarations (Skip) until reliable. - AddNew/AddMissingFun: MaxAttempts=3 (harness retries on a fresh VS instance per attempt; a pass on any attempt reports green). - Force diagnostics with a net-zero buffer edit after the project is loaded, raising per-attempt success. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Build 1475156 crashed with a fatal 'Duplicate attribute' error: MaxAttempts reports a failed non-final attempt as skip+retry, and the xunit.console.exe -xml reporter used by the inttests leg throws when the same test is written to the results XML twice. Drop MaxAttempts and rely on the real-session reader plus the re-analysis trigger; the diagnostic probe stays so the next run gives a clean signal. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Build 1475226: AddMissingFun still failed with 'fix never offered' (probe HasSuggestedActions=False) even with a single pre-loop re-analysis trigger - that one edit can fire before the freshly-created project's F# checker has options, so analysis yields no diagnostic and nothing re-triggers it for 64s. Move the trigger into the loop: odd attempts re-force analysis (net-zero buffer edit), even attempts harvest, so we keep getting fresh chances until the checker is ready. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…le infra) Code-action diagnostics often aren't produced in the headless CI VS because view-driven producers (error-squiggle tagger) need a realized, visible desktop. Port Roslyn's integration-test machine setup so devenv runs interactively: - New eng/build-utils-win.ps1 with Capture-Screenshot. - eng/Build.ps1: Setup-IntegrationTestRun (screenshot probe; on failure tsdiscon/tscon /dest:console reconnect; re-capture fail-fast) + MinimizeAll, wired into the CI testIntegration path; add devenv to the on-exit stop list. - eng/SetupVSHive.ps1: VS/UI registry hardening (roaming, IntelliCode, background download, spell checker, Report Exceptions dialog, targeted notifications). - azure-pipelines-PR.yml: pass -prepareMachine to the inttests leg; publish the startup screenshot to prove a live desktop. - Un-quarantine UnusedOpenDeclarations to measure the full effect. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Instrument the full unused-opens diagnostic flow to figure out why the UnusedOpenDeclarations integration test sees 0 diagnostics after 60s on CI. Tracing is added at every decision point: - FCS getUnusedOpens: log OpenDeclarations count, symbol uses, open statements, and result. - UnusedOpensDiagnosticAnalyzer.AnalyzeSemanticsAsync: log whether the analyzer is called, IsFSharpMiscellaneousOrMetadata check, IsFSharpCodeFixesUnusedOpensEnabled check, FCS results, and final diagnostic count. - AsyncOperationWaiter: log feature drain timing and whether Roslyn tracking is enabled, workspace type. - LightBulbHelper: per-attempt detail logging. - EditorInProcess: log document text, caret position, feature drain progress. - CodeActionTests: pre-invoke error list dump, step-by-step trace. - AbstractIntegrationTest: add ConsoleTraceListener so all Trace output appears in the xunit test log. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tests # Conflicts: # vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs
Collapse an over-long multi-line Trace.TraceInformation call in ServiceAnalysis.fs that fantomas --check flagged. No behavior change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…sis completes Build 1477103 (interactive desktop) fixed AddNew + AddMissingFun; only UnusedOpens still failed. The log showed UnusedOpensAnalyzer restarting every ~11s - exactly our odd-attempt re-trigger cadence - and the Library project's F# IncrementalBuilder was only created at 64s (the timeout). The periodic net-zero buffer edit was cancelling the slow unused-opens computation (which needs the project builder + a full check) before it could finish. Trigger reanalysis only on the first attempt and leave the buffer quiet thereafter, and raise the overall timeout 60s->120s so the background analysis has room to complete. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ing the lightbulb (B+) Build 1477635 showed the single-trigger change regressed ALL code-action tests: our retry loop repeatedly posted ShowQuickFixes and dismissed the session every ~5s, which cancels the slow F# unused-opens query (and starved the fast ones of a post-project-ready touch). Redesign GetCodeActionsAsync: - PRODUCE-GATE: touch the buffer once, then gently poll broker.HasSuggestedActionsAsync (producer-agnostic; no session create/dismiss, no re-touch) until a fix is offered. This gives slow background analyzers an uninterrupted window. Bounded re-touch only if the gate stays false for 45s (covers a touch that fired before the checker was ready). - READ: only after a fix is confirmed offered do we invoke the real lightbulb and read its session - session churn there can no longer cancel the producer. - Distinct failure messages (gate vs read) make the next run self-diagnosing. Overall budget 150s; 30s read budget. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Build 1477939: B+ produce-gate FIXED UnusedOpens (gate flips in 0.2s; the prior slowness was entirely our churn) and AddNew. Only AddMissingFun failed: broker.HasSuggestedActionsAsync is a false-negative for the F# parse-error ErrorFix - the fix IS available (ShowQuickFixes + session read found it in builds 1469227/1477103) but the broker's query API never reports it. Keep the gate (instant for CodeFix) but ALSO attempt a real lightbulb read every ~15s even when the gate is false, to catch gate-blind fixes. UnusedOpens gates in ~1s and is read before any fallback fires, so the 15s spacing only runs for the gate-blind case and still protects slow producers from churn. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
AddMissingFun + UnusedOpens flake on F# diagnostic production (failing one rotates; both pass most runs). Roslyn absorbs identical flakiness with MaxAttempts=2, but it crashes our xunit.console -xml runner ('Duplicate attribute'). Switch the inttests-only runner to VSTest, which tolerates the harness's retry reporting:
- csproj (inttests only): add xunit.runner.visualstudio (v2 adapter) + XunitXml.TestLogger + Microsoft.TestPlatform.ObjectModel; drop the xunit.runner.console PackageDownload.
- eng/Build.ps1: TestUsingXUnitConsole -> TestUsingVSTestConsole = dotnet exec vstest.console.dll <dll> /Platform:x64 /Logger:xunit;LogFilePath=<same path> /TestAdapterPath:<outdir>. XUnit results format preserved, so the shared ADO publish step and all other legs are unchanged; not dotnet test (avoids MTP). This function is used ONLY by the integration leg.
- AbstractIntegrationTest: MaxAttempts=2 (retry a failed test once on a fresh VS).
- Revert the premise-wrong fallback read to the pure B+ produce-gate (when the gate is false the fix is genuinely absent; retry covers the flaky runs).
Validated locally: vstest.console + the xUnit v2 adapter discovers all 13 IdeFact tests (adapter<->xunit 2.9.2 + IdeTestFramework compatible).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…roduction Build 1478951 (VSTest + MaxAttempts=2): 12/13 green - AddMissingFun is now reliable via retry, but UnusedOpens failed BOTH fresh-VS attempts (gate stayed false 150s, HasSuggestedActions=False each time). Its fix comes from F#'s background unused-opens analyzer (SemanticDocumentAnalysis needing the IncrementalBuilder + full check), whose production is non-deterministic for a freshly-opened single-file project headless - offered in <1s on some runs, never on others - so MaxAttempts can't absorb the low per-attempt rate. Roslyn's C# 'Remove unnecessary usings' is produced reliably and needs no special handling; the durable fix is on the F# analyzer-scheduling side, tracked separately. Skip until then. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…issingFun passed This reverts commit c899537. Build 1479180 showed MaxAttempts=2 (with the pure B+ gate) still failed AddMissingFunKeyword on both fresh-VS attempts (gate stayed false). The earlier hybrid produce-gate + spaced fallback read (commit 42debbe, build 1478724) passed AddMissingFun reliably because the fallback read catches the fix even when broker.HasSuggestedActions is a false-negative for it. Go back to that state: xunit.console runner, the fallback read, and no MaxAttempts. UnusedOpenDeclarations stays quarantined (commit 88ec329, untouched by this revert). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ession
Build 1479289 showed AddMissingFun's failure is a dismissal race, not a production failure: at 16.9s a real session appeared and was immediately dismissed ('real session dismissed'), then the 15s-spaced fallback reads all missed it ('no active lightbulb session'). The broken-syntax (error-recovery) lightbulb is unstable headless - it flickers - so a sparse cadence can't catch it. This is the same reader that passed AddMissingFun in builds 1469227/1477103 (aggressive invoke+read), before the produce-gate (added for unused-opens) starved it.
Rework GetCodeActionsAsync: drop the broker.HasSuggestedActions gate (a proven false-negative for the parse-error fix) and read aggressively - invoke ShowQuickFixes + read the session every ~0.5s, with a short 3s active-wait so a no-session attempt retries fast. Use the Error List (the F# diagnostic is error/warning severity and reliably appears there) as the produce signal that gates re-touch. Safe to be aggressive now that the slow unused-opens test, which needed an uninterrupted quiet window, is quarantined.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
🔍 Tooling Safety Check — Affects-Build-Infra, Affects-Design-Time, Affects-Restore, Affects-Test-Tooling
|
T-Gro
left a comment
There was a problem hiding this comment.
🤖 This review was generated by AI (@expert-reviewer agent). Findings may contain inaccuracies — please verify independently.
Scope: This is a WIP CI test-enablement PR; the bulk of the diff is VS integration-test harness/build-infra and is reasonable. I focused on the production (non-test) code changes.
- The
splitSymbolUses/filterOpenStatementsrefactor inServiceAnalysis.fs(explicit(symbolUses1, symbolUses2)tuple) is behavior-preserving —Array.partitionalready returns that tuple, so no functional change. - Main finding: extensive
Trace.TraceInformationdebug instrumentation was added to two IDE hot paths (getUnusedOpensandUnusedOpensDiagnosticAnalyzer). See inline comments — this should be removed or gated before the PR leaves WIP.
| openStatements.Length | ||
| ) | ||
|
|
||
| for os in openStatements do |
There was a problem hiding this comment.
This (and the for r in result loop at line 346, plus the surrounding Trace.TraceInformation calls) is debug instrumentation in a hot path. getUnusedOpens runs on every semantic analysis pass in the IDE. Trace.TraceInformation(fmt, args) always allocates a boxed object[] and evaluates the args (os.Range, os.OpenedGroups.Length, etc.) at the call site regardless of whether any listener is attached; the two per-item loops make this scale with the number of open statements and results. With a TraceListener attached in VS this also does synchronous I/O on every keystroke-driven analysis. This looks like temporary debugging for the CI investigation (the PR is WIP) — please strip all the [UnusedOpens] tracing before merging, or gate it behind Trace.WriteLineIf/a compile-time switch so it can't regress IDE responsiveness.
|
|
||
| member _.AnalyzeSemanticsAsync(descriptor, document: Document, cancellationToken: CancellationToken) = | ||
| Trace.TraceInformation( | ||
| "[UnusedOpensAnalyzer] AnalyzeSemanticsAsync ENTER: doc={0} proj={1} isMisc={2} isScript={3}", |
There was a problem hiding this comment.
Same concern as in ServiceAnalysis.fs: AnalyzeSemanticsAsync / GetUnusedOpenRanges run on every document-change analysis, and each added Trace.TraceInformation call allocates and boxes its args every invocation even with no listener attached. This is clearly temporary diagnostic logging for the CI test-enablement work — it should be removed (or guarded) before this leaves WIP so it doesn't ship as per-keystroke tracing overhead in the editor.
WIP Testing on CI