Skip to content

Commit

Permalink
Improve output of the Markdown summary.
Browse files Browse the repository at this point in the history
Show the sub-scores as individual entries of the summary. Skip the
toplevel overview of the scores.
  • Loading branch information
uhafner committed Apr 10, 2024
1 parent f690511 commit 24b2345
Show file tree
Hide file tree
Showing 16 changed files with 397 additions and 77 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
</developer>
</developers>

<url>https://www.cs.hm.edu/die_fakultaet/ansprechpartner/professoren/hafner/index.de.html</url>
<url>https://cs.hm.edu/~hafner</url>

<dependencies>
<dependency>
Expand Down
49 changes: 48 additions & 1 deletion src/main/java/edu/hm/hafner/grading/AnalysisMarkdown.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.List;
import java.util.function.Function;

import edu.hm.hafner.analysis.registry.ParserRegistry;
import edu.hm.hafner.grading.TruncatedString.TruncatedStringBuilder;

/**
Expand All @@ -12,13 +13,15 @@
* @author Ullrich Hafner
*/
public class AnalysisMarkdown extends ScoreMarkdown<AnalysisScore, AnalysisConfiguration> {
private static final ParserRegistry REGISTRY = new ParserRegistry();

static final String TYPE = "Static Analysis Warnings Score";

/**
* Creates a new Markdown renderer for static analysis results.
*/
public AnalysisMarkdown() {
super(TYPE, "warning");
super(TYPE, "exclamation");
}

@Override
Expand Down Expand Up @@ -81,4 +84,48 @@ protected void createSpecificDetails(final AggregatedScore aggregation, final Li
private int sum(final AnalysisScore score, final Function<AnalysisScore, Integer> property) {
return score.getSubScores().stream().map(property).reduce(Integer::sum).orElse(0);
}

protected String extractSeverities(final AnalysisScore score) {
if (score.getReport().isEmpty()) {
return "No warnings found";
}
else {
return String.format("%d warning%s found (%d error%s, %d high, %d normal, %d low)",
score.getTotalSize(), AnalysisScore.plural(score.getTotalSize()),
score.getErrorSize(), AnalysisScore.plural(score.getErrorSize()),
score.getHighSeveritySize(),
score.getNormalSeveritySize(),
score.getLowSeveritySize());
}
}

@Override
protected String createSummary(final AnalysisScore score) {
var builder = new StringBuilder();
for (AnalysisScore analysisScore : score.getSubScores()) {
builder.append(SPACE)
.append(SPACE)
.append(getIconAndName(analysisScore))
.append(": ")
.append(extractSeverities(analysisScore))
.append(LINE_BREAK);
}
return builder.toString();
}

private String getIconAndName(final AnalysisScore analysisScore) {
return " %s &nbsp; %s".formatted(extractParserIcon(analysisScore), analysisScore.getName())
+ createScoreTitle(analysisScore);
}

private String extractParserIcon(final AnalysisScore analysisScore) {
var descriptor = REGISTRY.get(analysisScore.getId());
if (descriptor.getIconUrl().isEmpty()) {
return ":exclamation:";
}
else {
return "<img src=\"%s\" alt=\"%s\" height=\"%d\" width=\"%d\">"
.formatted(descriptor.getIconUrl(), analysisScore.getName(), ICON_SIZE, ICON_SIZE);
}
}
}
2 changes: 1 addition & 1 deletion src/main/java/edu/hm/hafner/grading/AnalysisScore.java
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ protected String createSummary() {
}
}

private String plural(final int score) {
static String plural(final int score) {
return score > 1 ? "s" : "";
}

Expand Down
5 changes: 5 additions & 0 deletions src/main/java/edu/hm/hafner/grading/CodeCoverageMarkdown.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,9 @@ public CodeCoverageMarkdown() {
protected List<CoverageScore> createScores(final AggregatedScore aggregation) {
return aggregation.getCodeCoverageScores();
}

@Override
protected String getIcon(final CoverageScore score) {
return ":%s:".formatted(score.getIcon());
}
}
15 changes: 15 additions & 0 deletions src/main/java/edu/hm/hafner/grading/CoverageMarkdown.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,19 @@ private String getImageForScoreOrCoverage(final CoverageScore score) {
}
return getPercentageImage(score.getDisplayName(), score.getCoveredPercentage());
}

