From bd19287ad91337b7aca373e05fe43638aa179ce1 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 29 Jul 2025 13:56:24 -0700 Subject: [PATCH 01/32] add test resource --- .../testAssets/slnWithCsproj/src/scripts/app1.cs | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 test/lsptoolshost/integrationTests/testAssets/slnWithCsproj/src/scripts/app1.cs diff --git a/test/lsptoolshost/integrationTests/testAssets/slnWithCsproj/src/scripts/app1.cs b/test/lsptoolshost/integrationTests/testAssets/slnWithCsproj/src/scripts/app1.cs new file mode 100644 index 0000000000..7659398405 --- /dev/null +++ b/test/lsptoolshost/integrationTests/testAssets/slnWithCsproj/src/scripts/app1.cs @@ -0,0 +1,4 @@ + +using Newton; + +Console.WriteLine("Hello World!"); From 1305e2180025e6bdfe75a4a6771a43d719de9355 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 29 Jul 2025 15:31:39 -0700 Subject: [PATCH 02/32] WIP: Integration test for file-based programs restore scenario --- CONTRIBUTING.md | 4 +- src/lsptoolshost/projectRestore/restore.ts | 10 ++- .../server/languageServerEvents.ts | 31 ++++++++ .../server/roslynLanguageServer.ts | 20 ++++++ src/lsptoolshost/server/roslynProtocol.ts | 12 ++++ .../completion.integration.test.ts | 21 +----- .../integrationTests/integrationHelpers.ts | 23 +++++- .../restore.integration.test.ts | 71 +++++++++++++++++++ 8 files changed, 167 insertions(+), 25 deletions(-) create mode 100644 test/lsptoolshost/integrationTests/restore.integration.test.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 51fc79f9d1..04c545db45 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -64,9 +64,9 @@ To debug unit tests locally, press F5 in VS Code with the "Launch Tes To debug integration tests 1. Import the `csharp-test-profile.code-profile` in VSCode to setup a clean profile in which to run integration tests. This must be imported at least once to use the launch configurations (ensure the extensions are updated in the profile). 2. Open any integration test file and F5 launch with the correct launch configuration selected. - - For integration tests inside `test/lsptoolshost`, use either `Launch Current File slnWithCsproj Integration Tests` or `[DevKit] Launch Current File slnWithCsproj Integration Tests` (to run tests using C# + C# Dev Kit) + - For integration tests inside `test/lsptoolshost`, use either `[Roslyn] Run Current File Integration Test` or `[DevKit] Launch Current File Integration Tests` (to run tests using C# + C# Dev Kit) - For integration tests inside `test/razor`, use `[Razor] Run Current File Integration Test` - - For integration tests inside `test/omnisharp`, use one of the `Omnisharp:` current file profiles + - For integration tests inside `test/omnisharp`, use one of the `[O#] Run Current File Integration Test` current file profiles These will allow you to actually debug the test, but the 'Razor integration tests' configuration does not. diff --git a/src/lsptoolshost/projectRestore/restore.ts b/src/lsptoolshost/projectRestore/restore.ts index f9e58d9681..ec97bb597e 100644 --- a/src/lsptoolshost/projectRestore/restore.ts +++ b/src/lsptoolshost/projectRestore/restore.ts @@ -137,8 +137,14 @@ export async function restore( ); await responsePromise.then( - (result) => result.forEach((r) => writeOutput(r)), - (err) => outputChannel.error(`[.NET Restore] ${err}`) + (result) => { + result.forEach((r) => writeOutput(r)) + languageServer.fireProjectsRestoredEvent({ success: true }); + }, + (err) => { + outputChannel.error(`[.NET Restore] ${err}`) + languageServer.fireProjectsRestoredEvent({ success: false }); + } ); } ) diff --git a/src/lsptoolshost/server/languageServerEvents.ts b/src/lsptoolshost/server/languageServerEvents.ts index 3aa9756ef3..14b4512d11 100644 --- a/src/lsptoolshost/server/languageServerEvents.ts +++ b/src/lsptoolshost/server/languageServerEvents.ts @@ -18,12 +18,25 @@ export interface ServerStateChangeEvent { workspaceLabel: string; } +export interface ProjectsRestoredEvent { + success: boolean; +} + +export interface ProjectReloadStartedEvent { +} + +export interface ProjectReloadCompletedEvent { +} + /** * Defines events that are fired by the language server. * These events can be consumed to wait for the server to reach a certain state. */ export interface LanguageServerEvents { readonly onServerStateChange: vscode.Event; + readonly onProjectsRestored: vscode.Event; + readonly onProjectReloadStarted: vscode.Event; + readonly onProjectReloadCompleted: vscode.Event; } /** @@ -33,12 +46,30 @@ export interface LanguageServerEvents { */ export class RoslynLanguageServerEvents implements LanguageServerEvents, IDisposable { public readonly onServerStateChangeEmitter = new vscode.EventEmitter(); + public readonly onProjectsRestoredEmitter = new vscode.EventEmitter(); + public readonly onProjectReloadStartedEmitter = new vscode.EventEmitter(); + public readonly onProjectReloadCompletedEmitter = new vscode.EventEmitter(); public get onServerStateChange(): vscode.Event { return this.onServerStateChangeEmitter.event; } + public get onProjectsRestored(): vscode.Event { + return this.onProjectsRestoredEmitter.event; + } + + public get onProjectReloadStarted(): vscode.Event { + return this.onProjectReloadStartedEmitter.event; + } + + public get onProjectReloadCompleted(): vscode.Event { + return this.onProjectReloadCompletedEmitter.event; + } + dispose(): void { this.onServerStateChangeEmitter.dispose(); + this.onProjectsRestoredEmitter.dispose(); + this.onProjectReloadStartedEmitter.dispose(); + this.onProjectReloadCompletedEmitter.dispose(); } } diff --git a/src/lsptoolshost/server/roslynLanguageServer.ts b/src/lsptoolshost/server/roslynLanguageServer.ts index dc7c0250e4..e538d1180c 100644 --- a/src/lsptoolshost/server/roslynLanguageServer.ts +++ b/src/lsptoolshost/server/roslynLanguageServer.ts @@ -132,6 +132,8 @@ export class RoslynLanguageServer { this.registerSetTrace(); this.registerSendOpenSolution(); this.registerProjectInitialization(); + this.registerProjectReloadStarted(); + this.registerProjectReloadCompleted(); this.registerServerStateChanged(); this.registerServerStateTracking(); this.registerReportProjectConfiguration(); @@ -208,6 +210,10 @@ export class RoslynLanguageServer { }); } + public fireProjectsRestoredEvent(eventArgs: { success: boolean }) { + this._languageServerEvents.onProjectsRestoredEmitter.fire(eventArgs); + } + private registerServerStateTracking() { this._languageServerEvents.onServerStateChange((e) => { this._state = e.state; @@ -236,6 +242,20 @@ export class RoslynLanguageServer { }); } + private registerProjectReloadStarted() { + this._languageClient.onNotification(RoslynProtocol.ProjectReloadStartedNotification.type, () => { + this._languageServerEvents.onProjectReloadStartedEmitter.fire({ + }); + }); + } + + private registerProjectReloadCompleted() { + this._languageClient.onNotification(RoslynProtocol.ProjectReloadCompletedNotification.type, () => { + this._languageServerEvents.onProjectReloadCompletedEmitter.fire({ + }); + }); + } + private registerReportProjectConfiguration() { // Store the dotnet info outside of the notification so we're not running dotnet --info every time the project changes. let dotnetInfo: DotnetInfo | undefined = undefined; diff --git a/src/lsptoolshost/server/roslynProtocol.ts b/src/lsptoolshost/server/roslynProtocol.ts index 5db567112f..cafd775180 100644 --- a/src/lsptoolshost/server/roslynProtocol.ts +++ b/src/lsptoolshost/server/roslynProtocol.ts @@ -288,6 +288,18 @@ export namespace ProjectInitializationCompleteNotification { export const type = new NotificationType(method); } +export namespace ProjectReloadStartedNotification { + export const method = 'workspace/projectReloadStarted'; + export const messageDirection: MessageDirection = MessageDirection.serverToClient; + export const type = new NotificationType(method); +} + +export namespace ProjectReloadCompletedNotification { + export const method = 'workspace/projectReloadCompleted'; + export const messageDirection: MessageDirection = MessageDirection.serverToClient; + export const type = new NotificationType(method); +} + export namespace ProjectConfigurationNotification { export const method = 'workspace/projectConfigurationTelemetry'; export const messageDirection: MessageDirection = MessageDirection.serverToClient; diff --git a/test/lsptoolshost/integrationTests/completion.integration.test.ts b/test/lsptoolshost/integrationTests/completion.integration.test.ts index fa3a150082..60788f801a 100644 --- a/test/lsptoolshost/integrationTests/completion.integration.test.ts +++ b/test/lsptoolshost/integrationTests/completion.integration.test.ts @@ -7,7 +7,7 @@ import * as vscode from 'vscode'; import * as path from 'path'; import { describe, beforeAll, beforeEach, afterAll, test, expect, afterEach } from '@jest/globals'; import testAssetWorkspace from './testAssets/testAssetWorkspace'; -import { activateCSharpExtension, closeAllEditorsAsync, openFileInWorkspaceAsync } from './integrationHelpers'; +import { activateCSharpExtension, closeAllEditorsAsync, getCompletionsAsync, openFileInWorkspaceAsync } from './integrationHelpers'; describe(`Completion Tests`, () => { beforeAll(async () => { @@ -70,23 +70,4 @@ describe(`Completion Tests`, () => { expect(methodOverrideLine).toContain('override void Method(NeedsImport n)'); expect(methodOverrideImplLine).toContain('base.Method(n);'); }); - - async function getCompletionsAsync( - position: vscode.Position, - triggerCharacter: string | undefined, - completionsToResolve: number - ): Promise { - const activeEditor = vscode.window.activeTextEditor; - if (!activeEditor) { - throw new Error('No active editor'); - } - - return await vscode.commands.executeCommand( - 'vscode.executeCompletionItemProvider', - activeEditor.document.uri, - position, - triggerCharacter, - completionsToResolve - ); - } }); diff --git a/test/lsptoolshost/integrationTests/integrationHelpers.ts b/test/lsptoolshost/integrationTests/integrationHelpers.ts index b4e2e8ea90..a2a3e149b0 100644 --- a/test/lsptoolshost/integrationTests/integrationHelpers.ts +++ b/test/lsptoolshost/integrationTests/integrationHelpers.ts @@ -13,7 +13,7 @@ import testAssetWorkspace from './testAssets/testAssetWorkspace'; import { EOL, platform } from 'os'; import { describe, expect, test } from '@jest/globals'; -export async function activateCSharpExtension(): Promise { +export async function activateCSharpExtension(): Promise { const csharpExtension = vscode.extensions.getExtension('ms-dotnettools.csharp'); if (!csharpExtension) { throw new Error('Failed to find installation of ms-dotnettools.csharp'); @@ -53,6 +53,8 @@ export async function activateCSharpExtension(): Promise { if (shouldRestart) { await restartLanguageServer(); } + + return csharpExtension.exports; } export function usingDevKit(): boolean { @@ -113,6 +115,25 @@ export function isSlnWithGenerator(workspace: typeof vscode.workspace) { return isGivenSln(workspace, 'slnWithGenerator'); } +export async function getCompletionsAsync( + position: vscode.Position, + triggerCharacter: string | undefined, + completionsToResolve: number +): Promise { + const activeEditor = vscode.window.activeTextEditor; + if (!activeEditor) { + throw new Error('No active editor'); + } + + return await vscode.commands.executeCommand( + 'vscode.executeCompletionItemProvider', + activeEditor.document.uri, + position, + triggerCharacter, + completionsToResolve + ); +} + export async function getCodeLensesAsync(): Promise { const activeEditor = vscode.window.activeTextEditor; if (!activeEditor) { diff --git a/test/lsptoolshost/integrationTests/restore.integration.test.ts b/test/lsptoolshost/integrationTests/restore.integration.test.ts new file mode 100644 index 0000000000..9656373079 --- /dev/null +++ b/test/lsptoolshost/integrationTests/restore.integration.test.ts @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import * as path from 'path'; +import testAssetWorkspace from './testAssets/testAssetWorkspace'; +import { + activateCSharpExtension, + closeAllEditorsAsync, + getCompletionsAsync, + openFileInWorkspaceAsync, + revertActiveFile, + sleep, + waitForExpectedResult, +} from './integrationHelpers'; +import { describe, beforeAll, beforeEach, afterAll, test, expect, afterEach } from '@jest/globals'; +import { CSharpExtensionExports } from '../../../src/csharpExtensionExports'; + +const timeout = 15*60*1000; + +describe(`Restore Tests`, () => { + let exports: CSharpExtensionExports; + + beforeAll(async () => { + exports = await activateCSharpExtension(); + }); + + beforeEach(async () => { + await openFileInWorkspaceAsync(path.join('src', 'scripts', 'app1.cs')); + }); + + afterEach(async () => { + await revertActiveFile(); + await closeAllEditorsAsync(); + }); + + afterAll(async () => { + await testAssetWorkspace.cleanupWorkspace(); + }); + + test('Inserting package directive triggers a restore', async () => { + await sleep(1); + await vscode.window.activeTextEditor!.edit((editBuilder) => { + editBuilder.insert(new vscode.Position(0, 0), '#:package Newtonsoft.Json@13.0.3'); + }); + await vscode.window.activeTextEditor!.document.save(); + + let designTimeBuildFinished = false; + exports.experimental.languageServerEvents.onProjectsRestored(() => { + // Restore has finished. Now subscribe to the next design time build. + // TODO: seems racy. + exports.experimental.languageServerEvents.onProjectReloadCompleted(() => { + designTimeBuildFinished = true; + }); + }); + + await waitForExpectedResult( + () => designTimeBuildFinished, + timeout, + 100, + (designTimeBuildFinished) => expect(designTimeBuildFinished).toBe(true) + ); + + // we restored, then completed a design-time build. + const completionItems = await getCompletionsAsync(new vscode.Position(1, "using Newton".length), undefined, 10); + + expect(completionItems.items.map(item => item.label)).toContain("Newtonsoft"); + }, timeout); +}); From 3458d521392b3d78ad4f85ec0dc1d17f78d0c059 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Thu, 31 Jul 2025 14:08:04 -0700 Subject: [PATCH 03/32] add D.B.props --- .../slnWithCsproj/src/scripts/Directory.Build.props | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 test/lsptoolshost/integrationTests/testAssets/slnWithCsproj/src/scripts/Directory.Build.props diff --git a/test/lsptoolshost/integrationTests/testAssets/slnWithCsproj/src/scripts/Directory.Build.props b/test/lsptoolshost/integrationTests/testAssets/slnWithCsproj/src/scripts/Directory.Build.props new file mode 100644 index 0000000000..028c8dee38 --- /dev/null +++ b/test/lsptoolshost/integrationTests/testAssets/slnWithCsproj/src/scripts/Directory.Build.props @@ -0,0 +1,6 @@ + + + + $(MSBuildThisFileDirectory) + + \ No newline at end of file From d4b59972aeb943f0b8343cf91d4e4039b9423c47 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Thu, 31 Jul 2025 14:16:00 -0700 Subject: [PATCH 04/32] cleanup --- .../restore.integration.test.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/lsptoolshost/integrationTests/restore.integration.test.ts b/test/lsptoolshost/integrationTests/restore.integration.test.ts index 9656373079..5b87f22586 100644 --- a/test/lsptoolshost/integrationTests/restore.integration.test.ts +++ b/test/lsptoolshost/integrationTests/restore.integration.test.ts @@ -18,8 +18,6 @@ import { import { describe, beforeAll, beforeEach, afterAll, test, expect, afterEach } from '@jest/globals'; import { CSharpExtensionExports } from '../../../src/csharpExtensionExports'; -const timeout = 15*60*1000; - describe(`Restore Tests`, () => { let exports: CSharpExtensionExports; @@ -50,7 +48,6 @@ describe(`Restore Tests`, () => { let designTimeBuildFinished = false; exports.experimental.languageServerEvents.onProjectsRestored(() => { // Restore has finished. Now subscribe to the next design time build. - // TODO: seems racy. exports.experimental.languageServerEvents.onProjectReloadCompleted(() => { designTimeBuildFinished = true; }); @@ -58,14 +55,17 @@ describe(`Restore Tests`, () => { await waitForExpectedResult( () => designTimeBuildFinished, - timeout, + 10*1000, 100, (designTimeBuildFinished) => expect(designTimeBuildFinished).toBe(true) ); - // we restored, then completed a design-time build. - const completionItems = await getCompletionsAsync(new vscode.Position(1, "using Newton".length), undefined, 10); - - expect(completionItems.items.map(item => item.label)).toContain("Newtonsoft"); - }, timeout); + const position = new vscode.Position(1, "using Newton".length); + await waitForExpectedResult( + () => getCompletionsAsync(position, undefined, 10), + 10*1000, + 100, + (completionItems) => expect(completionItems.items.map(item => item.label)).toContain("Newtonsoft") + ); + }); }); From f9be1576ed5e0d1d68971e818f4610d4533a2fd9 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Thu, 31 Jul 2025 15:04:30 -0700 Subject: [PATCH 05/32] inspect details of notifications from server --- .../server/languageServerEvents.ts | 2 ++ .../server/roslynLanguageServer.ts | 10 ++++------ src/lsptoolshost/server/roslynProtocol.ts | 18 ++++++++++++++++-- .../restore.integration.test.ts | 6 ++++-- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/lsptoolshost/server/languageServerEvents.ts b/src/lsptoolshost/server/languageServerEvents.ts index 14b4512d11..95711b63c1 100644 --- a/src/lsptoolshost/server/languageServerEvents.ts +++ b/src/lsptoolshost/server/languageServerEvents.ts @@ -23,9 +23,11 @@ export interface ProjectsRestoredEvent { } export interface ProjectReloadStartedEvent { + projectFilePaths: string[]; } export interface ProjectReloadCompletedEvent { + projectFilePaths: string[]; } /** diff --git a/src/lsptoolshost/server/roslynLanguageServer.ts b/src/lsptoolshost/server/roslynLanguageServer.ts index e538d1180c..c701accdc5 100644 --- a/src/lsptoolshost/server/roslynLanguageServer.ts +++ b/src/lsptoolshost/server/roslynLanguageServer.ts @@ -243,16 +243,14 @@ export class RoslynLanguageServer { } private registerProjectReloadStarted() { - this._languageClient.onNotification(RoslynProtocol.ProjectReloadStartedNotification.type, () => { - this._languageServerEvents.onProjectReloadStartedEmitter.fire({ - }); + this._languageClient.onNotification(RoslynProtocol.ProjectReloadStartedNotification.type, params => { + this._languageServerEvents.onProjectReloadStartedEmitter.fire(params); }); } private registerProjectReloadCompleted() { - this._languageClient.onNotification(RoslynProtocol.ProjectReloadCompletedNotification.type, () => { - this._languageServerEvents.onProjectReloadCompletedEmitter.fire({ - }); + this._languageClient.onNotification(RoslynProtocol.ProjectReloadCompletedNotification.type, params => { + this._languageServerEvents.onProjectReloadCompletedEmitter.fire(params); }); } diff --git a/src/lsptoolshost/server/roslynProtocol.ts b/src/lsptoolshost/server/roslynProtocol.ts index cafd775180..6d418d1f01 100644 --- a/src/lsptoolshost/server/roslynProtocol.ts +++ b/src/lsptoolshost/server/roslynProtocol.ts @@ -239,6 +239,20 @@ export interface ProjectNeedsRestoreName { projectFilePaths: string[]; } +export interface ProjectReloadStartedName { + /** + * The set of projects whose reload started. + */ + projectFilePaths: string[]; +} + +export interface ProjectReloadCompletedName { + /** + * The set of projects whose reload completed. + */ + projectFilePaths: string[]; +} + export interface CopilotRelatedDocumentsParams extends WorkDoneProgressParams, PartialResultParams { _vs_textDocument: TextDocumentIdentifier; position: Position; @@ -291,13 +305,13 @@ export namespace ProjectInitializationCompleteNotification { export namespace ProjectReloadStartedNotification { export const method = 'workspace/projectReloadStarted'; export const messageDirection: MessageDirection = MessageDirection.serverToClient; - export const type = new NotificationType(method); + export const type = new NotificationType(method); } export namespace ProjectReloadCompletedNotification { export const method = 'workspace/projectReloadCompleted'; export const messageDirection: MessageDirection = MessageDirection.serverToClient; - export const type = new NotificationType(method); + export const type = new NotificationType(method); } export namespace ProjectConfigurationNotification { diff --git a/test/lsptoolshost/integrationTests/restore.integration.test.ts b/test/lsptoolshost/integrationTests/restore.integration.test.ts index 5b87f22586..035b965a1e 100644 --- a/test/lsptoolshost/integrationTests/restore.integration.test.ts +++ b/test/lsptoolshost/integrationTests/restore.integration.test.ts @@ -46,9 +46,11 @@ describe(`Restore Tests`, () => { await vscode.window.activeTextEditor!.document.save(); let designTimeBuildFinished = false; - exports.experimental.languageServerEvents.onProjectsRestored(() => { + exports.experimental.languageServerEvents.onProjectsRestored(args => { + expect(args.success).toBe(true); // Restore has finished. Now subscribe to the next design time build. - exports.experimental.languageServerEvents.onProjectReloadCompleted(() => { + exports.experimental.languageServerEvents.onProjectReloadCompleted(args => { + expect(args.projectFilePaths).toContain(vscode.window.activeTextEditor!.document.uri.fsPath); designTimeBuildFinished = true; }); }); From 5fd1d9c685672369b2f6dfe14be771204b97cc1c Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Wed, 6 Aug 2025 19:09:12 -0700 Subject: [PATCH 06/32] use 'waitForAllAsyncOperationsAsync' --- .../integrationTests/integrationHelpers.ts | 6 +++++ .../restore.integration.test.ts | 19 ++------------ .../integrationTests/testHooks.ts | 25 +++++++++++++++++++ 3 files changed, 33 insertions(+), 17 deletions(-) create mode 100644 test/lsptoolshost/integrationTests/testHooks.ts diff --git a/test/lsptoolshost/integrationTests/integrationHelpers.ts b/test/lsptoolshost/integrationTests/integrationHelpers.ts index a2a3e149b0..4d0e8e36d6 100644 --- a/test/lsptoolshost/integrationTests/integrationHelpers.ts +++ b/test/lsptoolshost/integrationTests/integrationHelpers.ts @@ -12,6 +12,7 @@ import { ServerState } from '../../../src/lsptoolshost/server/languageServerEven import testAssetWorkspace from './testAssets/testAssetWorkspace'; import { EOL, platform } from 'os'; import { describe, expect, test } from '@jest/globals'; +import { WaitForAsyncOperationsRequest } from './testHooks'; export async function activateCSharpExtension(): Promise { const csharpExtension = vscode.extensions.getExtension('ms-dotnettools.csharp'); @@ -320,3 +321,8 @@ function isWindows() { function isLinux() { return !(isMacOS() || isWindows()); } + +export async function waitForAllAsyncOperationsAsync(exports: CSharpExtensionExports): Promise { + const source = new vscode.CancellationTokenSource(); + await exports.experimental.sendServerRequest(WaitForAsyncOperationsRequest.type, { operations: [] }, source.token); +} \ No newline at end of file diff --git a/test/lsptoolshost/integrationTests/restore.integration.test.ts b/test/lsptoolshost/integrationTests/restore.integration.test.ts index 035b965a1e..06f7afe9fa 100644 --- a/test/lsptoolshost/integrationTests/restore.integration.test.ts +++ b/test/lsptoolshost/integrationTests/restore.integration.test.ts @@ -13,6 +13,7 @@ import { openFileInWorkspaceAsync, revertActiveFile, sleep, + waitForAllAsyncOperationsAsync, waitForExpectedResult, } from './integrationHelpers'; import { describe, beforeAll, beforeEach, afterAll, test, expect, afterEach } from '@jest/globals'; @@ -44,23 +45,7 @@ describe(`Restore Tests`, () => { editBuilder.insert(new vscode.Position(0, 0), '#:package Newtonsoft.Json@13.0.3'); }); await vscode.window.activeTextEditor!.document.save(); - - let designTimeBuildFinished = false; - exports.experimental.languageServerEvents.onProjectsRestored(args => { - expect(args.success).toBe(true); - // Restore has finished. Now subscribe to the next design time build. - exports.experimental.languageServerEvents.onProjectReloadCompleted(args => { - expect(args.projectFilePaths).toContain(vscode.window.activeTextEditor!.document.uri.fsPath); - designTimeBuildFinished = true; - }); - }); - - await waitForExpectedResult( - () => designTimeBuildFinished, - 10*1000, - 100, - (designTimeBuildFinished) => expect(designTimeBuildFinished).toBe(true) - ); + await waitForAllAsyncOperationsAsync(exports); const position = new vscode.Position(1, "using Newton".length); await waitForExpectedResult( diff --git a/test/lsptoolshost/integrationTests/testHooks.ts b/test/lsptoolshost/integrationTests/testHooks.ts new file mode 100644 index 0000000000..95af0a2fba --- /dev/null +++ b/test/lsptoolshost/integrationTests/testHooks.ts @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as lsp from 'vscode-languageserver-protocol'; + +export interface WaitForAsyncOperationsParams { + /** + * The operations to wait for. + */ + operations: string[]; +} + +export namespace WaitForAsyncOperationsRequest { + export const method = 'workspace/waitForAsyncOperations'; + export const messageDirection: lsp.MessageDirection = lsp.MessageDirection.clientToServer; + export const type = new lsp.RequestType< + WaitForAsyncOperationsParams, + { + /* empty */ + }, + void + >(method); +} From 45e4d45467562a4961890072633a07629b44a3da Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Wed, 6 Aug 2025 19:14:40 -0700 Subject: [PATCH 07/32] simplify --- src/lsptoolshost/projectRestore/restore.ts | 10 ++---- .../server/languageServerEvents.ts | 33 ------------------- .../server/roslynLanguageServer.ts | 18 ---------- src/lsptoolshost/server/roslynProtocol.ts | 26 --------------- 4 files changed, 2 insertions(+), 85 deletions(-) diff --git a/src/lsptoolshost/projectRestore/restore.ts b/src/lsptoolshost/projectRestore/restore.ts index ec97bb597e..f9e58d9681 100644 --- a/src/lsptoolshost/projectRestore/restore.ts +++ b/src/lsptoolshost/projectRestore/restore.ts @@ -137,14 +137,8 @@ export async function restore( ); await responsePromise.then( - (result) => { - result.forEach((r) => writeOutput(r)) - languageServer.fireProjectsRestoredEvent({ success: true }); - }, - (err) => { - outputChannel.error(`[.NET Restore] ${err}`) - languageServer.fireProjectsRestoredEvent({ success: false }); - } + (result) => result.forEach((r) => writeOutput(r)), + (err) => outputChannel.error(`[.NET Restore] ${err}`) ); } ) diff --git a/src/lsptoolshost/server/languageServerEvents.ts b/src/lsptoolshost/server/languageServerEvents.ts index 95711b63c1..3aa9756ef3 100644 --- a/src/lsptoolshost/server/languageServerEvents.ts +++ b/src/lsptoolshost/server/languageServerEvents.ts @@ -18,27 +18,12 @@ export interface ServerStateChangeEvent { workspaceLabel: string; } -export interface ProjectsRestoredEvent { - success: boolean; -} - -export interface ProjectReloadStartedEvent { - projectFilePaths: string[]; -} - -export interface ProjectReloadCompletedEvent { - projectFilePaths: string[]; -} - /** * Defines events that are fired by the language server. * These events can be consumed to wait for the server to reach a certain state. */ export interface LanguageServerEvents { readonly onServerStateChange: vscode.Event; - readonly onProjectsRestored: vscode.Event; - readonly onProjectReloadStarted: vscode.Event; - readonly onProjectReloadCompleted: vscode.Event; } /** @@ -48,30 +33,12 @@ export interface LanguageServerEvents { */ export class RoslynLanguageServerEvents implements LanguageServerEvents, IDisposable { public readonly onServerStateChangeEmitter = new vscode.EventEmitter(); - public readonly onProjectsRestoredEmitter = new vscode.EventEmitter(); - public readonly onProjectReloadStartedEmitter = new vscode.EventEmitter(); - public readonly onProjectReloadCompletedEmitter = new vscode.EventEmitter(); public get onServerStateChange(): vscode.Event { return this.onServerStateChangeEmitter.event; } - public get onProjectsRestored(): vscode.Event { - return this.onProjectsRestoredEmitter.event; - } - - public get onProjectReloadStarted(): vscode.Event { - return this.onProjectReloadStartedEmitter.event; - } - - public get onProjectReloadCompleted(): vscode.Event { - return this.onProjectReloadCompletedEmitter.event; - } - dispose(): void { this.onServerStateChangeEmitter.dispose(); - this.onProjectsRestoredEmitter.dispose(); - this.onProjectReloadStartedEmitter.dispose(); - this.onProjectReloadCompletedEmitter.dispose(); } } diff --git a/src/lsptoolshost/server/roslynLanguageServer.ts b/src/lsptoolshost/server/roslynLanguageServer.ts index c701accdc5..dc7c0250e4 100644 --- a/src/lsptoolshost/server/roslynLanguageServer.ts +++ b/src/lsptoolshost/server/roslynLanguageServer.ts @@ -132,8 +132,6 @@ export class RoslynLanguageServer { this.registerSetTrace(); this.registerSendOpenSolution(); this.registerProjectInitialization(); - this.registerProjectReloadStarted(); - this.registerProjectReloadCompleted(); this.registerServerStateChanged(); this.registerServerStateTracking(); this.registerReportProjectConfiguration(); @@ -210,10 +208,6 @@ export class RoslynLanguageServer { }); } - public fireProjectsRestoredEvent(eventArgs: { success: boolean }) { - this._languageServerEvents.onProjectsRestoredEmitter.fire(eventArgs); - } - private registerServerStateTracking() { this._languageServerEvents.onServerStateChange((e) => { this._state = e.state; @@ -242,18 +236,6 @@ export class RoslynLanguageServer { }); } - private registerProjectReloadStarted() { - this._languageClient.onNotification(RoslynProtocol.ProjectReloadStartedNotification.type, params => { - this._languageServerEvents.onProjectReloadStartedEmitter.fire(params); - }); - } - - private registerProjectReloadCompleted() { - this._languageClient.onNotification(RoslynProtocol.ProjectReloadCompletedNotification.type, params => { - this._languageServerEvents.onProjectReloadCompletedEmitter.fire(params); - }); - } - private registerReportProjectConfiguration() { // Store the dotnet info outside of the notification so we're not running dotnet --info every time the project changes. let dotnetInfo: DotnetInfo | undefined = undefined; diff --git a/src/lsptoolshost/server/roslynProtocol.ts b/src/lsptoolshost/server/roslynProtocol.ts index 6d418d1f01..5db567112f 100644 --- a/src/lsptoolshost/server/roslynProtocol.ts +++ b/src/lsptoolshost/server/roslynProtocol.ts @@ -239,20 +239,6 @@ export interface ProjectNeedsRestoreName { projectFilePaths: string[]; } -export interface ProjectReloadStartedName { - /** - * The set of projects whose reload started. - */ - projectFilePaths: string[]; -} - -export interface ProjectReloadCompletedName { - /** - * The set of projects whose reload completed. - */ - projectFilePaths: string[]; -} - export interface CopilotRelatedDocumentsParams extends WorkDoneProgressParams, PartialResultParams { _vs_textDocument: TextDocumentIdentifier; position: Position; @@ -302,18 +288,6 @@ export namespace ProjectInitializationCompleteNotification { export const type = new NotificationType(method); } -export namespace ProjectReloadStartedNotification { - export const method = 'workspace/projectReloadStarted'; - export const messageDirection: MessageDirection = MessageDirection.serverToClient; - export const type = new NotificationType(method); -} - -export namespace ProjectReloadCompletedNotification { - export const method = 'workspace/projectReloadCompleted'; - export const messageDirection: MessageDirection = MessageDirection.serverToClient; - export const type = new NotificationType(method); -} - export namespace ProjectConfigurationNotification { export const method = 'workspace/projectConfigurationTelemetry'; export const messageDirection: MessageDirection = MessageDirection.serverToClient; From fa315bc8a5b0edfc77d2189c580fd2ea916ff186 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Wed, 6 Aug 2025 21:40:37 -0700 Subject: [PATCH 08/32] fix lint errors --- .../integrationTests/completion.integration.test.ts | 7 ++++++- .../integrationTests/integrationHelpers.ts | 2 +- .../integrationTests/restore.integration.test.ts | 6 +++--- test/lsptoolshost/integrationTests/testHooks.ts | 11 ++++------- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/test/lsptoolshost/integrationTests/completion.integration.test.ts b/test/lsptoolshost/integrationTests/completion.integration.test.ts index 60788f801a..4924a9a99a 100644 --- a/test/lsptoolshost/integrationTests/completion.integration.test.ts +++ b/test/lsptoolshost/integrationTests/completion.integration.test.ts @@ -7,7 +7,12 @@ import * as vscode from 'vscode'; import * as path from 'path'; import { describe, beforeAll, beforeEach, afterAll, test, expect, afterEach } from '@jest/globals'; import testAssetWorkspace from './testAssets/testAssetWorkspace'; -import { activateCSharpExtension, closeAllEditorsAsync, getCompletionsAsync, openFileInWorkspaceAsync } from './integrationHelpers'; +import { + activateCSharpExtension, + closeAllEditorsAsync, + getCompletionsAsync, + openFileInWorkspaceAsync, +} from './integrationHelpers'; describe(`Completion Tests`, () => { beforeAll(async () => { diff --git a/test/lsptoolshost/integrationTests/integrationHelpers.ts b/test/lsptoolshost/integrationTests/integrationHelpers.ts index 4d0e8e36d6..5af4b49242 100644 --- a/test/lsptoolshost/integrationTests/integrationHelpers.ts +++ b/test/lsptoolshost/integrationTests/integrationHelpers.ts @@ -325,4 +325,4 @@ function isLinux() { export async function waitForAllAsyncOperationsAsync(exports: CSharpExtensionExports): Promise { const source = new vscode.CancellationTokenSource(); await exports.experimental.sendServerRequest(WaitForAsyncOperationsRequest.type, { operations: [] }, source.token); -} \ No newline at end of file +} diff --git a/test/lsptoolshost/integrationTests/restore.integration.test.ts b/test/lsptoolshost/integrationTests/restore.integration.test.ts index 06f7afe9fa..8cb130f7f1 100644 --- a/test/lsptoolshost/integrationTests/restore.integration.test.ts +++ b/test/lsptoolshost/integrationTests/restore.integration.test.ts @@ -47,12 +47,12 @@ describe(`Restore Tests`, () => { await vscode.window.activeTextEditor!.document.save(); await waitForAllAsyncOperationsAsync(exports); - const position = new vscode.Position(1, "using Newton".length); + const position = new vscode.Position(1, 'using Newton'.length); await waitForExpectedResult( () => getCompletionsAsync(position, undefined, 10), - 10*1000, + 10 * 1000, 100, - (completionItems) => expect(completionItems.items.map(item => item.label)).toContain("Newtonsoft") + (completionItems) => expect(completionItems.items.map((item) => item.label)).toContain('Newtonsoft') ); }); }); diff --git a/test/lsptoolshost/integrationTests/testHooks.ts b/test/lsptoolshost/integrationTests/testHooks.ts index 95af0a2fba..464a3c2044 100644 --- a/test/lsptoolshost/integrationTests/testHooks.ts +++ b/test/lsptoolshost/integrationTests/testHooks.ts @@ -12,14 +12,11 @@ export interface WaitForAsyncOperationsParams { operations: string[]; } +export interface WaitForAsyncOperationsResponse { +} + export namespace WaitForAsyncOperationsRequest { export const method = 'workspace/waitForAsyncOperations'; export const messageDirection: lsp.MessageDirection = lsp.MessageDirection.clientToServer; - export const type = new lsp.RequestType< - WaitForAsyncOperationsParams, - { - /* empty */ - }, - void - >(method); + export const type = new lsp.RequestType(method); } From 0ef293107ed10b87ec23cb5659dfd84126d00841 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Sun, 10 Aug 2025 10:24:28 -0700 Subject: [PATCH 09/32] WIP: bump roslyn version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8906a6459d..99b92800ad 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "workspace" ], "defaults": { - "roslyn": "5.0.0-2.25405.5", + "roslyn": "5.0.0-2.25408.10", "omniSharp": "1.39.14", "razor": "10.0.0-preview.25403.1", "razorOmnisharp": "7.0.0-preview.23363.1", From 5a32bb2cd4c5603a188ef86d4219b726282a27a5 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Sun, 10 Aug 2025 10:31:28 -0700 Subject: [PATCH 10/32] rekick CI after adding package to feeds From ad92e6264ddc23e869ca93153c53c0ab1eeee453 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Sun, 10 Aug 2025 14:06:02 -0700 Subject: [PATCH 11/32] fix lint errors --- test/lsptoolshost/integrationTests/restore.integration.test.ts | 2 +- test/lsptoolshost/integrationTests/testHooks.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/test/lsptoolshost/integrationTests/restore.integration.test.ts b/test/lsptoolshost/integrationTests/restore.integration.test.ts index 8cb130f7f1..b85c370540 100644 --- a/test/lsptoolshost/integrationTests/restore.integration.test.ts +++ b/test/lsptoolshost/integrationTests/restore.integration.test.ts @@ -49,7 +49,7 @@ describe(`Restore Tests`, () => { const position = new vscode.Position(1, 'using Newton'.length); await waitForExpectedResult( - () => getCompletionsAsync(position, undefined, 10), + async () => getCompletionsAsync(position, undefined, 10), 10 * 1000, 100, (completionItems) => expect(completionItems.items.map((item) => item.label)).toContain('Newtonsoft') diff --git a/test/lsptoolshost/integrationTests/testHooks.ts b/test/lsptoolshost/integrationTests/testHooks.ts index 464a3c2044..3fd6936114 100644 --- a/test/lsptoolshost/integrationTests/testHooks.ts +++ b/test/lsptoolshost/integrationTests/testHooks.ts @@ -12,8 +12,7 @@ export interface WaitForAsyncOperationsParams { operations: string[]; } -export interface WaitForAsyncOperationsResponse { -} +export interface WaitForAsyncOperationsResponse {} // eslint-disable-line @typescript-eslint/no-empty-object-type export namespace WaitForAsyncOperationsRequest { export const method = 'workspace/waitForAsyncOperations'; From 2fd27741491b39c881f24f0e1f7de1ec6d9fdf8a Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 12 Aug 2025 10:16:03 -0700 Subject: [PATCH 12/32] enable waiter in restore tests via env var --- test/lsptoolshost/integrationTests/restore.integration.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/lsptoolshost/integrationTests/restore.integration.test.ts b/test/lsptoolshost/integrationTests/restore.integration.test.ts index b85c370540..f6d2051a6e 100644 --- a/test/lsptoolshost/integrationTests/restore.integration.test.ts +++ b/test/lsptoolshost/integrationTests/restore.integration.test.ts @@ -23,6 +23,7 @@ describe(`Restore Tests`, () => { let exports: CSharpExtensionExports; beforeAll(async () => { + process.env.RoslynWaiterEnabled = "true"; exports = await activateCSharpExtension(); }); From 3beee8e9ee20e59c455b50f003eed739b51c2101 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 12 Aug 2025 11:14:25 -0700 Subject: [PATCH 13/32] I installed eslint finally --- test/lsptoolshost/integrationTests/restore.integration.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lsptoolshost/integrationTests/restore.integration.test.ts b/test/lsptoolshost/integrationTests/restore.integration.test.ts index f6d2051a6e..dd3146264e 100644 --- a/test/lsptoolshost/integrationTests/restore.integration.test.ts +++ b/test/lsptoolshost/integrationTests/restore.integration.test.ts @@ -23,7 +23,7 @@ describe(`Restore Tests`, () => { let exports: CSharpExtensionExports; beforeAll(async () => { - process.env.RoslynWaiterEnabled = "true"; + process.env.RoslynWaiterEnabled = 'true'; exports = await activateCSharpExtension(); }); From 305491989bae439b8b1ff70dd34e344f2b012120 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 12 Aug 2025 14:07:27 -0700 Subject: [PATCH 14/32] condition test on new enough sdk --- azure-pipelines.yml | 19 +++++++++++++++++++ azure-pipelines/dotnet-variables.yml | 2 +- .../restore.integration.test.ts | 3 ++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a3b14805c7..0b90cf6adf 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -76,9 +76,28 @@ stages: demands: ImageOverride -equals 1es-ubuntu-2004-open containerName: mcr.microsoft.com/dotnet/sdk:9.0 +- stage: + displayName: Test Linux (.NET 10) + dependsOn: [] + variables: + RoslynTestFileBasedPrograms: 'true' + jobs: + - template: azure-pipelines/test-matrix.yml + parameters: + # Prefer the dotnet from the container. + dotnetVersion: '' + testVSCodeVersion: $(testVSCodeVersion) + installAdditionalLinuxDependencies: true + pool: + name: NetCore-Public + demands: ImageOverride -equals 1es-ubuntu-2004-open + containerName: mcr.microsoft.com/dotnet/sdk:10.0.100-preview7 + - stage: Test_Windows_Stage displayName: Test Windows dependsOn: [] + variables: + RoslynTestFileBasedPrograms: 'true' jobs: - template: azure-pipelines/test-matrix.yml parameters: diff --git a/azure-pipelines/dotnet-variables.yml b/azure-pipelines/dotnet-variables.yml index 9381ea25dc..a51084ff36 100644 --- a/azure-pipelines/dotnet-variables.yml +++ b/azure-pipelines/dotnet-variables.yml @@ -1,3 +1,3 @@ variables: - name: defaultDotnetVersion - value: '8.0.403' \ No newline at end of file + value: '10.0.100-preview7' \ No newline at end of file diff --git a/test/lsptoolshost/integrationTests/restore.integration.test.ts b/test/lsptoolshost/integrationTests/restore.integration.test.ts index dd3146264e..e9218c695a 100644 --- a/test/lsptoolshost/integrationTests/restore.integration.test.ts +++ b/test/lsptoolshost/integrationTests/restore.integration.test.ts @@ -19,7 +19,8 @@ import { import { describe, beforeAll, beforeEach, afterAll, test, expect, afterEach } from '@jest/globals'; import { CSharpExtensionExports } from '../../../src/csharpExtensionExports'; -describe(`Restore Tests`, () => { +const doRunSuite = process.env.RoslynTestFileBasedPrograms == 'true'; +(doRunSuite ? describe : describe.skip)(`Restore Tests`, () => { let exports: CSharpExtensionExports; beforeAll(async () => { From 94a2175b3b3b52847e56175739779dd0adfb46a6 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 12 Aug 2025 14:19:08 -0700 Subject: [PATCH 15/32] includePreviewVersions --- azure-pipelines/prereqs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/azure-pipelines/prereqs.yml b/azure-pipelines/prereqs.yml index 3932f029d8..a7558dd0e7 100644 --- a/azure-pipelines/prereqs.yml +++ b/azure-pipelines/prereqs.yml @@ -19,6 +19,7 @@ steps: displayName: 'Install .NET SDK' inputs: version: ${{ parameters.dotnetVersion }} + includePreviewVersions: true - script: dotnet --info displayName: Display dotnet info From 386f63e8738e87867c01dc5e772dc6adad2911f1 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 12 Aug 2025 14:28:24 -0700 Subject: [PATCH 16/32] use exact version --- azure-pipelines/dotnet-variables.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines/dotnet-variables.yml b/azure-pipelines/dotnet-variables.yml index a51084ff36..a1137f9563 100644 --- a/azure-pipelines/dotnet-variables.yml +++ b/azure-pipelines/dotnet-variables.yml @@ -1,3 +1,3 @@ variables: - name: defaultDotnetVersion - value: '10.0.100-preview7' \ No newline at end of file + value: '10.0.100-preview.7.25380.108' \ No newline at end of file From f31eeaa986181c24842d72ebe298d9554aa59c58 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 12 Aug 2025 14:35:14 -0700 Subject: [PATCH 17/32] remove unnecessary(?) flag --- azure-pipelines/prereqs.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/azure-pipelines/prereqs.yml b/azure-pipelines/prereqs.yml index a7558dd0e7..3932f029d8 100644 --- a/azure-pipelines/prereqs.yml +++ b/azure-pipelines/prereqs.yml @@ -19,7 +19,6 @@ steps: displayName: 'Install .NET SDK' inputs: version: ${{ parameters.dotnetVersion }} - includePreviewVersions: true - script: dotnet --info displayName: Display dotnet info From 4fb723a0583f5a9f204de139ef1281ee4d3b3e17 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 12 Aug 2025 14:37:19 -0700 Subject: [PATCH 18/32] dot dot dot... --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 0b90cf6adf..7e7fcf2154 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -91,7 +91,7 @@ stages: pool: name: NetCore-Public demands: ImageOverride -equals 1es-ubuntu-2004-open - containerName: mcr.microsoft.com/dotnet/sdk:10.0.100-preview7 + containerName: mcr.microsoft.com/dotnet/sdk:10.0.100-preview.7 - stage: Test_Windows_Stage displayName: Test Windows From b9637ba9d9404c36c998e5a32b2a8a67dbbfc013 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 12 Aug 2025 15:00:03 -0700 Subject: [PATCH 19/32] update package name --- azure-pipelines/test-linux-docker-prereqs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines/test-linux-docker-prereqs.yml b/azure-pipelines/test-linux-docker-prereqs.yml index e89c6bdcfc..70a1a81a6b 100644 --- a/azure-pipelines/test-linux-docker-prereqs.yml +++ b/azure-pipelines/test-linux-docker-prereqs.yml @@ -13,7 +13,7 @@ steps: # Installing the dependencies requires root, but the docker image we're using doesn't have root permissions (nor sudo) # We can exec as root from outside the container from the host machine. # Really we should create our own image with these pre-installed, but we'll need to figure out how to publish the image. -- script: docker exec --user root $(containerId) bash -c 'apt-get update -y && apt-get install -y libglib2.0-0 libnss3 libatk-bridge2.0-dev libdrm2 libgtk-3-0 libgbm-dev libasound2 xvfb' +- script: docker exec --user root $(containerId) bash -c 'apt-get update -y && apt-get install -y libglib2.0-0 libnss3 libatk-bridge2.0-dev libdrm2 libgtk-3-0 libgbm-dev libasound2t64 xvfb' displayName: 'Install additional Linux dependencies' target: host condition: eq(variables['Agent.OS'], 'Linux') \ No newline at end of file From 2493d9d1f137a14590c19027f33b231339e9fabb Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 12 Aug 2025 15:23:57 -0700 Subject: [PATCH 20/32] Explicitly get ubuntu noble numbat containers --- azure-pipelines.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 7e7fcf2154..0f713a66a2 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -59,7 +59,7 @@ stages: pool: name: NetCore-Public demands: ImageOverride -equals 1es-ubuntu-2004-open - containerName: mcr.microsoft.com/dotnet/sdk:8.0 + containerName: mcr.microsoft.com/dotnet/sdk:8.0-noble - stage: displayName: Test Linux (.NET 9) @@ -74,7 +74,7 @@ stages: pool: name: NetCore-Public demands: ImageOverride -equals 1es-ubuntu-2004-open - containerName: mcr.microsoft.com/dotnet/sdk:9.0 + containerName: mcr.microsoft.com/dotnet/sdk:9.0-noble - stage: displayName: Test Linux (.NET 10) @@ -91,7 +91,7 @@ stages: pool: name: NetCore-Public demands: ImageOverride -equals 1es-ubuntu-2004-open - containerName: mcr.microsoft.com/dotnet/sdk:10.0.100-preview.7 + containerName: mcr.microsoft.com/dotnet/sdk:10.0.100-preview.7-noble - stage: Test_Windows_Stage displayName: Test Windows From 3c5d899c63d53506a1a27ce96b11c1ebae9a5d26 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 28 Oct 2025 15:22:42 -0700 Subject: [PATCH 21/32] fix yml. fix skip test variable. --- azure-pipelines.yml | 13 +++++++------ .../integrationTests/restore.integration.test.ts | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 6a6b971a7e..f28e01bb4f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -72,6 +72,8 @@ stages: - stage: displayName: Test Linux (.NET 8) dependsOn: [] + variables: + RoslynSkipTestFileBasedPrograms: 'true' jobs: - template: azure-pipelines/test-matrix.yml parameters: @@ -88,6 +90,8 @@ stages: - stage: displayName: Test Linux (.NET 9) dependsOn: [] + variables: + RoslynSkipTestFileBasedPrograms: 'true' jobs: - template: azure-pipelines/test-matrix.yml parameters: @@ -104,25 +108,22 @@ stages: - stage: displayName: Test Linux (.NET 10) dependsOn: [] - variables: - RoslynTestFileBasedPrograms: 'true' jobs: - template: azure-pipelines/test-matrix.yml parameters: + os: linux # Prefer the dotnet from the container. - dotnetVersion: '' + installDotNet: false testVSCodeVersion: $(testVSCodeVersion) installAdditionalLinuxDependencies: true pool: name: NetCore-Public demands: ImageOverride -equals 1es-ubuntu-2004-open - containerName: mcr.microsoft.com/dotnet/sdk:10.0.100-preview.7-noble + containerName: mcr.microsoft.com/dotnet/sdk:10.0.100-rc2-noble - stage: Test_Windows_Stage displayName: Test Windows dependsOn: [] - variables: - RoslynTestFileBasedPrograms: 'true' jobs: - template: azure-pipelines/test-matrix.yml parameters: diff --git a/test/lsptoolshost/integrationTests/restore.integration.test.ts b/test/lsptoolshost/integrationTests/restore.integration.test.ts index e9218c695a..2ec481ca23 100644 --- a/test/lsptoolshost/integrationTests/restore.integration.test.ts +++ b/test/lsptoolshost/integrationTests/restore.integration.test.ts @@ -19,7 +19,7 @@ import { import { describe, beforeAll, beforeEach, afterAll, test, expect, afterEach } from '@jest/globals'; import { CSharpExtensionExports } from '../../../src/csharpExtensionExports'; -const doRunSuite = process.env.RoslynTestFileBasedPrograms == 'true'; +const doRunSuite = process.env.RoslynSkipTestFileBasedPrograms !== 'true'; (doRunSuite ? describe : describe.skip)(`Restore Tests`, () => { let exports: CSharpExtensionExports; From 77781dc16f47e853cad30c9447b50622e6d91872 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 28 Oct 2025 15:29:26 -0700 Subject: [PATCH 22/32] fix container name --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index f28e01bb4f..680c575dc9 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -119,7 +119,7 @@ stages: pool: name: NetCore-Public demands: ImageOverride -equals 1es-ubuntu-2004-open - containerName: mcr.microsoft.com/dotnet/sdk:10.0.100-rc2-noble + containerName: mcr.microsoft.com/dotnet/sdk:10.0.100-noble - stage: Test_Windows_Stage displayName: Test Windows From cc3d74faff90ff227627fc1ae7d5fd8e9931a7f4 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 28 Oct 2025 15:44:07 -0700 Subject: [PATCH 23/32] fix for I bet the last time --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 680c575dc9..00cbc713c6 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -119,7 +119,7 @@ stages: pool: name: NetCore-Public demands: ImageOverride -equals 1es-ubuntu-2004-open - containerName: mcr.microsoft.com/dotnet/sdk:10.0.100-noble + containerName: mcr.microsoft.com/dotnet/sdk:10.0.100-rc.2-noble - stage: Test_Windows_Stage displayName: Test Windows From 9d742a3d6092e951de51a8822403811a12c87ddc Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 28 Oct 2025 16:22:18 -0700 Subject: [PATCH 24/32] Move env var in pipeline --- azure-pipelines.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 00cbc713c6..3b113c39bb 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -72,10 +72,10 @@ stages: - stage: displayName: Test Linux (.NET 8) dependsOn: [] - variables: - RoslynSkipTestFileBasedPrograms: 'true' jobs: - template: azure-pipelines/test-matrix.yml + variables: + RoslynSkipTestFileBasedPrograms: 'true' parameters: os: linux # Prefer the dotnet from the container. @@ -90,10 +90,10 @@ stages: - stage: displayName: Test Linux (.NET 9) dependsOn: [] - variables: - RoslynSkipTestFileBasedPrograms: 'true' jobs: - template: azure-pipelines/test-matrix.yml + variables: + RoslynSkipTestFileBasedPrograms: 'true' parameters: os: linux # Prefer the dotnet from the container. From 7cf3de26e2b184ce5ea49324aa5cd26a9eb7db9c Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 28 Oct 2025 16:27:17 -0700 Subject: [PATCH 25/32] try to inspect what is happening in CI --- azure-pipelines.yml | 8 ++++---- .../integrationTests/restore.integration.test.ts | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 3b113c39bb..00cbc713c6 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -72,10 +72,10 @@ stages: - stage: displayName: Test Linux (.NET 8) dependsOn: [] + variables: + RoslynSkipTestFileBasedPrograms: 'true' jobs: - template: azure-pipelines/test-matrix.yml - variables: - RoslynSkipTestFileBasedPrograms: 'true' parameters: os: linux # Prefer the dotnet from the container. @@ -90,10 +90,10 @@ stages: - stage: displayName: Test Linux (.NET 9) dependsOn: [] + variables: + RoslynSkipTestFileBasedPrograms: 'true' jobs: - template: azure-pipelines/test-matrix.yml - variables: - RoslynSkipTestFileBasedPrograms: 'true' parameters: os: linux # Prefer the dotnet from the container. diff --git a/test/lsptoolshost/integrationTests/restore.integration.test.ts b/test/lsptoolshost/integrationTests/restore.integration.test.ts index 2ec481ca23..d4d319c4ca 100644 --- a/test/lsptoolshost/integrationTests/restore.integration.test.ts +++ b/test/lsptoolshost/integrationTests/restore.integration.test.ts @@ -20,6 +20,8 @@ import { describe, beforeAll, beforeEach, afterAll, test, expect, afterEach } fr import { CSharpExtensionExports } from '../../../src/csharpExtensionExports'; const doRunSuite = process.env.RoslynSkipTestFileBasedPrograms !== 'true'; +console.log(`process.env.RoslynSkipTestFileBasedPrograms: ${process.env.RoslynSkipTestFileBasedPrograms}`); +console.log(`doRunSuite: ${doRunSuite}`); (doRunSuite ? describe : describe.skip)(`Restore Tests`, () => { let exports: CSharpExtensionExports; From 78c2213ed1bcafd4e839126baa7f96c73acdf092 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 28 Oct 2025 16:36:26 -0700 Subject: [PATCH 26/32] try different name --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 00cbc713c6..74aaaceb85 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -72,7 +72,7 @@ stages: - stage: displayName: Test Linux (.NET 8) dependsOn: [] - variables: + env: RoslynSkipTestFileBasedPrograms: 'true' jobs: - template: azure-pipelines/test-matrix.yml @@ -90,7 +90,7 @@ stages: - stage: displayName: Test Linux (.NET 9) dependsOn: [] - variables: + env: RoslynSkipTestFileBasedPrograms: 'true' jobs: - template: azure-pipelines/test-matrix.yml From 3e60d31cf86254b42b24a4bbad43f0e636debeec Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 28 Oct 2025 16:39:10 -0700 Subject: [PATCH 27/32] use template parameter --- azure-pipelines.yml | 6 ++---- azure-pipelines/test-matrix.yml | 5 +++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 74aaaceb85..840af870aa 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -72,8 +72,6 @@ stages: - stage: displayName: Test Linux (.NET 8) dependsOn: [] - env: - RoslynSkipTestFileBasedPrograms: 'true' jobs: - template: azure-pipelines/test-matrix.yml parameters: @@ -82,6 +80,7 @@ stages: installDotNet: false testVSCodeVersion: $(testVSCodeVersion) installAdditionalLinuxDependencies: true + RoslynSkipTestFileBasedPrograms: 'true' pool: name: NetCore-Public demands: ImageOverride -equals 1es-ubuntu-2004-open @@ -90,8 +89,6 @@ stages: - stage: displayName: Test Linux (.NET 9) dependsOn: [] - env: - RoslynSkipTestFileBasedPrograms: 'true' jobs: - template: azure-pipelines/test-matrix.yml parameters: @@ -100,6 +97,7 @@ stages: installDotNet: false testVSCodeVersion: $(testVSCodeVersion) installAdditionalLinuxDependencies: true + RoslynSkipTestFileBasedPrograms: 'true' pool: name: NetCore-Public demands: ImageOverride -equals 1es-ubuntu-2004-open diff --git a/azure-pipelines/test-matrix.yml b/azure-pipelines/test-matrix.yml index 822bb17c9c..687776fa0d 100644 --- a/azure-pipelines/test-matrix.yml +++ b/azure-pipelines/test-matrix.yml @@ -13,6 +13,9 @@ parameters: default: false - name: testVSCodeVersion type: string + - name: RoslynSkipTestFileBasedPrograms + type: string + default: '' jobs: - job: @@ -39,6 +42,8 @@ jobs: container: ${{ parameters.containerName }} steps: - template: /azure-pipelines/test.yml@self + env: + skipFileBasedProgramsTests: $ {{ parameters.skipFileBasedProgramsTests }} parameters: installDotNet: ${{ parameters.installDotNet }} installAdditionalLinuxDependencies: ${{ parameters.installAdditionalLinuxDependencies }} From c2f35aa000a2218937d90b0c23192ea8ee877517 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 28 Oct 2025 16:39:28 -0700 Subject: [PATCH 28/32] fix name --- azure-pipelines/test-matrix.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines/test-matrix.yml b/azure-pipelines/test-matrix.yml index 687776fa0d..63af7e45c9 100644 --- a/azure-pipelines/test-matrix.yml +++ b/azure-pipelines/test-matrix.yml @@ -43,7 +43,7 @@ jobs: steps: - template: /azure-pipelines/test.yml@self env: - skipFileBasedProgramsTests: $ {{ parameters.skipFileBasedProgramsTests }} + RoslynSkipTestFileBasedPrograms: $ {{ parameters.RoslynSkipTestFileBasedPrograms }} parameters: installDotNet: ${{ parameters.installDotNet }} installAdditionalLinuxDependencies: ${{ parameters.installAdditionalLinuxDependencies }} From dd46ccea29530be105da0ac9688e146fb176765b Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 28 Oct 2025 16:44:53 -0700 Subject: [PATCH 29/32] try to deal with azdo env var weirdness --- azure-pipelines.yml | 6 ++++-- azure-pipelines/test-matrix.yml | 5 ----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 840af870aa..00cbc713c6 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -72,6 +72,8 @@ stages: - stage: displayName: Test Linux (.NET 8) dependsOn: [] + variables: + RoslynSkipTestFileBasedPrograms: 'true' jobs: - template: azure-pipelines/test-matrix.yml parameters: @@ -80,7 +82,6 @@ stages: installDotNet: false testVSCodeVersion: $(testVSCodeVersion) installAdditionalLinuxDependencies: true - RoslynSkipTestFileBasedPrograms: 'true' pool: name: NetCore-Public demands: ImageOverride -equals 1es-ubuntu-2004-open @@ -89,6 +90,8 @@ stages: - stage: displayName: Test Linux (.NET 9) dependsOn: [] + variables: + RoslynSkipTestFileBasedPrograms: 'true' jobs: - template: azure-pipelines/test-matrix.yml parameters: @@ -97,7 +100,6 @@ stages: installDotNet: false testVSCodeVersion: $(testVSCodeVersion) installAdditionalLinuxDependencies: true - RoslynSkipTestFileBasedPrograms: 'true' pool: name: NetCore-Public demands: ImageOverride -equals 1es-ubuntu-2004-open diff --git a/azure-pipelines/test-matrix.yml b/azure-pipelines/test-matrix.yml index 63af7e45c9..822bb17c9c 100644 --- a/azure-pipelines/test-matrix.yml +++ b/azure-pipelines/test-matrix.yml @@ -13,9 +13,6 @@ parameters: default: false - name: testVSCodeVersion type: string - - name: RoslynSkipTestFileBasedPrograms - type: string - default: '' jobs: - job: @@ -42,8 +39,6 @@ jobs: container: ${{ parameters.containerName }} steps: - template: /azure-pipelines/test.yml@self - env: - RoslynSkipTestFileBasedPrograms: $ {{ parameters.RoslynSkipTestFileBasedPrograms }} parameters: installDotNet: ${{ parameters.installDotNet }} installAdditionalLinuxDependencies: ${{ parameters.installAdditionalLinuxDependencies }} From b78119abd003731a75cc6b0037d0bd6d8ed9886d Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 28 Oct 2025 16:48:40 -0700 Subject: [PATCH 30/32] finish rename --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 00cbc713c6..a66d81275a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -73,7 +73,7 @@ stages: displayName: Test Linux (.NET 8) dependsOn: [] variables: - RoslynSkipTestFileBasedPrograms: 'true' + ROSLYN_SKIP_TEST_FILE_BASED_PROGRAMS: 'true' jobs: - template: azure-pipelines/test-matrix.yml parameters: @@ -91,7 +91,7 @@ stages: displayName: Test Linux (.NET 9) dependsOn: [] variables: - RoslynSkipTestFileBasedPrograms: 'true' + ROSLYN_SKIP_TEST_FILE_BASED_PROGRAMS: 'true' jobs: - template: azure-pipelines/test-matrix.yml parameters: From 0baa5ab871ca75d4e723216c9f25a0068a19ac67 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 28 Oct 2025 16:49:28 -0700 Subject: [PATCH 31/32] rename test file --- ...ration.test.ts => fileBasedPrograms.integration.test.ts} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename test/lsptoolshost/integrationTests/{restore.integration.test.ts => fileBasedPrograms.integration.test.ts} (88%) diff --git a/test/lsptoolshost/integrationTests/restore.integration.test.ts b/test/lsptoolshost/integrationTests/fileBasedPrograms.integration.test.ts similarity index 88% rename from test/lsptoolshost/integrationTests/restore.integration.test.ts rename to test/lsptoolshost/integrationTests/fileBasedPrograms.integration.test.ts index d4d319c4ca..476aab277f 100644 --- a/test/lsptoolshost/integrationTests/restore.integration.test.ts +++ b/test/lsptoolshost/integrationTests/fileBasedPrograms.integration.test.ts @@ -19,10 +19,10 @@ import { import { describe, beforeAll, beforeEach, afterAll, test, expect, afterEach } from '@jest/globals'; import { CSharpExtensionExports } from '../../../src/csharpExtensionExports'; -const doRunSuite = process.env.RoslynSkipTestFileBasedPrograms !== 'true'; -console.log(`process.env.RoslynSkipTestFileBasedPrograms: ${process.env.RoslynSkipTestFileBasedPrograms}`); +const doRunSuite = process.env['ROSLYN_SKIP_TEST_FILE_BASED_PROGRAMS'] !== 'true'; +console.log(`process.env.ROSLYN_SKIP_TEST_FILE_BASED_PROGRAMS: ${process.env.ROSLYN_SKIP_TEST_FILE_BASED_PROGRAMS}`); console.log(`doRunSuite: ${doRunSuite}`); -(doRunSuite ? describe : describe.skip)(`Restore Tests`, () => { +(doRunSuite ? describe : describe.skip)(`File-based Programs Tests`, () => { let exports: CSharpExtensionExports; beforeAll(async () => { From 885011ddb3ac4cfbd88bb6ec72e3591eb61449a9 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Wed, 29 Oct 2025 12:17:36 -0700 Subject: [PATCH 32/32] More skips --- azure-pipelines.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a66d81275a..2541efc1cc 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -124,6 +124,8 @@ stages: - stage: Test_Windows_Stage displayName: Test Windows dependsOn: [] + variables: + ROSLYN_SKIP_TEST_FILE_BASED_PROGRAMS: 'true' jobs: - template: azure-pipelines/test-matrix.yml parameters: @@ -137,6 +139,8 @@ stages: - stage: Test_MacOS_Stage displayName: Test MacOS dependsOn: [] + variables: + ROSLYN_SKIP_TEST_FILE_BASED_PROGRAMS: 'true' jobs: - template: azure-pipelines/test-matrix.yml parameters: