diff --git a/package.json b/package.json index e10303a6..d1cdf38b 100644 --- a/package.json +++ b/package.json @@ -341,6 +341,14 @@ "type": "boolean", "description": "%configuration.java.test.config.coverage.appendResult.description%", "default": true + }, + "excludes": { + "type": "array", + "items": { + "type": "string" + }, + "description": "%configuration.java.test.config.coverage.excludes.description%", + "default": [] } } } @@ -501,6 +509,14 @@ "type": "boolean", "description": "%configuration.java.test.config.coverage.appendResult.description%", "default": true + }, + "excludes": { + "type": "array", + "items": { + "type": "string" + }, + "description": "%configuration.java.test.config.coverage.excludes.description%", + "default": [] } } } diff --git a/package.nls.json b/package.nls.json index 4828c20f..312af320 100644 --- a/package.nls.json +++ b/package.nls.json @@ -36,6 +36,7 @@ "configuration.java.test.config.javaExec.description": "The path to java executable to use. For example: `C:\\Program Files\\jdk\\bin\\java.exe`. If unset project JDK's java executable is used.", "configuration.java.test.config.coverage.description": "The configurations for test coverage.", "configuration.java.test.config.coverage.appendResult.description": "Whether the coverage result is appended.", + "configuration.java.test.config.coverage.excludes.description": "A list of source files that should be excluded from coverage analysis. The can use any valid [minimatch](https://www.npmjs.com/package/minimatch) pattern.", "contributes.viewsWelcome.inLightWeightMode": "No test cases are listed because the Java Language Server is currently running in [LightWeight Mode](https://aka.ms/vscode-java-lightweight). To show test cases, click on the button to switch to Standard Mode.\n[Switch to Standard Mode](command:java.server.mode.switch?%5B%22Standard%22,true%5D)", "contributes.viewsWelcome.enableTests": "Click below button to configure a test framework for your project.\n[Enable Java Tests](command:_java.test.enableTests)" } diff --git a/src/controller/testController.ts b/src/controller/testController.ts index 4242f7ae..6a8b9def 100644 --- a/src/controller/testController.ts +++ b/src/controller/testController.ts @@ -177,6 +177,8 @@ export const runTests: (request: TestRunRequest, option: IRunOption) => any = in let coverageProvider: JavaTestCoverageProvider | undefined; if (request.profile?.kind === TestRunProfileKind.Coverage) { coverageProvider = new JavaTestCoverageProvider(); + // QUESTION: Fix this? + // eslint-disable-next-line @typescript-eslint/no-unused-vars request.profile.loadDetailedCoverage = (_testRun: TestRun, fileCoverage: FileCoverage, _token: CancellationToken): Promise => { return Promise.resolve(coverageProvider!.getCoverageDetails(fileCoverage.uri)); }; @@ -265,7 +267,7 @@ export const runTests: (request: TestRunRequest, option: IRunOption) => any = in } } if (request.profile?.kind === TestRunProfileKind.Coverage) { - await coverageProvider!.provideFileCoverage(run, projectName); + await coverageProvider!.provideFileCoverage(testContext); } } } diff --git a/src/java-test-runner.api.ts b/src/java-test-runner.api.ts index df35283b..fa0d5147 100644 --- a/src/java-test-runner.api.ts +++ b/src/java-test-runner.api.ts @@ -352,6 +352,14 @@ export interface IExecutionConfig { * @since 0.41.0 */ appendResult?: boolean; + + /** + * A list of source files that should be excluded from coverage analysis. + * The can use any valid [minimatch](https://www.npmjs.com/package/minimatch) + * pattern. + * @since 0.43.2 + */ + excludes?: string[]; } /** diff --git a/src/provider/JavaTestCoverageProvider.ts b/src/provider/JavaTestCoverageProvider.ts index 6f10671b..4a4fbada 100644 --- a/src/provider/JavaTestCoverageProvider.ts +++ b/src/provider/JavaTestCoverageProvider.ts @@ -1,19 +1,34 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. - -import { BranchCoverage, DeclarationCoverage, FileCoverage, FileCoverageDetail, Position, StatementCoverage, TestRun, Uri } from 'vscode'; +import * as minimatch from 'minimatch'; +import { BranchCoverage, DeclarationCoverage, FileCoverage, FileCoverageDetail, Position, StatementCoverage, Uri } from 'vscode'; import { getJacocoReportBasePath } from '../utils/coverageUtils'; import { executeJavaLanguageServerCommand } from '../utils/commandUtils'; import { JavaTestRunnerDelegateCommands } from '../constants'; +import { IRunTestContext } from '../java-test-runner.api'; export class JavaTestCoverageProvider { private coverageDetails: Map = new Map(); - public async provideFileCoverage(run: TestRun, projectName: string): Promise { + public async provideFileCoverage({testRun: run, projectName, testConfig}: IRunTestContext): Promise { const sourceFileCoverages: ISourceFileCoverage[] = await executeJavaLanguageServerCommand(JavaTestRunnerDelegateCommands.GET_COVERAGE_DETAIL, projectName, getJacocoReportBasePath(projectName)) || []; - for (const sourceFileCoverage of sourceFileCoverages) { + const sourceFileCoverageExclusions: minimatch.Minimatch[] = (testConfig?.coverage?.excludes ?? []).map((exclusion: string) => + new minimatch.Minimatch(exclusion, {flipNegate: true, nonegate: true})); + const sourceFileCoveragesToReport: ISourceFileCoverage[] = []; + if (sourceFileCoverageExclusions.length <= 0) { + sourceFileCoveragesToReport.push(...sourceFileCoverages); + } else { + sourceFileCoverages.forEach((sourceFileCoverage: ISourceFileCoverage) => { + const uri: Uri = Uri.parse(sourceFileCoverage.uriString); + if (!sourceFileCoverageExclusions.some((exclusion: minimatch.Minimatch) => + exclusion.match(uri.fsPath))) { + sourceFileCoveragesToReport.push(sourceFileCoverage); + } + }); + } + for (const sourceFileCoverage of sourceFileCoveragesToReport) { const uri: Uri = Uri.parse(sourceFileCoverage.uriString); const detailedCoverage: FileCoverageDetail[] = []; for (const lineCoverage of sourceFileCoverage.lineCoverages) {