Skip to content

Commit f32ccef

Browse files
authored
Integration Tests - First look (#5921)
* wip * wip * wip * wip * wip * wip * wip * wip * remove changes * working :) * working inaly hint test * cleanup * revert * revert to diff names * fix launch path * revert
1 parent e2407a5 commit f32ccef

File tree

12 files changed

+271
-41
lines changed

12 files changed

+271
-41
lines changed

.vscode/launch.json

Lines changed: 27 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,6 @@
4848
"--extensionTestsPath=${workspaceRoot}/out/test/featureTests"
4949
],
5050
"env": {
51-
"CODE_WORKSPACE_ROOT": "${workspaceRoot}",
52-
"CODE_TESTS_PATH": "${workspaceRoot}/out/test/featureTests",
5351
"CODE_EXTENSIONS_PATH": "${workspaceRoot}",
5452
"OSVC_SUITE": "featureTests",
5553
},
@@ -73,12 +71,8 @@
7371
"--extensionTestsPath=${workspaceRoot}/out/test/integrationTests"
7472
],
7573
"env": {
76-
"CODE_WORKSPACE_ROOT": "${workspaceRoot}",
77-
"CODE_TESTS_PATH": "${workspaceRoot}/out/test/integrationTests",
78-
"CODE_TESTS_WORKSPACE": "${workspaceRoot}/test/integrationTests/testAssets/singleCsproj",
7974
"CODE_EXTENSIONS_PATH": "${workspaceRoot}",
8075
"OSVC_SUITE": "singleCsproj",
81-
"OMNISHARP_ENGINE": "stdio",
8276
},
8377
"sourceMaps": true,
8478
"outFiles": [
@@ -101,12 +95,8 @@
10195
"--extensionTestsPath=${workspaceRoot}/out/test/integrationTests"
10296
],
10397
"env": {
104-
"CODE_WORKSPACE_ROOT": "${workspaceRoot}",
105-
"CODE_TESTS_PATH": "${workspaceRoot}/out/test/integrationTests",
106-
"CODE_TESTS_WORKSPACE": "${workspaceRoot}/test/integrationTests/testAssets/BasicRazorApp2_1",
10798
"CODE_EXTENSIONS_PATH": "${workspaceRoot}",
10899
"OSVC_SUITE": "BasicRazorApp2_1",
109-
"OMNISHARP_ENGINE": "stdio",
110100
},
111101
},
112102
{
@@ -118,26 +108,45 @@
118108
// Create a temp profile that has no extensions / user settings.
119109
// This allows us to only have the C# extension + the dotnet runtime installer extension dependency.
120110
"--profile-temp",
121-
"${workspaceRoot}/test/integrationTests/testAssets/slnWithCsproj",
111+
"${workspaceRoot}/test/integrationTests/testAssets/slnWithCsproj/.vscode/omnisharp_slnWithCsproj.code-workspace",
122112
"--extensionDevelopmentPath=${workspaceRoot}",
123113
"--extensionTestsPath=${workspaceRoot}/out/test/integrationTests"
124114
],
125115
"env": {
126-
"CODE_WORKSPACE_ROOT": "${workspaceRoot}",
127-
"CODE_TESTS_PATH": "${workspaceRoot}/out/test/integrationTests",
128-
"CODE_TESTS_WORKSPACE": "${workspaceRoot}/test/integrationTests/testAssets/slnWithCsproj",
129116
"CODE_EXTENSIONS_PATH": "${workspaceRoot}",
130117
"OSVC_SUITE": "slnWithCsproj",
131-
"OMNISHARP_ENGINE": "stdio",
132118
},
133-
"stopOnEntry": false,
134119
"sourceMaps": true,
135120
"outFiles": [
136121
"${workspaceRoot}/dist/*.js",
137122
"${workspaceRoot}/out/test/**/*.js"
138123
],
139124
"preLaunchTask": "buildDev"
140125
},
126+
{
127+
"name": "LSP Tools Host: Launch slnWithCsproj Workspace Tests",
128+
"type": "extensionHost",
129+
"request": "launch",
130+
"runtimeExecutable": "${execPath}",
131+
"args": [
132+
// Create a temp profile that has no extensions / user settings.
133+
// This allows us to only have the C# extension + the dotnet runtime installer extension dependency.
134+
"--profile-temp",
135+
"${workspaceRoot}/test/integrationTests/testAssets/slnWithCsproj/.vscode/lsp_tools_host_slnWithCsproj.code-workspace",
136+
"--extensionDevelopmentPath=${workspaceRoot}",
137+
"--extensionTestsPath=${workspaceRoot}/out/lsptoolshosttest/lspToolsHostIntegrationTests",
138+
],
139+
"env": {
140+
"CODE_EXTENSIONS_PATH": "${workspaceRoot}",
141+
"OSVC_SUITE": "slnWithCsproj",
142+
},
143+
"sourceMaps": true,
144+
"outFiles": [
145+
"${workspaceRoot}/dist/*.js",
146+
"${workspaceRoot}/out/bluetest/**/*.js"
147+
],
148+
"preLaunchTask": "buildDev"
149+
},
141150
{
142151
"name": "Launch singleCsproj Workspace Tests [LSP]",
143152
"type": "extensionHost",
@@ -147,19 +156,14 @@
147156
// Create a temp profile that has no extensions / user settings.
148157
// This allows us to only have the C# extension + the dotnet runtime installer extension dependency.
149158
"--profile-temp",
150-
"${workspaceRoot}/test/integrationTests/testAssets/singleCsproj",
159+
"${workspaceRoot}/test/integrationTests/testAssets/slnWithCsproj/.vscode/omnisharp_lsp_slnWithCsproj.code-workspace",
151160
"--extensionDevelopmentPath=${workspaceRoot}",
152161
"--extensionTestsPath=${workspaceRoot}/out/test/integrationTests"
153162
],
154163
"env": {
155-
"CODE_WORKSPACE_ROOT": "${workspaceRoot}",
156-
"CODE_TESTS_PATH": "${workspaceRoot}/out/test/integrationTests",
157-
"CODE_TESTS_WORKSPACE": "${workspaceRoot}/test/integrationTests/testAssets/singleCsproj",
158164
"CODE_EXTENSIONS_PATH": "${workspaceRoot}",
159165
"OSVC_SUITE": "singleCsproj",
160-
"OMNISHARP_ENGINE": "lsp",
161166
},
162-
"stopOnEntry": false,
163167
"sourceMaps": true,
164168
"outFiles": [
165169
"${workspaceRoot}/dist/*.js",
@@ -181,12 +185,8 @@
181185
"--extensionTestsPath=${workspaceRoot}/out/test/integrationTests"
182186
],
183187
"env": {
184-
"CODE_WORKSPACE_ROOT": "${workspaceRoot}",
185-
"CODE_TESTS_PATH": "${workspaceRoot}/out/test/integrationTests",
186-
"CODE_TESTS_WORKSPACE": "${workspaceRoot}/test/integrationTests/testAssets/BasicRazorApp2_1",
187188
"CODE_EXTENSIONS_PATH": "${workspaceRoot}",
188189
"OSVC_SUITE": "BasicRazorApp2_1",
189-
"OMNISHARP_ENGINE": "lsp",
190190
},
191191
},
192192
{
@@ -198,17 +198,13 @@
198198
// Create a temp profile that has no extensions / user settings.
199199
// This allows us to only have the C# extension + the dotnet runtime installer extension dependency.
200200
"--profile-temp",
201-
"${workspaceRoot}/test/integrationTests/testAssets/slnWithCsproj",
201+
"${workspaceRoot}/test/integrationTests/testAssets/slnWithCsproj/.vscode/omnisharp_lsp_slnWithCsproj.code-workspace",
202202
"--extensionDevelopmentPath=${workspaceRoot}",
203203
"--extensionTestsPath=${workspaceRoot}/out/test/integrationTests"
204204
],
205205
"env": {
206-
"CODE_WORKSPACE_ROOT": "${workspaceRoot}",
207-
"CODE_TESTS_PATH": "${workspaceRoot}/out/test/integrationTests",
208-
"CODE_TESTS_WORKSPACE": "${workspaceRoot}/test/integrationTests/testAssets/slnWithCsproj",
209206
"CODE_EXTENSIONS_PATH": "${workspaceRoot}",
210207
"OSVC_SUITE": "slnWithCsproj",
211-
"OMNISHARP_ENGINE": "lsp",
212208
},
213209
"sourceMaps": true,
214210
"outFiles": [
@@ -231,9 +227,6 @@
231227
"--extensionTestsPath=${workspaceRoot}/out/test/integrationTests"
232228
],
233229
"env": {
234-
"CODE_WORKSPACE_ROOT": "${workspaceRoot}",
235-
"CODE_TESTS_PATH": "${workspaceRoot}/out/test/integrationTests",
236-
"CODE_TESTS_WORKSPACE": "${workspaceRoot}/test/integrationTests/testAssets/slnFilterWithCsproj",
237230
"CODE_EXTENSIONS_PATH": "${workspaceRoot}",
238231
"OSVC_SUITE": "slnFilterWithCsproj",
239232
},
@@ -258,9 +251,6 @@
258251
"--extensionTestsPath=${workspaceRoot}/out/test/integrationTests"
259252
],
260253
"env": {
261-
"CODE_WORKSPACE_ROOT": "${workspaceRoot}",
262-
"CODE_TESTS_PATH": "${workspaceRoot}/out/test/integrationTests",
263-
"CODE_TESTS_WORKSPACE": "${workspaceRoot}/test/integrationTests/testAssets/slnWithGenerator",
264254
"CODE_EXTENSIONS_PATH": "${workspaceRoot}",
265255
"OSVC_SUITE": "slnWithGenerator",
266256
},
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import * as path from 'path';
7+
import * as testRunner from '../testRunner';
8+
9+
export async function run() {
10+
process.env.RUNNING_INTEGRATION_TESTS = 'true';
11+
12+
return testRunner.run(path.resolve(__dirname, '.'), {
13+
timeout: 120000, // this seems to timing out often in our pipeline
14+
ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.)
15+
useColors: true, // colored output from test results
16+
});
17+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import * as vscode from 'vscode';
7+
8+
import { should, assert } from 'chai';
9+
import testAssetWorkspace from '../../test/integrationTests/testAssets/testAssetWorkspace';
10+
import * as path from 'path';
11+
import { activateCSharpExtension, isRazorWorkspace, isSlnWithGenerator } from './lspToolsHostIntegrationHelpers';
12+
import { InlayHint, InlayHintKind, Position } from 'vscode-languageserver-protocol';
13+
14+
suite(`LSP Inlay Hints ${testAssetWorkspace.description}`, function () {
15+
let fileUri: vscode.Uri;
16+
17+
suiteSetup(async function () {
18+
should();
19+
20+
if (isRazorWorkspace(vscode.workspace) || isSlnWithGenerator(vscode.workspace)) {
21+
this.skip();
22+
}
23+
24+
const editorConfig = vscode.workspace.getConfiguration('editor');
25+
await editorConfig.update('inlayHints.enabled', true);
26+
const dotnetConfig = vscode.workspace.getConfiguration('dotnet');
27+
await dotnetConfig.update('inlayHints.enableInlayHintsForParameters', true);
28+
await dotnetConfig.update('inlayHints.enableInlayHintsForLiteralParameters', true);
29+
await dotnetConfig.update('inlayHints.enableInlayHintsForObjectCreationParameters', true);
30+
await dotnetConfig.update('inlayHints.enableInlayHintsForIndexerParameters', true);
31+
await dotnetConfig.update('inlayHints.enableInlayHintsForOtherParameters', true);
32+
await dotnetConfig.update('inlayHints.suppressInlayHintsForParametersThatDifferOnlyBySuffix', true);
33+
await dotnetConfig.update('inlayHints.suppressInlayHintsForParametersThatMatchMethodIntent', true);
34+
await dotnetConfig.update('inlayHints.suppressInlayHintsForParametersThatMatchArgumentName', true);
35+
36+
const csharpConfig = vscode.workspace.getConfiguration('csharp');
37+
await csharpConfig.update('inlayHints.enableInlayHintsForTypes', true);
38+
await csharpConfig.update('inlayHints.enableInlayHintsForImplicitVariableTypes', true);
39+
await csharpConfig.update('inlayHints.enableInlayHintsForLambdaParameterTypes', true);
40+
await csharpConfig.update('inlayHints.enableInlayHintsForImplicitObjectCreation', true);
41+
42+
const fileName = 'inlayHints.cs';
43+
const projectDirectory = testAssetWorkspace.projects[0].projectDirectoryPath;
44+
const filePath = path.join(projectDirectory, fileName);
45+
fileUri = vscode.Uri.file(filePath);
46+
47+
await vscode.commands.executeCommand('vscode.open', fileUri);
48+
await activateCSharpExtension();
49+
});
50+
51+
suiteTeardown(async () => {
52+
await testAssetWorkspace.cleanupWorkspace();
53+
});
54+
55+
test('Hints retrieved for region', async () => {
56+
const range = new vscode.Range(new vscode.Position(4, 8), new vscode.Position(15, 85));
57+
const hints: vscode.InlayHint[] = await vscode.commands.executeCommand(
58+
'vscode.executeInlayHintProvider',
59+
fileUri,
60+
range
61+
);
62+
63+
assert.lengthOf(hints, 5);
64+
65+
assertInlayHintEqual(hints[0], InlayHint.create(Position.create(6, 12), 'InlayHints', InlayHintKind.Type));
66+
assertInlayHintEqual(hints[1], InlayHint.create(Position.create(7, 27), 'InlayHints', InlayHintKind.Type));
67+
assertInlayHintEqual(hints[2], InlayHint.create(Position.create(9, 17), 'i:', InlayHintKind.Parameter));
68+
assertInlayHintEqual(hints[3], InlayHint.create(Position.create(10, 15), 'param1:', InlayHintKind.Parameter));
69+
assertInlayHintEqual(hints[4], InlayHint.create(Position.create(11, 27), 'param1:', InlayHintKind.Parameter));
70+
71+
function assertInlayHintEqual(actual: vscode.InlayHint, expected: InlayHint) {
72+
const actualLabel = actual.label as string;
73+
assert.equal(actualLabel, expected.label);
74+
assert.equal(actual.position.line, expected.position.line);
75+
assert.equal(actual.position.character, expected.position.character);
76+
assert.equal(actual.kind, expected.kind);
77+
}
78+
});
79+
});
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import * as vscode from 'vscode';
7+
import * as path from 'path';
8+
import { CSharpExtensionExports } from '../../src/csharpExtensionExports';
9+
10+
export async function activateCSharpExtension(): Promise<void> {
11+
// Ensure the dependent extension exists - when launching via F5 launch.json we can't install the extension prior to opening vscode.
12+
const vscodeDotnetRuntimeExtensionId = 'ms-dotnettools.vscode-dotnet-runtime';
13+
const dotnetRuntimeExtension =
14+
vscode.extensions.getExtension<CSharpExtensionExports>(vscodeDotnetRuntimeExtensionId);
15+
if (!dotnetRuntimeExtension) {
16+
await vscode.commands.executeCommand('workbench.extensions.installExtension', vscodeDotnetRuntimeExtensionId);
17+
await vscode.commands.executeCommand('workbench.action.reloadWindow');
18+
}
19+
20+
const csharpExtension = vscode.extensions.getExtension<CSharpExtensionExports>('ms-dotnettools.csharp');
21+
if (!csharpExtension) {
22+
throw new Error('Failed to find installation of ms-dotnettools.csharp');
23+
}
24+
25+
// Explicitly await the extension activation even if completed so that we capture any errors it threw during activation.
26+
await csharpExtension.activate();
27+
28+
await csharpExtension.exports.initializationFinished();
29+
console.log('ms-dotnettools.csharp activated');
30+
}
31+
32+
export function isRazorWorkspace(workspace: typeof vscode.workspace) {
33+
return isGivenSln(workspace, 'BasicRazorApp2_1');
34+
}
35+
36+
export function isSlnWithGenerator(workspace: typeof vscode.workspace) {
37+
return isGivenSln(workspace, 'slnWithGenerator');
38+
}
39+
40+
function isGivenSln(workspace: typeof vscode.workspace, expectedProjectFileName: string) {
41+
const primeWorkspace = workspace.workspaceFolders![0];
42+
const projectFileName = primeWorkspace.uri.fsPath.split(path.sep).pop();
43+
44+
return projectFileName === expectedProjectFileName;
45+
}

