Skip to content
This repository was archived by the owner on Nov 19, 2024. It is now read-only.

Commit 3f9224c

Browse files
committed
Rewrite rendering using dedicated classes for each coverage type.
Replace all if/else blocks with proper subclassing. This provides the possibility to enhance the source code tooltips for mutation results with additional details for the mutations.
1 parent b111a91 commit 3f9224c

File tree

12 files changed

+370
-167
lines changed

12 files changed

+370
-167
lines changed

plugin/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
<testcontainers.version>1.18.0</testcontainers.version>
3333
<job-dsl.version>1.83</job-dsl.version>
3434

35-
<coverage-model.version>0.24.0-rc427.d5fd580c7466</coverage-model.version>
35+
<coverage-model.version>0.24.0-rc428.9a_7f3f8f7729</coverage-model.version>
3636
<git-forensics.version>2.0.0</git-forensics.version>
3737
<prism-api.version>1.29.0-4</prism-api.version>
3838
<pull-request-monitoring.version>1.7.8</pull-request-monitoring.version>
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package io.jenkins.plugins.coverage.metrics.source;
2+
3+
import edu.hm.hafner.coverage.FileNode;
4+
5+
/**
6+
* A file with line and branch coverage information.
7+
*
8+
* @author Ullrich Hafner
9+
*/
10+
class FileWithBranchCoverage extends PaintedNode {
11+
private static final long serialVersionUID = -2703496036007699731L;
12+
13+
FileWithBranchCoverage(final FileNode file) {
14+
super(file);
15+
}
16+
17+
@Override
18+
public String getColorClass(final int line) {
19+
if (getCovered(line) == 0) {
20+
return NO_COVERAGE;
21+
}
22+
else if (getMissed(line) == 0) {
23+
return FULL_COVERAGE;
24+
}
25+
else {
26+
return PARTIAL_COVERAGE;
27+
}
28+
}
29+
30+
@Override
31+
public String getTooltip(final int line) {
32+
var covered = getCovered(line);
33+
var missed = getMissed(line);
34+
if (covered + missed > 1) {
35+
if (missed == 0) {
36+
return "All branches covered";
37+
}
38+
return String.format("Partially covered, branch coverage: %d/%d", covered, covered + missed);
39+
}
40+
else if (covered == 1) {
41+
return "Covered at least once";
42+
}
43+
else {
44+
return "Not covered";
45+
}
46+
}
47+
48+
@Override
49+
public String getSummaryColumn(final int line) {
50+
var covered = getCovered(line);
51+
var missed = getMissed(line);
52+
if (covered + missed > 1) {
53+
return String.format("%d/%d", covered, covered + missed);
54+
}
55+
return String.valueOf(covered);
56+
}
57+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package io.jenkins.plugins.coverage.metrics.source;
2+
3+
import java.util.Arrays;
4+
import java.util.List;
5+
import java.util.Map.Entry;
6+
import java.util.NavigableMap;
7+
import java.util.function.Predicate;
8+
import java.util.stream.Collectors;
9+
10+
import org.apache.commons.lang3.StringUtils;
11+
12+
import edu.hm.hafner.coverage.FileNode;
13+
import edu.hm.hafner.coverage.Mutation;
14+
15+
import j2html.tags.ContainerTag;
16+
import j2html.tags.UnescapedText;
17+
18+
import static j2html.TagCreator.*;
19+
20+
/**
21+
* A file with line and mutation coverage information.
22+
*
23+
* @author Ullrich Hafner
24+
*/
25+
class FileWithMutationCoverage extends FileWithBranchCoverage {
26+
private static final long serialVersionUID = -2215657894423024907L;
27+
28+
private final int[] survivedPerLine;
29+
private final int[] killedPerLine;
30+
private final String[] tooltipPerLine;
31+
32+
FileWithMutationCoverage(final FileNode file) {
33+
super(file);
34+
35+
survivedPerLine = new int[size()];
36+
killedPerLine = new int[size()];
37+
tooltipPerLine = new String[size()];
38+
Arrays.fill(tooltipPerLine, StringUtils.EMPTY);
39+
40+
extractMutationDetails(file.getMutationsPerLine());
41+
42+
for (Mutation mutation : file.getMutations()) {
43+
if (mutation.hasSurvived()) {
44+
survivedPerLine[findIndexOfLine(mutation.getLine())]++;
45+
}
46+
else if (mutation.isKilled()) {
47+
killedPerLine[findIndexOfLine(mutation.getLine())]++;
48+
}
49+
}
50+
}
51+
52+
private void extractMutationDetails(final NavigableMap<Integer, List<Mutation>> mutationsPerLine) {
53+
for (Entry<Integer, List<Mutation>> entry : mutationsPerLine.entrySet()) {
54+
var indexOfLine = findIndexOfLine(entry.getKey());
55+
56+
tooltipPerLine[indexOfLine] = createInfo(entry.getValue());
57+
}
58+
}
59+
60+
private String createInfo(final List<Mutation> allMutations) {
61+
ContainerTag killedContainer = listMutations(allMutations,
62+
Mutation::isKilled, "Killed Mutations:");
63+
ContainerTag survivedContainer = listMutations(allMutations,
64+
Mutation::hasSurvived, "Survived Mutations:");
65+
66+
return div().with(killedContainer, survivedContainer).render();
67+
}
68+
69+
private ContainerTag listMutations(final List<Mutation> allMutations,
70+
final Predicate<Mutation> predicate, final String title) {
71+
var filtered = div();
72+
var killed = asBulletPoints(allMutations, predicate);
73+
if (!killed.isEmpty()) {
74+
filtered.with(div().with(new UnescapedText(title), ul().with(killed)));
75+
}
76+
return filtered;
77+
}
78+
79+
private List<ContainerTag> asBulletPoints(final List<Mutation> mutations, final Predicate<Mutation> predicate) {
80+
return mutations.stream().filter(predicate).map(mutation ->
81+
li().withText(String.format("%s (%s)", mutation.getDescription(), mutation.getMutator())))
82+
.collect(Collectors.toList());
83+
}
84+
85+
public int getSurvived(final int line) {
86+
return getCounter(line, survivedPerLine);
87+
}
88+
89+
public int getKilled(final int line) {
90+
return getCounter(line, killedPerLine);
91+
}
92+
93+
@Override
94+
public String getColorClass(final int line) {
95+
if (getCovered(line) == 0) {
96+
return NO_COVERAGE;
97+
}
98+
if (getKilled(line) == 0) {
99+
return NO_COVERAGE;
100+
}
101+
else if (getSurvived(line) == 0) {
102+
return FULL_COVERAGE;
103+
}
104+
else {
105+
return PARTIAL_COVERAGE;
106+
}
107+
}
108+
109+
@Override
110+
public String getTooltip(final int line) {
111+
return StringUtils.defaultIfBlank(tooltipPerLine[findIndexOfLine(line)], super.getTooltip(line));
112+
}
113+
114+
@Override
115+
public String getSummaryColumn(final int line) {
116+
var killed = getKilled(line);
117+
var survived = getSurvived(line);
118+
if (survived + killed > 0) {
119+
return String.format("%d/%d", killed, survived + killed);
120+
}
121+
return String.valueOf(killed);
122+
}
123+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package io.jenkins.plugins.coverage.metrics.source;
2+
3+
import java.io.Serializable;
4+
import java.util.Arrays;
5+
6+
import edu.hm.hafner.coverage.FileNode;
7+
8+
/**
9+
* Provides all required information for a {@link FileNode} so that its source code can be rendered in HTML.
10+
*/
11+
abstract class PaintedNode implements Serializable {
12+
private static final long serialVersionUID = -6044649044983631852L;
13+
14+
static final String NO_COVERAGE = "coverNone";
15+
static final String FULL_COVERAGE = "coverFull";
16+
static final String PARTIAL_COVERAGE = "coverPart";
17+
18+
private final String path;
19+
private final int[] linesToPaint;
20+
private final int[] coveredPerLine;
21+
22+
private final int[] missedPerLine;
23+
24+
PaintedNode(final FileNode file) {
25+
path = file.getRelativePath();
26+
27+
linesToPaint = file.getLinesWithCoverage().stream().mapToInt(i -> i).toArray();
28+
coveredPerLine = file.getCoveredCounters();
29+
missedPerLine = file.getMissedCounters();
30+
}
31+
32+
int size() {
33+
return linesToPaint.length;
34+
}
35+
36+
abstract String getColorClass(int line);
37+
38+
abstract String getTooltip(int line);
39+
40+
abstract String getSummaryColumn(int line);
41+
42+
public String getPath() {
43+
return path;
44+
}
45+
46+
public boolean isPainted(final int line) {
47+
return findIndexOfLine(line) >= 0;
48+
}
49+
50+
int findIndexOfLine(final int line) {
51+
return Arrays.binarySearch(linesToPaint, line);
52+
}
53+
54+
public int getCovered(final int line) {
55+
return getCounter(line, coveredPerLine);
56+
}
57+
58+
public int getMissed(final int line) {
59+
return getCounter(line, missedPerLine);
60+
}
61+
62+
int getCounter(final int line, final int... counters) {
63+
var index = findIndexOfLine(line);
64+
if (index >= 0) {
65+
return counters[index];
66+
}
67+
return 0;
68+
}
69+
}

0 commit comments

Comments
 (0)