Skip to content

Commit 53ff5ca

Browse files
authored
testing: assorted TPI fixes (microsoft#208740)
* testing: bounds check coverage data Fixes microsoft#208663 * testing: fix coverage label of boolean counts Fixes microsoft#208463 * testing: clarify docs on loadDetailedCoverage Fixes microsoft#208724
1 parent a8a3859 commit 53ff5ca

File tree

4 files changed

+43
-15
lines changed

4 files changed

+43
-15
lines changed

src/vs/workbench/api/common/extHostTypeConverters.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2024,6 +2024,10 @@ export namespace TestCoverage {
20242024
}
20252025

20262026
export function fromDetails(coverage: vscode.FileCoverageDetail): CoverageDetails.Serialized {
2027+
if (typeof coverage.executed === 'number' && coverage.executed < 0) {
2028+
throw new Error(`Invalid coverage count ${coverage.executed}`);
2029+
}
2030+
20272031
if ('branches' in coverage) {
20282032
return {
20292033
count: coverage.executed,
@@ -2044,6 +2048,10 @@ export namespace TestCoverage {
20442048
}
20452049

20462050
export function fromFile(id: string, coverage: vscode.FileCoverage): IFileCoverage.Serialized {
2051+
types.validateTestCoverageCount(coverage.statementCoverage);
2052+
types.validateTestCoverageCount(coverage.branchCoverage);
2053+
types.validateTestCoverageCount(coverage.declarationCoverage);
2054+
20472055
return {
20482056
id,
20492057
uri: coverage.uri,

src/vs/workbench/api/common/extHostTypes.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4027,14 +4027,23 @@ export class TestTag implements vscode.TestTag {
40274027
//#region Test Coverage
40284028
export class TestCoverageCount implements vscode.TestCoverageCount {
40294029
constructor(public covered: number, public total: number) {
4030+
validateTestCoverageCount(this);
40304031
}
40314032
}
40324033

4033-
const validateCC = (cc?: vscode.TestCoverageCount) => {
4034-
if (cc && cc.covered > cc.total) {
4034+
export function validateTestCoverageCount(cc?: vscode.TestCoverageCount) {
4035+
if (!cc) {
4036+
return;
4037+
}
4038+
4039+
if (cc.covered > cc.total) {
40354040
throw new Error(`The total number of covered items (${cc.covered}) cannot be greater than the total (${cc.total})`);
40364041
}
4037-
};
4042+
4043+
if (cc.total < 0) {
4044+
throw new Error(`The number of covered items (${cc.total}) cannot be negative`);
4045+
}
4046+
}
40384047

40394048
export class FileCoverage implements vscode.FileCoverage {
40404049
public static fromDetails(uri: vscode.Uri, details: vscode.FileCoverageDetail[]): vscode.FileCoverage {
@@ -4077,9 +4086,6 @@ export class FileCoverage implements vscode.FileCoverage {
40774086
public branchCoverage?: vscode.TestCoverageCount,
40784087
public declarationCoverage?: vscode.TestCoverageCount,
40794088
) {
4080-
validateCC(statementCoverage);
4081-
validateCC(branchCoverage);
4082-
validateCC(declarationCoverage);
40834089
}
40844090
}
40854091

src/vs/workbench/contrib/testing/browser/codeCoverageDecorations.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import * as dom from 'vs/base/browser/dom';
77
import { HoverWidget } from 'vs/base/browser/ui/hover/hoverWidget';
88
import { mapFindFirst } from 'vs/base/common/arraysFind';
9+
import { assertNever } from 'vs/base/common/assert';
910
import { CancellationTokenSource } from 'vs/base/common/cancellation';
1011
import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent';
1112
import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
@@ -31,7 +32,7 @@ import { ILogService } from 'vs/platform/log/common/log';
3132
import { testingCoverageMissingBranch } from 'vs/workbench/contrib/testing/browser/icons';
3233
import { FileCoverage } from 'vs/workbench/contrib/testing/common/testCoverage';
3334
import { ITestCoverageService } from 'vs/workbench/contrib/testing/common/testCoverageService';
34-
import { CoverageDetails, DetailType, IStatementCoverage } from 'vs/workbench/contrib/testing/common/testTypes';
35+
import { CoverageDetails, DetailType, IDeclarationCoverage, IStatementCoverage } from 'vs/workbench/contrib/testing/common/testTypes';
3536
import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys';
3637

3738
const MAX_HOVERED_LINES = 30;
@@ -452,32 +453,42 @@ export class CoverageDetailsModel {
452453
/** Gets the markdown description for the given detail */
453454
public describe(detail: CoverageDetailsWithBranch, model: ITextModel): IMarkdownString | undefined {
454455
if (detail.type === DetailType.Declaration) {
455-
return new MarkdownString().appendMarkdown(localize('coverage.declExecutedCount', '`{0}` was executed {1} time(s).', detail.name, detail.count));
456+
return namedDetailLabel(detail.name, detail);
456457
} else if (detail.type === DetailType.Statement) {
457458
const text = wrapName(model.getValueInRange(tidyLocation(detail.location)).trim() || `<empty statement>`);
458-
const str = new MarkdownString();
459459
if (detail.branches?.length) {
460460
const covered = detail.branches.filter(b => !!b.count).length;
461-
str.appendMarkdown(localize('coverage.branches', '{0} of {1} of branches in {2} were covered.', covered, detail.branches.length, text));
461+
return new MarkdownString().appendMarkdown(localize('coverage.branches', '{0} of {1} of branches in {2} were covered.', covered, detail.branches.length, text));
462462
} else {
463-
str.appendMarkdown(localize('coverage.codeExecutedCount', '{0} was executed {1} time(s).', text, detail.count));
463+
return namedDetailLabel(text, detail);
464464
}
465-
return str;
466465
} else if (detail.type === DetailType.Branch) {
467466
const text = wrapName(model.getValueInRange(tidyLocation(detail.detail.location)).trim() || `<empty statement>`);
468467
const { count, label } = detail.detail.branches![detail.branch];
469468
const label2 = label ? wrapInBackticks(label) : `#${detail.branch + 1}`;
470-
if (count === 0) {
469+
if (!count) {
471470
return new MarkdownString().appendMarkdown(localize('coverage.branchNotCovered', 'Branch {0} in {1} was not covered.', label2, text));
471+
} else if (count === true) {
472+
return new MarkdownString().appendMarkdown(localize('coverage.branchCoveredYes', 'Branch {0} in {1} was executed.', label2, text));
472473
} else {
473474
return new MarkdownString().appendMarkdown(localize('coverage.branchCovered', 'Branch {0} in {1} was executed {2} time(s).', label2, text, count));
474475
}
475476
}
476477

477-
return undefined;
478+
assertNever(detail);
478479
}
479480
}
480481

482+
function namedDetailLabel(name: string, detail: IStatementCoverage | IDeclarationCoverage) {
483+
return new MarkdownString().appendMarkdown(
484+
!detail.count // 0 or false
485+
? localize('coverage.declExecutedNo', '`{0}` was not executed.', name)
486+
: typeof detail.count === 'number'
487+
? localize('coverage.declExecutedCount', '`{0}` was executed {1} time(s).', name, detail.count)
488+
: localize('coverage.declExecutedYes', '`{0}` was executed.', name)
489+
);
490+
}
491+
481492
// 'tidies' the range by normalizing it into a range and removing leading
482493
// and trailing whitespace.
483494
function tidyLocation(location: Range | Position): Range {

src/vscode-dts/vscode.d.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17195,7 +17195,10 @@ declare module 'vscode' {
1719517195
runHandler: (request: TestRunRequest, token: CancellationToken) => Thenable<void> | void;
1719617196

1719717197
/**
17198-
* A function that provides detailed statement and function-level coverage for a file.
17198+
* An extension-provided function that provides detailed statement and
17199+
* function-level coverage for a file. The editor will call this when more
17200+
* detail is needed for a file, such as when it's opened in an editor or
17201+
* expanded in the **Test Coverage** view.
1719917202
*
1720017203
* The {@link FileCoverage} object passed to this function is the same instance
1720117204
* emitted on {@link TestRun.addCoverage} calls associated with this profile.

0 commit comments

Comments
 (0)