lsptoolshosttest/testRunner.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import * as path from 'path';
7+
import * as Mocha from 'mocha';
8+
import * as glob from 'glob-promise';
9+
import * as tty from 'tty';
10+
11+
// Linux: prevent a weird NPE when mocha on Linux requires the window size from the TTY
12+
// Since we are not running in a tty environment, we just implement the method statically
13+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
14+
// @ts-ignore
15+
tty.getWindowSize = function () {
16+
return [80, 75];
17+
};
18+
19+
export async function run(testsRoot: string, options?: Mocha.MochaOptions) {
20+
options ??= {
21+
ui: 'tdd',
22+
useColors: true,
23+
retries: 2,
24+
};
25+
26+
const mocha = new Mocha(options);
27+
// Glob test files
28+
const files = await glob('**/**.test.js', { cwd: testsRoot });
29+
30+
// Fill into Mocha
31+
files.forEach((file) => mocha.addFile(path.join(testsRoot, file)));
32+
33+
return new Promise<number>((resolve) => {
34+
mocha.run(resolve);
35+
}).then((failures) => {
36+
if (failures > 0) {
37+
throw new Error(`${failures} tests failed.`);
38+
}
39+
});
40+
}

src/lsptoolshost/roslynLanguageServer.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,16 @@ function getInstalledServerPath(platformInfo: PlatformInformation): string {
750750
return pathWithExtension;
751751
}
752752

