Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

extension: run profile for coverage and coverage counts for files #3328

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions extension/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
"@types/node-fetch": "2.6.9",
"@types/semver": "7.3.4",
"@types/sinon": "9.0.11",
"@types/vscode": "1.75.0",
"@types/vscode": "1.88.0",
"@vscode/debugadapter-testsupport": "1.58.0",
"@vscode/test-electron": "2.3.8",
"@vscode/vsce": "2.23.0",
Expand Down
48 changes: 48 additions & 0 deletions extension/src/goCover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,12 +184,28 @@ function disposeDecorators() {
}
}

export function GetCoverageCounts(): CoverageCountsMap {
return coverageCounts;
}

interface CoverageData {
uncoveredOptions: vscode.DecorationOptions[];
coveredOptions: vscode.DecorationOptions[];
}

interface CoverageCounts {
Covered: number;
Total: number;
}

interface CoverageCountsMap {
[key: string]: CoverageCounts;
}

// type CoverageCounts = Map<string, {Covered: number, Total: number}>;

let coverageData: { [key: string]: CoverageData } = {}; // actual file path to the coverage data.
let coverageCounts: CoverageCountsMap = {}; // actual file path to the coverage counts.
let isCoverageApplied = false;

function emptyCoverageData(): CoverageData {
Expand Down Expand Up @@ -219,6 +235,7 @@ export function applyCodeCoverageToAllEditors(coverProfilePath: string, dir?: st
try {
const showCounts = getGoConfig().get('coverShowCounts') as boolean;
const coveragePath = new Map<string, CoverageData>(); // <filename> from the cover profile to the coverage data.
const covCounts = new Map<string, CoverageCounts>(); // <filename> from the cover profile to the coverage counts.

// Clear existing coverage files
clearCoverage();
Expand Down Expand Up @@ -261,6 +278,8 @@ export function applyCodeCoverageToAllEditors(coverProfilePath: string, dir?: st

// and fill in coveragePath
const coverage = coveragePath.get(parse[1]) || emptyCoverageData();
// fill in file stats
const fileCounts = covCounts.get(filename) || { Covered: 0, Total: 0 };
// When line directive is used this information is artificial and
// the source code file can be non-existent or wrong (go.dev/issues/41222).
// There is no perfect way to guess whether the line/col in coverage profile
Expand All @@ -280,6 +299,7 @@ export function applyCodeCoverageToAllEditors(coverProfilePath: string, dir?: st
endCol - 1
);

const statements = parseInt(parse[6], 10);
const counts = parseInt(parse[7], 10);
// If is Covered (CoverCount > 0)
if (counts > 0) {
Expand All @@ -288,11 +308,19 @@ export function applyCodeCoverageToAllEditors(coverProfilePath: string, dir?: st
coverage.uncoveredOptions.push(...elaborate(range, counts, showCounts));
}


if (counts > 0) {
fileCounts.Covered += statements;
}
fileCounts.Total += statements;

covCounts.set(filename, fileCounts);
coveragePath.set(filename, coverage);
});

getImportPathToFolder([...seenPaths], dir).then((pathsToDirs) => {
createCoverageData(pathsToDirs, coveragePath);
createCoverageCounts(pathsToDirs, covCounts);
setDecorators();
vscode.window.visibleTextEditors.forEach(applyCodeCoverage);
resolve();
Expand Down Expand Up @@ -349,6 +377,26 @@ function createCoverageData(pathsToDirs: Map<string, string>, coveragePath: Map<
});
}

function createCoverageCounts(pathsToDirs: Map<string, string>, counts: Map<string, CoverageCounts>) {
counts.forEach((cc, ip) => {
if (path.isAbsolute(ip)) {
coverageCounts[ip] = cc;
return;
}

const lastSlash = ip.lastIndexOf('/');
if (lastSlash === -1) {
coverageCounts[ip] = cc;
return;
}

const maybePkgPath = ip.slice(0, lastSlash);
const fileDir = pathsToDirs.get(maybePkgPath) || path.resolve(maybePkgPath);
const file = fileDir + path.sep + ip.slice(lastSlash + 1);
coverageCounts[file] = cc;
});
}

/**
* Set the object that holds the coverage data for given file path.
* @param filePath
Expand Down
38 changes: 36 additions & 2 deletions extension/src/goTest/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { debugTestAtCursor } from '../goTest';
import { GoExtensionContext } from '../context';
import path = require('path');
import { escapeRegExp } from '../subTestUtils';
import { GetCoverageCounts } from '../goCover';

let debugSessionID = 0;

Expand Down Expand Up @@ -129,6 +130,21 @@ export class GoTestRunner {
false
);

ctrl.createRunProfile(
'Go (Coverage)',
TestRunProfileKind.Coverage,
async (request, token) => {
try {
await this.run(request, token, {}, true);
} catch (error) {
const m = 'Failed to execute tests';
outputChannel.error(`${m}: ${error}`);
await vscode.window.showErrorMessage(m);
}
},
false
);

pprof.configureHandler = async () => {
const state = await this.profiler.configure();
if (!state) return;
Expand Down Expand Up @@ -228,7 +244,7 @@ export class GoTestRunner {
}

// Execute tests - TestController.runTest callback
async run(request: TestRunRequest, token?: CancellationToken, options: ProfilingOptions = {}): Promise<boolean> {
async run(request: TestRunRequest, token?: CancellationToken, options: ProfilingOptions = {}, applyCodeCoverage = false): Promise<boolean> {
const collected = new Map<TestItem, CollectedTest[]>();
const files = new Set<TestItem>();
if (request.include) {
Expand Down Expand Up @@ -360,7 +376,8 @@ export class GoTestRunner {
options,
pkg,
record,
concat
concat,
applyCodeCoverage,
};

// Run tests
Expand Down Expand Up @@ -392,6 +409,10 @@ export class GoTestRunner {
if (token?.isCancellationRequested) {
break;
}

if (applyCodeCoverage) {
this.collectCoverage(run);
}
}

run.end();
Expand Down Expand Up @@ -726,6 +747,19 @@ export class GoTestRunner {
// TODO(firelizzard18): Add more sophisticated check for build failures?
return output.some((x) => rePkg.test(x));
}

collectCoverage(run: TestRun) {
const coverageData = GetCoverageCounts();
for (const filename in coverageData) {
const data = coverageData[filename];
run.addCoverage(
new vscode.FileCoverage(
Uri.file(filename),
new vscode.TestCoverageCount(data.Covered, data.Total),
),
);
}
}
}

// escapeSubTestName escapes regexp-like metacharacters. Unlike
Expand Down
14 changes: 10 additions & 4 deletions extension/test/mocks/MockTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import path = require('path');
import {
CancellationToken,
EndOfLine,
FileCoverage,
FileType,
MarkdownString,
Position,
Expand All @@ -24,7 +25,8 @@ import {
TextDocument,
TextLine,
Uri,
WorkspaceFolder
WorkspaceFolder,
EventEmitter
} from 'vscode';
import { FileSystem, Workspace } from '../../src/goTest/utils';

Expand Down Expand Up @@ -110,9 +112,11 @@ class MockTestRunProfile implements TestRunProfile {
public label: string,
public kind: TestRunProfileKind,
public runHandler: TestRunHandler,
public isDefault: boolean
) {}
tag: TestTag | undefined;
isDefault = false;
onDidChangeDefault = new EventEmitter<boolean>().event;
supportsContinuousRun= false;

configureHandler(): void {}
dispose(): void {}
Expand All @@ -121,6 +125,7 @@ class MockTestRunProfile implements TestRunProfile {
class MockTestRun implements TestRun {
name = 'test run';
isPersisted = false;
onDidDispose = new EventEmitter<void>().event;

get token(): CancellationToken {
throw new Error('Method not implemented.');
Expand All @@ -133,6 +138,7 @@ class MockTestRun implements TestRun {
errored(test: TestItem, message: TestMessage | readonly TestMessage[], duration?: number): void {}
passed(test: TestItem, duration?: number): void {}
appendOutput(output: string): void {}
addCoverage(fileCoverage: FileCoverage): void {}
end(): void {}
}

Expand All @@ -152,16 +158,16 @@ export class MockTestController implements TestController {
label: string,
kind: TestRunProfileKind,
runHandler: TestRunHandler,
isDefault = false
): TestRunProfile {
return new MockTestRunProfile(label, kind, runHandler, isDefault);
return new MockTestRunProfile(label, kind, runHandler);
}

createTestItem(id: string, label: string, uri?: Uri): TestItem {
return new MockTestItem(id, label, uri, this);
}

dispose(): void {}
invalidateTestResults(items?: TestItem | readonly TestItem[]): void {}
}

type DirEntry = [string, FileType];
Expand Down