@Override
protected String createSummary(final CoverageScore score) {
var summary = new StringBuilder(CAPACITY);

for (CoverageScore coverageScore : score.getSubScores()) {
summary.append(SPACE)
.append(SPACE)
.append(getTitle(coverageScore, 0))
.append(": ")
.append(coverageScore.createSummary())
.append(LINE_BREAK);
}
return summary.toString();
}
}
62 changes: 59 additions & 3 deletions src/main/java/edu/hm/hafner/grading/CoverageScore.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ public final class CoverageScore extends Score<CoverageScore, CoverageConfigurat

private final int coveredPercentage;
private final Metric metric;
private final String icon;
private final int missedItems;
private transient Node report; // do not persist the coverage tree

private CoverageScore(final String id, final String name, final CoverageConfiguration configuration,
Expand All @@ -41,8 +43,11 @@ private CoverageScore(final String id, final String name, final CoverageConfigur
this.coveredPercentage = scores.stream()
.reduce(0, (sum, score) -> sum + score.getCoveredPercentage(), Integer::sum)
/ scores.size();
this.missedItems = scores.stream()
.reduce(0, (sum, score) -> sum + score.getMissedItems(), Integer::sum);
this.metric = scores.stream().map(CoverageScore::getMetric).filter(Objects::nonNull).findFirst().orElseThrow(
() -> new IllegalArgumentException("No metric found in scores."));
this.icon = selectIcon(Metric.FILE);

this.report = new ContainerNode(name);
scores.stream().map(CoverageScore::getReport).forEach(report::addChild);
Expand All @@ -54,13 +59,44 @@ private CoverageScore(final String id, final String name, final CoverageConfigur

this.report = report;
this.metric = metric;
this.icon = selectIcon(metric);

var value = report.getValue(metric);
if (value.isPresent() && value.get() instanceof Coverage) {
this.coveredPercentage = ((Coverage) value.get()).getCoveredPercentage().toInt();
this.missedItems = ((Coverage) value.get()).getMissed();
}
else {
this.coveredPercentage = 0; // If there is no coverage, then there is no code yet
this.missedItems = 0;
}
}

public int getMissedItems() {
return missedItems;
}

public String getIcon() {
return icon;
}

private String selectIcon(final Metric iconMetric) {
switch (iconMetric) {
case BRANCH -> {
return "curly_loop";
}
case LINE -> {
return "wavy_dash";
}
case COMPLEXITY -> {
return "part_alternation_mark";
}
case LOC -> {
return "pencil2";
}
default -> {
return "footprints";
}
}
}

Expand All @@ -69,7 +105,8 @@ private CoverageScore(final String id, final String name, final CoverageConfigur
*
* @return this
*/
@Serial @CanIgnoreReturnValue
@Serial
@CanIgnoreReturnValue
private Object readResolve() {
report = new ModuleNode("empty");

Expand Down Expand Up @@ -111,11 +148,30 @@ public int getMissedPercentage() {

@Override
protected String createSummary() {
return String.format("%d%% %s", getCoveredPercentage(), getItemName());
return String.format("%d%% (%d %s)", getCoveredPercentage(), getMissedItems(), getItemName());
}

private String getItemName() {
return getConfiguration().isMutationCoverage() ? "mutations killed" : "coverage achieved";
switch (metric) {
case MUTATION -> {
return "survived mutations";
}
case BRANCH -> {
return "missed branches";
}
case LINE -> {
return "missed lines";
}
case COMPLEXITY -> {
return "complexity";
}
case LOC -> {
return "lines of code";
}
default -> {
return "items";
}
}
}

@Override
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/edu/hm/hafner/grading/GradingReport.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public String getMarkdownSummary(final AggregatedScore score) {
* @return Markdown text
*/
public String getMarkdownSummary(final AggregatedScore score, final String title) {
return createMarkdownTotal(score, title, 3) + PARAGRAPH + getSubScoreDetails(score) + HORIZONTAL_RULE;
return createMarkdownTotal(score, title, 3) + getSubScoreDetails(score) + HORIZONTAL_RULE;
}

private String createPercentage(final AggregatedScore score) {
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/edu/hm/hafner/grading/MutationCoverageMarkdown.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,15 @@ public MutationCoverageMarkdown() {
protected List<CoverageScore> createScores(final AggregatedScore aggregation) {
return aggregation.getMutationCoverageScores();
}

@Override
protected String getIcon(final CoverageScore score) {
if (score.getId().equals("pit")) {
return "<img src=\"https://pitest.org/images/pit-black-150x152.png\" alt=\"PIT\" height=\"%d\" width=\"%d\">"
.formatted(ICON_SIZE, ICON_SIZE);
}
else {
return super.getIcon(score);
}
}
}
28 changes: 14 additions & 14 deletions src/main/java/edu/hm/hafner/grading/ScoreMarkdown.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,17 @@
* @author Ullrich Hafner
*/
abstract class ScoreMarkdown<S extends Score<S, C>, C extends Configuration> {
protected static final int ICON_SIZE = 18;
static final String SPACE = "&nbsp;";
static final String LINE_BREAK = "\n";
static final String LEDGER = ":heavy_minus_sign:";
static final String IMPACT = ":moneybag:";
static final String TOTAL = ":heavy_minus_sign:";
static final String EMPTY = ":heavy_minus_sign:";

static final String N_A = "-";
static final int CAPACITY = 1024;

static final int MESSAGE_INITIAL_CAPACITY = 1024;
private static final int MAX_SIZE = 10_000; // limit the size of the output to this number of characters
private static final String TRUNCATION_TEXT = "\n\nToo many test failures. Grading output truncated.";
private static final int HUNDRED_PERCENT = 100;
Expand Down Expand Up @@ -101,30 +103,28 @@ public String createDetails(final AggregatedScore aggregation) {
protected abstract void createSpecificDetails(AggregatedScore aggregation, List<S> scores, TruncatedStringBuilder details);

/**
* Renders the test results in Markdown.
* Renders a summary of all scores in Markdown.
*
* @param aggregation
* Aggregated score
* aggregated score
*
* @return returns formatted string
* @return returns the summary in Markdown
*/
public String createSummary(final AggregatedScore aggregation) {
var scores = createScores(aggregation);
if (scores.isEmpty()) {
return createNotEnabled();
}

var summary = new StringBuilder(MESSAGE_INITIAL_CAPACITY);
var summary = new StringBuilder(CAPACITY);
for (S score : scores) {
summary.append("-")
.append(getTitle(score, 0))
.append(": ")
.append(score.createSummary())
.append(LINE_BREAK);
summary.append(createSummary(score));
}
return summary.toString();
}

protected abstract String createSummary(S score);

/**
* Creates the scores to render.
*
Expand All @@ -137,11 +137,11 @@ public String createSummary(final AggregatedScore aggregation) {

protected String getTitle(final S score, final int size) {
return "#".repeat(size)
+ String.format(" :%s: &nbsp; %s", getIcon(score), score.getName())
+ String.format(" %s &nbsp; %s", getIcon(score), score.getName())
+ createScoreTitle(score);
}

private String createScoreTitle(final S score) {
protected String createScoreTitle(final S score) {
var maxScore = score.getMaxScore();
var value = score.getValue();
var percentage = score.getPercentage();
Expand All @@ -158,8 +158,8 @@ static String createScoreTitleSuffix(final int maxScore, final int value, final
return String.format(" - %d of %d (%d%%)", value, maxScore, percentage);
}

private String getIcon(final S score) {
return StringUtils.defaultIfBlank(score.getConfiguration().getIcon(), icon);
protected String getIcon(final S score) {
return ":%s:".formatted(StringUtils.defaultIfBlank(score.getConfiguration().getIcon(), icon));
}

String formatColumns(final Object... columns) {
Expand Down
Loading

0 comments on commit 24b2345

Please sign in to comment.