753+
export async function waitForProjectInitialization(): Promise<void> {
754+
return new Promise((resolve, _) => {
755+
_languageServer.registerStateChangeEvent(async (state) => {
756+
if (state === ServerStateChange.ProjectInitializationComplete) {
757+
resolve();
758+
}
759+
});
760+
});
761+
}
762+
753763
function registerRazorCommands(context: vscode.ExtensionContext, languageServer: RoslynLanguageServer) {
754764
// Razor will call into us (via command) for generated file didChange/didClose notifications. We'll then forward these
755765
// notifications along to Roslyn. didOpen notifications are handled separately via the vscode.openTextDocument method.

src/main.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import {
4646
RoslynLanguageServer,
4747
SolutionSnapshotProvider,
4848
activateRoslynLanguageServer,
49+
waitForProjectInitialization,
4950
} from './lsptoolshost/roslynLanguageServer';
5051
import { Options } from './shared/options';
5152
import { MigrateOptions } from './shared/migrateOptions';
@@ -123,6 +124,7 @@ export async function activate(
123124
let omnisharpLangServicePromise: Promise<OmniSharp.ActivationResult> | undefined = undefined;
124125
let omnisharpRazorPromise: Promise<void> | undefined = undefined;
125126
let roslynLanguageServerPromise: Promise<RoslynLanguageServer> | undefined = undefined;
127+
let projectInitializationCompletePromise: Promise<void> | undefined = undefined;
126128

127129
if (!useOmnisharpServer) {
128130
// Activate Razor. Needs to be activated before Roslyn so commands are registered in the correct order.
@@ -157,6 +159,7 @@ export async function activate(
157159
dotnetTestChannel,
158160
reporter
159161
);
162+
projectInitializationCompletePromise = waitForProjectInitialization();
160163
} else {
161164
const dotnetChannel = vscode.window.createOutputChannel('.NET');
162165
const dotnetChannelObserver = new DotNetChannelObserver(dotnetChannel);
@@ -316,6 +319,7 @@ export async function activate(
316319
initializationFinished: async () => {
317320
await coreClrDebugPromise;
318321
await roslynLanguageServerPromise;
322+
await projectInitializationCompletePromise;
319323
},
320324
profferBrokeredServices: (container) => profferBrokeredServices(context, container),
321325
logDirectory: context.logUri.fsPath,

0 commit comments

Comments
 (0)