Skip to content

Commit ff08836

Browse files
authored
Improving coverage (#13)
* Trying to improve coverage * updated codecov * renamed * Improving... * Adding more tests * Adding more tests * downgrade mockito-core to be compatible with java 8 * move reflections in the generic class * WIP * More coverage * Only compile on Java21 now (anyway, we're targetting Java8 bytecode) * 100% coverage now I think
1 parent e868875 commit ff08836

File tree

13 files changed

+672
-39
lines changed

13 files changed

+672
-39
lines changed

.github/workflows/build.yml

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,14 @@ permissions:
2020
jobs:
2121
build:
2222
runs-on: ubuntu-latest
23-
strategy:
24-
matrix:
25-
java: ['8', '11', '17', '21']
26-
fail-fast: false
2723

28-
name: Java ${{ matrix.java }}
2924
steps:
3025
- uses: actions/checkout@v4
3126

3227
- name: Set up JDK
3328
uses: actions/setup-java@v4
3429
with:
35-
java-version: ${{ matrix.java }}
30+
java-version: '21'
3631
distribution: 'temurin'
3732
cache: 'maven'
3833

@@ -43,14 +38,14 @@ jobs:
4338
if: always()
4439
uses: actions/upload-artifact@v4
4540
with:
46-
name: test-results-java-${{ matrix.java }}
41+
name: test-results-java
4742
path: '**/target/surefire-reports/'
4843

4944
- name: Upload coverage report
5045
if: always()
5146
uses: actions/upload-artifact@v4
5247
with:
53-
name: coverage-report-java-${{ matrix.java }}
48+
name: coverage-report-java
5449
path: '**/target/site/jacoco/'
5550

5651
- name: Upload test results to Codecov

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
- name: Set up JDK
2525
uses: actions/setup-java@v4
2626
with:
27-
java-version: '8'
27+
java-version: '21'
2828
distribution: 'temurin'
2929
cache: 'maven'
3030
server-id: central

codecov.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
codecov:
2+
require_ci_to_pass: true
3+
4+
coverage:
5+
precision: 2
6+
round: down
7+
range: "70...100"
8+
9+
status:
10+
project: yes
11+
patch: true
12+
changes: false
13+
14+
ignore:
15+
- "test-project"

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

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package io.github.svaningelgem;
22

3-
import lombok.var;
43
import org.apache.maven.execution.DefaultMavenExecutionRequest;
54
import org.apache.maven.execution.DefaultMavenExecutionResult;
65
import org.apache.maven.execution.MavenExecutionRequest;
@@ -23,6 +22,7 @@
2322
import java.io.File;
2423
import java.io.IOException;
2524
import java.io.InputStream;
25+
import java.lang.reflect.Method;
2626
import java.nio.file.Files;
2727
import java.nio.file.StandardCopyOption;
2828
import java.util.ArrayList;
@@ -54,6 +54,17 @@ public class BaseTestClass {
5454

5555
private int fileCounter = 0;
5656

57+
protected Method scanDirectoryForExecFiles;
58+
protected Method generateReport;
59+
protected Method analyzeCoverage;
60+
protected Method printTree;
61+
protected Method printSummary;
62+
protected Method getConfiguredExecFilePatterns;
63+
protected Method shouldReport;
64+
protected Method buildDirectoryTree;
65+
protected Method loadExecutionData;
66+
protected Method loadExecFile;
67+
5768
protected String getBasedir() {
5869
return System.getProperty("basedir", new File("").getAbsolutePath());
5970
}
@@ -90,6 +101,31 @@ public void setUp() throws Exception {
90101

91102
log = new MyLog();
92103
mojo.setLog(log);
104+
105+
reflectOnMethods();
106+
}
107+
108+
private void reflectOnMethods() throws NoSuchMethodException {
109+
scanDirectoryForExecFiles = JacocoConsoleReporterMojo.class.getDeclaredMethod("scanDirectoryForExecFiles", File.class, List.class);
110+
scanDirectoryForExecFiles.setAccessible(true);
111+
generateReport = JacocoConsoleReporterMojo.class.getDeclaredMethod("generateReport");
112+
generateReport.setAccessible(true);
113+
printTree = JacocoConsoleReporterMojo.class.getDeclaredMethod("printTree", DirectoryNode.class);
114+
printTree.setAccessible(true);
115+
analyzeCoverage = JacocoConsoleReporterMojo.class.getDeclaredMethod("analyzeCoverage", org.jacoco.core.data.ExecutionDataStore.class);
116+
analyzeCoverage.setAccessible(true);
117+
getConfiguredExecFilePatterns = JacocoConsoleReporterMojo.class.getDeclaredMethod("getConfiguredExecFilePatterns");
118+
getConfiguredExecFilePatterns.setAccessible(true);
119+
shouldReport = JacocoConsoleReporterMojo.class.getDeclaredMethod("shouldReport");
120+
shouldReport.setAccessible(true);
121+
loadExecFile = JacocoConsoleReporterMojo.class.getDeclaredMethod("loadExecFile", File.class, org.jacoco.core.data.ExecutionDataStore.class, org.jacoco.core.data.SessionInfoStore.class);
122+
loadExecFile.setAccessible(true);
123+
loadExecutionData = JacocoConsoleReporterMojo.class.getDeclaredMethod("loadExecutionData");
124+
loadExecutionData.setAccessible(true);
125+
buildDirectoryTree = JacocoConsoleReporterMojo.class.getDeclaredMethod("buildDirectoryTree", org.jacoco.core.analysis.IBundleCoverage.class);
126+
buildDirectoryTree.setAccessible(true);
127+
printSummary = JacocoConsoleReporterMojo.class.getDeclaredMethod("printSummary", DirectoryNode.class);
128+
printSummary.setAccessible(true);
93129
}
94130

95131
@After
@@ -106,7 +142,7 @@ private static int nextInt(int bound) {
106142

107143
@Contract(" -> new")
108144
protected static @NotNull CoverageMetrics getRandomCoverage() {
109-
var cm = new CoverageMetrics();
145+
CoverageMetrics cm = new CoverageMetrics();
110146

111147
cm.totalClasses = nextInt(100);
112148
cm.coveredClasses = nextInt(cm.totalClasses);
@@ -207,7 +243,7 @@ protected void addFiles(DirectoryNode toNode, @Nullable CoverageMetrics defaultC
207243
continue;
208244
}
209245

210-
var file = new SourceFileNode(name, defaultCoverage == null ? getRandomCoverage() : defaultCoverage.clone());
246+
SourceFileNode file = new SourceFileNode(name, defaultCoverage == null ? getRandomCoverage() : defaultCoverage.clone());
211247
toNode.getSourceFiles().add(file);
212248
}
213249
}
@@ -283,6 +319,7 @@ protected void copyResourceToFile(String resourcePath, File destFile) throws IOE
283319
/**
284320
* Creates a real MavenSession with multiple projects
285321
*/
322+
@SuppressWarnings("deprecation")
286323
protected @NotNull MavenSession createRealMavenSession(List<MavenProject> projects) {
287324
PlexusContainer container = rule.getContainer();
288325
MavenExecutionRequest request = new DefaultMavenExecutionRequest();
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package io.github.svaningelgem;
2+
3+
import org.junit.Test;
4+
5+
import java.util.Collections;
6+
import java.util.List;
7+
8+
import static org.junit.Assert.*;
9+
10+
public class DirectoryNodeTest extends BaseTestClass {
11+
12+
@Test
13+
public void testShouldInclude() {
14+
DirectoryNode emptyNode = new DirectoryNode("empty");
15+
assertFalse("Empty directory should not be included", emptyNode.shouldInclude());
16+
17+
DirectoryNode withFiles = new DirectoryNode("withFiles");
18+
withFiles.getSourceFiles().add(new SourceFileNode("Test.java", new CoverageMetrics()));
19+
assertTrue("Directory with files should be included", withFiles.shouldInclude());
20+
21+
DirectoryNode withSubDir = new DirectoryNode("withSubDir");
22+
DirectoryNode subDir = new DirectoryNode("subDir");
23+
subDir.getSourceFiles().add(new SourceFileNode("Test.java", new CoverageMetrics()));
24+
withSubDir.getSubdirectories().put("subDir", subDir);
25+
assertTrue("Directory with non-empty subdirectory should be included", withSubDir.shouldInclude());
26+
27+
DirectoryNode withEmptySubDir = new DirectoryNode("withEmptySubDir");
28+
withEmptySubDir.getSubdirectories().put("emptySubDir", new DirectoryNode("emptySubDir"));
29+
assertFalse("Directory with only empty subdirectory should not be included", withEmptySubDir.shouldInclude());
30+
}
31+
32+
@Test
33+
public void testCollapseSingleNodeDirectory() {
34+
DirectoryNode root = new DirectoryNode("");
35+
DirectoryNode singleChild = new DirectoryNode("single");
36+
root.getSubdirectories().put("single", singleChild);
37+
38+
// This will test the shouldCollapse condition: dirNodes.size() == 1 && fileNodes.isEmpty()
39+
root.printTree(log, "", Defaults.LINE_FORMAT, "", false);
40+
41+
// The log should only contain the collapsed path, not separate entries for root and single
42+
assertFalse("Log should not contain the root node separately",
43+
log.writtenData.stream().anyMatch(s -> s.contains("<root>")));
44+
}
45+
46+
@Test
47+
public void testNoCollapseWithMultipleDirectories() {
48+
DirectoryNode root = new DirectoryNode("");
49+
root.getSubdirectories().put("dir1", new DirectoryNode("dir1"));
50+
root.getSubdirectories().put("dir2", new DirectoryNode("dir2"));
51+
52+
// Add a file to each directory so shouldInclude() returns true
53+
root.getSubdirectories().get("dir1").getSourceFiles().add(
54+
new SourceFileNode("Test1.java", new CoverageMetrics()));
55+
root.getSubdirectories().get("dir2").getSourceFiles().add(
56+
new SourceFileNode("Test2.java", new CoverageMetrics()));
57+
58+
// This will test the !shouldCollapse condition
59+
root.printTree(log, "", Defaults.LINE_FORMAT, "", false);
60+
61+
// The log should contain the root and both directories as separate entries
62+
assertTrue("Log should contain the root node",
63+
log.writtenData.stream().anyMatch(s -> s.contains("<root>")));
64+
}
65+
66+
@Test
67+
public void testNoCollapseWithFiles() {
68+
DirectoryNode root = new DirectoryNode("");
69+
root.getSubdirectories().put("dir", new DirectoryNode("dir"));
70+
root.getSourceFiles().add(new SourceFileNode("RootTest.java", new CoverageMetrics()));
71+
72+
// Add a file to the directory so shouldInclude() returns true
73+
root.getSubdirectories().get("dir").getSourceFiles().add(
74+
new SourceFileNode("Test.java", new CoverageMetrics()));
75+
76+
// This will test the !shouldCollapse condition due to files in root
77+
root.printTree(log, "", Defaults.LINE_FORMAT, "", true);
78+
79+
// The log should contain the root and directory as separate entries
80+
assertTrue("Log should contain the root node",
81+
log.writtenData.stream().anyMatch(s -> s.contains("<root>")));
82+
}
83+
84+
@Test
85+
public void testPrintNodesWithEmptyList() {
86+
DirectoryNode root = new DirectoryNode("");
87+
88+
// Access the private method using reflection
89+
try {
90+
java.lang.reflect.Method printNodesMethod = DirectoryNode.class.getDeclaredMethod(
91+
"printNodes",
92+
org.apache.maven.plugin.logging.Log.class,
93+
String.class,
94+
String.class,
95+
String.class,
96+
boolean.class,
97+
List.class,
98+
boolean.class
99+
);
100+
printNodesMethod.setAccessible(true);
101+
102+
// Call the method with an empty list - should not throw exception
103+
printNodesMethod.invoke(root, log, "", "", "", true, Collections.emptyList(), true);
104+
105+
// Test passed if no exception was thrown
106+
assertTrue(true);
107+
} catch (Exception e) {
108+
fail("Exception should not be thrown: " + e.getMessage());
109+
}
110+
}
111+
112+
@Test
113+
public void testDetermineNewPrefixCases() {
114+
DirectoryNode node = new DirectoryNode("test");
115+
116+
try {
117+
java.lang.reflect.Method determineNewPrefixMethod = DirectoryNode.class.getDeclaredMethod(
118+
"determineNewPrefix",
119+
String.class,
120+
boolean.class
121+
);
122+
determineNewPrefixMethod.setAccessible(true);
123+
124+
// Test with corner prefix
125+
String result1 = (String) determineNewPrefixMethod.invoke(node, Defaults.CORNER, true);
126+
assertEquals("Should replace corner with space and add corner",
127+
Defaults.LAST_DIR_SPACE + Defaults.CORNER, result1);
128+
129+
// Test with tee prefix
130+
String result2 = (String) determineNewPrefixMethod.invoke(node, Defaults.TEE, false);
131+
assertEquals("Should replace tee with vertical line and add tee",
132+
Defaults.VERTICAL_LINE + Defaults.TEE, result2);
133+
134+
// Test with other prefix not ending in corner or tee
135+
String otherPrefix = " ";
136+
String result3 = (String) determineNewPrefixMethod.invoke(node, otherPrefix, true);
137+
assertEquals("Should add corner to prefix without modification",
138+
otherPrefix + Defaults.CORNER, result3);
139+
140+
} catch (Exception e) {
141+
fail("Exception should not be thrown: " + e.getMessage());
142+
}
143+
}
144+
}

0 commit comments

Comments
 (0)