Skip to content

Commit ac460c3

Browse files
svaningelgemSteven Van Ingelgem
andauthored
fix: no coverage report when plugin not defined (#74)
* Fixing the build by providing defaults for the jacoco exec file resolution. * - execution-level coverage fixed. - ','-seperated exclusion patterns are being interpreted too now. - plugin-iteration allows for a default value to be passed. * merged * Coverage improvement --------- Co-authored-by: Steven Van Ingelgem <steven.vaningelgem@bnpparibasfortis.com>
1 parent e92a99a commit ac460c3

File tree

5 files changed

+207
-44
lines changed

5 files changed

+207
-44
lines changed

jacoco-console-reporter/src/main/java/io/github/svaningelgem/JacocoConsoleReporterMojo.java

Lines changed: 90 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
package io.github.svaningelgem;
22

3+
import lombok.var;
4+
import org.apache.maven.execution.MavenSession;
5+
import org.apache.maven.model.Plugin;
6+
import org.apache.maven.model.PluginExecution;
37
import org.apache.maven.plugin.AbstractMojo;
8+
import org.apache.maven.plugin.MojoExecution;
49
import org.apache.maven.plugin.MojoExecutionException;
510
import org.apache.maven.plugins.annotations.LifecyclePhase;
611
import org.apache.maven.plugins.annotations.Mojo;
712
import org.apache.maven.plugins.annotations.Parameter;
13+
import org.apache.maven.project.MavenProject;
814
import org.codehaus.plexus.util.xml.Xpp3Dom;
915
import org.jacoco.core.analysis.Analyzer;
1016
import org.jacoco.core.analysis.CoverageBuilder;
@@ -18,6 +24,7 @@
1824
import org.jacoco.report.MultiSourceFileLocator;
1925
import org.jacoco.report.xml.XMLFormatter;
2026
import org.jetbrains.annotations.NotNull;
27+
import org.jetbrains.annotations.Nullable;
2128

2229
import java.io.File;
2330
import java.io.FileInputStream;
@@ -36,9 +43,11 @@
3643
import java.util.LinkedList;
3744
import java.util.List;
3845
import java.util.Locale;
46+
import java.util.Objects;
3947
import java.util.Properties;
4048
import java.util.Queue;
4149
import java.util.Set;
50+
import java.util.concurrent.atomic.AtomicBoolean;
4251
import java.util.function.Consumer;
4352
import java.util.regex.Matcher;
4453
import java.util.regex.Pattern;
@@ -141,13 +150,19 @@ public class JacocoConsoleReporterMojo extends AbstractMojo {
141150
* The Maven project.
142151
*/
143152
@Parameter(defaultValue = "${project}", readonly = true, required = true)
144-
org.apache.maven.project.MavenProject project;
153+
MavenProject project;
145154

146155
/**
147156
* The Maven session.
148157
*/
149158
@Parameter(defaultValue = "${session}", readonly = true, required = true)
150-
org.apache.maven.execution.MavenSession mavenSession;
159+
MavenSession mavenSession;
160+
161+
/**
162+
* The execution step
163+
*/
164+
@Parameter(defaultValue = "${mojoExecution}", readonly = true)
165+
MojoExecution mojoExecution;
151166

152167
/**
153168
* JaCoCo plugin info for dependency discovery
@@ -310,12 +325,16 @@ void addSonarFileExclusions(@NotNull String exclusions) {
310325
* <exclude>com/baeldung/** /*DTO.*</exclude>
311326
* <exclude>** /config/*</exclude>
312327
*/
313-
Pattern convertExclusionToPattern(@NotNull String jacocoPattern) {
314-
jacocoPattern = jacocoPattern.replace("\\", "/");
328+
@Nullable Pattern convertExclusionToPattern(@NotNull String jacocoPattern) {
329+
jacocoPattern = jacocoPattern.replace("\\", "/").trim();
315330

316331
// Handle the .class extension
317332
if (jacocoPattern.toLowerCase(Locale.ENGLISH).endsWith(".class")) {
318-
jacocoPattern = jacocoPattern.substring(0, jacocoPattern.length() - 6);
333+
jacocoPattern = jacocoPattern.substring(0, jacocoPattern.length() - 6).trim();
334+
}
335+
336+
if (jacocoPattern.isEmpty()) {
337+
return null;
319338
}
320339

321340
// Use temporary placeholders to avoid interference between replacements
@@ -335,8 +354,12 @@ Pattern convertExclusionToPattern(@NotNull String jacocoPattern) {
335354
}
336355

337356
void addExclusion(@NotNull String jacocoPattern) {
338-
Pattern pattern = convertExclusionToPattern(jacocoPattern);
339-
collectedExcludePatterns.add(pattern);
357+
for (String pattern : jacocoPattern.split(",")) {
358+
var converted = convertExclusionToPattern(pattern);
359+
if (converted != null) {
360+
collectedExcludePatterns.add(converted);
361+
}
362+
}
340363
}
341364

342365
/**
@@ -400,6 +423,57 @@ boolean shouldReport() {
400423
return !deferReporting || project.getId().equals(mavenSession.getProjects().get(mavenSession.getProjects().size() - 1).getId());
401424
}
402425

426+
Queue<Xpp3Dom> digIntoConfig(Xpp3Dom config, String[] parts) {
427+
if (config == null || parts == null || parts.length == 0) {
428+
return null;
429+
}
430+
431+
// Queue of nodes to process at the current level
432+
Queue<Xpp3Dom> currentLevelNodes = new LinkedList<>();
433+
Queue<Xpp3Dom> nextLevelNodes = null;
434+
currentLevelNodes.add(config);
435+
436+
// Process each part of the path
437+
for (String part : parts) {
438+
nextLevelNodes = new LinkedList<>();
439+
440+
// Process all nodes at the current level
441+
for (Xpp3Dom currentNode : currentLevelNodes) {
442+
// Get all children with matching name
443+
Xpp3Dom[] children = currentNode.getChildren(part);
444+
Collections.addAll(nextLevelNodes, children);
445+
}
446+
447+
// If this is the last part in the path, apply consumer to all matching nodes
448+
if (nextLevelNodes.isEmpty()) {
449+
// If no matching nodes found at this level, stop processing
450+
return null;
451+
}
452+
453+
// Continue with the next level
454+
currentLevelNodes = nextLevelNodes;
455+
}
456+
457+
return nextLevelNodes;
458+
}
459+
460+
Queue<Xpp3Dom> getConfiguration(Plugin plugin, String[] parts) {
461+
Queue<Xpp3Dom> config = null;
462+
463+
if (mojoExecution != null) {
464+
PluginExecution exec = plugin.getExecutionsAsMap().get(mojoExecution.getGoal());
465+
if (exec != null) {
466+
config = digIntoConfig((Xpp3Dom) exec.getConfiguration(), parts);
467+
}
468+
}
469+
470+
if (config != null) {
471+
return config;
472+
}
473+
474+
return digIntoConfig((Xpp3Dom) plugin.getConfiguration(), parts);
475+
}
476+
403477
void doSomethingForEachPluginConfiguration(String groupId, String artifactId, @NotNull String configValue, Consumer<String> configurationConsumer, String defaultValue) {
404478
final String[] parts = Arrays.stream(configValue.split("\\.")).filter(s -> !s.isEmpty()).toArray(String[]::new);
405479

@@ -408,47 +482,20 @@ void doSomethingForEachPluginConfiguration(String groupId, String artifactId, @N
408482
boolean artifactEquals = artifactId.equals(plugin.getArtifactId());
409483
return groupEquals && artifactEquals;
410484
}).forEach(plugin -> {
411-
boolean[] foundValue = new boolean[1];
485+
AtomicBoolean foundValue = new AtomicBoolean(false);
412486

413-
Xpp3Dom config = (Xpp3Dom) plugin.getConfiguration();
487+
Queue<Xpp3Dom> config = getConfiguration(plugin, parts);
414488
if (config != null) {
489+
config.forEach(node -> {
490+
String value = node.getValue().trim();
491+
if (value.isEmpty()) return;
415492

416-
// Queue of nodes to process at the current level
417-
Queue<Xpp3Dom> currentLevelNodes = new LinkedList<>();
418-
currentLevelNodes.add(config);
419-
420-
// Process each part of the path
421-
for (int i = 0; i < parts.length; i++) {
422-
String part = parts[i];
423-
Queue<Xpp3Dom> nextLevelNodes = new LinkedList<>();
424-
425-
// Process all nodes at the current level
426-
for (Xpp3Dom currentNode : currentLevelNodes) {
427-
// Get all children with matching name
428-
Xpp3Dom[] children = currentNode.getChildren(part);
429-
Collections.addAll(nextLevelNodes, children);
430-
}
431-
432-
// If this is the last part in the path, apply consumer to all matching nodes
433-
if (i == parts.length - 1) {
434-
nextLevelNodes.forEach(node -> {
435-
String value = node.getValue().trim();
436-
if (value.isEmpty()) return;
437-
438-
configurationConsumer.accept(value);
439-
foundValue[0] = true;
440-
});
441-
} else if (nextLevelNodes.isEmpty()) {
442-
// If no matching nodes found at this level, stop processing
443-
break;
444-
} else {
445-
// Continue with the next level
446-
currentLevelNodes = nextLevelNodes;
447-
}
448-
}
493+
configurationConsumer.accept(value);
494+
foundValue.set(true);
495+
});
449496
}
450497

451-
if (!foundValue[0] && defaultValue != null) {
498+
if (!foundValue.get() && defaultValue != null) {
452499
configurationConsumer.accept(defaultValue);
453500
}
454501
});

jacoco-console-reporter/src/test/java/io/github/svaningelgem/BaseTestClass.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
import org.apache.maven.model.Model;
99
import org.apache.maven.model.Plugin;
1010
import org.apache.maven.plugin.Mojo;
11+
import org.apache.maven.plugin.MojoExecution;
12+
import org.apache.maven.plugin.descriptor.MojoDescriptor;
13+
import org.apache.maven.plugin.descriptor.PluginDescriptor;
1114
import org.apache.maven.plugin.testing.MojoRule;
1215
import org.apache.maven.project.MavenProject;
1316
import org.codehaus.plexus.PlexusContainer;
@@ -99,6 +102,7 @@ public void setUp() throws Exception {
99102

100103
mojo.project = project;
101104
mojo.mavenSession = createRealMavenSession(Collections.singletonList(project));
105+
mojo.mojoExecution = createExecution();
102106

103107
mojo.setupDefaultVariables();
104108

@@ -121,6 +125,21 @@ public void setUp() throws Exception {
121125
mojo.setLog(log);
122126
}
123127

128+
private MojoExecution createExecution() {
129+
Plugin plugin = new Plugin();
130+
plugin.setGroupId("io.github.svaningelgem");
131+
plugin.setArtifactId("jacoco-console-reporter");
132+
plugin.setVersion("1.0.0");
133+
134+
MojoDescriptor mojoDescriptor = new MojoDescriptor();
135+
mojoDescriptor.setGoal("report");
136+
mojoDescriptor.setPluginDescriptor(new PluginDescriptor());
137+
138+
MojoExecution mojoExecution = new MojoExecution(plugin, "report", "default-report");
139+
mojoExecution.setMojoDescriptor(mojoDescriptor);
140+
return mojoExecution;
141+
}
142+
124143
@After
125144
public void tearDown() {
126145
JacocoConsoleReporterMojo.collectedClassesPaths.clear();
@@ -132,7 +151,7 @@ public void tearDown() {
132151
/**
133152
* Configure the project's build directories and JaCoCo settings
134153
*/
135-
protected void configureProjectForTesting(File targetDir, File classesDir, @Nullable File jacocoExecFile) throws IOException {
154+
protected void configureProjectForTesting(File targetDir, File classesDir, @Nullable File jacocoExecFile) {
136155
if (targetDir != null) {
137156
targetDir.mkdirs();
138157
mojo.project.getBuild().setDirectory(targetDir.getAbsolutePath());

jacoco-console-reporter/src/test/java/io/github/svaningelgem/BuildDirectoryTreeExclusionTest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@
2222
import static org.mockito.Mockito.when;
2323

2424
public class BuildDirectoryTreeExclusionTest extends BaseTestClass {
25+
@Test
26+
public void testEmptyExclusionWithClassFileSuffix() {
27+
mojo.addExclusion(".Class");
28+
assertTrue(JacocoConsoleReporterMojo.collectedExcludePatterns.isEmpty());
29+
}
30+
2531
@Test
2632
public void testBuildDirectoryTreeWithExcludedFiles() throws Exception {
2733
// Create test files in the build directory to match our exclusion patterns
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package io.github.svaningelgem;
2+
3+
import lombok.var;
4+
import org.apache.maven.model.Plugin;
5+
import org.apache.maven.model.PluginExecution;
6+
import org.codehaus.plexus.util.xml.Xpp3Dom;
7+
import org.junit.Before;
8+
import org.junit.Test;
9+
10+
import java.util.Collections;
11+
import java.util.stream.Collectors;
12+
13+
import static org.junit.Assert.assertEquals;
14+
import static org.junit.Assert.assertNull;
15+
16+
public class ConfigRetrievalTest extends BaseTestClass {
17+
Plugin jacocoPlugin;
18+
19+
@Before
20+
public void setUp() throws Exception {
21+
super.setUp();
22+
23+
jacocoPlugin = findOrCreateJacocoPlugin(mojo.project);
24+
var configuration = new Xpp3Dom("configuration");
25+
jacocoPlugin.setConfiguration(configuration);
26+
27+
var destFileNode = new Xpp3Dom("destFile");
28+
destFileNode.setValue("jacoco.exec");
29+
configuration.addChild(destFileNode);
30+
31+
var configuration2 = new Xpp3Dom("configuration");
32+
var destFile2 = new Xpp3Dom("destFile");
33+
destFile2.setValue("jacoco2.exec");
34+
configuration2.addChild(destFile2);
35+
36+
var pe = new PluginExecution();
37+
pe.setId("report");
38+
pe.setPhase("verify");
39+
pe.setGoals(Collections.singletonList("report"));
40+
pe.setConfiguration(configuration2);
41+
jacocoPlugin.addExecution(pe);
42+
}
43+
44+
@Test
45+
public void testConfigRetrievalViaExecutionStep() {
46+
var x = mojo.getConfiguration(jacocoPlugin, new String[]{"destFile"});
47+
var allValues = x.stream().map(Xpp3Dom::getValue).collect(Collectors.toList());
48+
assertEquals(1, allValues.size());
49+
assertEquals("jacoco2.exec", allValues.get(0)); // execution step overrules
50+
}
51+
52+
@Test
53+
public void testConfigRetrievalWithoutExecutionStep() {
54+
mojo.mojoExecution = null;
55+
var x = mojo.getConfiguration(jacocoPlugin, new String[]{"destFile"});
56+
var allValues = x.stream().map(Xpp3Dom::getValue).collect(Collectors.toList());
57+
assertEquals(1, allValues.size());
58+
assertEquals("jacoco.exec", allValues.get(0)); // we retrieve the generic configuration value
59+
}
60+
61+
@Test
62+
public void testConfigRetrievalViaGenericConfiguration() {
63+
jacocoPlugin.setExecutions(null);
64+
var x = mojo.getConfiguration(jacocoPlugin, new String[]{"destFile"});
65+
var allValues = x.stream().map(Xpp3Dom::getValue).collect(Collectors.toList());
66+
assertEquals(1, allValues.size());
67+
assertEquals("jacoco.exec", allValues.get(0)); // we retrieve the generic configuration value
68+
}
69+
70+
@Test
71+
public void testNullConfig() {
72+
assertNull(mojo.digIntoConfig(null, new String[]{"destFile"}));
73+
}
74+
75+
@Test
76+
public void testNullParts() {
77+
assertNull(mojo.digIntoConfig(new Xpp3Dom("dom"), null));
78+
}
79+
80+
@Test
81+
public void testNoParts() {
82+
assertNull(mojo.digIntoConfig(new Xpp3Dom("dom"), new String[]{}));
83+
}
84+
}

jacoco-console-reporter/src/test/resources/unit/pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@
1919
<jacocoExecFile>${basedir}/src/test/resources/jacoco.exec</jacocoExecFile>
2020
<classesDirectory>${basedir}/target/classes</classesDirectory>
2121
</configuration>
22+
<executions>
23+
<execution>
24+
<configuration>
25+
<destFile>...</destFile>
26+
</configuration>
27+
</execution>
28+
</executions>
2229
</plugin>
2330
</plugins>
2431
</build>

0 commit comments

Comments
 (0)