This repository has been archived by the owner on Jul 15, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 646
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test/gopls: initial test setup for gopls and hover tests (#3157)
This CL includes only HoverProvider tests, that use the same scenario as the hover provider tests with godoc and gogetdoc in integration/extension.test.ts. Gopls test setup is complicated because gopls currently does not work in a single-file mode nor support mono repos with multiple modules. VS Code in test environment does not support dynamic registration of workspace folders during testing. So, the workspace folder needs to be specified before the extension host launches (by vscode-test.runTests or launch.json extensionHost type tasks). As a workaround, I use an empty directory and start the extension host with the empty directory for a testing. Then, let the test populate the directory with necessary files. This is not ideal; file copy is slow and the use of the single workspace directory prevents parallelized testing. Commiting an empty directory to use as a scratch space is not ideal either, but no worse than what vscode dev in my opinion. https://github.com/microsoft/vscode/blob/master/.vscode/launch.json I also considered shelling out the extension host launch after creating a temp directory and using it as a workspace directory. That may work inside test/runTest.ts, but complicates debugging when sarting the test using launch.json. Also passes '--user-data-dir' flag to prevent the tests from using my personal vscode settings and interfere tests. Change-Id: I7b064441720da5c89833afab35e8273de808cfad
- Loading branch information
Showing
6 changed files
with
227 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
import * as assert from 'assert'; | ||
import cp = require('child_process'); | ||
import * as fs from 'fs-extra'; | ||
import * as path from 'path'; | ||
import * as vscode from 'vscode'; | ||
import { updateGoPathGoRootFromConfig } from '../../src/goInstallTools'; | ||
import { extensionId } from '../../src/telemetry'; | ||
import { getCurrentGoPath } from '../../src/util'; | ||
|
||
// Env is a collection of test related variables | ||
// that define the test environment such as vscode workspace. | ||
class Env { | ||
|
||
// Currently gopls requires a workspace and does not work in a single-file mode. | ||
// Code in test environment does not support dynamically adding folders. | ||
// tslint:disable-next-line:max-line-length | ||
// https://github.com/microsoft/vscode/blob/890f62dfd9f3e70198931f788c5c332b3e8b7ad7/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts#L281 | ||
// | ||
// So, when we start the gopls tests, we start the test extension host with a | ||
// dummy workspace, ${projectDir}/test/gopls/testfixtures/src/workspace | ||
// (see test/runTest.ts and launch.json). | ||
// Then copy necessary files to the workspace using Env.reset() from the | ||
// fixturesRoot directory. | ||
public workspaceDir: string; | ||
public fixturesRoot: string; | ||
|
||
public extension: vscode.Extension<any>; | ||
|
||
constructor(projectDir: string) { | ||
if (!projectDir) { | ||
assert.fail('project directory cannot be determined'); | ||
} | ||
this.workspaceDir = path.resolve(projectDir, 'test/gopls/testfixtures/src/workspace'); | ||
this.fixturesRoot = path.resolve(projectDir, 'test/fixtures'); | ||
this.extension = vscode.extensions.getExtension(extensionId); | ||
|
||
// Ensure the vscode extension host is configured as expected. | ||
const workspaceFolder = path.resolve(vscode.workspace.workspaceFolders[0].uri.fsPath); | ||
if (this.workspaceDir !== workspaceFolder) { | ||
assert.fail(`specified workspaceDir: ${this.workspaceDir} does not match the workspace folder: ${workspaceFolder}`); | ||
} | ||
} | ||
|
||
public async setup() { | ||
const wscfg = vscode.workspace.getConfiguration('go'); | ||
if (!wscfg.get('useLanguageServer')) { | ||
wscfg.update('useLanguageServer', true, vscode.ConfigurationTarget.Workspace); | ||
} | ||
|
||
await this.reset(); | ||
await this.extension.activate(); | ||
await sleep(2000); // allow extension host + gopls to start. | ||
} | ||
|
||
public async reset(fixtureDirName?: string) { // name of the fixtures subdirectory to use. | ||
try { | ||
// clean everything except the .gitignore file | ||
// needed to keep the empty directory in vcs. | ||
await fs.readdir(this.workspaceDir).then((files) => { | ||
return Promise.all( | ||
files.filter((filename) => filename !== '.gitignore').map((file) => { | ||
fs.remove(path.resolve(this.workspaceDir, file)); | ||
})); | ||
}); | ||
|
||
if (!fixtureDirName) { | ||
return; | ||
} | ||
const src = path.resolve(this.fixturesRoot, fixtureDirName); | ||
const dst = this.workspaceDir; | ||
await fs.copy(src, dst, { recursive: true }); | ||
} catch (err) { | ||
assert.fail(err); | ||
} | ||
} | ||
|
||
// openDoc opens the file in the workspace with the given path (paths | ||
// are the path elements of a file). | ||
public async openDoc(...paths: string[]) { | ||
const uri = vscode.Uri.file(path.resolve(this.workspaceDir, ...paths)); | ||
const doc = await vscode.workspace.openTextDocument(uri); | ||
return { uri, doc }; | ||
} | ||
} | ||
|
||
async function sleep(ms: number) { | ||
return new Promise((resolve) => setTimeout(resolve, ms)); | ||
} | ||
|
||
suite('Go Extension Tests With Gopls', function() { | ||
this.timeout(1000000); | ||
const projectDir = path.join(__dirname, '..', '..', '..'); | ||
const env = new Env(projectDir); | ||
|
||
suiteSetup(async () => { await env.setup(); }); | ||
suiteTeardown(async () => { await env.reset(); }); | ||
|
||
test('HoverProvider', async () => { | ||
await env.reset('gogetdocTestData'); | ||
const { uri, doc } = await env.openDoc('test.go'); | ||
|
||
// TODO(hyangah): find a way to wait for the language server to complete processing. | ||
|
||
const testCases: [string, vscode.Position, string | null, string | null][] = [ | ||
// [new vscode.Position(3,3), '/usr/local/go/src/fmt'], | ||
['keyword', new vscode.Position(0, 3), null, null], // keyword | ||
['inside a string', new vscode.Position(23, 14), null, null], // inside a string | ||
['just a }', new vscode.Position(20, 0), null, null], // just a } | ||
['inside a number', new vscode.Position(28, 16), null, null], // inside a number | ||
['func main()', new vscode.Position(22, 5), 'func main()', null], | ||
['import "math"', new vscode.Position(40, 23), 'package math', '`math` on'], | ||
['func Println()', new vscode.Position(19, 6), 'func fmt.Println(a ...interface{}) (n int, err error)', 'Println formats '], | ||
['func print()', new vscode.Position(23, 4), 'func print(txt string)', 'This is an unexported function '] | ||
]; | ||
|
||
const promises = testCases.map(async ([name, position, expectedSignature, expectedDoc]) => { | ||
const hovers = await vscode.commands.executeCommand( | ||
'vscode.executeHoverProvider', uri, position) as vscode.Hover[]; | ||
|
||
if (expectedSignature === null && expectedDoc === null) { | ||
assert.equal(hovers.length, 0, `check hovers over ${name} failed: unexpected non-empty hover message.`); | ||
return; | ||
} | ||
|
||
const hover = hovers[0]; | ||
assert.equal(hover.contents.length, 1, `check hovers over ${name} failed: unexpected number of hover messages.`); | ||
const gotMessage = (<vscode.MarkdownString>hover.contents[0]).value; | ||
assert.ok( | ||
gotMessage.includes('```go\n' + expectedSignature + '\n```') | ||
&& (!expectedDoc || gotMessage.includes(expectedDoc)), | ||
`check hovers over ${name} failed: got ${gotMessage}`); | ||
}); | ||
return Promise.all(promises); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
/*--------------------------------------------------------- | ||
* Copyright (C) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See License.txt in the project root for license information. | ||
*--------------------------------------------------------*/ | ||
import * as glob from 'glob'; | ||
import * as Mocha from 'mocha'; | ||
import * as path from 'path'; | ||
export function run(): Promise<void> { | ||
// Create the mocha test | ||
const mocha = new Mocha({ | ||
ui: 'tdd' | ||
}); | ||
mocha.useColors(true); | ||
|
||
const testsRoot = path.resolve(__dirname, '..'); | ||
|
||
return new Promise((c, e) => { | ||
glob('gopls/**.test.js', { cwd: testsRoot }, (err, files) => { | ||
if (err) { | ||
return e(err); | ||
} | ||
|
||
// Add files to the test suite | ||
files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f))); | ||
|
||
try { | ||
// Run the mocha test | ||
mocha.run((failures) => { | ||
if (failures > 0) { | ||
e(new Error(`${failures} tests failed.`)); | ||
} else { | ||
c(); | ||
} | ||
}); | ||
} catch (err) { | ||
e(err); | ||
} | ||
}); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
* | ||
!.gitignore |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,46 @@ | ||
import * as fs from 'fs-extra'; | ||
import * as path from 'path'; | ||
|
||
import { runTests } from 'vscode-test'; | ||
import { extensionId } from '../src/telemetry'; | ||
|
||
async function main() { | ||
try { | ||
// The folder containing the Extension Manifest package.json | ||
// Passed to `--extensionDevelopmentPath` | ||
const extensionDevelopmentPath = path.resolve(__dirname, '../../'); | ||
const extensionDevelopmentPath = path.resolve(__dirname, '../../'); | ||
|
||
try { | ||
// The path to the extension test script | ||
// Passed to --extensionTestsPath | ||
const extensionTestsPath = path.resolve(__dirname, './integration/index'); | ||
|
||
// Download VS Code, unzip it and run the integration test | ||
await runTests({ extensionDevelopmentPath, extensionTestsPath }); | ||
} catch (err) { | ||
console.error('Failed to run tests'); | ||
console.error('Failed to run integration tests' + err); | ||
process.exit(1); | ||
} | ||
|
||
// Integration tests using gopls. | ||
try { | ||
// Currently gopls requires a workspace. Code in test environment does not support | ||
// dynamically adding folders. | ||
// tslint:disable-next-line:max-line-length | ||
// https://github.com/microsoft/vscode/blob/890f62dfd9f3e70198931f788c5c332b3e8b7ad7/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts#L281 | ||
// So, we start the test extension host with a dummy workspace (test/gopls/testfixtures/src/workspace) | ||
// and copy necessary files to the workspace. | ||
const ws = path.resolve(extensionDevelopmentPath, 'test/gopls/testfixtures/src/workspace'); | ||
|
||
await runTests({ | ||
extensionDevelopmentPath, | ||
extensionTestsPath: path.resolve(__dirname, './gopls/index'), | ||
launchArgs: [ | ||
'--disable-extensions', // disable all other extensions | ||
ws // dummy workspace to start with | ||
], | ||
}); | ||
} catch (err) { | ||
console.error('Failed to run gopls tests' + err); | ||
} | ||
} | ||
|
||
main(); |