| /*--------------------------------------------------------- |
| * Copyright (C) Microsoft Corporation. All rights reserved. |
| * Licensed under the MIT License. See License.txt in the project root for license information. |
| *--------------------------------------------------------*/ |
| |
| import * as assert from 'assert'; |
| import * as fs from 'fs-extra'; |
| import * as path from 'path'; |
| import * as vscode from 'vscode'; |
| import { FilePatch, getEdits, getEditsFromUnifiedDiffStr } from '../../src/diffUtils'; |
| import { check } from '../../src/goCheck'; |
| import { GoDefinitionProvider } from '../../src/goDeclaration'; |
| import { GoHoverProvider } from '../../src/goExtraInfo'; |
| import { runFillStruct } from '../../src/goFillStruct'; |
| import { generateTestCurrentFile, generateTestCurrentFunction, generateTestCurrentPackage } from '../../src/goGenerateTests'; |
| import { getTextEditForAddImport, listPackages } from '../../src/goImport'; |
| import { documentSymbols, GoDocumentSymbolProvider, GoOutlineImportsOptions } from '../../src/goOutline'; |
| import { getAllPackages } from '../../src/goPackages'; |
| import { goPlay } from '../../src/goPlayground'; |
| import { GoSignatureHelpProvider } from '../../src/goSignature'; |
| import { GoCompletionItemProvider } from '../../src/goSuggest'; |
| import { getWorkspaceSymbols } from '../../src/goSymbol'; |
| import { testCurrentFile } from '../../src/goTest'; |
| import { getBinPath, getCurrentGoPath, getGoVersion, getImportPath, getToolsGopath, ICheckResult, isVendorSupported } from '../../src/util'; |
| import cp = require('child_process'); |
| |
| suite('Go Extension Tests', () => { |
| const gopath = getCurrentGoPath(); |
| if (!gopath) { |
| assert.ok(gopath, 'Cannot run tests if GOPATH is not set as environment variable'); |
| return; |
| } |
| |
| const repoPath = path.join(gopath, 'src', 'test'); |
| const fixturePath = path.join(repoPath, 'testfixture'); |
| const fixtureSourcePath = path.join(__dirname, '..', '..', '..', 'test', 'fixtures'); |
| const generateTestsSourcePath = path.join(repoPath, 'generatetests'); |
| const generateFunctionTestSourcePath = path.join(repoPath, 'generatefunctiontest'); |
| const generatePackageTestSourcePath = path.join(repoPath, 'generatePackagetest'); |
| const testPath = path.join(__dirname, 'tests'); |
| const toolsGopath = getToolsGopath() || getCurrentGoPath(); |
| |
| suiteSetup(() => { |
| |
| fs.removeSync(repoPath); |
| fs.removeSync(testPath); |
| fs.copySync(path.join(fixtureSourcePath, 'baseTest', 'test.go'), path.join(fixturePath, 'baseTest', 'test.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'baseTest', 'sample_test.go'), path.join(fixturePath, 'baseTest', 'sample_test.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'errorsTest', 'errors.go'), path.join(fixturePath, 'errorsTest', 'errors.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'gogetdocTestData', 'test.go'), path.join(fixturePath, 'gogetdocTestData', 'test.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'generatetests', 'generatetests.go'), path.join(generateTestsSourcePath, 'generatetests.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'generatetests', 'generatetests.go'), path.join(generateFunctionTestSourcePath, 'generatetests.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'generatetests', 'generatetests.go'), path.join(generatePackageTestSourcePath, 'generatetests.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'diffTestData', 'file1.go'), path.join(fixturePath, 'diffTest1Data', 'file1.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'diffTestData', 'file2.go'), path.join(fixturePath, 'diffTest1Data', 'file2.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'diffTestData', 'file1.go'), path.join(fixturePath, 'diffTest2Data', 'file1.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'diffTestData', 'file2.go'), path.join(fixturePath, 'diffTest2Data', 'file2.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'linterTest', 'linter_1.go'), path.join(fixturePath, 'linterTest', 'linter_1.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'linterTest', 'linter_2.go'), path.join(fixturePath, 'linterTest', 'linter_2.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'errorsTest', 'errors.go'), path.join(testPath, 'errorsTest', 'errors.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'linterTest', 'linter_1.go'), path.join(testPath, 'linterTest', 'linter_1.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'linterTest', 'linter_2.go'), path.join(testPath, 'linterTest', 'linter_2.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'buildTags', 'hello.go'), path.join(fixturePath, 'buildTags', 'hello.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'testTags', 'hello_test.go'), path.join(fixturePath, 'testTags', 'hello_test.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'completions', 'unimportedPkgs.go'), path.join(fixturePath, 'completions', 'unimportedPkgs.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'completions', 'unimportedMultiplePkgs.go'), path.join(fixturePath, 'completions', 'unimportedMultiplePkgs.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'completions', 'snippets.go'), path.join(fixturePath, 'completions', 'snippets.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'completions', 'nosnippets.go'), path.join(fixturePath, 'completions', 'nosnippets.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'completions', 'exportedMemberDocs.go'), path.join(fixturePath, 'completions', 'exportedMemberDocs.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'importTest', 'noimports.go'), path.join(fixturePath, 'importTest', 'noimports.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'importTest', 'groupImports.go'), path.join(fixturePath, 'importTest', 'groupImports.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'importTest', 'singleImports.go'), path.join(fixturePath, 'importTest', 'singleImports.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'fillStruct', 'input_1.go'), path.join(fixturePath, 'fillStruct', 'input_1.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'fillStruct', 'golden_1.go'), path.join(fixturePath, 'fillStruct', 'golden_1.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'fillStruct', 'input_2.go'), path.join(fixturePath, 'fillStruct', 'input_2.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'fillStruct', 'golden_2.go'), path.join(fixturePath, 'fillStruct', 'golden_2.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'fillStruct', 'input_2.go'), path.join(fixturePath, 'fillStruct', 'input_3.go')); |
| fs.copySync(path.join(fixtureSourcePath, 'outlineTest', 'test.go'), path.join(fixturePath, 'outlineTest', 'test.go')); |
| }); |
| |
| suiteTeardown(() => { |
| fs.removeSync(repoPath); |
| fs.removeSync(testPath); |
| }); |
| |
| async function testDefinitionProvider(goConfig: vscode.WorkspaceConfiguration): Promise<any> { |
| const provider = new GoDefinitionProvider(goConfig); |
| const uri = vscode.Uri.file(path.join(fixturePath, 'baseTest', 'test.go')); |
| const position = new vscode.Position(10, 3); |
| try { |
| const textDocument = await vscode.workspace.openTextDocument(uri); |
| const definitionInfo = await provider.provideDefinition(textDocument, position, null); |
| assert.equal(definitionInfo.uri.path.toLowerCase(), uri.path.toLowerCase(), `${definitionInfo.uri.path} is not the same as ${uri.path}`); |
| assert.equal(definitionInfo.range.start.line, 6); |
| assert.equal(definitionInfo.range.start.character, 5); |
| } catch (err) { |
| assert.ok(false, `error in OpenTextDocument ${err}`); |
| return Promise.reject(err); |
| } |
| } |
| |
| async function testSignatureHelpProvider(goConfig: vscode.WorkspaceConfiguration, testCases: [vscode.Position, string, string, string[]][]): Promise<any> { |
| const provider = new GoSignatureHelpProvider(goConfig); |
| const uri = vscode.Uri.file(path.join(fixturePath, 'gogetdocTestData', 'test.go')); |
| try { |
| const textDocument = await vscode.workspace.openTextDocument(uri); |
| const promises = testCases.map(([position, expected, expectedDoc, expectedParams]) => provider.provideSignatureHelp(textDocument, position, null).then(sigHelp => { |
| assert.ok(sigHelp, `No signature for gogetdocTestData/test.go:${position.line + 1}:${position.character + 1}`); |
| assert.equal(sigHelp.signatures.length, 1, 'unexpected number of overloads'); |
| assert.equal(sigHelp.signatures[0].label, expected); |
| assert.equal(sigHelp.signatures[0].documentation, expectedDoc); |
| assert.equal(sigHelp.signatures[0].parameters.length, expectedParams.length); |
| for (let i = 0; i < expectedParams.length; i++) { |
| assert.equal(sigHelp.signatures[0].parameters[i].label, expectedParams[i]); |
| } |
| })); |
| return Promise.all(promises); |
| } catch (err) { |
| assert.ok(false, `error in OpenTextDocument ${err}`); |
| return Promise.reject(err); |
| } |
| } |
| |
| async function testHoverProvider(goConfig: vscode.WorkspaceConfiguration, testCases: [vscode.Position, string, string][]): Promise<any> { |
| const provider = new GoHoverProvider(goConfig); |
| const uri = vscode.Uri.file(path.join(fixturePath, 'gogetdocTestData', 'test.go')); |
| try { |
| const textDocument = await vscode.workspace.openTextDocument(uri); |
| const promises = testCases.map(([position, expectedSignature, expectedDocumentation]) => provider.provideHover(textDocument, position, null).then(res => { |
| if (expectedSignature === null && expectedDocumentation === null) { |
| assert.equal(res, null); |
| return; |
| } |
| let expectedHover = '\n```go\n' + expectedSignature + '\n```\n'; |
| if (expectedDocumentation != null) { |
| expectedHover += expectedDocumentation; |
| } |
| assert.equal(res.contents.length, 1); |
| assert.equal((<vscode.MarkdownString>res.contents[0]).value, expectedHover); |
| })); |
| return Promise.all(promises); |
| } catch (err) { |
| assert.ok(false, `error in OpenTextDocument ${err}`); |
| return Promise.reject(err); |
| } |
| } |
| |
| test('Test Definition Provider using godoc', (done) => { |
| const config = Object.create(vscode.workspace.getConfiguration('go'), { |
| 'docsTool': { value: 'godoc' } |
| }); |
| testDefinitionProvider(config).then(() => done(), done); |
| }); |
| |
| test('Test Definition Provider using gogetdoc', (done) => { |
| const gogetdocPath = getBinPath('gogetdoc'); |
| if (gogetdocPath === 'gogetdoc') { |
| return done(); |
| } |
| const config = Object.create(vscode.workspace.getConfiguration('go'), { |
| 'docsTool': { value: 'gogetdoc' } |
| }); |
| testDefinitionProvider(config).then(() => done(), done); |
| }).timeout(10000); |
| |
| test('Test SignatureHelp Provider using godoc', (done) => { |
| const printlnDoc = `Println formats using the default formats for its operands and writes to |
| standard output. Spaces are always added between operands and a newline is |
| appended. It returns the number of bytes written and any write error |
| encountered. |
| `; |
| |
| const testCases: [vscode.Position, string, string, string[]][] = [ |
| [new vscode.Position(19, 13), 'Println(a ...interface{}) (n int, err error)', printlnDoc, ['a ...interface{}']], |
| [new vscode.Position(23, 7), 'print(txt string)', 'This is an unexported function so couldn\'t get this comment on hover :( Not\nanymore!!\n', ['txt string']], |
| [new vscode.Position(41, 19), 'Hello(s string, exclaim bool) string', 'Hello is a method on the struct ABC. Will signature help understand this\ncorrectly\n', ['s string', 'exclaim bool']], |
| [new vscode.Position(41, 47), 'EmptyLine(s string) string', 'EmptyLine has docs\n\nwith a blank line in the middle\n', ['s string']] |
| ]; |
| const config = Object.create(vscode.workspace.getConfiguration('go'), { |
| 'docsTool': { value: 'godoc' } |
| }); |
| testSignatureHelpProvider(config, testCases).then(() => done(), done); |
| }); |
| |
| test('Test SignatureHelp Provider using gogetdoc', (done) => { |
| const gogetdocPath = getBinPath('gogetdoc'); |
| if (gogetdocPath === 'gogetdoc') { |
| return done(); |
| } |
| |
| const printlnDoc = `Println formats using the default formats for its operands and writes to standard output. |
| Spaces are always added between operands and a newline is appended. |
| It returns the number of bytes written and any write error encountered. |
| `; |
| const testCases: [vscode.Position, string, string, string[]][] = [ |
| [new vscode.Position(19, 13), 'Println(a ...interface{}) (n int, err error)', printlnDoc, ['a ...interface{}']], |
| [new vscode.Position(23, 7), 'print(txt string)', 'This is an unexported function so couldn\'t get this comment on hover :(\nNot anymore!!\n', ['txt string']], |
| [new vscode.Position(41, 19), 'Hello(s string, exclaim bool) string', 'Hello is a method on the struct ABC. Will signature help understand this correctly\n', ['s string', 'exclaim bool']], |
| [new vscode.Position(41, 47), 'EmptyLine(s string) string', 'EmptyLine has docs\n\nwith a blank line in the middle\n', ['s string']] |
| ]; |
| const config = Object.create(vscode.workspace.getConfiguration('go'), { |
| 'docsTool': { value: 'gogetdoc' } |
| }); |
| testSignatureHelpProvider(config, testCases).then(() => done(), done); |
| }).timeout(10000); |
| |
| test('Test Hover Provider using godoc', (done) => { |
| const printlnDoc = `Println formats using the default formats for its operands and writes to |
| standard output. Spaces are always added between operands and a newline is |
| appended. It returns the number of bytes written and any write error |
| encountered. |
| `; |
| const testCases: [vscode.Position, string, string][] = [ |
| // [new vscode.Position(3,3), '/usr/local/go/src/fmt'], |
| [new vscode.Position(0, 3), null, null], // keyword |
| [new vscode.Position(23, 14), null, null], // inside a string |
| [new vscode.Position(20, 0), null, null], // just a } |
| [new vscode.Position(28, 16), null, null], // inside a number |
| [new vscode.Position(22, 5), 'main func()', '\n'], |
| [new vscode.Position(40, 23), 'import (math "math")', null], |
| [new vscode.Position(19, 6), 'Println func(a ...interface{}) (n int, err error)', printlnDoc], |
| [new vscode.Position(23, 4), 'print func(txt string)', 'This is an unexported function so couldn\'t get this comment on hover :( Not\nanymore!!\n'] |
| ]; |
| const config = Object.create(vscode.workspace.getConfiguration('go'), { |
| 'docsTool': { value: 'godoc' } |
| }); |
| testHoverProvider(config, testCases).then(() => done(), done); |
| }).timeout(10000); |
| |
| test('Test Hover Provider using gogetdoc', (done) => { |
| const gogetdocPath = getBinPath('gogetdoc'); |
| if (gogetdocPath === 'gogetdoc') { |
| return done(); |
| } |
| |
| const printlnDoc = `Println formats using the default formats for its operands and writes to standard output. |
| Spaces are always added between operands and a newline is appended. |
| It returns the number of bytes written and any write error encountered. |
| `; |
| const testCases: [vscode.Position, string, string][] = [ |
| [new vscode.Position(0, 3), null, null], // keyword |
| [new vscode.Position(23, 11), null, null], // inside a string |
| [new vscode.Position(20, 0), null, null], // just a } |
| [new vscode.Position(28, 16), null, null], // inside a number |
| [new vscode.Position(22, 5), 'func main()', ''], |
| [new vscode.Position(23, 4), 'func print(txt string)', 'This is an unexported function so couldn\'t get this comment on hover :(\nNot anymore!!\n'], |
| [new vscode.Position(40, 23), 'package math', 'Package math provides basic constants and mathematical functions.\n\nThis package does not guarantee bit-identical results across architectures.\n'], |
| [new vscode.Position(19, 6), 'func Println(a ...interface{}) (n int, err error)', printlnDoc], |
| [new vscode.Position(27, 14), 'type ABC struct {\n a int\n b int\n c int\n}', 'ABC is a struct, you coudn\'t use Goto Definition or Hover info on this before\nNow you can due to gogetdoc and go doc\n'], |
| [new vscode.Position(28, 6), 'func IPv4Mask(a, b, c, d byte) IPMask', 'IPv4Mask returns the IP mask (in 4-byte form) of the\nIPv4 mask a.b.c.d.\n'] |
| ]; |
| const config = Object.create(vscode.workspace.getConfiguration('go'), { |
| 'docsTool': { value: 'gogetdoc' } |
| }); |
| testHoverProvider(config, testCases).then(() => done(), done); |
| }).timeout(10000); |
| |
| test('Error checking', (done) => { |
| const config = Object.create(vscode.workspace.getConfiguration('go'), { |
| 'vetOnSave': { value: 'package' }, |
| 'vetFlags': { value: ['-all'] }, |
| 'lintOnSave': { value: 'package' }, |
| 'lintTool': { value: 'golint' }, |
| 'lintFlags': { value: [] }, |
| 'buildOnSave': { value: 'package' }, |
| }); |
| const expected = [ |
| { line: 7, severity: 'warning', msg: 'exported function Print2 should have comment or be unexported' }, |
| { line: 11, severity: 'error', msg: 'undefined: prin' }, |
| ]; |
| getGoVersion().then(async version => { |
| const diagnostics = await check(vscode.Uri.file(path.join(fixturePath, 'errorsTest', 'errors.go')), config); |
| const sortedDiagnostics = ([] as ICheckResult[]) |
| .concat.apply([], diagnostics.map(x => x.errors)) |
| .sort((a: any, b: any) => a.line - b.line); |
| assert.equal(sortedDiagnostics.length > 0, true, `Failed to get linter results`); |
| const matchCount = expected.filter(expectedItem => { |
| return sortedDiagnostics.some((diag: any) => { |
| return expectedItem.line === diag.line |
| && expectedItem.severity === diag.severity |
| && expectedItem.msg === diag.msg; |
| }); |
| }); |
| assert.equal(matchCount.length >= expected.length, true, `Failed to match expected errors`); |
| }).then(() => done(), done); |
| }).timeout(10000); |
| |
| test('Test Generate unit tests skeleton for file', (done) => { |
| const gotestsPath = getBinPath('gotests'); |
| if (gotestsPath === 'gotests') { |
| return done(); |
| } |
| |
| getGoVersion().then(async version => { |
| const uri = vscode.Uri.file(path.join(generateTestsSourcePath, 'generatetests.go')); |
| const document = await vscode.workspace.openTextDocument(uri); |
| const editor = await vscode.window.showTextDocument(document); |
| const result = await generateTestCurrentFile(); |
| assert.equal(result, true); |
| await Promise.resolve(); |
| vscode.commands.executeCommand('workbench.action.closeActiveEditor'); |
| if (fs.existsSync(path.join(generateTestsSourcePath, 'generatetests_test.go'))) { |
| return Promise.resolve(); |
| } |
| else { |
| return Promise.reject('generatetests_test.go not found'); |
| } |
| }).then(() => done(), done); |
| }); |
| |
| test('Test Generate unit tests skeleton for a function', (done) => { |
| const gotestsPath = getBinPath('gotests'); |
| if (gotestsPath === 'gotests') { |
| return done(); |
| } |
| |
| getGoVersion().then(async version => { |
| const uri = vscode.Uri.file(path.join(generateFunctionTestSourcePath, 'generatetests.go')); |
| const document = await vscode.workspace.openTextDocument(uri); |
| const editor = await vscode.window.showTextDocument(document); |
| assert(vscode.window.activeTextEditor, 'No active editor'); |
| const selection = new vscode.Selection(5, 0, 6, 0); |
| editor.selection = selection; |
| const result = await generateTestCurrentFunction(); |
| assert.equal(result, true); |
| await Promise.resolve(); |
| vscode.commands.executeCommand('workbench.action.closeActiveEditor'); |
| if (fs.existsSync(path.join(generateTestsSourcePath, 'generatetests_test.go'))) { |
| return Promise.resolve(); |
| } |
| else { |
| return Promise.reject('generatetests_test.go not found'); |
| } |
| }).then(() => done(), done); |
| }); |
| |
| test('Test Generate unit tests skeleton for package', (done) => { |
| const gotestsPath = getBinPath('gotests'); |
| if (gotestsPath === 'gotests') { |
| return done(); |
| } |
| |
| getGoVersion().then(async version => { |
| const uri = vscode.Uri.file(path.join(generatePackageTestSourcePath, 'generatetests.go')); |
| const document = await vscode.workspace.openTextDocument(uri); |
| const editor = await vscode.window.showTextDocument(document); |
| const result = await generateTestCurrentPackage(); |
| assert.equal(result, true); |
| await Promise.resolve(); |
| vscode.commands.executeCommand('workbench.action.closeActiveEditor'); |
| if (fs.existsSync(path.join(generateTestsSourcePath, 'generatetests_test.go'))) { |
| return Promise.resolve(); |
| } |
| else { |
| return Promise.reject('generatetests_test.go not found'); |
| } |
| }).then(() => done(), done); |
| }); |
| |
| test('Test diffUtils.getEditsFromUnifiedDiffStr', (done) => { |
| const file1path = path.join(fixturePath, 'diffTest1Data', 'file1.go'); |
| const file2path = path.join(fixturePath, 'diffTest1Data', 'file2.go'); |
| const file1uri = vscode.Uri.file(file1path); |
| const file2contents = fs.readFileSync(file2path, 'utf8'); |
| |
| const diffPromise = new Promise((resolve, reject) => { |
| cp.exec(`diff -u ${file1path} ${file2path}`, (err, stdout, stderr) => { |
| const filePatches: FilePatch[] = getEditsFromUnifiedDiffStr(stdout); |
| |
| if (!filePatches && filePatches.length !== 1) { |
| assert.fail(null, null, 'Failed to get patches for the test file', ''); |
| return reject(); |
| } |
| |
| if (!filePatches[0].fileName) { |
| assert.fail(null, null, 'Failed to parse the file path from the diff output', ''); |
| return reject(); |
| } |
| |
| if (!filePatches[0].edits) { |
| assert.fail(null, null, 'Failed to parse edits from the diff output', ''); |
| return reject(); |
| } |
| resolve(filePatches); |
| }); |
| }); |
| |
| diffPromise.then(async (filePatches: any | FilePatch[]) => { |
| const textDocument = await vscode.workspace.openTextDocument(file1uri); |
| const editor = await vscode.window.showTextDocument(textDocument); |
| await editor.edit((editBuilder) => { |
| filePatches[0].edits.forEach((edit: any) => { |
| edit.applyUsingTextEditorEdit(editBuilder); |
| }); |
| }); |
| assert.equal(editor.document.getText(), file2contents); |
| return Promise.resolve(); |
| }).then(() => done(), done); |
| }); |
| |
| test('Test diffUtils.getEdits', (done) => { |
| const file1path = path.join(fixturePath, 'diffTest2Data', 'file1.go'); |
| const file2path = path.join(fixturePath, 'diffTest2Data', 'file2.go'); |
| const file1uri = vscode.Uri.file(file1path); |
| const file1contents = fs.readFileSync(file1path, 'utf8'); |
| const file2contents = fs.readFileSync(file2path, 'utf8'); |
| |
| const fileEdits = getEdits(file1path, file1contents, file2contents); |
| |
| if (!fileEdits) { |
| assert.fail(null, null, 'Failed to get patches for the test file', ''); |
| done(); |
| return; |
| } |
| |
| if (!fileEdits.fileName) { |
| assert.fail(null, null, 'Failed to parse the file path from the diff output', ''); |
| done(); |
| return; |
| } |
| |
| if (!fileEdits.edits) { |
| assert.fail(null, null, 'Failed to parse edits from the diff output', ''); |
| done(); |
| return; |
| } |
| |
| vscode.workspace.openTextDocument(file1uri).then(async (textDocument) => { |
| try { |
| const editor = await vscode.window.showTextDocument(textDocument); |
| await editor.edit((editBuilder) => { |
| fileEdits.edits.forEach(edit => { |
| edit.applyUsingTextEditorEdit(editBuilder); |
| }); |
| }); |
| assert.equal(editor.document.getText(), file2contents); |
| await Promise.resolve(); |
| return done(); |
| } catch (err) { |
| return done(err); |
| } |
| }); |
| }); |
| |
| test('Test Env Variables are passed to Tests', (done) => { |
| const config = Object.create(vscode.workspace.getConfiguration('go'), { |
| 'testEnvVars': { value: { 'dummyEnvVar': 'dummyEnvValue', 'dummyNonString': 1 } } |
| }); |
| |
| const uri = vscode.Uri.file(path.join(fixturePath, 'baseTest', 'sample_test.go')); |
| vscode.workspace.openTextDocument(uri).then(async document => { |
| const editor = await vscode.window.showTextDocument(document); |
| const result = await testCurrentFile(config, false, []); |
| assert.equal(result, true); |
| return Promise.resolve(); |
| }).then(() => done(), done); |
| }).timeout(10000); |
| |
| test('Test Outline', (done) => { |
| const uri = vscode.Uri.file(path.join(fixturePath, 'outlineTest', 'test.go')); |
| vscode.workspace.openTextDocument(uri).then(document => { |
| const options = { document, fileName: document.fileName, importsOption: GoOutlineImportsOptions.Include }; |
| |
| documentSymbols(options, null).then(outlines => { |
| const packageSymbols = outlines.filter((x: any) => x.kind === vscode.SymbolKind.Package); |
| const imports = outlines[0].children.filter((x: any) => x.kind === vscode.SymbolKind.Namespace); |
| const functions = outlines[0].children.filter((x: any) => x.kind === vscode.SymbolKind.Function); |
| |
| assert.equal(packageSymbols.length, 1); |
| assert.equal(packageSymbols[0].name, 'main'); |
| assert.equal(imports.length, 1); |
| assert.equal(imports[0].name, '"fmt"'); |
| assert.equal(functions.length, 2); |
| assert.equal(functions[0].name, 'print'); |
| assert.equal(functions[1].name, 'main'); |
| done(); |
| }, done); |
| }); |
| |
| }); |
| |
| test('Test Outline imports only', (done) => { |
| const uri = vscode.Uri.file(path.join(fixturePath, 'outlineTest', 'test.go')); |
| vscode.workspace.openTextDocument(uri).then(document => { |
| const options = { document, fileName: document.fileName, importsOption: GoOutlineImportsOptions.Only }; |
| |
| documentSymbols(options, null).then(outlines => { |
| const packageSymbols = outlines.filter(x => x.kind === vscode.SymbolKind.Package); |
| const imports = outlines[0].children.filter((x: any) => x.kind === vscode.SymbolKind.Namespace); |
| const functions = outlines[0].children.filter((x: any) => x.kind === vscode.SymbolKind.Function); |
| |
| assert.equal(packageSymbols.length, 1); |
| assert.equal(packageSymbols[0].name, 'main'); |
| assert.equal(imports.length, 1); |
| assert.equal(imports[0].name, '"fmt"'); |
| assert.equal(functions.length, 0); |
| done(); |
| }, done); |
| }); |
| }); |
| |
| test('Test Outline document symbols', (done) => { |
| const uri = vscode.Uri.file(path.join(fixturePath, 'outlineTest', 'test.go')); |
| vscode.workspace.openTextDocument(uri).then(document => { |
| new GoDocumentSymbolProvider().provideDocumentSymbols(document, null).then(outlines => { |
| const packages = outlines.filter(x => x.kind === vscode.SymbolKind.Package); |
| const variables = outlines[0].children.filter((x: any) => x.kind === vscode.SymbolKind.Variable); |
| const functions = outlines[0].children.filter((x: any) => x.kind === vscode.SymbolKind.Function); |
| const structs = outlines[0].children.filter((x: any) => x.kind === vscode.SymbolKind.Struct); |
| const interfaces = outlines[0].children.filter((x: any) => x.kind === vscode.SymbolKind.Interface); |
| |
| assert.equal(packages[0].name, 'main'); |
| assert.equal(variables.length, 0); |
| assert.equal(functions[0].name, 'print'); |
| assert.equal(functions[1].name, 'main'); |
| assert.equal(structs.length, 1); |
| assert.equal(structs[0].name, 'foo'); |
| assert.equal(interfaces.length, 1); |
| assert.equal(interfaces[0].name, 'circle'); |
| }); |
| }).then(() => done(), done); |
| }); |
| |
| test('Test listPackages', (done) => { |
| const uri = vscode.Uri.file(path.join(fixturePath, 'baseTest', 'test.go')); |
| vscode.workspace.openTextDocument(uri).then(document => vscode.window.showTextDocument(document) |
| .then(async editor => { |
| const includeImportedPkgs = await listPackages(false); |
| const excludeImportedPkgs = await listPackages(true); |
| assert.equal(includeImportedPkgs.indexOf('fmt') > -1, true); |
| assert.equal(excludeImportedPkgs.indexOf('fmt') > -1, false); |
| })).then(() => done(), done); |
| }); |
| |
| test('Replace vendor packages with relative path', (done) => { |
| // This test needs a go project that has vendor folder and vendor packages |
| // Since the Go extension takes a dependency on the godef tool at github.com/rogpeppe/godef |
| // which has vendor packages, we are using it here to test the "replace vendor packages with relative path" feature. |
| // If the extension ever stops depending on godef tool or if godef ever stops having vendor packages, then this test |
| // will fail and will have to be replaced with any other go project with vendor packages |
| |
| const vendorSupportPromise = isVendorSupported(); |
| const filePath = path.join(toolsGopath, 'src', 'github.com', 'rogpeppe', 'godef', 'go', 'ast', 'ast.go'); |
| const workDir = path.dirname(filePath); |
| const vendorPkgsFullPath = [ |
| 'github.com/rogpeppe/godef/vendor/9fans.net/go/acme', |
| 'github.com/rogpeppe/godef/vendor/9fans.net/go/plan9', |
| 'github.com/rogpeppe/godef/vendor/9fans.net/go/plan9/client' |
| ]; |
| const vendorPkgsRelativePath = [ |
| '9fans.net/go/acme', |
| '9fans.net/go/plan9', |
| '9fans.net/go/plan9/client' |
| ]; |
| |
| vendorSupportPromise.then(async (vendorSupport: boolean) => { |
| const gopkgsPromise = getAllPackages(workDir).then(pkgMap => { |
| const pkgs = Array.from(pkgMap.keys()).filter(p => pkgMap.get(p).name !== 'main'); |
| if (vendorSupport) { |
| vendorPkgsFullPath.forEach(pkg => { |
| assert.equal(pkgs.indexOf(pkg) > -1, true, `Package not found by goPkgs: ${pkg}`); |
| }); |
| vendorPkgsRelativePath.forEach(pkg => { |
| assert.equal(pkgs.indexOf(pkg), -1, `Relative path to vendor package ${pkg} should not be returned by gopkgs command`); |
| }); |
| } |
| return Promise.resolve(pkgs); |
| }); |
| |
| const listPkgPromise: Thenable<string[]> = vscode.workspace.openTextDocument(vscode.Uri.file(filePath)).then(async document => { |
| const editor = await vscode.window.showTextDocument(document); |
| const pkgs = await listPackages(); |
| if (vendorSupport) { |
| vendorPkgsRelativePath.forEach(pkg => { |
| assert.equal(pkgs.indexOf(pkg) > -1, true, `Relative path for vendor package ${pkg} not found`); |
| }); |
| vendorPkgsFullPath.forEach(pkg => { |
| assert.equal(pkgs.indexOf(pkg), -1, `Full path for vendor package ${pkg} should be shown by listPackages method`); |
| }); |
| } |
| return Promise.resolve(pkgs); |
| }); |
| |
| const values = await Promise.all<string[]>([gopkgsPromise, listPkgPromise]); |
| if (!vendorSupport) { |
| const originalPkgs = values[0].sort(); |
| const updatedPkgs = values[1].sort(); |
| assert.equal(originalPkgs.length, updatedPkgs.length); |
| for (let index = 0; index < originalPkgs.length; index++) { |
| assert.equal(updatedPkgs[index], originalPkgs[index]); |
| } |
| } |
| }).then(() => done(), done); |
| }); |
| |
| test('Vendor pkgs from other projects should not be allowed to import', (done) => { |
| // This test needs a go project that has vendor folder and vendor packages |
| // Since the Go extension takes a dependency on the godef tool at github.com/rogpeppe/godef |
| // which has vendor packages, we are using it here to test the "replace vendor packages with relative path" feature. |
| // If the extension ever stops depending on godef tool or if godef ever stops having vendor packages, then this test |
| // will fail and will have to be replaced with any other go project with vendor packages |
| |
| const vendorSupportPromise = isVendorSupported(); |
| const filePath = path.join(toolsGopath, 'src', 'github.com', 'ramya-rao-a', 'go-outline', 'main.go'); |
| const vendorPkgs = [ |
| 'github.com/rogpeppe/godef/vendor/9fans.net/go/acme', |
| 'github.com/rogpeppe/godef/vendor/9fans.net/go/plan9', |
| 'github.com/rogpeppe/godef/vendor/9fans.net/go/plan9/client' |
| ]; |
| |
| vendorSupportPromise.then((vendorSupport: boolean) => { |
| const gopkgsPromise = new Promise<void>((resolve, reject) => { |
| const cmd = cp.spawn(getBinPath('gopkgs'), ['-format', '{{.ImportPath}}'], { env: process.env }); |
| const chunks: any[] = []; |
| cmd.stdout.on('data', (d) => chunks.push(d)); |
| cmd.on('close', () => { |
| const pkgs = chunks.join('').split('\n').filter((pkg) => pkg).sort(); |
| if (vendorSupport) { |
| vendorPkgs.forEach(pkg => { |
| assert.equal(pkgs.indexOf(pkg) > -1, true, `Package not found by goPkgs: ${pkg}`); |
| }); |
| } |
| return resolve(); |
| }); |
| }); |
| |
| const listPkgPromise: Thenable<void> = vscode.workspace.openTextDocument(vscode.Uri.file(filePath)).then(async document => { |
| const editor = await vscode.window.showTextDocument(document); |
| const pkgs = await listPackages(); |
| if (vendorSupport) { |
| vendorPkgs.forEach(pkg => { |
| assert.equal(pkgs.indexOf(pkg), -1, `Vendor package ${pkg} should not be shown by listPackages method`); |
| }); |
| } |
| return Promise.resolve(); |
| }); |
| |
| return Promise.all<void>([gopkgsPromise, listPkgPromise]); |
| }).then(() => done(), done); |
| }); |
| |
| test('Workspace Symbols', () => { |
| // This test needs a go project that has vendor folder and vendor packages |
| // Since the Go extension takes a dependency on the godef tool at github.com/rogpeppe/godef |
| // which has vendor packages, we are using it here to test the "replace vendor packages with relative path" feature. |
| // If the extension ever stops depending on godef tool or if godef ever stops having vendor packages, then this test |
| // will fail and will have to be replaced with any other go project with vendor packages |
| |
| const workspacePath = path.join(toolsGopath, 'src', 'github.com', 'rogpeppe', 'godef'); |
| const configWithoutIgnoringFolders = Object.create(vscode.workspace.getConfiguration('go'), { |
| 'gotoSymbol': { |
| value: { |
| 'ignoreFolders': [] |
| } |
| } |
| }); |
| const configWithIgnoringFolders = Object.create(vscode.workspace.getConfiguration('go'), { |
| 'gotoSymbol': { |
| value: { |
| 'ignoreFolders': ['vendor'] |
| } |
| } |
| }); |
| const configWithIncludeGoroot = Object.create(vscode.workspace.getConfiguration('go'), { |
| 'gotoSymbol': { |
| value: { |
| 'includeGoroot': true |
| } |
| } |
| }); |
| const configWithoutIncludeGoroot = Object.create(vscode.workspace.getConfiguration('go'), { |
| 'gotoSymbol': { |
| value: { |
| 'includeGoroot': false |
| } |
| } |
| }); |
| |
| const withoutIgnoringFolders = getWorkspaceSymbols(workspacePath, 'WinInfo', null, configWithoutIgnoringFolders).then(results => { |
| assert.equal(results[0].name, 'WinInfo'); |
| assert.equal(results[0].path, path.join(workspacePath, 'vendor/9fans.net/go/acme/acme.go')); |
| }); |
| const withIgnoringFolders = getWorkspaceSymbols(workspacePath, 'WinInfo', null, configWithIgnoringFolders).then(results => { |
| assert.equal(results.length, 0); |
| }); |
| const withoutIncludingGoroot = getWorkspaceSymbols(workspacePath, 'Mutex', null, configWithoutIncludeGoroot).then(results => { |
| assert.equal(results.length, 0); |
| }); |
| const withIncludingGoroot = getWorkspaceSymbols(workspacePath, 'Mutex', null, configWithIncludeGoroot).then(results => { |
| assert(results.some(result => result.name === 'Mutex')); |
| }); |
| |
| return Promise.all([withIgnoringFolders, withoutIgnoringFolders, withIncludingGoroot, withoutIncludingGoroot]); |
| }).timeout(10000); |
| |
| test('Test Completion', (done) => { |
| const printlnDoc = `Println formats using the default formats for its operands and writes to |
| standard output. Spaces are always added between operands and a newline is |
| appended. It returns the number of bytes written and any write error |
| encountered. |
| `; |
| const provider = new GoCompletionItemProvider(); |
| const testCases: [vscode.Position, string, string, string][] = [ |
| [new vscode.Position(7, 4), 'fmt', 'fmt', null], |
| [new vscode.Position(7, 6), 'Println', 'func(a ...interface{}) (n int, err error)', printlnDoc] |
| ]; |
| const uri = vscode.Uri.file(path.join(fixturePath, 'baseTest', 'test.go')); |
| vscode.workspace.openTextDocument(uri).then(async (textDocument) => { |
| const editor = await vscode.window.showTextDocument(textDocument); |
| const promises = testCases.map(([position, expectedLabel, expectedDetail, expectedDoc]) => provider.provideCompletionItems(editor.document, position, null).then(async items => { |
| const item = items.items.find(x => x.label === expectedLabel); |
| assert.equal(!!item, true, 'missing expected item in completion list'); |
| assert.equal(item.detail, expectedDetail); |
| const resolvedItemResult: vscode.ProviderResult<vscode.CompletionItem> = provider.resolveCompletionItem(item, null); |
| if (!resolvedItemResult) { |
| return; |
| } |
| if (resolvedItemResult instanceof vscode.CompletionItem) { |
| if (resolvedItemResult.documentation) { |
| assert.equal((<vscode.MarkdownString>resolvedItemResult.documentation).value, expectedDoc); |
| } |
| return; |
| } |
| const resolvedItem = await resolvedItemResult; |
| if (resolvedItem) { |
| assert.equal((<vscode.MarkdownString>resolvedItem.documentation).value, expectedDoc); |
| } |
| })); |
| await Promise.all(promises); |
| vscode.commands.executeCommand('workbench.action.closeActiveEditor'); |
| return Promise.resolve(); |
| }, (err) => { |
| assert.ok(false, `error in OpenTextDocument ${err}`); |
| }).then(() => done(), done); |
| }); |
| |
| test('Test Completion Snippets For Functions', (done) => { |
| const provider = new GoCompletionItemProvider(); |
| const uri = vscode.Uri.file(path.join(fixturePath, 'completions', 'snippets.go')); |
| const testCases: [vscode.Position, string[]][] = [ |
| [new vscode.Position(5, 6), ['Print']] |
| ]; |
| const baseConfig = vscode.workspace.getConfiguration('go'); |
| vscode.workspace.openTextDocument(uri).then(async (textDocument) => { |
| const editor = await vscode.window.showTextDocument(textDocument); |
| const noFunctionSnippet = provider.provideCompletionItemsInternal(editor.document, new vscode.Position(9, 6), null, Object.create(baseConfig, { 'useCodeSnippetsOnFunctionSuggest': { value: false } })).then(items => { |
| items = items instanceof vscode.CompletionList ? items.items : items; |
| const item = items.find(x => x.label === 'Print'); |
| assert.equal(!item.insertText, true); |
| }); |
| const withFunctionSnippet = provider.provideCompletionItemsInternal(editor.document, new vscode.Position(9, 6), null, Object.create(baseConfig, { 'useCodeSnippetsOnFunctionSuggest': { value: true } })).then(items1 => { |
| items1 = items1 instanceof vscode.CompletionList ? items1.items : items1; |
| const item1 = items1.find(x => x.label === 'Print'); |
| assert.equal((<vscode.SnippetString>item1.insertText).value, 'Print(${1:a ...interface{\\}})'); |
| }); |
| const withFunctionSnippetNotype = provider.provideCompletionItemsInternal(editor.document, new vscode.Position(9, 6), null, Object.create(baseConfig, { 'useCodeSnippetsOnFunctionSuggestWithoutType': { value: true } })).then(items2 => { |
| items2 = items2 instanceof vscode.CompletionList ? items2.items : items2; |
| const item2 = items2.find(x => x.label === 'Print'); |
| assert.equal((<vscode.SnippetString>item2.insertText).value, 'Print(${1:a})'); |
| }); |
| const noFunctionAsVarSnippet = provider.provideCompletionItemsInternal(editor.document, new vscode.Position(11, 3), null, Object.create(baseConfig, { 'useCodeSnippetsOnFunctionSuggest': { value: false } })).then(items3 => { |
| items3 = items3 instanceof vscode.CompletionList ? items3.items : items3; |
| const item3 = items3.find(x => x.label === 'funcAsVariable'); |
| assert.equal(!item3.insertText, true); |
| }); |
| const withFunctionAsVarSnippet = provider.provideCompletionItemsInternal(editor.document, new vscode.Position(11, 3), null, Object.create(baseConfig, { 'useCodeSnippetsOnFunctionSuggest': { value: true } })).then(items4 => { |
| items4 = items4 instanceof vscode.CompletionList ? items4.items : items4; |
| const item4 = items4.find(x => x.label === 'funcAsVariable'); |
| assert.equal((<vscode.SnippetString>item4.insertText).value, 'funcAsVariable(${1:k string})'); |
| }); |
| const withFunctionAsVarSnippetNoType = provider.provideCompletionItemsInternal(editor.document, new vscode.Position(11, 3), null, Object.create(baseConfig, { 'useCodeSnippetsOnFunctionSuggestWithoutType': { value: true } })).then(items5 => { |
| items5 = items5 instanceof vscode.CompletionList ? items5.items : items5; |
| const item5 = items5.find(x => x.label === 'funcAsVariable'); |
| assert.equal((<vscode.SnippetString>item5.insertText).value, 'funcAsVariable(${1:k})'); |
| }); |
| const noFunctionAsTypeSnippet = provider.provideCompletionItemsInternal(editor.document, new vscode.Position(14, 0), null, Object.create(baseConfig, { 'useCodeSnippetsOnFunctionSuggest': { value: false } })).then(items6 => { |
| items6 = items6 instanceof vscode.CompletionList ? items6.items : items6; |
| const item1 = items6.find(x => x.label === 'HandlerFunc'); |
| const item2 = items6.find(x => x.label === 'HandlerFuncWithArgNames'); |
| const item3 = items6.find(x => x.label === 'HandlerFuncNoReturnType'); |
| assert.equal(!item1.insertText, true); |
| assert.equal(!item2.insertText, true); |
| assert.equal(!item3.insertText, true); |
| }); |
| const withFunctionAsTypeSnippet = provider.provideCompletionItemsInternal(editor.document, new vscode.Position(14, 0), null, Object.create(baseConfig, { 'useCodeSnippetsOnFunctionSuggest': { value: true } })).then(items7 => { |
| items7 = items7 instanceof vscode.CompletionList ? items7.items : items7; |
| const item11 = items7.find(x => x.label === 'HandlerFunc'); |
| const item21 = items7.find(x => x.label === 'HandlerFuncWithArgNames'); |
| const item31 = items7.find(x => x.label === 'HandlerFuncNoReturnType'); |
| assert.equal((<vscode.SnippetString>item11.insertText).value, 'HandlerFunc(func(${1:arg1} string, ${2:arg2} string) {\n\t$3\n}) (string, string)'); |
| assert.equal((<vscode.SnippetString>item21.insertText).value, 'HandlerFuncWithArgNames(func(${1:w} string, ${2:r} string) {\n\t$3\n}) int'); |
| assert.equal((<vscode.SnippetString>item31.insertText).value, 'HandlerFuncNoReturnType(func(${1:arg1} string, ${2:arg2} string) {\n\t$3\n})'); |
| }); |
| await Promise.all([ |
| noFunctionSnippet, |
| withFunctionSnippet, |
| withFunctionSnippetNotype, |
| noFunctionAsVarSnippet, |
| withFunctionAsVarSnippet, |
| withFunctionAsVarSnippetNoType, |
| noFunctionAsTypeSnippet, |
| withFunctionAsTypeSnippet |
| ]); |
| return await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); |
| }, (err) => { |
| assert.ok(false, `error in OpenTextDocument ${err}`); |
| }).then(() => done(), done); |
| }).timeout(10000); |
| |
| test('Test No Completion Snippets For Functions', (done) => { |
| const provider = new GoCompletionItemProvider(); |
| const uri = vscode.Uri.file(path.join(fixturePath, 'completions', 'nosnippets.go')); |
| const baseConfig = vscode.workspace.getConfiguration('go'); |
| vscode.workspace.openTextDocument(uri).then(async (textDocument) => { |
| const editor = await vscode.window.showTextDocument(textDocument); |
| const symbolFollowedByBrackets = provider.provideCompletionItemsInternal(editor.document, new vscode.Position(5, 10), null, Object.create(baseConfig, { 'useCodeSnippetsOnFunctionSuggest': { value: true } })).then(items => { |
| items = items instanceof vscode.CompletionList ? items.items : items; |
| const item = items.find(x => x.label === 'Print'); |
| assert.equal(!item.insertText, true, 'Unexpected snippet when symbol is followed by ().'); |
| }); |
| const symbolAsLastParameter = provider.provideCompletionItemsInternal(editor.document, new vscode.Position(7, 13), null, Object.create(baseConfig, { 'useCodeSnippetsOnFunctionSuggest': { value: true } })).then(items1 => { |
| items1 = items1 instanceof vscode.CompletionList ? items1.items : items1; |
| const item1 = items1.find(x => x.label === 'funcAsVariable'); |
| assert.equal(!item1.insertText, true, 'Unexpected snippet when symbol is a parameter inside func call'); |
| }); |
| const symbolsAsNonLastParameter = provider.provideCompletionItemsInternal(editor.document, new vscode.Position(8, 11), null, Object.create(baseConfig, { 'useCodeSnippetsOnFunctionSuggest': { value: true } })).then(items2 => { |
| items2 = items2 instanceof vscode.CompletionList ? items2.items : items2; |
| const item2 = items2.find(x => x.label === 'funcAsVariable'); |
| assert.equal(!item2.insertText, true, 'Unexpected snippet when symbol is one of the parameters inside func call.'); |
| }); |
| await Promise.all([ |
| symbolFollowedByBrackets, |
| symbolAsLastParameter, |
| symbolsAsNonLastParameter |
| ]); |
| return await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); |
| }, (err) => { |
| assert.ok(false, `error in OpenTextDocument ${err}`); |
| }).then(() => done(), done); |
| }); |
| |
| test('Test Completion on unimported packages', (done) => { |
| const config = Object.create(vscode.workspace.getConfiguration('go'), { |
| 'autocompleteUnimportedPackages': { value: true } |
| }); |
| const provider = new GoCompletionItemProvider(); |
| const testCases: [vscode.Position, string[]][] = [ |
| [new vscode.Position(10, 3), ['bytes']], |
| [new vscode.Position(11, 6), ['Abs', 'Acos', 'Asin']] |
| ]; |
| const uri = vscode.Uri.file(path.join(fixturePath, 'completions', 'unimportedPkgs.go')); |
| |
| vscode.workspace.openTextDocument(uri).then(async (textDocument) => { |
| const editor = await vscode.window.showTextDocument(textDocument); |
| const promises = testCases.map(([position, expected]) => provider.provideCompletionItemsInternal(editor.document, position, null, config).then(items => { |
| items = items instanceof vscode.CompletionList ? items.items : items; |
| const labels = items.map(x => x.label); |
| for (const entry of expected) { |
| assert.equal(labels.indexOf(entry) > -1, true, `missing expected item in completion list: ${entry} Actual: ${labels}`); |
| } |
| })); |
| await Promise.all(promises); |
| return await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); |
| }, (err) => { |
| assert.ok(false, `error in OpenTextDocument ${err}`); |
| }).then(() => done(), done); |
| }); |
| |
| test('Test Completion on unimported packages (multiple)', (done) => { |
| const config = Object.create(vscode.workspace.getConfiguration('go'), { |
| 'gocodeFlags': { value: ['-builtin'] } |
| }); |
| const provider = new GoCompletionItemProvider(); |
| const position = new vscode.Position(3, 14); |
| const expectedItems = [ |
| { |
| label: 'template (html/template)', |
| import: '\nimport (\n\t"html/template"\n)\n' |
| }, |
| { |
| label: 'template (text/template)', |
| import: '\nimport (\n\t"text/template"\n)\n' |
| } |
| ]; |
| const uri = vscode.Uri.file(path.join(fixturePath, 'completions', 'unimportedMultiplePkgs.go')); |
| vscode.workspace.openTextDocument(uri).then(async (textDocument) => { |
| const editor = await vscode.window.showTextDocument(textDocument); |
| let items = await provider.provideCompletionItemsInternal(editor.document, position, null, config); |
| items = items instanceof vscode.CompletionList ? items.items : items; |
| const labels = items.map(x => x.label); |
| expectedItems.forEach(expectedItem => { |
| items = items instanceof vscode.CompletionList ? items.items : items; |
| const actualItem: vscode.CompletionItem = items.filter(item => item.label === expectedItem.label)[0]; |
| if (!actualItem) { |
| assert.fail(actualItem, expectedItem, `Missing expected item in completion list: ${expectedItem.label} Actual: ${labels}`); |
| return; |
| } |
| assert.equal(actualItem.additionalTextEdits.length, 1); |
| assert.equal(actualItem.additionalTextEdits[0].newText, expectedItem.import); |
| }); |
| return await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); |
| }, (err) => { |
| assert.ok(false, `error in OpenTextDocument ${err}`); |
| }).then(() => done(), done); |
| }); |
| |
| test('Test Completion on Comments for Exported Members', (done) => { |
| const provider = new GoCompletionItemProvider(); |
| const testCases: [vscode.Position, string[]][] = [ |
| [new vscode.Position(6, 4), ['Language']], |
| [new vscode.Position(9, 4), ['GreetingText']], |
| // checking for comment completions with begining of comment without space |
| [new vscode.Position(12, 2), []], |
| // cursor between /$/ this should not trigger any completion |
| [new vscode.Position(12, 1), []], |
| [new vscode.Position(12, 4), ['SayHello']], |
| [new vscode.Position(17, 5), ['HelloParams']], |
| [new vscode.Position(26, 5), ['Abs']], |
| ]; |
| const uri = vscode.Uri.file(path.join(fixturePath, 'completions', 'exportedMemberDocs.go')); |
| |
| vscode.workspace.openTextDocument(uri).then(async (textDocument) => { |
| const editor = await vscode.window.showTextDocument(textDocument); |
| const promises = testCases.map(([position, expected]) => provider.provideCompletionItems(editor.document, position, null).then(items => { |
| const labels = items.items.map(x => x.label); |
| assert.equal(expected.length, labels.length, `expected number of completions: ${expected.length} Actual: ${labels.length} at position(${position.line + 1},${position.character + 1}) ${labels}`); |
| expected.forEach((entry, index) => { |
| assert.equal(entry, labels[index], `mismatch in comment completion list Expected: ${entry} Actual: ${labels[index]}`); |
| }); |
| })); |
| await Promise.all(promises); |
| return await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); |
| }, (err) => { |
| assert.ok(false, `error in OpenTextDocument ${err}`); |
| }).then(() => done(), done); |
| }); |
| |
| test('getImportPath()', () => { |
| const testCases: [string, string][] = [ |
| ['import "github.com/sirupsen/logrus"', 'github.com/sirupsen/logrus'], |
| ['import "net/http"', 'net/http'], |
| ['"github.com/sirupsen/logrus"', 'github.com/sirupsen/logrus'], |
| ['', ''], |
| ['func foo(bar int) (int, error) {', ''], |
| ['// This is a comment, complete with punctuation.', ''] |
| ]; |
| |
| testCases.forEach(run => { |
| assert.equal(run[1], getImportPath(run[0])); |
| }); |
| }); |
| |
| test('goPlay - success run', (done) => { |
| const goplayPath = getBinPath('goplay'); |
| if (goplayPath === 'goplay') { |
| return done(); |
| } |
| |
| const validCode = ` |
| package main |
| import ( |
| "fmt" |
| ) |
| func main() { |
| for i := 1; i < 4; i++ { |
| fmt.Printf("%v ", i) |
| } |
| fmt.Print("Go!") |
| }`; |
| const goConfig = Object.create(vscode.workspace.getConfiguration('go'), { |
| 'playground': { value: { run: true, openbrowser: false, share: false } } |
| }); |
| |
| goPlay(validCode, goConfig['playground']).then(result => { |
| assert( |
| result.includes('1 2 3 Go!') |
| ); |
| }, (e) => { |
| assert.ifError(e); |
| }).then(() => done(), done); |
| }); |
| |
| test('goPlay - success run & share', (done) => { |
| const goplayPath = getBinPath('goplay'); |
| if (goplayPath === 'goplay') { |
| return done(); |
| } |
| |
| const validCode = ` |
| package main |
| import ( |
| "fmt" |
| ) |
| func main() { |
| for i := 1; i < 4; i++ { |
| fmt.Printf("%v ", i) |
| } |
| fmt.Print("Go!") |
| }`; |
| const goConfig = Object.create(vscode.workspace.getConfiguration('go'), { |
| 'playground': { value: { run: true, openbrowser: false, share: true } } |
| }); |
| |
| goPlay(validCode, goConfig['playground']).then(result => { |
| assert(result.includes('1 2 3 Go!')); |
| assert(result.includes('https://play.golang.org/')); |
| }, (e) => { |
| assert.ifError(e); |
| }).then(() => done(), done); |
| }); |
| |
| test('goPlay - fail', (done) => { |
| const goplayPath = getBinPath('goplay'); |
| if (goplayPath === 'goplay') { |
| return done(); |
| } |
| |
| const invalidCode = ` |
| package main |
| import ( |
| "fmt" |
| ) |
| func fantasy() { |
| fmt.Print("not a main package, sorry") |
| }`; |
| const goConfig = Object.create(vscode.workspace.getConfiguration('go'), { |
| 'playground': { value: { run: true, openbrowser: false, share: false } } |
| }); |
| |
| goPlay(invalidCode, goConfig['playground']).then(result => { |
| assert.ifError(result); |
| }, (e) => { |
| assert.ok(e); |
| }).then(() => done(), done); |
| }); |
| |
| test('Build Tags checking', (done) => { |
| const config1 = Object.create(vscode.workspace.getConfiguration('go'), { |
| 'vetOnSave': { value: 'off' }, |
| 'lintOnSave': { value: 'off' }, |
| 'buildOnSave': { value: 'package' }, |
| 'buildTags': { value: 'randomtag' } |
| }); |
| |
| const checkWithTags = check(vscode.Uri.file(path.join(fixturePath, 'buildTags', 'hello.go')), config1).then(diagnostics => { |
| assert.equal(1, diagnostics.length, 'check with buildtag failed. Unexpected errors found'); |
| assert.equal(1, diagnostics[0].errors.length, 'check with buildtag failed. Unexpected errors found'); |
| assert.equal(diagnostics[0].errors[0].msg, 'undefined: fmt.Prinln'); |
| }); |
| |
| const config2 = Object.create(vscode.workspace.getConfiguration('go'), { |
| 'vetOnSave': { value: 'off' }, |
| 'lintOnSave': { value: 'off' }, |
| 'buildOnSave': { value: 'package' }, |
| 'buildTags': { value: 'randomtag othertag' } |
| }); |
| |
| const checkWithMultipleTags = check(vscode.Uri.file(path.join(fixturePath, 'buildTags', 'hello.go')), config2).then(diagnostics => { |
| assert.equal(1, diagnostics.length, 'check with multiple buildtags failed. Unexpected errors found'); |
| assert.equal(1, diagnostics[0].errors.length, 'check with multiple buildtags failed. Unexpected errors found'); |
| assert.equal(diagnostics[0].errors[0].msg, 'undefined: fmt.Prinln'); |
| }); |
| |
| const config3 = Object.create(vscode.workspace.getConfiguration('go'), { |
| 'vetOnSave': { value: 'off' }, |
| 'lintOnSave': { value: 'off' }, |
| 'buildOnSave': { value: 'package' }, |
| 'buildTags': { value: '' } |
| }); |
| |
| const checkWithoutTags = check(vscode.Uri.file(path.join(fixturePath, 'buildTags', 'hello.go')), config3).then(diagnostics => { |
| assert.equal(1, diagnostics.length, 'check without buildtags failed. Unexpected errors found'); |
| assert.equal(1, diagnostics[0].errors.length, 'check without buildtags failed. Unexpected errors found'); |
| assert.equal(diagnostics[0].errors[0].msg.indexOf('can\'t load package: package test/testfixture/buildTags') > -1, true, `check without buildtags failed. Go files not excluded. ${diagnostics[0].errors[0].msg}`); |
| }); |
| |
| Promise.all([checkWithTags, checkWithMultipleTags, checkWithoutTags]).then(() => done(), done); |
| |
| }); |
| |
| test('Test Tags checking', (done) => { |
| |
| const config1 = Object.create(vscode.workspace.getConfiguration('go'), { |
| 'vetOnSave': { value: 'off' }, |
| 'lintOnSave': { value: 'off' }, |
| 'buildOnSave': { value: 'package' }, |
| 'testTags': { value: null }, |
| 'buildTags': { value: 'randomtag' } |
| }); |
| |
| const config2 = Object.create(vscode.workspace.getConfiguration('go'), { |
| 'vetOnSave': { value: 'off' }, |
| 'lintOnSave': { value: 'off' }, |
| 'buildOnSave': { value: 'package' }, |
| 'testTags': { value: 'randomtag' } |
| }); |
| |
| const config3 = Object.create(vscode.workspace.getConfiguration('go'), { |
| 'vetOnSave': { value: 'off' }, |
| 'lintOnSave': { value: 'off' }, |
| 'buildOnSave': { value: 'package' }, |
| 'testTags': { value: 'randomtag othertag' } |
| }); |
| |
| const config4 = Object.create(vscode.workspace.getConfiguration('go'), { |
| 'vetOnSave': { value: 'off' }, |
| 'lintOnSave': { value: 'off' }, |
| 'buildOnSave': { value: 'package' }, |
| 'testTags': { value: '' } |
| }); |
| |
| const uri = vscode.Uri.file(path.join(fixturePath, 'testTags', 'hello_test.go')); |
| vscode.workspace.openTextDocument(uri).then(document => { |
| return vscode.window.showTextDocument(document).then(editor => { |
| return testCurrentFile(config1, false, []).then((result: boolean) => { |
| assert.equal(result, true); |
| return testCurrentFile(config2, false, []).then((result: boolean) => { |
| assert.equal(result, true); |
| return testCurrentFile(config3, false, []).then((result: boolean) => { |
| assert.equal(result, true); |
| return testCurrentFile(config4, false, []).then((result: boolean) => { |
| assert.equal(result, false); |
| return Promise.resolve(); |
| }); |
| }); |
| }); |
| }); |
| }); |
| }).then(done, done); |
| }).timeout(10000); |
| |
| test('Add imports when no imports', (done) => { |
| const uri = vscode.Uri.file(path.join(fixturePath, 'importTest', 'noimports.go')); |
| vscode.workspace.openTextDocument(uri).then(document => { |
| return vscode.window.showTextDocument(document).then(editor => { |
| const expectedText = document.getText() + '\n' + 'import (\n\t"bytes"\n)\n'; |
| const edits = getTextEditForAddImport('bytes'); |
| const edit = new vscode.WorkspaceEdit(); |
| edit.set(document.uri, edits); |
| return vscode.workspace.applyEdit(edit).then(() => { |
| assert.equal(vscode.window.activeTextEditor.document.getText(), expectedText); |
| return Promise.resolve(); |
| }); |
| }); |
| }).then(() => done(), done); |
| }); |
| |
| test('Add imports to an import block', (done) => { |
| const uri = vscode.Uri.file(path.join(fixturePath, 'importTest', 'groupImports.go')); |
| vscode.workspace.openTextDocument(uri).then(async document => { |
| const editor = await vscode.window.showTextDocument(document); |
| const expectedText = document.getText().replace('\t"fmt"\n\t"math"', '\t"bytes"\n\t"fmt"\n\t"math"'); |
| const edits = getTextEditForAddImport('bytes'); |
| const edit = new vscode.WorkspaceEdit(); |
| edit.set(document.uri, edits); |
| await vscode.workspace.applyEdit(edit); |
| assert.equal(vscode.window.activeTextEditor.document.getText(), expectedText); |
| return Promise.resolve(); |
| }).then(() => done(), done); |
| }); |
| |
| test('Add imports and collapse single imports to an import block', (done) => { |
| const uri = vscode.Uri.file(path.join(fixturePath, 'importTest', 'singleImports.go')); |
| vscode.workspace.openTextDocument(uri).then(async document => { |
| const editor = await vscode.window.showTextDocument(document); |
| const expectedText = document.getText().replace('import "fmt"\nimport . "math" // comment', 'import (\n\t"bytes"\n\t"fmt"\n\t. "math" // comment\n)'); |
| const edits = getTextEditForAddImport('bytes'); |
| const edit = new vscode.WorkspaceEdit(); |
| edit.set(document.uri, edits); |
| await vscode.workspace.applyEdit(edit); |
| assert.equal(vscode.window.activeTextEditor.document.getText(), expectedText); |
| return Promise.resolve(); |
| }).then(() => done(), done); |
| }); |
| |
| test('Fill struct', (done) => { |
| const uri = vscode.Uri.file(path.join(fixturePath, 'fillStruct', 'input_1.go')); |
| const golden = fs.readFileSync(path.join(fixturePath, 'fillStruct', 'golden_1.go'), 'utf-8'); |
| |
| vscode.workspace.openTextDocument(uri).then(async (textDocument) => { |
| const editor = await vscode.window.showTextDocument(textDocument); |
| const selection = new vscode.Selection(12, 15, 12, 15); |
| editor.selection = selection; |
| await runFillStruct(editor); |
| assert.equal(vscode.window.activeTextEditor.document.getText(), golden); |
| return Promise.resolve(); |
| }).then(() => done(), done); |
| }).timeout(10000); |
| |
| test('Fill struct - select line', (done) => { |
| const uri = vscode.Uri.file(path.join(fixturePath, 'fillStruct', 'input_2.go')); |
| const golden = fs.readFileSync(path.join(fixturePath, 'fillStruct', 'golden_2.go'), 'utf-8'); |
| |
| vscode.workspace.openTextDocument(uri).then(async (textDocument) => { |
| const editor = await vscode.window.showTextDocument(textDocument); |
| const selection = new vscode.Selection(7, 0, 7, 10); |
| editor.selection = selection; |
| await runFillStruct(editor); |
| assert.equal(vscode.window.activeTextEditor.document.getText(), golden); |
| return Promise.resolve(); |
| }).then(() => done(), done); |
| }).timeout(10000); |
| }); |