blob: 27ef442b7b89b560519d63592fc4b13686e3a839 [file] [log] [blame]
/*---------------------------------------------------------
* Copyright 2020 The Go Authors. All rights reserved.
* Licensed under the MIT License. See LICENSE in the project root for license information.
*--------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import fs = require('fs-extra');
import os = require('os');
import path = require('path');
import sinon = require('sinon');
import vscode = require('vscode');
import { getGoConfig } from '../../src/config';
import { computeTestCommand, getTestFlags, goTest } from '../../src/testUtils';
import { rmdirRecursive } from '../../src/util';
suite('Test Go Test Args', () => {
function runTest(param: {
expectedArgs: string,
expectedOutArgs: string,
flags?: string[],
functions?: string[],
isBenchmark?: boolean
}) {
const {args, outArgs} = computeTestCommand({
dir: '',
goConfig: getGoConfig(),
flags: param.flags || [],
functions: param.functions || [],
isBenchmark: param.isBenchmark || false,
applyCodeCoverage: false,
}, ['./...']);
assert.strictEqual(args.join(' '), param.expectedArgs, 'actual command');
assert.strictEqual(outArgs.join(' '), param.expectedOutArgs, 'displayed command');
}
test('default config', () => {
runTest({
expectedArgs: 'test -timeout 30s ./...',
expectedOutArgs: 'test -timeout 30s ./...'
});
});
test('user flag [-v] enables -json flag', () => {
runTest({
expectedArgs: 'test -timeout 30s -json ./... -v',
expectedOutArgs: 'test -timeout 30s ./... -v',
flags: ['-v']
});
});
test('user flag [-json -v] prevents -json flag addition', () => {
runTest({
expectedArgs: 'test -timeout 30s ./... -json -v',
expectedOutArgs: 'test -timeout 30s ./... -json -v',
flags: ['-json', '-v']
});
});
test('user flag [-args] does not crash', () => {
runTest({
expectedArgs: 'test -timeout 30s ./... -args',
expectedOutArgs: 'test -timeout 30s ./... -args',
flags: ['-args']
});
});
test('user flag [-args -v] does not enable -json flag', () => {
runTest({
expectedArgs: 'test -timeout 30s ./... -args -v',
expectedOutArgs: 'test -timeout 30s ./... -args -v',
flags: ['-args', '-v']
});
});
test('specifying functions adds -run flags', () => {
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({
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({
expectedArgs: 'test -timeout 30s -run ^(TestA|TestB)$ ./...',
expectedOutArgs: 'test -timeout 30s -run ^(TestA|TestB)$ ./...',
functions: ['TestA', 'TestB'],
flags: ['-run', 'TestC']
});
});
});
suite('Test Go Test', function () {
this.timeout(50000);
const sourcePath = path.join(__dirname, '..', '..', '..', 'test', 'testdata', 'goTestTest');
let tmpGopath: string;
let repoPath: string;
let previousEnv: any;
setup(() => {
previousEnv = Object.assign({}, process.env);
});
teardown(async () => {
process.env = previousEnv;
rmdirRecursive(tmpGopath);
});
function setupRepo(modulesMode: boolean) {
tmpGopath = fs.mkdtempSync(path.join(os.tmpdir(), 'go-test-test'));
fs.mkdirSync(path.join(tmpGopath, 'src'));
repoPath = path.join(tmpGopath, 'src', 'goTestTest');
fs.copySync(sourcePath, repoPath, {
recursive: true,
filter: (src: string): boolean => {
if (modulesMode) {
return true;
}
return path.basename(src) !== 'go.mod'; // skip go.mod file.
},
});
process.env.GOPATH = tmpGopath;
}
async function runTest(
input: { isMod: boolean, includeSubDirectories: boolean, testFlags?: string[], applyCodeCoverage?: boolean },
wantFiles: string[]) {
const config = Object.create(getGoConfig());
const outputChannel = new FakeOutputChannel();
const testConfig = {
goConfig: config,
outputChannel,
dir: repoPath,
flags: input.testFlags ? input.testFlags : getTestFlags(config),
isMod: input.isMod,
includeSubDirectories: input.includeSubDirectories,
applyCodeCoverage: input.applyCodeCoverage
};
try {
// TODO: instead of calling goTest, consider to test
// testCurrentPackage, testCurrentWorkspace, testCurrentFile
// which are closer to the features exposed to users.
const result = await goTest(testConfig);
assert.equal(result, false); // we expect tests to fail.
} catch (e) {
console.log('exception: ${e}');
}
const testOutput = outputChannel.toString();
for (const want of wantFiles) {
assert.ok(testOutput.includes(want), `\nFully resolved file path "${want}" not found in \n${testOutput}`);
}
}
test('resolves file names in logs (modules)', async () => {
setupRepo(true);
await runTest(
{ isMod: true, includeSubDirectories: true },
[path.join(repoPath, 'a_test.go'), path.join(repoPath, 'b', 'b_test.go')]);
await runTest(
{ isMod: true, includeSubDirectories: false },
[path.join(repoPath, 'a_test.go')]);
await runTest(
{ isMod: true, includeSubDirectories: true, testFlags: ['-v'] },
[path.join(repoPath, 'a_test.go'), path.join(repoPath, 'b', 'b_test.go')]);
await runTest(
{ isMod: true, includeSubDirectories: true, testFlags: ['-race'], applyCodeCoverage: true },
[path.join(repoPath, 'a_test.go'), path.join(repoPath, 'b', 'b_test.go')]);
await runTest(
{ isMod: true, includeSubDirectories: false, testFlags: ['-v'] },
[path.join(repoPath, 'a_test.go')]);
});
test('resolves file names in logs (GOPATH)', async () => {
setupRepo(false);
await runTest(
{ isMod: false, includeSubDirectories: true },
[path.join(repoPath, 'a_test.go'), path.join(repoPath, 'b', 'b_test.go')]);
await runTest(
{ isMod: false, includeSubDirectories: false },
[path.join(repoPath, 'a_test.go')]);
await runTest(
{ isMod: false, includeSubDirectories: true, testFlags: ['-v'] },
[path.join(repoPath, 'a_test.go'), path.join(repoPath, 'b', 'b_test.go')]);
await runTest(
{ isMod: false, includeSubDirectories: true, testFlags: ['-race'], applyCodeCoverage: true },
[path.join(repoPath, 'a_test.go'), path.join(repoPath, 'b', 'b_test.go')]);
await runTest(
{ isMod: false, includeSubDirectories: false, testFlags: ['-v'] },
[path.join(repoPath, 'a_test.go')]);
});
});
// FakeOutputChannel is a fake output channel used to buffer
// the output of the tested language client in an in-memory
// string array until cleared.
class FakeOutputChannel implements vscode.OutputChannel {
public name = 'FakeOutputChannel';
public show = sinon.fake(); // no-empty
public hide = sinon.fake(); // no-empty
public dispose = sinon.fake(); // no-empty
private buf = [] as string[];
public append = (v: string) => this.enqueue(v);
public appendLine = (v: string) => this.enqueue(v);
public clear = () => { this.buf = []; };
public toString = () => {
return this.buf.join('\n');
}
private enqueue = (v: string) => {
if (this.buf.length > 1024) { this.buf.shift(); }
this.buf.push(v.trim());
}
}