Skip to content

Commit

Permalink
Add minimum coverage threshold
Browse files Browse the repository at this point in the history
Right now delta of code coverage is analyzed always then code coverage
check is enabled. This makes this feature a bit annoying when you have
decent code coverage of the project in general and decrease line count
during refactoring of the code. Added minimum coverage threshold to
be able to disable the check for coverage drop when overall code
coverage is higher than this threshold. If coverage is lower than
minimum threshold - than code coverage delta will be analyzed.
  • Loading branch information
mfesenko committed Jun 1, 2017
1 parent 7feefbe commit 945427c
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public class BuildResultProcessor {
public BuildResultProcessor(
Logger logger, AbstractBuild build, Differential diff, DifferentialClient diffClient,
String phid, CodeCoverageMetrics coverageResult, String buildUrl, boolean preserveFormatting,
double maximumCoverageDecreaseInPercent) {
CoverageCheckSettings coverageCheckSettings) {
this.logger = logger;
this.diff = diff;
this.diffClient = diffClient;
Expand All @@ -80,7 +80,7 @@ public BuildResultProcessor(

this.commentAction = "none";
this.commenter = new CommentBuilder(logger, build.getResult(), coverageResult, buildUrl, preserveFormatting,
maximumCoverageDecreaseInPercent);
coverageCheckSettings);
this.runHarbormaster = !CommonUtils.isBlank(phid);
}

Expand Down
22 changes: 15 additions & 7 deletions src/main/java/com/uber/jenkins/phabricator/CommentBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,16 @@ class CommentBuilder {
private final String buildURL;
private final Result result;
private final boolean preserveFormatting;
private final double maximumCoverageDecreaseInPercent;
private final CoverageCheckSettings coverageCheckSettings;

public CommentBuilder(Logger logger, Result result, CodeCoverageMetrics currentCoverage, String buildURL,
boolean preserveFormatting, double maximumCoverageDecreaseInPercent) {
this.maximumCoverageDecreaseInPercent = maximumCoverageDecreaseInPercent;
boolean preserveFormatting, CoverageCheckSettings coverageCheckSettings) {
this.logger = logger;
this.result = result;
this.currentCoverage = currentCoverage;
this.buildURL = buildURL;
this.preserveFormatting = preserveFormatting;
this.coverageCheckSettings = coverageCheckSettings;
this.comment = new StringBuilder();
}

Expand Down Expand Up @@ -99,11 +99,14 @@ public boolean processParentCoverage(CodeCoverageMetrics parentCoverage, String
comment.append(baseCommit.substring(0, 7));
comment.append(".");

// If coverage change is less than zero and dips below a certain threshold fail the build
if (coverageDelta < 0 && Math.abs(coverageDelta) > Math.abs(maximumCoverageDecreaseInPercent)) {
// If line coverage is less than allowed minimum, coverage change is less than zero and dips below
// a certain threshold fail the build
if (isBuildFailingCoverageCheck(lineCoveragePercent, coverageDelta)) {
passCoverage = false;
String message = "Build failed because coverage decreased more than allowed " +
Math.abs(maximumCoverageDecreaseInPercent) + "%";
String message = "Build failed because coverage is lower than minimum " +
coverageCheckSettings.getMinCoverageInPercent() +
"% and decreased more than allowed " +
Math.abs(coverageCheckSettings.getMaxCoverageDecreaseInPercent()) + "%";
logger.info(UBERALLS_TAG, message);
comment.append("\n");
comment.append(message);
Expand All @@ -113,6 +116,11 @@ public boolean processParentCoverage(CodeCoverageMetrics parentCoverage, String
return passCoverage;
}

private boolean isBuildFailingCoverageCheck(double lineCoveragePercent, double coverageDelta) {
return coverageCheckSettings.isCoverageCheckEnabled() && lineCoveragePercent < coverageCheckSettings.getMinCoverageInPercent() &&
coverageDelta < 0 && Math.abs(coverageDelta) > Math.abs(coverageCheckSettings.getMaxCoverageDecreaseInPercent());
}

public void processBuildResult(boolean commentOnSuccess, boolean commentWithConsoleLinkOnFailure, boolean runHarbormaster) {
if (result == Result.SUCCESS) {
if (comment.length() == 0 && (commentOnSuccess || !runHarbormaster)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package com.uber.jenkins.phabricator;

public class CoverageCheckSettings {
private final boolean coverageCheckEnabled;
private final double maxCoverageDecreaseInPercent;
private final double minCoverageInPercent;

public CoverageCheckSettings(boolean coverageCheckEnabled,
double maxCoverageDecreaseInPercent,
double minCoverageInPercent) {
this.coverageCheckEnabled = coverageCheckEnabled;
this.maxCoverageDecreaseInPercent = maxCoverageDecreaseInPercent;
this.minCoverageInPercent = minCoverageInPercent;
}

public boolean isCoverageCheckEnabled() {
return coverageCheckEnabled;
}

public double getMaxCoverageDecreaseInPercent() {
return maxCoverageDecreaseInPercent;
}

public double getMinCoverageInPercent() {
return minCoverageInPercent;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public class PhabricatorNotifier extends Notifier {
// Post a comment on success. Useful for lengthy builds.
private final boolean commentOnSuccess;
private final boolean uberallsEnabled;
private final boolean coverageCheck;
private final CoverageCheckSettings coverageCheckSettings;
private final boolean commentWithConsoleLinkOnFailure;
private final boolean preserveFormatting;
private final String commentFile;
Expand All @@ -76,20 +76,19 @@ public class PhabricatorNotifier extends Notifier {
private final boolean processLint;
private final String lintFile;
private final String lintFileSize;
private final double coverageThreshold;
private final String coverageReportPattern;
private UberallsClient uberallsClient;

// Fields in config.jelly must match the parameter names in the "DataBoundConstructor"
@DataBoundConstructor
public PhabricatorNotifier(boolean commentOnSuccess, boolean uberallsEnabled, boolean coverageCheck,
double coverageThreshold, String coverageReportPattern,
double coverageThreshold, double minCoverageThreshold, String coverageReportPattern,
boolean preserveFormatting, String commentFile, String commentSize,
boolean commentWithConsoleLinkOnFailure, boolean customComment, boolean processLint,
String lintFile, String lintFileSize) {
this.commentOnSuccess = commentOnSuccess;
this.uberallsEnabled = uberallsEnabled;
this.coverageCheck = coverageCheck;
this.coverageCheckSettings = new CoverageCheckSettings(coverageCheck, coverageThreshold, minCoverageThreshold);
this.commentFile = commentFile;
this.commentSize = commentSize;
this.lintFile = lintFile;
Expand All @@ -98,7 +97,6 @@ public PhabricatorNotifier(boolean commentOnSuccess, boolean uberallsEnabled, bo
this.commentWithConsoleLinkOnFailure = commentWithConsoleLinkOnFailure;
this.customComment = customComment;
this.processLint = processLint;
this.coverageThreshold = coverageThreshold;
this.coverageReportPattern = coverageReportPattern;
}

Expand Down Expand Up @@ -223,12 +221,12 @@ public final boolean perform(final AbstractBuild<?, ?> build, final Launcher lau
coverageResult,
buildUrl,
preserveFormatting,
coverageThreshold
coverageCheckSettings
);

if (uberallsEnabled) {
boolean passBuildOnUberalls = resultProcessor.processParentCoverage(uberallsClient);
if (!passBuildOnUberalls && coverageCheck) {
if (!passBuildOnUberalls) {
build.setResult(Result.FAILURE);
}
}
Expand Down Expand Up @@ -358,7 +356,7 @@ public boolean isUberallsEnabled() {

@SuppressWarnings("UnusedDeclaration")
public boolean isCoverageCheck() {
return coverageCheck;
return coverageCheckSettings.isCoverageCheckEnabled();
}

@SuppressWarnings("UnusedDeclaration")
Expand All @@ -378,7 +376,12 @@ public String getCommentSize() {

@SuppressWarnings("UnusedDeclaration")
public double getCoverageThreshold() {
return coverageThreshold;
return coverageCheckSettings.getMaxCoverageDecreaseInPercent();
}

@SuppressWarnings("UnusedDeclaration")
public double getMinCoverageThreshold() {
return coverageCheckSettings.getMinCoverageInPercent();
}

@SuppressWarnings("UnusedDeclaration")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,17 @@
<f:optionalBlock field="uberallsEnabled" name="uberallsEnabled" title="Enable Uberalls" checked="${instance.isUberallsEnabled()}" inline="true"
description="Enable code coverage reporting">
<f:entry title="Fail on decreased coverage" field="coverageCheck"
description="Fail the build if coverage percentage goes down from last build">
description="Fail the build if coverage percentage is lower than minimum threshold and goes down from last build">
<f:checkbox default="false" />
</f:entry>
<f:entry title="Coverage failure threshold" field="coverageThreshold"
description="Fail builds if coverage is below this threshold. Should be a negative number i.e. -5.0 as percent">
description="Fail builds if coverage change is below this threshold. Should be a negative number i.e. -5.0 as percent. This check will be performed only if overall coverage is lower than minimum threshold defined below">
<f:textbox default="0" />
</f:entry>
<f:entry title="Minimum line coverage threshold" field="minCoverageThreshold"
description="If line coverage is higher than this threshold - drop in coverage will not fail the build. Should be a positive number i.e. 85.0 as percent">
<f:textbox default="100" />
</f:entry>
<f:entry title="Coverage report pattern" field="coverageReportPattern"
description="The coverage xml report pattern. Use this if any jenkins coverage plugins are not applied.">
<f:textbox default="" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

// Copyright (c) 2015 Uber
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
Expand Down Expand Up @@ -69,7 +70,7 @@ public void setUp() throws IOException {
mock(CodeCoverageMetrics.class),
TestUtils.TEST_BASE_URL,
true,
0.0
new CoverageCheckSettings(true, 0.0, 100.0)
);
project = j.createFreeStyleProject();
}
Expand Down Expand Up @@ -175,7 +176,7 @@ private BuildResultProcessor runProcessLintViolationsTest(String lintFileContent
mock(CodeCoverageMetrics.class),
TestUtils.TEST_BASE_URL,
true,
0.0
new CoverageCheckSettings(true, 0.0, 100.0)
);
processor.processLintResults(fileName, "1000");
processor.processHarbormaster();
Expand Down
26 changes: 22 additions & 4 deletions src/test/java/com/uber/jenkins/phabricator/CommentBuilderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public void testProcessWithDecreaseFailingTheBuild() {

assertFalse(passCoverage);
assertThat(comment, containsString("decreased (-50.000%)"));
assertThat(comment, containsString("Build failed because coverage decreased more than allowed 10.0%."));
assertThat(comment, containsString("Build failed because coverage is lower than minimum 100.0% and decreased more than allowed 10.0%."));
}

@Test
Expand All @@ -119,7 +119,20 @@ public void testProcessWithDecreaseNotFailingTheBuild() {

assertTrue(passCoverage);
assertThat(comment, containsString("decreased (-5.000%)"));
assertFalse(comment.contains("Build failed because coverage decreased more than allowed 10.0%."));
assertFalse(comment.contains("Build failed because coverage is lower than minimum 100.0% and decreased more than allowed 10.0%."));
}

@Test
public void testProcessWithDecreaseButHigherThanMinNotFailingTheBuild() {
CodeCoverageMetrics fifteenPercentDrop = TestUtils.getCoverageResult(100.0f, 100.0f, 100.0f, 100.0f, 85.0f);
CommentBuilder commenter = createCommenter(Result.SUCCESS, fifteenPercentDrop, false, -10.0f, 80.0f);
boolean passCoverage = commenter.processParentCoverage(TestUtils.getDefaultCodeCoverageMetrics(),
TestUtils.TEST_SHA, FAKE_BRANCH_NAME);
String comment = commenter.getComment();

assertTrue(passCoverage);
assertThat(comment, containsString("decreased (-15.000%)"));
assertFalse(comment.contains("Build failed because coverage is lower than minimum 80.0% and decreased more than allowed 10.0%."));
}

@Test
Expand Down Expand Up @@ -223,8 +236,13 @@ private CommentBuilder createCommenter(Result result, CodeCoverageMetrics covera
}

private CommentBuilder createCommenter(Result result, CodeCoverageMetrics coverage, boolean preserveFormatting,
double maximumCoverageDecreaseInPercent) {
double maxCoverageDecreaseInPercent) {
return createCommenter(result, coverage, preserveFormatting, maxCoverageDecreaseInPercent, 100.0);
}

private CommentBuilder createCommenter(Result result, CodeCoverageMetrics coverage, boolean preserveFormatting,
double maxCoverageDecreaseInPercent, double minCoverageInPercent) {
return new CommentBuilder(logger, result, coverage, FAKE_BUILD_URL, preserveFormatting,
maximumCoverageDecreaseInPercent);
new CoverageCheckSettings(true, maxCoverageDecreaseInPercent, minCoverageInPercent));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public void setUp() throws IOException {
true,
false,
0.0,
0.0,
COVERAGE_REPORT_FILTER,
true,
".phabricator-comment",
Expand Down Expand Up @@ -173,10 +174,10 @@ public void testFailBuildOnDecreasedCoverage() throws Exception {
}

@Test
public void testPassBuildOnDecreasedCoverageGreaterThanMaxPercent() throws Exception {
public void testPassBuildOnDecreasedCoverageButGreaterThanMinPercent() throws Exception {
TestUtils.addCopyBuildStep(p, TestUtils.COBERTURA_XML, CoberturaXMLParser.class, "go-torch-coverage2.xml");
UberallsClient uberalls = TestUtils.getDefaultUberallsClient();
notifier = getDecreasedLineCoverageNotifier(-5.0);
notifier = getNotifierWithCoverageCheck(0.0, 90.0);

when(uberalls.getCoverage(any(String.class))).thenReturn("{\n" +
" \"sha\": \"deadbeef\",\n" +
Expand Down Expand Up @@ -276,6 +277,7 @@ public void testPostCoverageUberallsDisabled() throws Exception {
false,
false,
0.0,
0.0,
COVERAGE_REPORT_FILTER,
true,
".phabricator-comment",
Expand Down Expand Up @@ -313,12 +315,17 @@ protected void addBuildStep() {
p.getPublishersList().add(notifier);
}

protected PhabricatorNotifier getDecreasedLineCoverageNotifier(double threshold) {
private PhabricatorNotifier getDecreasedLineCoverageNotifier(double threshold) {
return getNotifierWithCoverageCheck(threshold, 100.0);
}

private PhabricatorNotifier getNotifierWithCoverageCheck(double maxCoverageDecrease, double minCoverage) {
return new PhabricatorNotifier(
false,
true,
true,
threshold,
maxCoverageDecrease,
minCoverage,
COVERAGE_REPORT_FILTER,
true,
".phabricator-comment",
Expand Down

0 comments on commit 945427c

Please sign in to comment.