From bf7e461a5f888c03b4fae4080aedfc82b7e37ea3 Mon Sep 17 00:00:00 2001 From: G'lek Tarssza Date: Tue, 4 Nov 2025 13:24:46 -0700 Subject: [PATCH 1/8] Disable a small linting thing. --- src/controller/testController.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/controller/testController.ts b/src/controller/testController.ts index 4242f7ae..6bbbde00 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)); }; From 276ef16c616fb5af6188e51021214cd3ca32ed18 Mon Sep 17 00:00:00 2001 From: G'lek Tarssza Date: Tue, 4 Nov 2025 13:25:05 -0700 Subject: [PATCH 2/8] Pass the whole test run context down. --- src/controller/testController.ts | 2 +- src/provider/JavaTestCoverageProvider.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/controller/testController.ts b/src/controller/testController.ts index 6bbbde00..6a8b9def 100644 --- a/src/controller/testController.ts +++ b/src/controller/testController.ts @@ -267,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/provider/JavaTestCoverageProvider.ts b/src/provider/JavaTestCoverageProvider.ts index 6f10671b..1e09a144 100644 --- a/src/provider/JavaTestCoverageProvider.ts +++ b/src/provider/JavaTestCoverageProvider.ts @@ -5,12 +5,13 @@ import { BranchCoverage, DeclarationCoverage, FileCoverage, FileCoverageDetail, 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) { From 0a72338b86268195c67d5f7d7d15e8f1ba536d73 Mon Sep 17 00:00:00 2001 From: G'lek Tarssza Date: Tue, 4 Nov 2025 13:25:43 -0700 Subject: [PATCH 3/8] Define our new setting. --- package.json | 16 ++++++++++++++++ package.nls.json | 1 + src/java-test-runner.api.ts | 8 ++++++++ 3 files changed, 25 insertions(+) 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/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[]; } /** From 0e88964128ceeb9a9a11f16a61679774fcbf25ee Mon Sep 17 00:00:00 2001 From: G'lek Tarssza Date: Tue, 4 Nov 2025 13:26:34 -0700 Subject: [PATCH 4/8] Remove an unused import. --- src/provider/JavaTestCoverageProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/provider/JavaTestCoverageProvider.ts b/src/provider/JavaTestCoverageProvider.ts index 1e09a144..9b996f8d 100644 --- a/src/provider/JavaTestCoverageProvider.ts +++ b/src/provider/JavaTestCoverageProvider.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -import { BranchCoverage, DeclarationCoverage, FileCoverage, FileCoverageDetail, Position, StatementCoverage, TestRun, Uri } from 'vscode'; +import { BranchCoverage, DeclarationCoverage, FileCoverage, FileCoverageDetail, Position, StatementCoverage, Uri } from 'vscode'; import { getJacocoReportBasePath } from '../utils/coverageUtils'; import { executeJavaLanguageServerCommand } from '../utils/commandUtils'; import { JavaTestRunnerDelegateCommands } from '../constants'; From ea81f251713e97474a4388d9dac66e7752b890ef Mon Sep 17 00:00:00 2001 From: G'lek Tarssza Date: Tue, 4 Nov 2025 13:26:51 -0700 Subject: [PATCH 5/8] Implement new feature. --- src/provider/JavaTestCoverageProvider.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/provider/JavaTestCoverageProvider.ts b/src/provider/JavaTestCoverageProvider.ts index 9b996f8d..4c6b91b9 100644 --- a/src/provider/JavaTestCoverageProvider.ts +++ b/src/provider/JavaTestCoverageProvider.ts @@ -1,6 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. - +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'; @@ -14,7 +14,13 @@ export class JavaTestCoverageProvider { 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})); + const sourceFileCoveragesToReport: ISourceFileCoverage[] = sourceFileCoverageExclusions + .reduce((results: ISourceFileCoverage[], exclusion: minimatch.Minimatch) => + results.filter((sourceFile: ISourceFileCoverage) => + exclusion.match(Uri.parse(sourceFile.uriString).fsPath)), sourceFileCoverages); + for (const sourceFileCoverage of sourceFileCoveragesToReport) { const uri: Uri = Uri.parse(sourceFileCoverage.uriString); const detailedCoverage: FileCoverageDetail[] = []; for (const lineCoverage of sourceFileCoverage.lineCoverages) { From 870dcdbc884a86232a99013a9c4a0ce018efe685 Mon Sep 17 00:00:00 2001 From: G'lek Tarssza Date: Tue, 4 Nov 2025 13:29:33 -0700 Subject: [PATCH 6/8] Ensure coverage surfaces when no exclusions are set. --- src/provider/JavaTestCoverageProvider.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/provider/JavaTestCoverageProvider.ts b/src/provider/JavaTestCoverageProvider.ts index 4c6b91b9..c69f24a2 100644 --- a/src/provider/JavaTestCoverageProvider.ts +++ b/src/provider/JavaTestCoverageProvider.ts @@ -16,10 +16,12 @@ export class JavaTestCoverageProvider { projectName, getJacocoReportBasePath(projectName)) || []; const sourceFileCoverageExclusions: minimatch.Minimatch[] = (testConfig?.coverage?.excludes ?? []).map((exclusion: string) => new minimatch.Minimatch(exclusion, {flipNegate: true})); - const sourceFileCoveragesToReport: ISourceFileCoverage[] = sourceFileCoverageExclusions - .reduce((results: ISourceFileCoverage[], exclusion: minimatch.Minimatch) => - results.filter((sourceFile: ISourceFileCoverage) => - exclusion.match(Uri.parse(sourceFile.uriString).fsPath)), sourceFileCoverages); + const sourceFileCoveragesToReport: ISourceFileCoverage[] = sourceFileCoverageExclusions.length > 0 ? + sourceFileCoverageExclusions + .reduce((results: ISourceFileCoverage[], exclusion: minimatch.Minimatch) => + results.filter((sourceFile: ISourceFileCoverage) => + exclusion.match(Uri.parse(sourceFile.uriString).fsPath)), sourceFileCoverages) : + sourceFileCoverages; for (const sourceFileCoverage of sourceFileCoveragesToReport) { const uri: Uri = Uri.parse(sourceFileCoverage.uriString); const detailedCoverage: FileCoverageDetail[] = []; From ac6e71e633b49a3e3534551cb6f2216b91e5fdb9 Mon Sep 17 00:00:00 2001 From: G'lek Tarssza Date: Tue, 4 Nov 2025 13:45:22 -0700 Subject: [PATCH 7/8] Rewrote solution. Sometimes simplicity is best. --- src/provider/JavaTestCoverageProvider.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/provider/JavaTestCoverageProvider.ts b/src/provider/JavaTestCoverageProvider.ts index c69f24a2..fcc50d12 100644 --- a/src/provider/JavaTestCoverageProvider.ts +++ b/src/provider/JavaTestCoverageProvider.ts @@ -16,12 +16,18 @@ export class JavaTestCoverageProvider { projectName, getJacocoReportBasePath(projectName)) || []; const sourceFileCoverageExclusions: minimatch.Minimatch[] = (testConfig?.coverage?.excludes ?? []).map((exclusion: string) => new minimatch.Minimatch(exclusion, {flipNegate: true})); - const sourceFileCoveragesToReport: ISourceFileCoverage[] = sourceFileCoverageExclusions.length > 0 ? - sourceFileCoverageExclusions - .reduce((results: ISourceFileCoverage[], exclusion: minimatch.Minimatch) => - results.filter((sourceFile: ISourceFileCoverage) => - exclusion.match(Uri.parse(sourceFile.uriString).fsPath)), sourceFileCoverages) : - sourceFileCoverages; + 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[] = []; From 38d6f127146446e56eacec1f450fd64cab0228b9 Mon Sep 17 00:00:00 2001 From: G'lek Tarssza Date: Tue, 4 Nov 2025 13:58:51 -0700 Subject: [PATCH 8/8] Do not permit negating. --- src/provider/JavaTestCoverageProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/provider/JavaTestCoverageProvider.ts b/src/provider/JavaTestCoverageProvider.ts index fcc50d12..4a4fbada 100644 --- a/src/provider/JavaTestCoverageProvider.ts +++ b/src/provider/JavaTestCoverageProvider.ts @@ -15,7 +15,7 @@ export class JavaTestCoverageProvider { const sourceFileCoverages: ISourceFileCoverage[] = await executeJavaLanguageServerCommand(JavaTestRunnerDelegateCommands.GET_COVERAGE_DETAIL, projectName, getJacocoReportBasePath(projectName)) || []; const sourceFileCoverageExclusions: minimatch.Minimatch[] = (testConfig?.coverage?.excludes ?? []).map((exclusion: string) => - new minimatch.Minimatch(exclusion, {flipNegate: true})); + new minimatch.Minimatch(exclusion, {flipNegate: true, nonegate: true})); const sourceFileCoveragesToReport: ISourceFileCoverage[] = []; if (sourceFileCoverageExclusions.length <= 0) { sourceFileCoveragesToReport.push(...sourceFileCoverages);