From 485fd3cee892f122118dacd794d708d78a09789c Mon Sep 17 00:00:00 2001 From: Jim Calabro Date: Mon, 10 May 2021 00:06:40 -0400 Subject: [PATCH 1/3] compatability with gopkg.in/check.v1 --- src/testUtils.ts | 70 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 14 deletions(-) diff --git a/src/testUtils.ts b/src/testUtils.ts index 77e812ded7..6c960ca78f 100644 --- a/src/testUtils.ts +++ b/src/testUtils.ts @@ -41,6 +41,9 @@ const testFuncRegex = /^Test\P{Ll}.*|^Example\P{Ll}.*/u; const testMethodRegex = /^\(([^)]+)\)\.(Test\P{Ll}.*)$/u; const benchmarkRegex = /^Benchmark\P{Ll}.*/u; +const checkPkg = '"gopkg.in/check.v1"' +const testifyPkg = '"github.com/stretchr/testify/suite"' + /** * Input to goTest. */ @@ -144,13 +147,22 @@ export async function getTestFunctions( return; } const children = symbol.children; - const testify = children.some( - (sym) => sym.kind === vscode.SymbolKind.Namespace && sym.name === '"github.com/stretchr/testify/suite"' - ); + + // include test functions and methods from 3rd party testing packages + let containsExternalPackages = false; + const allExternalPackages = [checkPkg, testifyPkg]; + allExternalPackages.forEach((pkg) => { + const ext = children.some( + (sym) => sym.kind === vscode.SymbolKind.Namespace && sym.name === pkg + ); + if (ext) { + containsExternalPackages = ext; + } + }); return children.filter( (sym) => sym.kind === vscode.SymbolKind.Function && - (testFuncRegex.test(sym.name) || (testify && testMethodRegex.test(sym.name))) + (testFuncRegex.test(sym.name) || (containsExternalPackages && testMethodRegex.test(sym.name))) ); } @@ -184,9 +196,12 @@ export function getTestFunctionDebugArgs( } const instanceMethod = extractInstanceTestName(testFunctionName); if (instanceMethod) { + if (containsThirdPartyTestPackages(document, null, [checkPkg])) { + return ['-check.f', `^${instanceMethod}$`]; + } + const testFns = findAllTestSuiteRuns(document, testFunctions); const testSuiteRuns = ['-test.run', `^${testFns.map((t) => t.name).join('|')}$`]; - const testSuiteTests = ['-testify.m', `^${instanceMethod}$`]; return [...testSuiteRuns, ...testSuiteTests]; } else { return ['-test.run', `^${testFunctionName}$`]; @@ -568,17 +583,27 @@ function targetArgs(testconfig: TestConfig): Array { if (testconfig.isBenchmark) { params = ['-bench', util.format('^(%s)$', testconfig.functions.join('|'))]; } else { + const editor = vscode.window.activeTextEditor; + if (!editor) { + vscode.window.showInformationMessage('No editor is active.'); + return; + } + if (!editor.document.fileName.endsWith('_test.go')) { + vscode.window.showInformationMessage('No tests found. Current file is not a test file.'); + return; + } + let testFunctions = testconfig.functions; - let testifyMethods = testFunctions.filter((fn) => testMethodRegex.test(fn)); - if (testifyMethods.length > 0) { - // filter out testify methods + let testMethods = testFunctions.filter((fn) => testMethodRegex.test(fn)); + if (testMethods.length > 0) { + // filter out methods testFunctions = testFunctions.filter((fn) => !testMethodRegex.test(fn)); - testifyMethods = testifyMethods.map(extractInstanceTestName); + testMethods = testMethods.map(extractInstanceTestName); } - // we might skip the '-run' param when running only testify methods, which will result - // in running all the test methods, but one of them should call testify's `suite.Run(...)` - // which will result in the correct thing to happen + // we might skip the '-run' param when running only external test package methods, which will + // result in running all the test methods, but in the case of testify, one of them should call + // testify's `suite.Run(...)`, which will cause the correct thing to happen if (testFunctions.length > 0) { if (testFunctions.length === 1) { params = params.concat(['-run', util.format('^%s$', testFunctions[0])]); @@ -586,8 +611,12 @@ function targetArgs(testconfig: TestConfig): Array { params = params.concat(['-run', util.format('^(%s)$', testFunctions.join('|'))]); } } - if (testifyMethods.length > 0) { - params = params.concat(['-testify.m', util.format('^(%s)$', testifyMethods.join('|'))]); + if (testMethods.length > 0) { + if (containsThirdPartyTestPackages(editor.document, null, [checkPkg])) { + params = params.concat(['-check.f', util.format('^(%s)$', testMethods.join('|'))]); + } else if (containsThirdPartyTestPackages(editor.document, null, [testifyPkg])) { + params = params.concat(['-testify.m', util.format('^(%s)$', testMethods.join('|'))]); + } } } return params; @@ -605,3 +634,16 @@ function removeRunFlag(flags: string[]): void { flags.splice(index, 2); } } + +export async function containsThirdPartyTestPackages(doc: vscode.TextDocument, token: vscode.CancellationToken, pkgs: string[]): Promise { + const documentSymbolProvider = new GoDocumentSymbolProvider(true); + const allPackages = await documentSymbolProvider + .provideDocumentSymbols(doc, token) + .then(symbols => symbols[0].children) + .then(symbols => { + return symbols.filter(sym => sym.kind === vscode.SymbolKind.Namespace && pkgs.some((pkg) => { + sym.name === pkg; + })); + }); + return allPackages.length > 0; +} From 0a3c242a57ed00787a29a9cc8c4db7aae2013b37 Mon Sep 17 00:00:00 2001 From: Jim Calabro Date: Mon, 10 May 2021 00:07:36 -0400 Subject: [PATCH 2/3] prettier -w src/testUtils.ts --- src/testUtils.ts | 90 ++++++++++++++++++++++++++---------------------- 1 file changed, 48 insertions(+), 42 deletions(-) diff --git a/src/testUtils.ts b/src/testUtils.ts index 6c960ca78f..adaee74ed9 100644 --- a/src/testUtils.ts +++ b/src/testUtils.ts @@ -41,8 +41,8 @@ const testFuncRegex = /^Test\P{Ll}.*|^Example\P{Ll}.*/u; const testMethodRegex = /^\(([^)]+)\)\.(Test\P{Ll}.*)$/u; const benchmarkRegex = /^Benchmark\P{Ll}.*/u; -const checkPkg = '"gopkg.in/check.v1"' -const testifyPkg = '"github.com/stretchr/testify/suite"' +const checkPkg = '"gopkg.in/check.v1"'; +const testifyPkg = '"github.com/stretchr/testify/suite"'; /** * Input to goTest. @@ -148,17 +148,15 @@ export async function getTestFunctions( } const children = symbol.children; - // include test functions and methods from 3rd party testing packages - let containsExternalPackages = false; - const allExternalPackages = [checkPkg, testifyPkg]; - allExternalPackages.forEach((pkg) => { - const ext = children.some( - (sym) => sym.kind === vscode.SymbolKind.Namespace && sym.name === pkg - ); - if (ext) { - containsExternalPackages = ext; - } - }); + // include test functions and methods from 3rd party testing packages + let containsExternalPackages = false; + const allExternalPackages = [checkPkg, testifyPkg]; + allExternalPackages.forEach((pkg) => { + const ext = children.some((sym) => sym.kind === vscode.SymbolKind.Namespace && sym.name === pkg); + if (ext) { + containsExternalPackages = ext; + } + }); return children.filter( (sym) => sym.kind === vscode.SymbolKind.Function && @@ -196,9 +194,9 @@ export function getTestFunctionDebugArgs( } const instanceMethod = extractInstanceTestName(testFunctionName); if (instanceMethod) { - if (containsThirdPartyTestPackages(document, null, [checkPkg])) { - return ['-check.f', `^${instanceMethod}$`]; - } + if (containsThirdPartyTestPackages(document, null, [checkPkg])) { + return ['-check.f', `^${instanceMethod}$`]; + } const testFns = findAllTestSuiteRuns(document, testFunctions); const testSuiteRuns = ['-test.run', `^${testFns.map((t) => t.name).join('|')}$`]; @@ -583,15 +581,15 @@ function targetArgs(testconfig: TestConfig): Array { if (testconfig.isBenchmark) { params = ['-bench', util.format('^(%s)$', testconfig.functions.join('|'))]; } else { - const editor = vscode.window.activeTextEditor; - if (!editor) { - vscode.window.showInformationMessage('No editor is active.'); - return; - } - if (!editor.document.fileName.endsWith('_test.go')) { - vscode.window.showInformationMessage('No tests found. Current file is not a test file.'); - return; - } + const editor = vscode.window.activeTextEditor; + if (!editor) { + vscode.window.showInformationMessage('No editor is active.'); + return; + } + if (!editor.document.fileName.endsWith('_test.go')) { + vscode.window.showInformationMessage('No tests found. Current file is not a test file.'); + return; + } let testFunctions = testconfig.functions; let testMethods = testFunctions.filter((fn) => testMethodRegex.test(fn)); @@ -603,7 +601,7 @@ function targetArgs(testconfig: TestConfig): Array { // we might skip the '-run' param when running only external test package methods, which will // result in running all the test methods, but in the case of testify, one of them should call - // testify's `suite.Run(...)`, which will cause the correct thing to happen + // testify's `suite.Run(...)`, which will cause the correct thing to happen if (testFunctions.length > 0) { if (testFunctions.length === 1) { params = params.concat(['-run', util.format('^%s$', testFunctions[0])]); @@ -612,11 +610,11 @@ function targetArgs(testconfig: TestConfig): Array { } } if (testMethods.length > 0) { - if (containsThirdPartyTestPackages(editor.document, null, [checkPkg])) { - params = params.concat(['-check.f', util.format('^(%s)$', testMethods.join('|'))]); - } else if (containsThirdPartyTestPackages(editor.document, null, [testifyPkg])) { - params = params.concat(['-testify.m', util.format('^(%s)$', testMethods.join('|'))]); - } + if (containsThirdPartyTestPackages(editor.document, null, [checkPkg])) { + params = params.concat(['-check.f', util.format('^(%s)$', testMethods.join('|'))]); + } else if (containsThirdPartyTestPackages(editor.document, null, [testifyPkg])) { + params = params.concat(['-testify.m', util.format('^(%s)$', testMethods.join('|'))]); + } } } return params; @@ -635,15 +633,23 @@ function removeRunFlag(flags: string[]): void { } } -export async function containsThirdPartyTestPackages(doc: vscode.TextDocument, token: vscode.CancellationToken, pkgs: string[]): Promise { - const documentSymbolProvider = new GoDocumentSymbolProvider(true); - const allPackages = await documentSymbolProvider - .provideDocumentSymbols(doc, token) - .then(symbols => symbols[0].children) - .then(symbols => { - return symbols.filter(sym => sym.kind === vscode.SymbolKind.Namespace && pkgs.some((pkg) => { - sym.name === pkg; - })); - }); - return allPackages.length > 0; +export async function containsThirdPartyTestPackages( + doc: vscode.TextDocument, + token: vscode.CancellationToken, + pkgs: string[] +): Promise { + const documentSymbolProvider = new GoDocumentSymbolProvider(true); + const allPackages = await documentSymbolProvider + .provideDocumentSymbols(doc, token) + .then((symbols) => symbols[0].children) + .then((symbols) => { + return symbols.filter( + (sym) => + sym.kind === vscode.SymbolKind.Namespace && + pkgs.some((pkg) => { + sym.name === pkg; + }) + ); + }); + return allPackages.length > 0; } From e13c506755c7c1d70ff869a5e187d246608b2dad Mon Sep 17 00:00:00 2001 From: Jim Calabro Date: Mon, 10 May 2021 13:32:21 -0400 Subject: [PATCH 3/3] fix up logical async/await errors --- src/goTest.ts | 2 +- src/testUtils.ts | 33 ++++++++++++++++---------------- test/integration/test.test.ts | 36 +++++++++++++++++------------------ 3 files changed, 36 insertions(+), 35 deletions(-) diff --git a/src/goTest.ts b/src/goTest.ts index e25c9d7b19..52d6958f6e 100644 --- a/src/goTest.ts +++ b/src/goTest.ts @@ -180,7 +180,7 @@ async function debugTestAtCursor( testFunctions: vscode.DocumentSymbol[], goConfig: vscode.WorkspaceConfiguration ) { - const args = getTestFunctionDebugArgs(editor.document, testFunctionName, testFunctions); + const args = await getTestFunctionDebugArgs(editor.document, testFunctionName, testFunctions); const tags = getTestTags(goConfig); const buildFlags = tags ? ['-tags', tags] : []; const flagsFromConfig = getTestFlags(goConfig); diff --git a/src/testUtils.ts b/src/testUtils.ts index adaee74ed9..de9cfdadf6 100644 --- a/src/testUtils.ts +++ b/src/testUtils.ts @@ -43,6 +43,7 @@ const benchmarkRegex = /^Benchmark\P{Ll}.*/u; const checkPkg = '"gopkg.in/check.v1"'; const testifyPkg = '"github.com/stretchr/testify/suite"'; +const allExternalPackages = [checkPkg, testifyPkg]; /** * Input to goTest. @@ -149,18 +150,17 @@ export async function getTestFunctions( const children = symbol.children; // include test functions and methods from 3rd party testing packages - let containsExternalPackages = false; - const allExternalPackages = [checkPkg, testifyPkg]; + let containsExternal = false; allExternalPackages.forEach((pkg) => { const ext = children.some((sym) => sym.kind === vscode.SymbolKind.Namespace && sym.name === pkg); if (ext) { - containsExternalPackages = ext; + containsExternal = ext; } }); return children.filter( (sym) => sym.kind === vscode.SymbolKind.Function && - (testFuncRegex.test(sym.name) || (containsExternalPackages && testMethodRegex.test(sym.name))) + (testFuncRegex.test(sym.name) || (containsExternal && testMethodRegex.test(sym.name))) ); } @@ -184,22 +184,23 @@ export function extractInstanceTestName(symbolName: string): string { * @param testFunctionName The test function to get the debug args * @param testFunctions The test functions found in the document */ -export function getTestFunctionDebugArgs( +export async function getTestFunctionDebugArgs( document: vscode.TextDocument, testFunctionName: string, testFunctions: vscode.DocumentSymbol[] -): string[] { +): Promise { if (benchmarkRegex.test(testFunctionName)) { return ['-test.bench', '^' + testFunctionName + '$', '-test.run', 'a^']; } const instanceMethod = extractInstanceTestName(testFunctionName); if (instanceMethod) { - if (containsThirdPartyTestPackages(document, null, [checkPkg])) { + if (await containsThirdPartyTestPackages(document, null, [checkPkg])) { return ['-check.f', `^${instanceMethod}$`]; } const testFns = findAllTestSuiteRuns(document, testFunctions); const testSuiteRuns = ['-test.run', `^${testFns.map((t) => t.name).join('|')}$`]; + const testSuiteTests = ['-testify.m', `^${instanceMethod}$`]; return [...testSuiteRuns, ...testSuiteTests]; } else { return ['-test.run', `^${testFunctionName}$`]; @@ -294,7 +295,7 @@ export async function goTest(testconfig: TestConfig): Promise { const { targets, pkgMap, currentGoWorkspace } = await getTestTargetPackages(testconfig, outputChannel); // generate full test args. - const { args, outArgs, tmpCoverPath, addJSONFlag } = computeTestCommand(testconfig, targets); + const { args, outArgs, tmpCoverPath, addJSONFlag } = await computeTestCommand(testconfig, targets); outputChannel.appendLine(['Running tool:', goRuntimePath, ...outArgs].join(' ')); outputChannel.appendLine(''); @@ -401,15 +402,15 @@ async function getTestTargetPackages(testconfig: TestConfig, outputChannel: vsco // computeTestCommand returns the test command argument list and extra info necessary // to post process the test results. // Exported for testing. -export function computeTestCommand( +export async function computeTestCommand( testconfig: TestConfig, targets: string[] -): { +): Promise<{ args: Array; // test command args. outArgs: Array; // compact test command args to show to user. tmpCoverPath?: string; // coverage file path if coverage info is necessary. addJSONFlag: boolean; // true if we add extra -json flag for stream processing. -} { +}> { const args: Array = ['test']; // user-specified flags const argsFlagIdx = testconfig.flags?.indexOf('-args') ?? -1; @@ -451,7 +452,7 @@ export function computeTestCommand( } // all other test run/benchmark flags - args.push(...targetArgs(testconfig)); + args.push(...(await targetArgs(testconfig))); const outArgs = args.slice(0); // command to show @@ -574,7 +575,7 @@ export function cancelRunningTests(): Thenable { * * @param testconfig Configuration for the Go extension. */ -function targetArgs(testconfig: TestConfig): Array { +async function targetArgs(testconfig: TestConfig): Promise> { let params: string[] = []; if (testconfig.functions) { @@ -610,9 +611,9 @@ function targetArgs(testconfig: TestConfig): Array { } } if (testMethods.length > 0) { - if (containsThirdPartyTestPackages(editor.document, null, [checkPkg])) { + if (await containsThirdPartyTestPackages(editor.document, null, [checkPkg])) { params = params.concat(['-check.f', util.format('^(%s)$', testMethods.join('|'))]); - } else if (containsThirdPartyTestPackages(editor.document, null, [testifyPkg])) { + } else if (await containsThirdPartyTestPackages(editor.document, null, [testifyPkg])) { params = params.concat(['-testify.m', util.format('^(%s)$', testMethods.join('|'))]); } } @@ -647,7 +648,7 @@ export async function containsThirdPartyTestPackages( (sym) => sym.kind === vscode.SymbolKind.Namespace && pkgs.some((pkg) => { - sym.name === pkg; + return sym.name === pkg; }) ); }); diff --git a/test/integration/test.test.ts b/test/integration/test.test.ts index 0896ea00ca..9f42873a1e 100644 --- a/test/integration/test.test.ts +++ b/test/integration/test.test.ts @@ -17,14 +17,14 @@ import sinon = require('sinon'); import vscode = require('vscode'); suite('Test Go Test Args', () => { - function runTest(param: { + async function runTest(param: { expectedArgs: string; expectedOutArgs: string; flags?: string[]; functions?: string[]; isBenchmark?: boolean; }) { - const { args, outArgs } = computeTestCommand( + const { args, outArgs } = await computeTestCommand( { dir: '', goConfig: getGoConfig(), @@ -40,57 +40,57 @@ suite('Test Go Test Args', () => { assert.strictEqual(outArgs.join(' '), param.expectedOutArgs, 'displayed command'); } - test('default config', () => { - runTest({ + test('default config', async () => { + await runTest({ expectedArgs: 'test -timeout 30s ./...', expectedOutArgs: 'test -timeout 30s ./...' }); }); - test('user flag [-v] enables -json flag', () => { - runTest({ + test('user flag [-v] enables -json flag', async () => { + await runTest({ expectedArgs: 'test -timeout 30s -json ./... -v', expectedOutArgs: 'test -timeout 30s ./... -v', flags: ['-v'] }); }); - test('user flag [-json -v] prevents -json flag addition', () => { - runTest({ + test('user flag [-json -v] prevents -json flag addition', async () => { + await runTest({ expectedArgs: 'test -timeout 30s ./... -json -v', expectedOutArgs: 'test -timeout 30s ./... -json -v', flags: ['-json', '-v'] }); }); - test('user flag [-args] does not crash', () => { - runTest({ + test('user flag [-args] does not crash', async () => { + await runTest({ expectedArgs: 'test -timeout 30s ./... -args', expectedOutArgs: 'test -timeout 30s ./... -args', flags: ['-args'] }); }); - test('user flag [-args -v] does not enable -json flag', () => { - runTest({ + test('user flag [-args -v] does not enable -json flag', async () => { + await runTest({ expectedArgs: 'test -timeout 30s ./... -args -v', expectedOutArgs: 'test -timeout 30s ./... -args -v', flags: ['-args', '-v'] }); }); - test('specifying functions adds -run flags', () => { - runTest({ + test('specifying functions adds -run flags', async () => { + await runTest({ expectedArgs: 'test -timeout 30s -run ^(TestA|TestB)$ ./...', expectedOutArgs: 'test -timeout 30s -run ^(TestA|TestB)$ ./...', functions: ['TestA', 'TestB'] }); }); - test('functions & benchmark adds -bench flags and skips timeout', () => { - runTest({ + test('functions & benchmark adds -bench flags and skips timeout', async () => { + await runTest({ expectedArgs: 'test -benchmem -run=^$ -bench ^(TestA|TestB)$ ./...', expectedOutArgs: 'test -benchmem -run=^$ -bench ^(TestA|TestB)$ ./...', functions: ['TestA', 'TestB'], isBenchmark: true }); }); - test('user -run flag is ignored when functions are provided', () => { - runTest({ + test('user -run flag is ignored when functions are provided', async () => { + await runTest({ expectedArgs: 'test -timeout 30s -run ^(TestA|TestB)$ ./...', expectedOutArgs: 'test -timeout 30s -run ^(TestA|TestB)$ ./...', functions: ['TestA', 'TestB'],