Skip to content

Commit

Permalink
Merge pull request #387 from uhafner/generic-metrics
Browse files Browse the repository at this point in the history
Add support for software metrics
  • Loading branch information
uhafner authored Oct 28, 2024
2 parents 2fdb33a + 0a3b67d commit 11e7198
Show file tree
Hide file tree
Showing 23 changed files with 2,355 additions and 158 deletions.
42 changes: 40 additions & 2 deletions .github/workflows/quality-monitor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
uses: jwalton/gh-find-current-pr@v1
id: pr
- name: Run Quality Monitor
uses: uhafner/quality-monitor@v1
uses: uhafner/quality-monitor@v1.12.0-beta-1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
pr-number: ${{ steps.pr.outputs.number }}
Expand All @@ -65,7 +65,7 @@ jobs:
},
{
"id": "pmd",
"pattern": "**/target/**pmd.xml"
"pattern": "**/target/pmd-*/pmd.xml"
}
]
},
Expand Down Expand Up @@ -126,6 +126,44 @@ jobs:
"metric": "mutation",
"sourcePath": "src/main/java",
"pattern": "**/target/pit-reports/mutations.xml"
},
{
"id": "pit",
"name": "Test Strength",
"metric": "test-strength",
"sourcePath": "src/main/java",
"pattern": "**/target/pit-reports/mutations.xml"
}
]
}
],
"metrics": [
{
"name": "Toplevel Metrics",
"tools": [
{
"name": "Cyclomatic Complexity",
"id": "metrics",
"pattern": "**/metrics/pmd.xml",
"metric": "CyclomaticComplexity"
},
{
"name": "Cognitive Complexity",
"id": "metrics",
"pattern": "**/metrics/pmd.xml",
"metric": "CognitiveComplexity"
},
{
"name": "Non Commenting Source Statements",
"id": "metrics",
"pattern": "**/metrics/pmd.xml",
"metric": "NCSS"
},
{
"name": "N-Path Complexity",
"id": "metrics",
"pattern": "**/metrics/pmd.xml",
"metric": "NPathComplexity"
}
]
}
Expand Down
23 changes: 12 additions & 11 deletions .github/workflows/update-badges.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,11 @@ jobs:
- name: Build and test with Maven
run: mvn -V --color always -ntp clean verify -Pci -Ppit -Pdepgraph | tee maven.log
- name: Run Quality Monitor
uses: uhafner/quality-monitor@v1
uses: uhafner/quality-monitor@v1.12.0-beta-1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
config: >
{
"tests": {
"tools": [
{
"id": "test",
"name": "Tests",
"pattern": "**/target/*-reports/TEST*.xml"
}
],
"name": "Tests"
},
"analysis": [
{
"name": "Style",
Expand Down Expand Up @@ -72,6 +62,17 @@ jobs:
"pattern": "**/maven.log"
}
]
},
{
"name": "Vulnerabilities",
"id": "vulnerabilities",
"icon": "shield",
"tools": [
{
"id": "owasp-dependency-check",
"pattern": "**/target/dependency-check-report.json"
}
]
}
],
"coverage": [
Expand Down
6 changes: 2 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>edu.hm.hafner</groupId>
<artifactId>codingstyle-pom</artifactId>
<version>4.16.0</version>
<version>5.2.0</version>
<relativePath />
</parent>

Expand Down Expand Up @@ -58,9 +58,7 @@

<jackson-databind.version>2.18.0</jackson-databind.version>
<analysis-model.version>12.9.0</analysis-model.version>
<coverage-model.version>0.47.0</coverage-model.version>
<java.version>17</java.version>

<coverage-model.version>0.48.0</coverage-model.version>
</properties>

<dependencies>
Expand Down
114 changes: 88 additions & 26 deletions src/main/java/edu/hm/hafner/grading/AggregatedScore.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import edu.hm.hafner.coverage.Node;
import edu.hm.hafner.grading.AnalysisScore.AnalysisScoreBuilder;
import edu.hm.hafner.grading.CoverageScore.CoverageScoreBuilder;
import edu.hm.hafner.grading.MetricScore.MetricScoreBuilder;
import edu.hm.hafner.grading.TestScore.TestScoreBuilder;
import edu.hm.hafner.util.FilteredLog;
import edu.hm.hafner.util.Generated;
Expand All @@ -43,10 +44,12 @@ public final class AggregatedScore implements Serializable {
private final List<TestScore> testScores = new ArrayList<>();
private final List<CoverageScore> coverageScores = new ArrayList<>();
private final List<AnalysisScore> analysisScores = new ArrayList<>();
private final List<MetricScore> metricScores = new ArrayList<>();

private final List<TestConfiguration> testConfigurations;
private final List<CoverageConfiguration> coverageConfigurations;
private final List<AnalysisConfiguration> analysisConfigurations;
private final List<MetricConfiguration> metricConfigurations;

private static FilteredLog createNullLogger() {
return new FilteredLog("Autograding");

Check warning on line 55 in src/main/java/edu/hm/hafner/grading/AggregatedScore.java

View workflow job for this annotation

GitHub Actions / Quality Monitor

Mutation survived

One mutation survived in line 55 (NullReturnValsMutator)
Raw output
Survived mutations:
- replaced return value with null for edu/hm/hafner/grading/AggregatedScore::createNullLogger (org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator)
Expand All @@ -68,6 +71,7 @@ public AggregatedScore(final String configuration, final FilteredLog log) {
analysisConfigurations = AnalysisConfiguration.from(configuration);
coverageConfigurations = CoverageConfiguration.from(configuration);
testConfigurations = TestConfiguration.from(configuration);
metricConfigurations = MetricConfiguration.from(configuration);

this.log = log;
}
Expand All @@ -86,7 +90,8 @@ public List<String> getErrorMessages() {
* @return the number of achieved points
*/
public int getAchievedScore() {
return getTestAchievedScore() + getCoverageAchievedScore() + getAnalysisAchievedScore();
return getTestAchievedScore() + getCoverageAchievedScore()
+ getAnalysisAchievedScore() + getMetricAchievedScore();

Check warning on line 94 in src/main/java/edu/hm/hafner/grading/AggregatedScore.java

View workflow job for this annotation

GitHub Actions / Quality Monitor

Mutation survived

One mutation survived in line 94 (MathMutator)
Raw output
Survived mutations:
- Replaced integer addition with subtraction (org.pitest.mutationtest.engine.gregor.mutators.MathMutator)
}

private int getAchievedScore(final List<? extends Score<?, ?>> scores) {
Expand Down Expand Up @@ -128,6 +133,10 @@ public int getAnalysisAchievedScore() {
return getAchievedScore(analysisScores);
}

public int getMetricAchievedScore() {
return getAchievedScore(metricScores);

Check warning on line 137 in src/main/java/edu/hm/hafner/grading/AggregatedScore.java

View workflow job for this annotation

GitHub Actions / Quality Monitor

Mutation survived

One mutation survived in line 137 (PrimitiveReturnsMutator)
Raw output
Survived mutations:
- replaced int return with 0 for edu/hm/hafner/grading/AggregatedScore::getMetricAchievedScore (org.pitest.mutationtest.engine.gregor.mutators.returns.PrimitiveReturnsMutator)
}

/**
* Returns the total number of points, i.e., the maximum score.
*
Expand Down Expand Up @@ -221,6 +230,19 @@ public boolean hasAnalysis() {
return !analysisConfigurations.isEmpty();
}

public int getMetricsMaxScore() {
return getMaxScore(metricConfigurations);

Check warning on line 234 in src/main/java/edu/hm/hafner/grading/AggregatedScore.java

View workflow job for this annotation

GitHub Actions / Quality Monitor

Not covered line

Line 234 is not covered by tests

Check warning on line 234 in src/main/java/edu/hm/hafner/grading/AggregatedScore.java

View workflow job for this annotation

GitHub Actions / Quality Monitor

Not covered line

Line 234 is not covered by tests
}

/**
* Returns whether at least one metric configuration has been defined.
*
* @return {@code true} if there are metric configurations, {@code false} otherwise
*/
public boolean hasMetrics() {
return !metricConfigurations.isEmpty();

Check warning on line 243 in src/main/java/edu/hm/hafner/grading/AggregatedScore.java

View workflow job for this annotation

GitHub Actions / Quality Monitor

Mutation survived

One mutation survived in line 243 (BooleanTrueReturnValsMutator)
Raw output
Survived mutations:
- replaced boolean return with true for edu/hm/hafner/grading/AggregatedScore::hasMetrics (org.pitest.mutationtest.engine.gregor.mutators.returns.BooleanTrueReturnValsMutator)
}

/**
* Returns the success ratio, i.e., the number of achieved points divided by total points.
*
Expand Down Expand Up @@ -257,6 +279,10 @@ public int getAnalysisRatio() {
return getRatio(getAnalysisAchievedScore(), getAnalysisMaxScore());
}

public int getMetricRatio() {
return getRatio(getMetricAchievedScore(), getMetricsMaxScore());

Check warning on line 283 in src/main/java/edu/hm/hafner/grading/AggregatedScore.java

View workflow job for this annotation

GitHub Actions / Quality Monitor

Not covered line

Line 283 is not covered by tests

Check warning on line 283 in src/main/java/edu/hm/hafner/grading/AggregatedScore.java

View workflow job for this annotation

GitHub Actions / Quality Monitor

Not covered line

Line 283 is not covered by tests
}

/**
* Returns whether at least one unit test failure has been recorded. In such a case, mutation results will not be
* available.
Expand Down Expand Up @@ -345,6 +371,10 @@ public List<AnalysisScore> getAnalysisScores() {
return List.copyOf(analysisScores);
}

public List<MetricScore> getMetricScores() {
return List.copyOf(metricScores);
}

@Override
@Generated
public boolean equals(final Object o) {
Expand All @@ -359,16 +389,18 @@ public boolean equals(final Object o) {
&& Objects.equals(testScores, that.testScores)
&& Objects.equals(coverageScores, that.coverageScores)
&& Objects.equals(analysisScores, that.analysisScores)
&& Objects.equals(metricScores, that.metricScores)
&& Objects.equals(testConfigurations, that.testConfigurations)
&& Objects.equals(coverageConfigurations, that.coverageConfigurations)
&& Objects.equals(analysisConfigurations, that.analysisConfigurations);
&& Objects.equals(analysisConfigurations, that.analysisConfigurations)
&& Objects.equals(metricConfigurations, that.metricConfigurations);
}

@Override
@Generated
public int hashCode() {
return Objects.hash(log, testScores, coverageScores, analysisScores, testConfigurations, coverageConfigurations,
analysisConfigurations);
return Objects.hash(log, testScores, coverageScores, analysisScores, metricScores, testConfigurations,
coverageConfigurations, analysisConfigurations, metricConfigurations);
}

@Override
Expand Down Expand Up @@ -452,7 +484,7 @@ public void gradeCoverage(final CoverageReportFactory factory) {
* @param factory
* the factory to create the reports
*/
public void gradeTests(final TestReportFactory factory) {
public void gradeTests(final CoverageReportFactory factory) {
log.logInfo("Processing %d test configuration(s)", testConfigurations.size());
for (TestConfiguration testConfiguration : testConfigurations) {
log.logInfo("%s Configuration:%n%s", testConfiguration.getName(), testConfiguration);

Check warning on line 490 in src/main/java/edu/hm/hafner/grading/AggregatedScore.java

View workflow job for this annotation

GitHub Actions / Quality Monitor

Mutation survived

One mutation survived in line 490 (VoidMethodCallMutator)
Raw output
Survived mutations:
- removed call to edu/hm/hafner/util/FilteredLog::logInfo (org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator)
Expand Down Expand Up @@ -481,6 +513,41 @@ public void gradeTests(final TestReportFactory factory) {
}
}

/**
* Grades the reports given by the report factory and creates corresponding scores for the metrics.
*
* @param factory
* the factory to create the reports
*/
public void gradeMetrics(final CoverageReportFactory factory) {
log.logInfo("Processing %d metric configuration(s)", metricConfigurations.size());
for (MetricConfiguration metricConfiguration : metricConfigurations) {
log.logInfo("%s Configuration:%n%s", metricConfiguration.getName(), metricConfiguration);

Check warning on line 525 in src/main/java/edu/hm/hafner/grading/AggregatedScore.java

View workflow job for this annotation

GitHub Actions / Quality Monitor

Mutation survived

One mutation survived in line 525 (VoidMethodCallMutator)
Raw output
Survived mutations:
- removed call to edu/hm/hafner/util/FilteredLog::logInfo (org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator)

List<MetricScore> scores = new ArrayList<>();
for (ToolConfiguration tool : metricConfiguration.getTools()) {
var report = factory.create(tool, log);
var score = new MetricScoreBuilder()
.withConfiguration(metricConfiguration)
.withId(tool.getId())
.withName(StringUtils.defaultIfBlank(tool.getName(), report.getName()))
.withReport(report, Metric.fromName(tool.getMetric()))
.build();
scores.add(score);
logSubResult(score);
}

var aggregation = new MetricScoreBuilder()
.withConfiguration(metricConfiguration)
.withScores(scores)
.build();

metricScores.add(aggregation);

logResult(metricConfiguration, aggregation);

Check warning on line 547 in src/main/java/edu/hm/hafner/grading/AggregatedScore.java

View workflow job for this annotation

GitHub Actions / Quality Monitor

Mutation survived

One mutation survived in line 547 (VoidMethodCallMutator)
Raw output
Survived mutations:
- removed call to edu/hm/hafner/grading/AggregatedScore::logResult (org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator)
}
}

private void logSubResult(final Score<?, ?> score) {
if (!score.hasMaxScore()) {
log.logInfo("=> %s: %s",
Expand Down Expand Up @@ -520,6 +587,9 @@ public Map<String, Integer> getMetrics() {
metrics.putAll(getAnalysisTopLevelMetrics());
metrics.putAll(getAnalysisMetrics());
}
if (hasMetrics()) {
metrics.putAll(getSoftwareMetrics());
}
return metrics;
}

Expand All @@ -529,6 +599,13 @@ private Map<String, Integer> getTestMetrics() {
return Map.of("tests", tests);
}

private Map<String, Integer> getSoftwareMetrics() {
return getMetricScores().stream()
.map(Score::getSubScores)
.flatMap(Collection::stream)
.collect(Collectors.toMap(MetricScore::getMetricTagName, MetricScore::getMetricValue));
}

private Map<String, Integer> getCoverageMetrics() {
return getCodeCoverageScores().stream()
.map(Score::getSubScores)
Expand Down Expand Up @@ -556,7 +633,9 @@ private Map<String, Integer> getAnalysisTopLevelMetrics() {
}

/**
* Factory to create the static analysis reports.
* Factory to create the static analysis reports based on the analysis-model.
*
* @see <a href="/~https://github.com/jenkinsci/analysis-model">Analysis Model</a>
*/
public interface AnalysisReportFactory {
/**
Expand All @@ -575,7 +654,9 @@ public interface AnalysisReportFactory {
}

/**
* Factory to create the coverage reports.
* Factory to create the coverage, test and metric reports that are based on the coverage-model.
*
* @see <a href="/~https://github.com/jenkinsci/coverage-model">Coverage Model</a>
*/
public interface CoverageReportFactory {
/**
Expand All @@ -592,23 +673,4 @@ public interface CoverageReportFactory {
*/
Node create(ToolConfiguration tool, FilteredLog log);
}

/**
* Factory to create the test reports.
*/
public interface TestReportFactory {
/**
* Creates a test report for the specified tool.
*
* @param tool
* the tool to create the report for
* @param log
* the logger to report the progress
*
* @return the created report
* @throws NoSuchElementException
* if there is no test report for the specified tool
*/
Node create(ToolConfiguration tool, FilteredLog log);
}
}
8 changes: 8 additions & 0 deletions src/main/java/edu/hm/hafner/grading/AnalysisScore.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import edu.hm.hafner.analysis.Report;
import edu.hm.hafner.analysis.Severity;
import edu.hm.hafner.analysis.registry.ParserDescriptor.Type;
import edu.hm.hafner.analysis.registry.ParserRegistry;
import edu.hm.hafner.util.Ensure;
import edu.hm.hafner.util.Generated;
Expand Down Expand Up @@ -164,6 +165,13 @@ private Optional<String> reportSeverity(final Severity severity) {
}

private String getItemCount(final int count) {
if (REGISTRY.contains(getId())
&& REGISTRY.get(getId()).getType() == Type.VULNERABILITY) {
if (count == 1) {

Check warning on line 170 in src/main/java/edu/hm/hafner/grading/AnalysisScore.java

View workflow job for this annotation

GitHub Actions / Quality Monitor

Partially covered line

Line 170 is only partially covered, one branch is missing
return "vulnerability";

Check warning on line 171 in src/main/java/edu/hm/hafner/grading/AnalysisScore.java

View workflow job for this annotation

GitHub Actions / Quality Monitor

Not covered line

Line 171 is not covered by tests

Check warning on line 171 in src/main/java/edu/hm/hafner/grading/AnalysisScore.java

View workflow job for this annotation

GitHub Actions / Quality Monitor

Not covered line

Line 171 is not covered by tests
}
return "vulnerabilities";
}
return getItemName() + plural(count);
}

Expand Down
Loading

0 comments on commit 11e7198

Please sign in to comment.