Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement extension for code coverage badges #280

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.jenkins.plugins</groupId>
<artifactId>coverage</artifactId>
<version>1.7.0</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.jenkins.plugins</groupId>
<artifactId>ionicons-api</artifactId>
Expand All @@ -80,6 +86,21 @@
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-step-api</artifactId>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-cps</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-job</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkinsci.plugins</groupId>
<artifactId>pipeline-model-definition</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package org.jenkinsci.plugins.badge.extensions;

import edu.hm.hafner.coverage.Coverage;
import edu.hm.hafner.coverage.Metric;
import edu.hm.hafner.coverage.Value;
import hudson.Extension;
import hudson.model.Actionable;
import hudson.model.Job;
import hudson.model.Run;
import io.jenkins.plugins.coverage.metrics.model.Baseline;
import io.jenkins.plugins.coverage.metrics.model.ElementFormatter;
import io.jenkins.plugins.coverage.metrics.steps.CoverageBuildAction;
import java.util.logging.Logger;
import jenkins.model.Jenkins;
import org.jenkinsci.plugins.badge.extensionpoints.ParameterResolverExtensionPoint;

@Extension(optional = true)
public class CoverageParameterResolverExtension implements ParameterResolverExtensionPoint {

private static final ElementFormatter FORMATTER = new ElementFormatter();

public static final Logger LOGGER = Logger.getLogger(CoverageParameterResolverExtension.class.getName());

@Override
public String resolve(Actionable actionable, String parameter) {

// Just return parameter if coverage plugin is not installed
if (Jenkins.get().getPlugin("coverage") == null) {

Check warning on line 28 in src/main/java/org/jenkinsci/plugins/badge/extensions/CoverageParameterResolverExtension.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 28 is only partially covered, one branch is missing
return parameter;

Check warning on line 29 in src/main/java/org/jenkinsci/plugins/badge/extensions/CoverageParameterResolverExtension.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 29 is not covered by tests
}

if (parameter != null) {

Check warning on line 32 in src/main/java/org/jenkinsci/plugins/badge/extensions/CoverageParameterResolverExtension.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 32 is only partially covered, one branch is missing
if (actionable instanceof Run<?, ?>) {

Check warning on line 33 in src/main/java/org/jenkinsci/plugins/badge/extensions/CoverageParameterResolverExtension.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 33 is only partially covered, one branch is missing
Run<?, ?> run = (Run<?, ?>) actionable;

// Get the action
CoverageBuildAction action = run.getAction(CoverageBuildAction.class);
if (action == null) {

Check warning on line 38 in src/main/java/org/jenkinsci/plugins/badge/extensions/CoverageParameterResolverExtension.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 38 is only partially covered, one branch is missing
return parameter;

Check warning on line 39 in src/main/java/org/jenkinsci/plugins/badge/extensions/CoverageParameterResolverExtension.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 39 is not covered by tests
}

// Get the values
Value intructionCoverage = action.getStatistics()
.getValue(Baseline.PROJECT, Metric.INSTRUCTION)
.orElse(null);
Value branchCoverage = action.getStatistics()
.getValue(Baseline.PROJECT, Metric.BRANCH)
.orElse(null);
Value lineOfCode = action.getStatistics()
.getValue(Baseline.PROJECT, Metric.LOC)
.orElse(null);

// Replace the parameters
parameter = parameter
.replace(
"intructionCoverage",
intructionCoverage != null ? FORMATTER.format(intructionCoverage) : parameter)

Check warning on line 57 in src/main/java/org/jenkinsci/plugins/badge/extensions/CoverageParameterResolverExtension.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 57 is only partially covered, one branch is missing
.replace(
"branchCoverage", branchCoverage != null ? FORMATTER.format(branchCoverage) : parameter)

Check warning on line 59 in src/main/java/org/jenkinsci/plugins/badge/extensions/CoverageParameterResolverExtension.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 59 is only partially covered, one branch is missing
.replace("lineOfCode", lineOfCode != null ? FORMATTER.format(lineOfCode) : parameter)

Check warning on line 60 in src/main/java/org/jenkinsci/plugins/badge/extensions/CoverageParameterResolverExtension.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 60 is only partially covered, one branch is missing
.replace("colorInstructionCoverage", getColor(intructionCoverage))
.replace("colorBranchCoverage", getColor(branchCoverage));

} else if (actionable instanceof Job<?, ?>) {
parameter = resolve(((Job<?, ?>) actionable).getLastBuild(), parameter);

Check warning on line 65 in src/main/java/org/jenkinsci/plugins/badge/extensions/CoverageParameterResolverExtension.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 64-65 are not covered by tests
}
}
return parameter;
}

private String getColor(Value value) {
if (value instanceof Coverage) {

Check warning on line 72 in src/main/java/org/jenkinsci/plugins/badge/extensions/CoverageParameterResolverExtension.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 72 is only partially covered, one branch is missing
Coverage coverage = (Coverage) value;
int percentage = coverage.getCoveredPercentage().toInt();
if (percentage <= 20.0) {

Check warning on line 75 in src/main/java/org/jenkinsci/plugins/badge/extensions/CoverageParameterResolverExtension.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 75 is only partially covered, one branch is missing
return "red";

Check warning on line 76 in src/main/java/org/jenkinsci/plugins/badge/extensions/CoverageParameterResolverExtension.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 76 is not covered by tests
} else if (percentage <= 30.0) {

Check warning on line 77 in src/main/java/org/jenkinsci/plugins/badge/extensions/CoverageParameterResolverExtension.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 77 is only partially covered, one branch is missing
return "orange";

Check warning on line 78 in src/main/java/org/jenkinsci/plugins/badge/extensions/CoverageParameterResolverExtension.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 78 is not covered by tests
} else if (percentage <= 40.0) {

Check warning on line 79 in src/main/java/org/jenkinsci/plugins/badge/extensions/CoverageParameterResolverExtension.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 79 is only partially covered, one branch is missing
return "yellow";

Check warning on line 80 in src/main/java/org/jenkinsci/plugins/badge/extensions/CoverageParameterResolverExtension.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 80 is not covered by tests
} else if (percentage <= 50.0) {
return "yellowgreen";
} else if (percentage <= 70.0) {

Check warning on line 83 in src/main/java/org/jenkinsci/plugins/badge/extensions/CoverageParameterResolverExtension.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 83 is only partially covered, one branch is missing
return "green";

Check warning on line 84 in src/main/java/org/jenkinsci/plugins/badge/extensions/CoverageParameterResolverExtension.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 84 is not covered by tests
} else {
return "brightgreen";
}
}
return "green";

Check warning on line 89 in src/main/java/org/jenkinsci/plugins/badge/extensions/CoverageParameterResolverExtension.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 89 is not covered by tests
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.jenkinsci.plugins.badge;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;

import java.nio.charset.StandardCharsets;
import org.apache.commons.io.IOUtils;
import org.jenkinsci.plugins.badge.extensions.CoverageParameterResolverExtension;
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.junit.jupiter.api.Test;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.junit.jupiter.WithJenkins;

@WithJenkins
class CoverageParameterResolverExtensionTest {

@Test
void shouldResolveIntructionCoverage(JenkinsRule jenkins) throws Exception {
String pipeline = IOUtils.toString(
CoverageParameterResolverExtensionTest.class.getResourceAsStream("/pipelines/coverage.groovy"),
StandardCharsets.UTF_8);
WorkflowJob workflowJob = jenkins.createProject(WorkflowJob.class);
workflowJob.setDefinition(new CpsFlowDefinition(pipeline, false));
WorkflowRun run1 = workflowJob.scheduleBuild2(0).waitForStart();
jenkins.waitForCompletion(run1);
assertThat(run1.getResult(), equalTo(hudson.model.Result.SUCCESS));

// Coverages badges
assertThat(new CoverageParameterResolverExtension().resolve(run1, "unknown"), is("unknown"));
assertThat(new CoverageParameterResolverExtension().resolve(run1, "intructionCoverage"), is("50.00%"));
assertThat(new CoverageParameterResolverExtension().resolve(run1, "branchCoverage"), is("100.00%"));
assertThat(new CoverageParameterResolverExtension().resolve(run1, "lineOfCode"), is("10"));
assertThat(
new CoverageParameterResolverExtension().resolve(run1, "colorInstructionCoverage"), is("yellowgreen"));
assertThat(new CoverageParameterResolverExtension().resolve(run1, "colorBranchCoverage"), is("brightgreen"));
}
}
36 changes: 36 additions & 0 deletions src/test/resources/pipelines/coverage.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
pipeline {
agent any
stages {
stage('Generate Jacoco XML') {
steps {
script {
def jacocoXmlContent = '''<?xml version="1.0" encoding="UTF-8"?>
<report name="Example">
<sessioninfo id="session1" start="2023-01-01T12:00:00" dump="2023-01-01T12:30:00" />
<group name="Group1">
<package name="com.example.package">
<class name="com.example.package.ExampleClass" sourcefilename="ExampleClass.java">
<method name="exampleMethod" desc="()V" line="10">
<counter type="INSTRUCTION" missed="50" covered="50" />
<counter type="BRANCH" missed="0" covered="50" />
<counter type="LINE" missed="5" covered="5" />
<counter type="COMPLEXITY" missed="5" covered="5" />
<counter type="METHOD" missed="1" covered="1" />
<counter type="CLASS" missed="0" covered="1" />
</method>
</class>
</package>
</group>
</report>
'''
writeFile(file: 'jacoco.xml', text: jacocoXmlContent, encoding: 'UTF-8')
}
}
}
stage('Record Test and coverage') {
steps {
recordCoverage(tools: [[parser: 'JACOCO']])
}
}
}
}
Loading