mirror of
https://github.com/Microsoft/vscode
synced 2024-08-28 05:19:39 +00:00
testing: assorted TPI fixes (#208740)
* testing: bounds check coverage data Fixes #208663 * testing: fix coverage label of boolean counts Fixes #208463 * testing: clarify docs on loadDetailedCoverage Fixes #208724
This commit is contained in:
parent
a8a38595ac
commit
53ff5ca19e
|
@ -2024,6 +2024,10 @@ export namespace TestCoverage {
|
|||
}
|
||||
|
||||
export function fromDetails(coverage: vscode.FileCoverageDetail): CoverageDetails.Serialized {
|
||||
if (typeof coverage.executed === 'number' && coverage.executed < 0) {
|
||||
throw new Error(`Invalid coverage count ${coverage.executed}`);
|
||||
}
|
||||
|
||||
if ('branches' in coverage) {
|
||||
return {
|
||||
count: coverage.executed,
|
||||
|
@ -2044,6 +2048,10 @@ export namespace TestCoverage {
|
|||
}
|
||||
|
||||
export function fromFile(id: string, coverage: vscode.FileCoverage): IFileCoverage.Serialized {
|
||||
types.validateTestCoverageCount(coverage.statementCoverage);
|
||||
types.validateTestCoverageCount(coverage.branchCoverage);
|
||||
types.validateTestCoverageCount(coverage.declarationCoverage);
|
||||
|
||||
return {
|
||||
id,
|
||||
uri: coverage.uri,
|
||||
|
|
|
@ -4027,14 +4027,23 @@ export class TestTag implements vscode.TestTag {
|
|||
//#region Test Coverage
|
||||
export class TestCoverageCount implements vscode.TestCoverageCount {
|
||||
constructor(public covered: number, public total: number) {
|
||||
validateTestCoverageCount(this);
|
||||
}
|
||||
}
|
||||
|
||||
const validateCC = (cc?: vscode.TestCoverageCount) => {
|
||||
if (cc && cc.covered > cc.total) {
|
||||
export function validateTestCoverageCount(cc?: vscode.TestCoverageCount) {
|
||||
if (!cc) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cc.covered > cc.total) {
|
||||
throw new Error(`The total number of covered items (${cc.covered}) cannot be greater than the total (${cc.total})`);
|
||||
}
|
||||
};
|
||||
|
||||
if (cc.total < 0) {
|
||||
throw new Error(`The number of covered items (${cc.total}) cannot be negative`);
|
||||
}
|
||||
}
|
||||
|
||||
export class FileCoverage implements vscode.FileCoverage {
|
||||
public static fromDetails(uri: vscode.Uri, details: vscode.FileCoverageDetail[]): vscode.FileCoverage {
|
||||
|
@ -4077,9 +4086,6 @@ export class FileCoverage implements vscode.FileCoverage {
|
|||
public branchCoverage?: vscode.TestCoverageCount,
|
||||
public declarationCoverage?: vscode.TestCoverageCount,
|
||||
) {
|
||||
validateCC(statementCoverage);
|
||||
validateCC(branchCoverage);
|
||||
validateCC(declarationCoverage);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
import * as dom from 'vs/base/browser/dom';
|
||||
import { HoverWidget } from 'vs/base/browser/ui/hover/hoverWidget';
|
||||
import { mapFindFirst } from 'vs/base/common/arraysFind';
|
||||
import { assertNever } from 'vs/base/common/assert';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
|
@ -31,7 +32,7 @@ import { ILogService } from 'vs/platform/log/common/log';
|
|||
import { testingCoverageMissingBranch } from 'vs/workbench/contrib/testing/browser/icons';
|
||||
import { FileCoverage } from 'vs/workbench/contrib/testing/common/testCoverage';
|
||||
import { ITestCoverageService } from 'vs/workbench/contrib/testing/common/testCoverageService';
|
||||
import { CoverageDetails, DetailType, IStatementCoverage } from 'vs/workbench/contrib/testing/common/testTypes';
|
||||
import { CoverageDetails, DetailType, IDeclarationCoverage, IStatementCoverage } from 'vs/workbench/contrib/testing/common/testTypes';
|
||||
import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys';
|
||||
|
||||
const MAX_HOVERED_LINES = 30;
|
||||
|
@ -452,32 +453,42 @@ export class CoverageDetailsModel {
|
|||
/** Gets the markdown description for the given detail */
|
||||
public describe(detail: CoverageDetailsWithBranch, model: ITextModel): IMarkdownString | undefined {
|
||||
if (detail.type === DetailType.Declaration) {
|
||||
return new MarkdownString().appendMarkdown(localize('coverage.declExecutedCount', '`{0}` was executed {1} time(s).', detail.name, detail.count));
|
||||
return namedDetailLabel(detail.name, detail);
|
||||
} else if (detail.type === DetailType.Statement) {
|
||||
const text = wrapName(model.getValueInRange(tidyLocation(detail.location)).trim() || `<empty statement>`);
|
||||
const str = new MarkdownString();
|
||||
if (detail.branches?.length) {
|
||||
const covered = detail.branches.filter(b => !!b.count).length;
|
||||
str.appendMarkdown(localize('coverage.branches', '{0} of {1} of branches in {2} were covered.', covered, detail.branches.length, text));
|
||||
return new MarkdownString().appendMarkdown(localize('coverage.branches', '{0} of {1} of branches in {2} were covered.', covered, detail.branches.length, text));
|
||||
} else {
|
||||
str.appendMarkdown(localize('coverage.codeExecutedCount', '{0} was executed {1} time(s).', text, detail.count));
|
||||
return namedDetailLabel(text, detail);
|
||||
}
|
||||
return str;
|
||||
} else if (detail.type === DetailType.Branch) {
|
||||
const text = wrapName(model.getValueInRange(tidyLocation(detail.detail.location)).trim() || `<empty statement>`);
|
||||
const { count, label } = detail.detail.branches![detail.branch];
|
||||
const label2 = label ? wrapInBackticks(label) : `#${detail.branch + 1}`;
|
||||
if (count === 0) {
|
||||
if (!count) {
|
||||
return new MarkdownString().appendMarkdown(localize('coverage.branchNotCovered', 'Branch {0} in {1} was not covered.', label2, text));
|
||||
} else if (count === true) {
|
||||
return new MarkdownString().appendMarkdown(localize('coverage.branchCoveredYes', 'Branch {0} in {1} was executed.', label2, text));
|
||||
} else {
|
||||
return new MarkdownString().appendMarkdown(localize('coverage.branchCovered', 'Branch {0} in {1} was executed {2} time(s).', label2, text, count));
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
assertNever(detail);
|
||||
}
|
||||
}
|
||||
|
||||
function namedDetailLabel(name: string, detail: IStatementCoverage | IDeclarationCoverage) {
|
||||
return new MarkdownString().appendMarkdown(
|
||||
!detail.count // 0 or false
|
||||
? localize('coverage.declExecutedNo', '`{0}` was not executed.', name)
|
||||
: typeof detail.count === 'number'
|
||||
? localize('coverage.declExecutedCount', '`{0}` was executed {1} time(s).', name, detail.count)
|
||||
: localize('coverage.declExecutedYes', '`{0}` was executed.', name)
|
||||
);
|
||||
}
|
||||
|
||||
// 'tidies' the range by normalizing it into a range and removing leading
|
||||
// and trailing whitespace.
|
||||
function tidyLocation(location: Range | Position): Range {
|
||||
|
|
5
src/vscode-dts/vscode.d.ts
vendored
5
src/vscode-dts/vscode.d.ts
vendored
|
@ -17195,7 +17195,10 @@ declare module 'vscode' {
|
|||
runHandler: (request: TestRunRequest, token: CancellationToken) => Thenable<void> | void;
|
||||
|
||||
/**
|
||||
* A function that provides detailed statement and function-level coverage for a file.
|
||||
* An extension-provided function that provides detailed statement and
|
||||
* function-level coverage for a file. The editor will call this when more
|
||||
* detail is needed for a file, such as when it's opened in an editor or
|
||||
* expanded in the **Test Coverage** view.
|
||||
*
|
||||
* The {@link FileCoverage} object passed to this function is the same instance
|
||||
* emitted on {@link TestRun.addCoverage} calls associated with this profile.
|
||||
|
|
Loading…
Reference in a new issue