blob: 5e028e5d1cec72b10ae1dc503f66dd240f388828 [file]
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable node/no-unpublished-import */
/*---------------------------------------------------------
* Copyright 2025 The Go Authors. All rights reserved.
* Licensed under the MIT License. See LICENSE in the project root for license information.
*--------------------------------------------------------*/
import assert from 'assert';
import * as fs from 'fs-extra';
import * as path from 'path';
import * as sinon from 'sinon';
import * as vscode from 'vscode';
import { getGoConfig } from '../../src/config';
import { updateGoVarsFromConfig } from '../../src/goInstallTools';
import { goLint } from '../../src/goLint';
import { handleDiagnosticErrors } from '../../src/util';
import os = require('os');
import { MockWorkspaceConfiguration } from './mocks/configuration';
suite('Linting', function () {
this.timeout(20000);
// suiteSetup will initialize the following vars.
let repoPath: string;
let fixturePath: string;
let fixtureSourcePath: string;
suiteSetup(async () => {
await updateGoVarsFromConfig({});
repoPath = fs.mkdtempSync(path.join(os.tmpdir(), 'lintertest'));
fixturePath = path.join(repoPath, 'testfixture');
fixtureSourcePath = path.join(__dirname, '..', '..', '..', 'test', 'testdata');
fs.removeSync(repoPath);
fs.copySync(fixtureSourcePath, fixturePath, {
recursive: true
});
});
suiteTeardown(() => {
fs.removeSync(repoPath);
});
test('Linting - concurrent process cancelation', async () => {
const util = require('../../src/util');
const processutil = require('../../src/utils/processUtils');
const runToolSpy = sinon.spy(util, 'runTool');
const killProcessTreeSpy = sinon.spy(processutil, 'killProcessTree');
try {
const config = new MockWorkspaceConfiguration(
getGoConfig(),
new Map<string, any>([
['vetOnSave', 'package'],
['vetFlags', ['-all']],
['buildOnSave', 'package'],
['lintOnSave', 'package'],
// simulate a long running lint process by sleeping for a couple seconds
['lintTool', process.platform !== 'win32' ? 'sleep' : 'timeout'],
['lintFlags', process.platform !== 'win32' ? ['2'] : ['/t', '2']]
])
);
await Promise.all([
goLint(vscode.Uri.file(path.join(fixturePath, 'linterTest', 'linter_1.go')), config),
goLint(vscode.Uri.file(path.join(fixturePath, 'linterTest', 'linter_2.go')), config)
]);
assert.strictEqual(runToolSpy.callCount, 2, 'should have launched 2 lint jobs');
assert.strictEqual(
killProcessTreeSpy.callCount,
1,
'should have killed 1 lint job before launching the next'
);
} finally {
runToolSpy.restore();
killProcessTreeSpy.restore();
}
});
test('Linting - lint errors with multiple open files', async () => {
try {
// handleDiagnosticErrors may adjust the lint errors' ranges to make the error more visible.
// This adjustment applies only to the text documents known to vscode. This test checks
// the adjustment is made consistently across multiple open text documents.
const file1 = await vscode.workspace.openTextDocument(
vscode.Uri.file(path.join(fixturePath, 'linterTest', 'linter_1.go'))
);
const file2 = await vscode.workspace.openTextDocument(
vscode.Uri.file(path.join(fixturePath, 'linterTest', 'linter_2.go'))
);
console.log('start linting');
const config = new MockWorkspaceConfiguration(
getGoConfig(),
new Map<string, any>([
['lintTool', 'staticcheck'],
['lintFlags', ['-checks', 'all,-ST1000,-ST1016']]
// staticcheck skips debatable checks such as ST1003 by default,
// but this test depends on ST1003 (MixedCaps package name)
// presented in both files in the same package. So, enable that.
])
);
const warnings = await goLint(file2.uri, config, 'package');
const diagnosticCollection = vscode.languages.createDiagnosticCollection('linttest');
handleDiagnosticErrors({}, file2, warnings, diagnosticCollection);
// The first diagnostic message for each file should be about the use of MixedCaps in package name.
// Both files belong to the same package name, and we want them to be identical.
const file1Diagnostics = diagnosticCollection.get(file1.uri);
const file2Diagnostics = diagnosticCollection.get(file2.uri);
assert(file1Diagnostics);
assert(file2Diagnostics);
assert(file1Diagnostics.length > 0);
assert(file2Diagnostics.length > 0);
assert.deepStrictEqual(file1Diagnostics[0], file2Diagnostics[0]);
} catch (e) {
assert.fail(`failed to lint: ${e}`);
}
});
test('Linting - skip linting if lintTool is unset', async () => {
const file = await vscode.workspace.openTextDocument(
vscode.Uri.file(path.join(fixturePath, 'linterTest', 'linter_1.go'))
);
const warnings = await goLint(
file.uri,
new MockWorkspaceConfiguration(
getGoConfig(),
new Map<string, any>([['useLanguageServer', true]])
)
);
assert(warnings.length === 0);
});
});