diff --git a/Directory.Build.props b/Directory.Build.props index 3720f4d06a..13843770d5 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -5,6 +5,7 @@ Defines the lowest supported target framework for the extension. Used by server download / integration tests to ensure they run when only this SDK is installed. --> + false net6.0 - \ No newline at end of file + diff --git a/azure-pipelines/test-matrix.yml b/azure-pipelines/test-matrix.yml index d816d3b4a2..9ed473b712 100644 --- a/azure-pipelines/test-matrix.yml +++ b/azure-pipelines/test-matrix.yml @@ -21,10 +21,9 @@ jobs: UnitTests: npmCommand: test:unit isIntegration: false - ${{ if ne(parameters.os, 'windows') }}: - CSharpIntegrationTests: - npmCommand: test:integration:csharp - isIntegration: true + CSharpIntegrationTests: + npmCommand: test:integration:csharp + isIntegration: true DevKitTests: npmCommand: test:integration:devkit isIntegration: true diff --git a/test/jestHelpers.ts b/test/jestHelpers.ts new file mode 100644 index 0000000000..6935224baf --- /dev/null +++ b/test/jestHelpers.ts @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { EOL, platform } from 'os'; +import { describe, expect, test } from '@jest/globals'; +import { usingDevKit } from './lsptoolshost/integrationTests/integrationHelpers'; + +export async function expectText(document: vscode.TextDocument, expectedLines: string[]) { + const expectedText = expectedLines.join(EOL); + expect(document.getText()).toBe(expectedText); +} + +export function expectPath(expected: vscode.Uri, actual: vscode.Uri) { + if (isLinux()) { + expect(actual.path).toBe(expected.path); + } else { + const actualPath = actual.path.toLowerCase(); + const expectedPath = expected.path.toLocaleLowerCase(); + expect(actualPath).toBe(expectedPath); + } +} + +export const describeIfCSharp = describeIf(!usingDevKit()); +export const describeIfDevKit = describeIf(usingDevKit()); +export const describeIfNotMacOS = describeIf(!isMacOS()); +export const describeIfWindows = describeIf(isWindows()); +export const testIfCSharp = testIf(!usingDevKit()); +export const testIfDevKit = testIf(usingDevKit()); +export const testIfNotMacOS = testIf(!isMacOS()); +export const testIfWindows = testIf(isWindows()); + +function describeIf(condition: boolean) { + return condition ? describe : describe.skip; +} + +function testIf(condition: boolean) { + return condition ? test : test.skip; +} + +function isMacOS() { + const currentPlatform = platform(); + return currentPlatform === 'darwin'; +} + +function isWindows() { + const currentPlatform = platform(); + return currentPlatform === 'win32'; +} + +function isLinux() { + return !(isMacOS() || isWindows()); +} diff --git a/test/lsptoolshost/integrationTests/buildDiagnostics.integration.test.ts b/test/lsptoolshost/integrationTests/buildDiagnostics.integration.test.ts index 7d49a91670..7e3dc5d81f 100644 --- a/test/lsptoolshost/integrationTests/buildDiagnostics.integration.test.ts +++ b/test/lsptoolshost/integrationTests/buildDiagnostics.integration.test.ts @@ -12,6 +12,7 @@ import { } from '../../../src/lsptoolshost/diagnostics/buildDiagnosticsService'; import * as integrationHelpers from './integrationHelpers'; import path from 'path'; + describe(`Build and live diagnostics dedupe`, () => { beforeAll(async () => { await integrationHelpers.activateCSharpExtension(); diff --git a/test/lsptoolshost/integrationTests/codeactions.integration.test.ts b/test/lsptoolshost/integrationTests/codeactions.integration.test.ts index e4b0c90bb5..a13a87f7ce 100644 --- a/test/lsptoolshost/integrationTests/codeactions.integration.test.ts +++ b/test/lsptoolshost/integrationTests/codeactions.integration.test.ts @@ -7,12 +7,8 @@ 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, - expectText, - openFileInWorkspaceAsync, -} from './integrationHelpers'; +import { activateCSharpExtension, closeAllEditorsAsync, openFileInWorkspaceAsync } from './integrationHelpers'; +import { expectText } from '../../jestHelpers'; describe(`Code Actions Tests`, () => { beforeAll(async () => { diff --git a/test/lsptoolshost/integrationTests/commandEnablement.integration.test.ts b/test/lsptoolshost/integrationTests/commandEnablement.integration.test.ts index c84d995fb1..ee0dbd1b54 100644 --- a/test/lsptoolshost/integrationTests/commandEnablement.integration.test.ts +++ b/test/lsptoolshost/integrationTests/commandEnablement.integration.test.ts @@ -5,7 +5,7 @@ import { expect, beforeAll, afterAll, describe } from '@jest/globals'; import * as vscode from 'vscode'; -import { activateCSharpExtension, testIfCSharp, testIfDevKit } from './integrationHelpers'; +import { activateCSharpExtension } from './integrationHelpers'; import testAssetWorkspace from './testAssets/testAssetWorkspace'; import { RoslynDevKitCommands, @@ -13,6 +13,7 @@ import { UnexpectedRoslynDevKitCommands, UnexpectedRoslynStandaloneCommands, } from './expectedCommands'; +import { testIfCSharp, testIfDevKit } from '../../jestHelpers'; describe(`Command Enablement Tests`, () => { beforeAll(async () => { diff --git a/test/lsptoolshost/integrationTests/documentDiagnostics.integration.test.ts b/test/lsptoolshost/integrationTests/documentDiagnostics.integration.test.ts index 149d6e6b5b..a69bb8b983 100644 --- a/test/lsptoolshost/integrationTests/documentDiagnostics.integration.test.ts +++ b/test/lsptoolshost/integrationTests/documentDiagnostics.integration.test.ts @@ -9,12 +9,8 @@ import testAssetWorkspace from './testAssets/testAssetWorkspace'; import { AnalysisSetting } from '../../../src/lsptoolshost/diagnostics/buildDiagnosticsService'; import path from 'path'; import { getCode, setDiagnosticSettings, waitForExpectedDiagnostics } from './diagnosticsHelpers'; -import { - activateCSharpExtension, - closeAllEditorsAsync, - describeIfCSharp, - openFileInWorkspaceAsync, -} from './integrationHelpers'; +import { activateCSharpExtension, closeAllEditorsAsync, openFileInWorkspaceAsync } from './integrationHelpers'; +import { describeIfCSharp } from '../../jestHelpers'; // Restarting the server is required for these tests, but not supported with C# Dev Kit. describeIfCSharp(`Document Diagnostics Tests`, () => { diff --git a/test/lsptoolshost/integrationTests/formatting.integration.test.ts b/test/lsptoolshost/integrationTests/formatting.integration.test.ts index 8549717ec6..2b9bad83b6 100644 --- a/test/lsptoolshost/integrationTests/formatting.integration.test.ts +++ b/test/lsptoolshost/integrationTests/formatting.integration.test.ts @@ -6,14 +6,10 @@ import * as vscode from 'vscode'; import * as path from 'path'; import testAssetWorkspace from './testAssets/testAssetWorkspace'; -import { - activateCSharpExtension, - closeAllEditorsAsync, - expectText, - openFileInWorkspaceAsync, -} from './integrationHelpers'; +import { activateCSharpExtension, closeAllEditorsAsync, openFileInWorkspaceAsync } from './integrationHelpers'; import { describe, beforeEach, afterAll, test, afterEach } from '@jest/globals'; import { formatDocumentAsync, formatOnTypeAsync, formatRangeAsync } from './formattingTestHelpers'; +import { expectText } from '../../jestHelpers'; describe(`Formatting Tests`, () => { beforeEach(async () => { diff --git a/test/lsptoolshost/integrationTests/formattingEditorConfig.integration.test.ts b/test/lsptoolshost/integrationTests/formattingEditorConfig.integration.test.ts index 67452574e1..e5f84771f7 100644 --- a/test/lsptoolshost/integrationTests/formattingEditorConfig.integration.test.ts +++ b/test/lsptoolshost/integrationTests/formattingEditorConfig.integration.test.ts @@ -6,14 +6,10 @@ import * as vscode from 'vscode'; import * as path from 'path'; import testAssetWorkspace from './testAssets/testAssetWorkspace'; -import { - activateCSharpExtension, - closeAllEditorsAsync, - expectText, - openFileInWorkspaceAsync, -} from './integrationHelpers'; +import { activateCSharpExtension, closeAllEditorsAsync, openFileInWorkspaceAsync } from './integrationHelpers'; import { describe, beforeAll, beforeEach, afterAll, test, afterEach } from '@jest/globals'; import { formatDocumentAsync, formatOnTypeAsync, formatRangeAsync } from './formattingTestHelpers'; +import { expectText } from '../../jestHelpers'; describe(`Formatting With EditorConfig Tests`, () => { beforeAll(async () => { diff --git a/test/lsptoolshost/integrationTests/gotoDefinition.integration.test.ts b/test/lsptoolshost/integrationTests/gotoDefinition.integration.test.ts index cfc31da372..983ace8ab8 100644 --- a/test/lsptoolshost/integrationTests/gotoDefinition.integration.test.ts +++ b/test/lsptoolshost/integrationTests/gotoDefinition.integration.test.ts @@ -12,10 +12,9 @@ import { findRangeOfString, navigate, openFileInWorkspaceAsync, - testIfCSharp, - testIfDevKit, } from './integrationHelpers'; import { describe, beforeAll, beforeEach, afterAll, test, expect, afterEach } from '@jest/globals'; +import { testIfCSharp, testIfDevKit } from '../../jestHelpers'; describe(`Go To Definition Tests`, () => { beforeAll(async () => { diff --git a/test/lsptoolshost/integrationTests/index.ts b/test/lsptoolshost/integrationTests/index.ts index ac2e6a90a2..4ee2cd1d3f 100644 --- a/test/lsptoolshost/integrationTests/index.ts +++ b/test/lsptoolshost/integrationTests/index.ts @@ -3,11 +3,73 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as vscode from 'vscode'; +import * as fsExtra from 'fs-extra'; +import path from 'path'; +import { AggregatedResult } from '@jest/test-result'; import { runIntegrationTests } from '../../runIntegrationTests'; import { jestIntegrationTestProjectName } from './jest.config'; +import { activateCSharpExtension } from './integrationHelpers'; export async function run() { process.env.RUNNING_INTEGRATION_TESTS = 'true'; - await runIntegrationTests(jestIntegrationTestProjectName); + await activateCSharpExtension(); + await moveLogs('activated'); + + let anyFailures = false; + let results: AggregatedResult | undefined; + if (process.env.TEST_FILE_FILTER) { + results = await runIntegrationTests(jestIntegrationTestProjectName); + await moveLogs(path.basename(process.env.TEST_FILE_FILTER, '.integration.test.ts')); + anyFailures = anyFailures || !results.success; + } else { + const workingDirectory = process.cwd(); + + const testFiles = (await fsExtra.readdir(__dirname)) + .filter((file) => file.endsWith('.integration.test.js')) + .map((file) => path.join(__dirname, file)); + + for (let file of testFiles) { + // We have to fix up the file path because the test file was discovered in the /out/ directory. + file = file.substring(0, file.length - 2) + 'ts'; + file = file.replace(/[\\/]out[\\/]/, path.sep); + file = workingDirectory[0] + file.substring(1); + + console.log(''); + console.log(`-- Running integration tests for ${path.basename(file)} --`); + console.log(''); + + process.env.TEST_FILE_FILTER = file; + + results = await runIntegrationTests(jestIntegrationTestProjectName); + await moveLogs(path.basename(process.env.TEST_FILE_FILTER, '.integration.test.ts')); + anyFailures = anyFailures || !results.success; + } + } + + // Explicitly exit the process - VSCode likes to write a bunch of cancellation errors to the console after this + // which make it look like the tests always fail. We're done with the tests at this point, so just exit. + process.exit(anyFailures ? 1 : 0); +} + +async function moveLogs(name: string) { + const exports = vscode.extensions.getExtension('ms-dotnettools.csharp')?.exports; + if (!exports) { + throw new Error('Failed to get C# extension exports for cleanup'); + } + + if (!exports.logDirectory) { + console.warn(`Failed to get log directory from C# extension exports`); + return; + } + + const targetLogDir = path.join( + path.dirname(exports.logDirectory), + `${name ?? 'unknown'}_${path.basename(exports.logDirectory)}` + ); + await fsExtra.copy(exports.logDirectory, targetLogDir); + console.log(`Copied extension logs from ${exports.logDirectory} to ${targetLogDir}`); + + await new Promise((resolve) => fsExtra.rm(path.join(exports.logDirectory, '*.log'), resolve)); } diff --git a/test/lsptoolshost/integrationTests/integrationHelpers.ts b/test/lsptoolshost/integrationTests/integrationHelpers.ts index b4e2e8ea90..85a1024d82 100644 --- a/test/lsptoolshost/integrationTests/integrationHelpers.ts +++ b/test/lsptoolshost/integrationTests/integrationHelpers.ts @@ -10,8 +10,6 @@ import { CSharpExtensionExports } from '../../../src/csharpExtensionExports'; import { existsSync } from 'fs'; import { ServerState } from '../../../src/lsptoolshost/server/languageServerEvents'; import testAssetWorkspace from './testAssets/testAssetWorkspace'; -import { EOL, platform } from 'os'; -import { describe, expect, test } from '@jest/globals'; export async function activateCSharpExtension(): Promise { const csharpExtension = vscode.extensions.getExtension('ms-dotnettools.csharp'); @@ -191,8 +189,6 @@ export async function navigate( // Navigation happens asynchronously when a different file is opened, so we need to wait for the window to change. await windowChanged; - - expect(vscode.window.activeTextEditor?.document.fileName).toContain(expectedFileName); } export function sortLocations(locations: vscode.Location[]): vscode.Location[] { @@ -253,49 +249,3 @@ export async function waitForExpectedResult( export async function sleep(ms = 0) { return new Promise((r) => setTimeout(r, ms)); } - -export async function expectText(document: vscode.TextDocument, expectedLines: string[]) { - const expectedText = expectedLines.join(EOL); - expect(document.getText()).toBe(expectedText); -} - -export function expectPath(expected: vscode.Uri, actual: vscode.Uri) { - if (isLinux()) { - expect(actual.path).toBe(expected.path); - } else { - const actualPath = actual.path.toLowerCase(); - const expectedPath = expected.path.toLocaleLowerCase(); - expect(actualPath).toBe(expectedPath); - } -} - -export const describeIfCSharp = describeIf(!usingDevKit()); -export const describeIfDevKit = describeIf(usingDevKit()); -export const describeIfNotMacOS = describeIf(!isMacOS()); -export const describeIfWindows = describeIf(isWindows()); -export const testIfCSharp = testIf(!usingDevKit()); -export const testIfDevKit = testIf(usingDevKit()); -export const testIfNotMacOS = testIf(!isMacOS()); -export const testIfWindows = testIf(isWindows()); - -function describeIf(condition: boolean) { - return condition ? describe : describe.skip; -} - -function testIf(condition: boolean) { - return condition ? test : test.skip; -} - -function isMacOS() { - const currentPlatform = platform(); - return currentPlatform === 'darwin'; -} - -function isWindows() { - const currentPlatform = platform(); - return currentPlatform === 'win32'; -} - -function isLinux() { - return !(isMacOS() || isWindows()); -} diff --git a/test/lsptoolshost/integrationTests/unitTests.integration.test.ts b/test/lsptoolshost/integrationTests/unitTests.integration.test.ts index 8753a65607..1916d52cfb 100644 --- a/test/lsptoolshost/integrationTests/unitTests.integration.test.ts +++ b/test/lsptoolshost/integrationTests/unitTests.integration.test.ts @@ -10,11 +10,11 @@ import testAssetWorkspace from './testAssets/testAssetWorkspace'; import { activateCSharpExtension, closeAllEditorsAsync, - describeIfCSharp, getCodeLensesAsync, openFileInWorkspaceAsync, } from './integrationHelpers'; import { TestProgress } from '../../../src/lsptoolshost/server/roslynProtocol'; +import { describeIfCSharp } from '../../jestHelpers'; describeIfCSharp(`Unit Testing Tests`, () => { beforeAll(async () => { diff --git a/test/lsptoolshost/integrationTests/workspaceDiagnostics.integration.test.ts b/test/lsptoolshost/integrationTests/workspaceDiagnostics.integration.test.ts index 50388aa0b7..bd1702fe2b 100644 --- a/test/lsptoolshost/integrationTests/workspaceDiagnostics.integration.test.ts +++ b/test/lsptoolshost/integrationTests/workspaceDiagnostics.integration.test.ts @@ -8,7 +8,8 @@ import { describe, test, expect, beforeAll, afterAll } from '@jest/globals'; import testAssetWorkspace from './testAssets/testAssetWorkspace'; import { AnalysisSetting } from '../../../src/lsptoolshost/diagnostics/buildDiagnosticsService'; import { getCode, setDiagnosticSettings, waitForExpectedDiagnostics } from './diagnosticsHelpers'; -import { activateCSharpExtension, describeIfCSharp } from './integrationHelpers'; +import { activateCSharpExtension } from './integrationHelpers'; +import { describeIfCSharp } from '../../jestHelpers'; // Restarting the server is required for these tests, but not supported with C# Dev Kit. describeIfCSharp(`Workspace Diagnostic Tests`, () => { diff --git a/test/omnisharp/omnisharpIntegrationTests/index.ts b/test/omnisharp/omnisharpIntegrationTests/index.ts index ac2e6a90a2..bb8b7b3372 100644 --- a/test/omnisharp/omnisharpIntegrationTests/index.ts +++ b/test/omnisharp/omnisharpIntegrationTests/index.ts @@ -9,5 +9,9 @@ import { jestIntegrationTestProjectName } from './jest.config'; export async function run() { process.env.RUNNING_INTEGRATION_TESTS = 'true'; - await runIntegrationTests(jestIntegrationTestProjectName); + const results = await runIntegrationTests(jestIntegrationTestProjectName); + + // Explicitly exit the process - VSCode likes to write a bunch of cancellation errors to the console after this + // which make it look like the tests always fail. We're done with the tests at this point, so just exit. + process.exit(results.success ? 0 : 1); } diff --git a/test/razor/razorIntegrationTests/index.ts b/test/razor/razorIntegrationTests/index.ts index ac2e6a90a2..bb8b7b3372 100644 --- a/test/razor/razorIntegrationTests/index.ts +++ b/test/razor/razorIntegrationTests/index.ts @@ -9,5 +9,9 @@ import { jestIntegrationTestProjectName } from './jest.config'; export async function run() { process.env.RUNNING_INTEGRATION_TESTS = 'true'; - await runIntegrationTests(jestIntegrationTestProjectName); + const results = await runIntegrationTests(jestIntegrationTestProjectName); + + // Explicitly exit the process - VSCode likes to write a bunch of cancellation errors to the console after this + // which make it look like the tests always fail. We're done with the tests at this point, so just exit. + process.exit(results.success ? 0 : 1); } diff --git a/test/razor/razorIntegrationTests/reference.integration.test.ts b/test/razor/razorIntegrationTests/reference.integration.test.ts index 5a15c3794d..4ed7d72f34 100644 --- a/test/razor/razorIntegrationTests/reference.integration.test.ts +++ b/test/razor/razorIntegrationTests/reference.integration.test.ts @@ -8,6 +8,7 @@ import * as vscode from 'vscode'; import { beforeAll, afterAll, test, expect, beforeEach, describe } from '@jest/globals'; import testAssetWorkspace from './testAssets/testAssetWorkspace'; import * as integrationHelpers from '../../lsptoolshost/integrationTests/integrationHelpers'; +import * as jestHelpers from '../../jestHelpers'; describe(`Razor References ${testAssetWorkspace.description}`, function () { beforeAll(async function () { @@ -151,9 +152,9 @@ describe(`Razor References ${testAssetWorkspace.description}`, function () { const razorFile = integrationHelpers.getFilePath(path.join('Pages', 'References.razor')); const csharpFile = integrationHelpers.getFilePath(path.join('Pages', 'References.razor.cs')); - integrationHelpers.expectPath(razorFile, sortedLocations[0].uri); - integrationHelpers.expectPath(csharpFile, sortedLocations[1].uri); - integrationHelpers.expectPath(csharpFile, sortedLocations[2].uri); + jestHelpers.expectPath(razorFile, sortedLocations[0].uri); + jestHelpers.expectPath(csharpFile, sortedLocations[1].uri); + jestHelpers.expectPath(csharpFile, sortedLocations[2].uri); } ); }); diff --git a/test/runIntegrationTests.ts b/test/runIntegrationTests.ts index 7acedc7156..697ad6a65b 100644 --- a/test/runIntegrationTests.ts +++ b/test/runIntegrationTests.ts @@ -31,11 +31,5 @@ export async function runIntegrationTests(projectName: string) { const { results } = await jest.runCLI(jestConfig, [projectName]); - if (!results.success) { - console.log('Tests failed.'); - } - - // Explicitly exit the process - VSCode likes to write a bunch of cancellation errors to the console after this - // which make it look like the tests always fail. We're done with the tests at this point, so just exit. - process.exit(results.success ? 0 : 1); + return results; } diff --git a/test/untrustedWorkspace/integrationTests/index.ts b/test/untrustedWorkspace/integrationTests/index.ts index ac2e6a90a2..bb8b7b3372 100644 --- a/test/untrustedWorkspace/integrationTests/index.ts +++ b/test/untrustedWorkspace/integrationTests/index.ts @@ -9,5 +9,9 @@ import { jestIntegrationTestProjectName } from './jest.config'; export async function run() { process.env.RUNNING_INTEGRATION_TESTS = 'true'; - await runIntegrationTests(jestIntegrationTestProjectName); + const results = await runIntegrationTests(jestIntegrationTestProjectName); + + // Explicitly exit the process - VSCode likes to write a bunch of cancellation errors to the console after this + // which make it look like the tests always fail. We're done with the tests at this point, so just exit. + process.exit(results.success ? 0 : 1); }