diff --git a/.azure-pipelines/vscode-java-test-nightly.yml b/.azure-pipelines/vscode-java-test-nightly.yml
index b6d4d210..0883cfa0 100644
--- a/.azure-pipelines/vscode-java-test-nightly.yml
+++ b/.azure-pipelines/vscode-java-test-nightly.yml
@@ -67,12 +67,6 @@ extends:
command: custom
verbose: false
customCommand: run build-plugin
- - task: Npm@1
- displayName: npm run compile
- inputs:
- command: custom
- verbose: false
- customCommand: run compile
- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2
displayName: ESRP CodeSigning
inputs:
diff --git a/.azure-pipelines/vscode-java-test-rc.yml b/.azure-pipelines/vscode-java-test-rc.yml
index 9c5e811e..cdcbe1d1 100644
--- a/.azure-pipelines/vscode-java-test-rc.yml
+++ b/.azure-pipelines/vscode-java-test-rc.yml
@@ -62,12 +62,6 @@ extends:
command: custom
verbose: false
customCommand: run build-plugin
- - task: Npm@1
- displayName: npm run compile
- inputs:
- command: custom
- verbose: false
- customCommand: run compile
- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2
displayName: ESRP CodeSigning
inputs:
diff --git a/.gitignore b/.gitignore
index 1e62f707..afc3a180 100644
--- a/.gitignore
+++ b/.gitignore
@@ -52,6 +52,4 @@ node_modules
resources/templates/css/**
resources/templates/js/**
resources/templates/fonts/**
-dist
-**/vscode.d.ts
-**/vscode.proposed.d.ts
\ No newline at end of file
+dist
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 647d5799..02210eba 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -13,5 +13,7 @@
"**/archetype-resources/**",
"**/META-INF/maven/**",
"**/test/test-projects/**"
- ]
+ ],
+ "java.checkstyle.version": "8.18",
+ "java.checkstyle.configuration": "${workspaceFolder}/java-extension/build-tools/src/main/resources/checkstyle/checkstyle.xml",
}
\ No newline at end of file
diff --git a/.vscodeignore b/.vscodeignore
index e64e5a26..1ef8a2ad 100644
--- a/.vscodeignore
+++ b/.vscodeignore
@@ -21,6 +21,7 @@ extension.bundle.ts
javaConfig.json
.github/**
.azure-pipelines/**
+vscode.d.ts
# Ignore output of code sign
server/*.md
diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt
index 20b9dedf..7458b648 100644
--- a/ThirdPartyNotices.txt
+++ b/ThirdPartyNotices.txt
@@ -12,12 +12,13 @@ This project incorporates components from the projects listed below. The origina
5. Eskibear/vscode-extension-telemetry-wrapper (https://github.com/Eskibear/vscode-extension-telemetry-wrapper)
6. google/gson (https://github.com/google/gson)
7. isaacs/node-lru-cache (https://github.com/isaacs/node-lru-cache)
-8. jprichardson/node-fs-extra (https://github.com/jprichardson/node-fs-extra)
-9. junit-team/junit5 (https://github.com/junit-team/junit5)
-10. lodash/lodash (https://github.com/lodash/lodash)
-11. microsoft/vscode-languageserver-node (https://github.com/microsoft/vscode-languageserver-node)
-12. ota4j-team/opentest4j (https://github.com/ota4j-team/opentest4j)
-13. sindresorhus/get-port (https://github.com/sindresorhus/get-port)
+8. jacoco/jacoco (https://github.com/jacoco/jacoco)
+9. jprichardson/node-fs-extra (https://github.com/jprichardson/node-fs-extra)
+10. junit-team/junit5 (https://github.com/junit-team/junit5)
+11. lodash/lodash (https://github.com/lodash/lodash)
+12. microsoft/vscode-languageserver-node (https://github.com/microsoft/vscode-languageserver-node)
+13. ota4j-team/opentest4j (https://github.com/ota4j-team/opentest4j)
+14. sindresorhus/get-port (https://github.com/sindresorhus/get-port)
%% Apache Commons Lang NOTICES AND INFORMATION BEGIN HERE
=========================================
@@ -976,6 +977,25 @@ IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
=========================================
END OF isaacs/node-lru-cache NOTICES AND INFORMATION
+%% jacoco/jacoco NOTICES AND INFORMATION BEGIN HERE
+=========================================
+License
+=======
+
+Copyright (c) 2009, 2023 Mountainminds GmbH & Co. KG and Contributors
+
+The JaCoCo Java Code Coverage Library and all included documentation is made
+available by Mountainminds GmbH & Co. KG, Munich. Except indicated below, the
+Content is provided to you under the terms and conditions of the Eclipse Public
+License Version 2.0 ("EPL"). A copy of the EPL is available at
+[https://www.eclipse.org/legal/epl-2.0/](https://www.eclipse.org/legal/epl-2.0/).
+
+Please visit
+[http://www.jacoco.org/jacoco/trunk/doc/license.html](http://www.jacoco.org/jacoco/trunk/doc/license.html)
+for the complete license information including third party licenses and trademarks.
+=========================================
+END OF jacoco/jacoco NOTICES AND INFORMATION
+
%% jprichardson/node-fs-extra NOTICES AND INFORMATION BEGIN HERE
=========================================
(The MIT License)
diff --git a/java-extension/com.microsoft.java.test.plugin.test/projects/coverage-test/.classpath b/java-extension/com.microsoft.java.test.plugin.test/projects/coverage-test/.classpath
new file mode 100644
index 00000000..df66b20f
--- /dev/null
+++ b/java-extension/com.microsoft.java.test.plugin.test/projects/coverage-test/.classpath
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/java-extension/com.microsoft.java.test.plugin.test/projects/coverage-test/.project b/java-extension/com.microsoft.java.test.plugin.test/projects/coverage-test/.project
new file mode 100644
index 00000000..ef32fdce
--- /dev/null
+++ b/java-extension/com.microsoft.java.test.plugin.test/projects/coverage-test/.project
@@ -0,0 +1,34 @@
+
+
+ coverage-test
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ org.eclipse.m2e.core.maven2Nature
+
+
+
+ 1709104275722
+
+ 30
+
+ org.eclipse.core.resources.regexFilterMatcher
+ node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__
+
+
+
+
diff --git a/java-extension/com.microsoft.java.test.plugin.test/projects/coverage-test/jacoco.exec b/java-extension/com.microsoft.java.test.plugin.test/projects/coverage-test/jacoco.exec
new file mode 100644
index 00000000..24af8ac9
Binary files /dev/null and b/java-extension/com.microsoft.java.test.plugin.test/projects/coverage-test/jacoco.exec differ
diff --git a/java-extension/com.microsoft.java.test.plugin.test/projects/coverage-test/pom.xml b/java-extension/com.microsoft.java.test.plugin.test/projects/coverage-test/pom.xml
new file mode 100644
index 00000000..39fb6783
--- /dev/null
+++ b/java-extension/com.microsoft.java.test.plugin.test/projects/coverage-test/pom.xml
@@ -0,0 +1,49 @@
+
+
+ 4.0.0
+
+ com.example
+ coverage-test
+ 1.0-SNAPSHOT
+
+
+ UTF-8
+ 17
+ ${maven.compiler.source}
+
+
+
+
+
+ org.junit
+ junit-bom
+ 5.7.1
+ pom
+ import
+
+
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+
+
+
+
+ maven-compiler-plugin
+ 3.8.1
+
+
+ maven-surefire-plugin
+ 2.22.2
+
+
+
+
+
diff --git a/java-extension/com.microsoft.java.test.plugin.test/projects/coverage-test/src/main/java/com/example/project/Sample.java b/java-extension/com.microsoft.java.test.plugin.test/projects/coverage-test/src/main/java/com/example/project/Sample.java
new file mode 100644
index 00000000..8424ead9
--- /dev/null
+++ b/java-extension/com.microsoft.java.test.plugin.test/projects/coverage-test/src/main/java/com/example/project/Sample.java
@@ -0,0 +1,54 @@
+package com.example.project;
+
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+public class Sample {
+
+ void sample(boolean x) {
+ Foo foo = getFoo();
+ if (x) {
+ foo = new Foo(); // <-- BOTH THIS LINE
+ }
+ Bar bar = new Bar();
+ Baz baz = new Baz();
+ baz.from(bar::bar)
+ .as(Sample::toArray) // <-- AND THIS LINE
+ .to(foo::foo);
+ }
+
+ static String[] toArray(List s) {
+ return s.toArray(String[]::new);
+ }
+
+ static Foo getFoo() {
+ return new Foo();
+ }
+
+ static class Foo {
+ void foo(String... foo) {
+ }
+ }
+
+ static class Bar {
+ List bar() {
+ return List.of("bar");
+ }
+ }
+
+ static class Baz {
+ Baz from(Supplier> from) {
+ return this;
+ }
+
+ Baz as(Function, String[]> as) {
+ return this;
+ }
+
+ Baz to(Consumer to) {
+ return this;
+ }
+ }
+}
diff --git a/java-extension/com.microsoft.java.test.plugin.test/projects/coverage-test/src/test/java/com/example/project/SampleTests.java b/java-extension/com.microsoft.java.test.plugin.test/projects/coverage-test/src/test/java/com/example/project/SampleTests.java
new file mode 100644
index 00000000..09c53088
--- /dev/null
+++ b/java-extension/com.microsoft.java.test.plugin.test/projects/coverage-test/src/test/java/com/example/project/SampleTests.java
@@ -0,0 +1,13 @@
+package com.example.project;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class SampleTests {
+
+ @Test
+ void test() {
+ assertTrue(true);
+ }
+}
diff --git a/java-extension/com.microsoft.java.test.plugin.test/src/com/microsoft/java/test/plugin/coverage/CoverageHandlerTest.java b/java-extension/com.microsoft.java.test.plugin.test/src/com/microsoft/java/test/plugin/coverage/CoverageHandlerTest.java
new file mode 100644
index 00000000..00b063e6
--- /dev/null
+++ b/java-extension/com.microsoft.java.test.plugin.test/src/com/microsoft/java/test/plugin/coverage/CoverageHandlerTest.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+* Copyright (c) 2024 Microsoft Corporation and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*
+* Contributors:
+* Microsoft Corporation - initial API and implementation
+*******************************************************************************/
+
+package com.microsoft.java.test.plugin.coverage;
+
+import com.microsoft.java.test.plugin.AbstractProjectsManagerBasedTest;
+import com.microsoft.java.test.plugin.coverage.model.LineCoverage;
+import com.microsoft.java.test.plugin.coverage.model.MethodCoverage;
+import com.microsoft.java.test.plugin.coverage.model.SourceFileCoverage;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.ls.core.internal.ProjectUtils;
+import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager;
+import org.junit.Test;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.Assert.assertTrue;
+
+public class CoverageHandlerTest extends AbstractProjectsManagerBasedTest {
+
+ @Test
+ public void testGetCoverageDetail() throws Exception {
+ importProjects(Collections.singleton("coverage-test"));
+ final IJavaProject javaProject = ProjectUtils.getJavaProject("coverage-test");
+ final String basePath = new File("projects/coverage-test").getAbsolutePath();
+ final CoverageHandler coverageHandler = new CoverageHandler(javaProject, basePath);
+ final List coverageDetail = coverageHandler.getCoverageDetail(new NullProgressMonitor());
+ for (final SourceFileCoverage fileCoverage : coverageDetail) {
+ for (final LineCoverage lineCoverage : fileCoverage.getLineCoverages()) {
+ assertTrue(lineCoverage.getLineNumber() > 0);
+ }
+
+ for (final MethodCoverage methodCoverage : fileCoverage.getMethodCoverages()) {
+ assertTrue(methodCoverage.getLineNumber() > 0);
+ }
+ }
+ }
+
+}
diff --git a/java-extension/com.microsoft.java.test.plugin/META-INF/MANIFEST.MF b/java-extension/com.microsoft.java.test.plugin/META-INF/MANIFEST.MF
index bce8d61c..491a9225 100644
--- a/java-extension/com.microsoft.java.test.plugin/META-INF/MANIFEST.MF
+++ b/java-extension/com.microsoft.java.test.plugin/META-INF/MANIFEST.MF
@@ -39,7 +39,11 @@ Require-Bundle: org.eclipse.jdt.core,
junit-platform-suite-engine;bundle-version="1.8.1",
org.apiguardian.api;bundle-version="1.0.0",
org.apache.commons.lang3;bundle-version="3.1.0",
- com.google.gson;bundle-version="2.7.0"
+ com.google.gson;bundle-version="2.7.0",
+ org.objectweb.asm;bundle-version="9.6.0",
+ org.jacoco.core;bundle-version="0.8.11"
Export-Package: com.microsoft.java.test.plugin.launchers;x-friends:="com.microsoft.java.test.plugin.test",
- com.microsoft.java.test.plugin.model;x-friends:="com.microsoft.java.test.plugin.test"
+ com.microsoft.java.test.plugin.model;x-friends:="com.microsoft.java.test.plugin.test",
+ com.microsoft.java.test.plugin.coverage;x-friends:="com.microsoft.java.test.plugin.test",
+ com.microsoft.java.test.plugin.coverage.model;x-friends:="com.microsoft.java.test.plugin.test"
Bundle-ClassPath: .
diff --git a/java-extension/com.microsoft.java.test.plugin/plugin.xml b/java-extension/com.microsoft.java.test.plugin/plugin.xml
index 7ec8a6ef..246edb61 100644
--- a/java-extension/com.microsoft.java.test.plugin/plugin.xml
+++ b/java-extension/com.microsoft.java.test.plugin/plugin.xml
@@ -13,6 +13,7 @@
+
diff --git a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/coverage/CoverageHandler.java b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/coverage/CoverageHandler.java
new file mode 100644
index 00000000..aefb55de
--- /dev/null
+++ b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/coverage/CoverageHandler.java
@@ -0,0 +1,270 @@
+/*******************************************************************************
+* Copyright (c) 2023 Microsoft Corporation and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*
+* Contributors:
+* Microsoft Corporation - initial API and implementation
+*******************************************************************************/
+
+package com.microsoft.java.test.plugin.coverage;
+
+import com.microsoft.java.test.plugin.coverage.model.BranchCoverage;
+import com.microsoft.java.test.plugin.coverage.model.LineCoverage;
+import com.microsoft.java.test.plugin.coverage.model.MethodCoverage;
+import com.microsoft.java.test.plugin.coverage.model.SourceFileCoverage;
+import com.microsoft.java.test.plugin.util.JUnitPlugin;
+import com.microsoft.java.test.plugin.util.ProjectTestUtils;
+
+import org.apache.commons.lang3.StringUtils;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.Signature;
+import org.eclipse.jdt.internal.core.ClasspathEntry;
+import org.eclipse.jdt.ls.core.internal.ProjectUtils;
+import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager;
+import org.jacoco.core.analysis.Analyzer;
+import org.jacoco.core.analysis.CoverageBuilder;
+import org.jacoco.core.analysis.IClassCoverage;
+import org.jacoco.core.analysis.ICounter;
+import org.jacoco.core.analysis.ILine;
+import org.jacoco.core.analysis.IMethodCoverage;
+import org.jacoco.core.analysis.ISourceFileCoverage;
+import org.jacoco.core.analysis.ISourceNode;
+import org.jacoco.core.tools.ExecFileLoader;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class CoverageHandler {
+
+ private IJavaProject javaProject;
+ private Path reportBasePath;
+
+ /**
+ * The Jacoco data file name
+ */
+ private static final String JACOCO_EXEC = "jacoco.exec";
+
+ public CoverageHandler(IJavaProject javaProject, String basePath) {
+ this.javaProject = javaProject;
+ reportBasePath = Paths.get(basePath);
+ }
+
+ public List getCoverageDetail(IProgressMonitor monitor) throws JavaModelException, IOException {
+ if (ProjectsManager.DEFAULT_PROJECT_NAME.equals(javaProject.getProject().getName())) {
+ return Collections.emptyList();
+ }
+ final List coverage = new LinkedList<>();
+ final List javaProjects = new LinkedList<>();
+ javaProjects.addAll(getAllJavaProjects(javaProject));
+ final Map> outputToSourcePaths = getOutputToSourcePathsMapping(javaProjects);
+
+ final File executionDataFile = reportBasePath.resolve(JACOCO_EXEC).toFile();
+ final ExecFileLoader execFileLoader = new ExecFileLoader();
+ execFileLoader.load(executionDataFile);
+ for (final Map.Entry> entry : outputToSourcePaths.entrySet()) {
+ final CoverageBuilder coverageBuilder = new CoverageBuilder();
+ final Analyzer analyzer = new Analyzer(
+ execFileLoader.getExecutionDataStore(), coverageBuilder);
+ final File outputDirectory = entry.getKey().toFile();
+ if (!outputDirectory.exists()) {
+ continue;
+ }
+ analyzer.analyzeAll(outputDirectory);
+ final Map> classCoverageBySourceFilePath =
+ groupClassCoverageBySourceFilePath(coverageBuilder.getClasses());
+ for (final ISourceFileCoverage sourceFileCoverage : coverageBuilder.getSourceFiles()) {
+ if (monitor.isCanceled()) {
+ return Collections.emptyList();
+ }
+
+ if (sourceFileCoverage.getFirstLine() == ISourceNode.UNKNOWN_LINE) {
+ JUnitPlugin.logError("Missing debug information for file: " + sourceFileCoverage.getName());
+ continue; // no debug information
+ }
+ final File sourceFile = getSourceFile(entry.getValue(), sourceFileCoverage);
+ if (sourceFile == null) {
+ JUnitPlugin.logError("Cannot find file: " + sourceFileCoverage.getName());
+ continue;
+ }
+
+ final URI uri = sourceFile.toURI();
+ final List lineCoverages = getLineCoverages(sourceFileCoverage);
+ final String sourcePath = sourceFileCoverage.getPackageName() + "/" +
+ sourceFileCoverage.getName();
+ final List methodCoverages = getMethodCoverages(
+ classCoverageBySourceFilePath.get(sourcePath), sourceFileCoverage);
+ coverage.add(new SourceFileCoverage(uri.toString(), lineCoverages, methodCoverages));
+ }
+ }
+ return coverage;
+ }
+
+ private Map> getOutputToSourcePathsMapping(List javaProjects)
+ throws JavaModelException {
+ final Map> outputToSourcePaths = new HashMap<>();
+ for (final IJavaProject javaProject : javaProjects) {
+ for (final IClasspathEntry entry : javaProject.getRawClasspath()) {
+ if (entry.getEntryKind() != ClasspathEntry.CPE_SOURCE ||
+ ProjectTestUtils.isTestEntry(entry)) {
+ continue;
+ }
+
+ final IProject project = javaProject.getProject();
+ final IPath sourceRelativePath = entry.getPath().makeRelativeTo(project.getFullPath());
+ final IPath realSourcePath = project.getFolder(sourceRelativePath).getLocation();
+
+ IPath outputLocation = entry.getOutputLocation();
+ if (outputLocation == null) {
+ outputLocation = javaProject.getOutputLocation();
+ }
+ final IPath outputRelativePath = outputLocation.makeRelativeTo(javaProject.getProject().getFullPath());
+ final IPath realOutputPath = project.getFolder(outputRelativePath).getLocation();
+
+ outputToSourcePaths.computeIfAbsent(realOutputPath, k -> new LinkedList<>())
+ .add(realSourcePath);
+ }
+ }
+ return outputToSourcePaths;
+ }
+
+ private Map> groupClassCoverageBySourceFilePath(
+ final Collection classCoverages) {
+ final Map> result = new HashMap<>();
+ for (final IClassCoverage classCoverage : classCoverages) {
+ final String key = classCoverage.getPackageName() + "/" + classCoverage.getSourceFileName();
+ result.computeIfAbsent(key, k -> new LinkedList<>()).add(classCoverage);
+ }
+ return result;
+ }
+
+ /**
+ * Infer the source file for the given {@link ISourceFileCoverage}. If no file found, return null
.
+ */
+ private File getSourceFile(List sourceRoots, ISourceFileCoverage sourceFileCoverage) {
+ final String packagePath = sourceFileCoverage.getPackageName().replace(".", "/");
+ final IPath sourceRelativePath = new org.eclipse.core.runtime.Path(packagePath)
+ .append(sourceFileCoverage.getName());
+ for (final IPath sourceRoot : sourceRoots) {
+ final File sourceFile = sourceRoot.append(sourceRelativePath).toFile();
+ if (sourceFile.exists()) {
+ return sourceFile;
+ }
+ }
+ return null;
+ }
+
+ private List getLineCoverages(final ISourceFileCoverage sourceFileCoverage) {
+ final List lineCoverages = new LinkedList<>();
+ final int last = sourceFileCoverage.getLastLine();
+ final int first = sourceFileCoverage.getFirstLine();
+ if (first == ISourceNode.UNKNOWN_LINE || last == ISourceNode.UNKNOWN_LINE) {
+ return lineCoverages;
+ }
+ for (int nr = first; nr <= last; nr++) {
+ final ILine line = sourceFileCoverage.getLine(nr);
+ if (line.getStatus() != ICounter.EMPTY) {
+ final List branchCoverages = new LinkedList<>();
+ for (int i = 0; i < line.getBranchCounter().getTotalCount(); i++) {
+ branchCoverages.add(new BranchCoverage(
+ i < line.getBranchCounter().getCoveredCount() ? 1 : 0));
+ }
+ lineCoverages.add(new LineCoverage(
+ nr,
+ line.getInstructionCounter().getCoveredCount(),
+ branchCoverages
+ ));
+ }
+ }
+ return lineCoverages;
+ }
+
+ private List getMethodCoverages(final Collection classCoverages,
+ final ISourceFileCoverage sourceFileCoverage) {
+ if (classCoverages == null || classCoverages.isEmpty()) {
+ return Collections.emptyList();
+ }
+ final List methodCoverages = new LinkedList<>();
+ for (final IClassCoverage classCoverage : classCoverages) {
+ for (final IMethodCoverage methodCoverage : classCoverage.getMethods()) {
+ if (methodCoverage.getFirstLine() == ISourceNode.UNKNOWN_LINE) {
+ continue;
+ }
+ methodCoverages.add(new MethodCoverage(
+ methodCoverage.getFirstLine(),
+ methodCoverage.getMethodCounter().getCoveredCount() > 0 ? 1 : 0,
+ getMethodName(methodCoverage)
+ ));
+ }
+ }
+ return methodCoverages;
+ }
+
+ private String getMethodName(IMethodCoverage methodCoverage) {
+ final String methodName = methodCoverage.getName();
+ if ("".equals(methodName) || "".equals(methodName)) {
+ return methodName;
+ }
+ final String signature = methodCoverage.getDesc();
+ if (StringUtils.isBlank(signature)) {
+ return methodName;
+ }
+
+ try {
+ final String[] parameterTypes = Signature.getParameterTypes(signature);
+ final List parameterNames = new LinkedList<>();
+ if (parameterTypes.length > 0) {
+ for (final String parameterType : parameterTypes) {
+ final String simpleName = Signature.getSignatureSimpleName(parameterType.replace("/", "."));
+ parameterNames.add(simpleName);
+ }
+ }
+ return String.format("%s(%s)", methodName, String.join(", ", parameterNames));
+ } catch (IllegalArgumentException e) {
+ return methodName;
+ }
+ }
+
+ /**
+ * Returns a collection containing all Java projects that are required by the given {@link IJavaProject}
+ * and the input java project as well.
+ */
+ private static Collection getAllJavaProjects(IJavaProject javaProject)
+ throws JavaModelException {
+ final Set result = new LinkedHashSet<>();
+ final List toScan = new LinkedList<>();
+ toScan.add(javaProject);
+ while (!toScan.isEmpty()) {
+ final IJavaProject currentProject = toScan.remove(0);
+ if (result.contains(currentProject)) {
+ continue;
+ }
+ result.add(currentProject);
+ for (final String projectName : currentProject.getRequiredProjectNames()) {
+ final IJavaProject requiredProject = ProjectUtils.getJavaProject(projectName);
+ if (requiredProject != null) {
+ toScan.add(requiredProject);
+ }
+ }
+ }
+ return result;
+ }
+}
diff --git a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/coverage/model/BranchCoverage.java b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/coverage/model/BranchCoverage.java
new file mode 100644
index 00000000..f0a6ca61
--- /dev/null
+++ b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/coverage/model/BranchCoverage.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+* Copyright (c) 2023 Microsoft Corporation and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*
+* Contributors:
+* Microsoft Corporation - initial API and implementation
+*******************************************************************************/
+
+package com.microsoft.java.test.plugin.coverage.model;
+
+public class BranchCoverage {
+ int hit;
+
+ public BranchCoverage(int hit) {
+ this.hit = hit;
+ }
+
+ public int getHit() {
+ return hit;
+ }
+
+ public void setHit(int hit) {
+ this.hit = hit;
+ }
+}
diff --git a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/coverage/model/LineCoverage.java b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/coverage/model/LineCoverage.java
new file mode 100644
index 00000000..fce6f361
--- /dev/null
+++ b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/coverage/model/LineCoverage.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+* Copyright (c) 2023 Microsoft Corporation and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*
+* Contributors:
+* Microsoft Corporation - initial API and implementation
+*******************************************************************************/
+
+package com.microsoft.java.test.plugin.coverage.model;
+
+import java.util.List;
+
+public class LineCoverage {
+ int lineNumber;
+ int hit;
+ List branchCoverages;
+
+ public LineCoverage(int lineNumber, int hit, List branchCoverages) {
+ this.lineNumber = lineNumber;
+ this.hit = hit;
+ this.branchCoverages = branchCoverages;
+ }
+
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ public void setLineNumber(int lineNumber) {
+ this.lineNumber = lineNumber;
+ }
+
+ public int getHit() {
+ return hit;
+ }
+
+ public void setHit(int hit) {
+ this.hit = hit;
+ }
+
+ public List getBranchCoverages() {
+ return branchCoverages;
+ }
+
+ public void setBranchCoverages(List branchCoverages) {
+ this.branchCoverages = branchCoverages;
+ }
+
+}
diff --git a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/coverage/model/MethodCoverage.java b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/coverage/model/MethodCoverage.java
new file mode 100644
index 00000000..9686e561
--- /dev/null
+++ b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/coverage/model/MethodCoverage.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+* Copyright (c) 2023 Microsoft Corporation and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*
+* Contributors:
+* Microsoft Corporation - initial API and implementation
+*******************************************************************************/
+
+package com.microsoft.java.test.plugin.coverage.model;
+
+public class MethodCoverage {
+ int lineNumber;
+ int hit;
+ String name;
+
+ public MethodCoverage(int lineNumber, int hit, String name) {
+ this.lineNumber = lineNumber;
+ this.hit = hit;
+ this.name = name;
+ }
+
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ public void setLineNumber(int lineNumber) {
+ this.lineNumber = lineNumber;
+ }
+
+ public int getHit() {
+ return hit;
+ }
+
+ public void setHit(int hit) {
+ this.hit = hit;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+}
diff --git a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/coverage/model/SourceFileCoverage.java b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/coverage/model/SourceFileCoverage.java
new file mode 100644
index 00000000..a9dae3ca
--- /dev/null
+++ b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/coverage/model/SourceFileCoverage.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+* Copyright (c) 2023 Microsoft Corporation and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*
+* Contributors:
+* Microsoft Corporation - initial API and implementation
+*******************************************************************************/
+
+package com.microsoft.java.test.plugin.coverage.model;
+
+import java.util.List;
+
+public class SourceFileCoverage {
+ String uriString;
+ List lineCoverages;
+ List methodCoverages;
+
+ public SourceFileCoverage(String uriString, List lineCoverages,
+ List methodCoverages) {
+ this.uriString = uriString;
+ this.lineCoverages = lineCoverages;
+ this.methodCoverages = methodCoverages;
+ }
+
+ public String getUriString() {
+ return uriString;
+ }
+
+ public void setUriString(String uriString) {
+ this.uriString = uriString;
+ }
+
+ public List getLineCoverages() {
+ return lineCoverages;
+ }
+
+ public void setLineCoverages(List lineCoverages) {
+ this.lineCoverages = lineCoverages;
+ }
+
+ public List getMethodCoverages() {
+ return methodCoverages;
+ }
+
+ public void setMethodCoverages(List methodCoverages) {
+ this.methodCoverages = methodCoverages;
+ }
+}
diff --git a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/handler/TestDelegateCommandHandler.java b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/handler/TestDelegateCommandHandler.java
index 4be392c1..3b726cef 100644
--- a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/handler/TestDelegateCommandHandler.java
+++ b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/handler/TestDelegateCommandHandler.java
@@ -11,7 +11,9 @@
package com.microsoft.java.test.plugin.handler;
+import com.microsoft.java.test.plugin.coverage.CoverageHandler;
import com.microsoft.java.test.plugin.launchers.JUnitLaunchUtils;
+import com.microsoft.java.test.plugin.util.JUnitPlugin;
import com.microsoft.java.test.plugin.util.ProjectTestUtils;
import com.microsoft.java.test.plugin.util.TestGenerationUtils;
import com.microsoft.java.test.plugin.util.TestNavigationUtils;
@@ -19,7 +21,9 @@
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.ls.core.internal.IDelegateCommandHandler;
+import org.eclipse.jdt.ls.core.internal.ProjectUtils;
import java.util.List;
@@ -36,6 +40,7 @@ public class TestDelegateCommandHandler implements IDelegateCommandHandler {
private static final String RESOLVE_PATH = "vscode.java.test.resolvePath";
private static final String FIND_TEST_LOCATION = "vscode.java.test.findTestLocation";
private static final String NAVIGATE_TO_TEST_OR_TARGET = "vscode.java.test.navigateToTestOrTarget";
+ private static final String GET_COVERAGE_DETAIL = "vscode.java.test.jacoco.getCoverageDetail";
@Override
public Object executeCommand(String commandId, List