diff --git a/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/CloverParser.java b/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/CloverParser.java index b5f2040..f07df78 100644 --- a/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/CloverParser.java +++ b/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/CloverParser.java @@ -36,6 +36,15 @@ class CloverParser implements CoverageReportParser { private static final String TOTAL_STATEMENTS_XPATH = "/coverage/project/metrics/@statements"; private static final String COVER_STATEMENTS_XPATH = "/coverage/project/metrics/@coveredstatements"; + @Override + public boolean canAggregate() { + return false; + } + @Override + public float getAggregate() { + throw new UnsupportedOperationException(); + } + private int getByXpath(final String filePath, final String content, final String xpath) { try { return Integer.parseInt(XmlUtils.findInXml(content, xpath)); diff --git a/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/CoberturaParser.java b/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/CoberturaParser.java index 1960013..72d777e 100644 --- a/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/CoberturaParser.java +++ b/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/CoberturaParser.java @@ -29,6 +29,15 @@ */ class CoberturaParser implements CoverageReportParser { + @Override + public boolean canAggregate() { + return false; + } + @Override + public float getAggregate() { + throw new UnsupportedOperationException(); + } + private static String findFirst(String string, String pattern) { String result = findFirstOrNull(string, pattern); if (result != null) { diff --git a/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/CompareCoverageAction.java b/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/CompareCoverageAction.java index 5508bf5..aa0dade 100644 --- a/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/CompareCoverageAction.java +++ b/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/CompareCoverageAction.java @@ -105,6 +105,16 @@ public void perform( final int prId = PrIdAndUrlUtils.getPrId(scmVars, build, listener); final String gitUrl = PrIdAndUrlUtils.getGitUrl(scmVars, build, listener); + if (settingsRepository.getSonarCoverageMetric() != null) { + buildLog.println(BUILD_LOG_PREFIX + "using coverage metrics: " + settingsRepository.getSonarCoverageMetric()); + } + + if (settingsRepository.getCoverageRoundingDigits() != 0) { + buildLog.println(BUILD_LOG_PREFIX + "using round-up to digits: " + settingsRepository.getCoverageRoundingDigits()); + } + + buildLog.println(BUILD_LOG_PREFIX + "using aggregates for coverage: " + settingsRepository.isUseAggregatesForCoverage()); + buildLog.println(BUILD_LOG_PREFIX + "getting master coverage..."); MasterCoverageRepository masterCoverageRepository = ServiceRegistry.getMasterCoverageRepository(buildLog, sonarLogin, sonarPassword); final GHRepository gitHubRepository = ServiceRegistry.getPullRequestRepository().getGitHubRepository(gitUrl); diff --git a/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/Configuration.java b/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/Configuration.java index 1121343..b3954ea 100644 --- a/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/Configuration.java +++ b/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/Configuration.java @@ -77,10 +77,22 @@ public static Boolean isUseSonarForMasterCoverage() { return DESCRIPTOR.isUseSonarForMasterCoverage(); } + public static String getSonarCoverageMetric() { + return DESCRIPTOR.getSonarCoverageMetric(); + } + + public static int getCoverageRoundingDigits() { + return DESCRIPTOR.getCoverageRoundingDigits(); + } + public static void setMasterCoverage(final String repo, final float coverage) { DESCRIPTOR.set(repo, coverage); } + public static Boolean isUseAggregatesForCoverage() { + return DESCRIPTOR.isUseAggregatesForCoverage(); + } + @Override public ConfigurationDescriptor getDescriptor() { return DESCRIPTOR; @@ -100,13 +112,16 @@ public static final class ConfigurationDescriptor extends Descriptor getFloats(File ws, String path, CoverageReportParser String[] files = ds.getIncludedFiles(); List cov = new ArrayList(); for (String file : files) cov.add(parser.get(new File(ds.getBasedir(), file).getAbsolutePath())); + if (cov.size() > 0 && parser.canAggregate()) { + cov.clear(); + cov.add(parser.getAggregate()); + } return cov; } diff --git a/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/JacocoParser.java b/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/JacocoParser.java index 58fe401..2b0840e 100644 --- a/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/JacocoParser.java +++ b/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/JacocoParser.java @@ -31,10 +31,14 @@ */ class JacocoParser implements CoverageReportParser { - private static final String MISSED_XPATH = "/report/counter[@type='LINE']/@missed"; - private static final String COVERAGE_XPATH = "/report/counter[@type='LINE']/@covered"; + public static final String MISSED_INSTRUCTION_XPATH = "/report/counter[@type='INSTRUCTION']/@missed"; + public static final String COVERAGE_INSTRUCTION_XPATH = "/report/counter[@type='INSTRUCTION']/@covered"; + public static final String MISSED_LINE_XPATH = "/report/counter[@type='LINE']/@missed"; + public static final String COVERAGE_LINE_XPATH = "/report/counter[@type='LINE']/@covered"; + public static final String MISSED_BRANCH_XPATH = "/report/counter[@type='BRANCH']/@missed"; + public static final String COVERAGE_BRANCH_XPATH = "/report/counter[@type='BRANCH']/@covered"; - private float getByXpath(final String filePath, final String content, final String xpath) { + public static float getByXpath(final String filePath, final String content, final String xpath) { try { return Float.parseFloat(XmlUtils.findInXml(content, xpath)); } catch (NumberFormatException e) { @@ -45,7 +49,16 @@ private float getByXpath(final String filePath, final String content, final Stri "from:\n" + content); } } + + @Override + public boolean canAggregate() { + final SettingsRepository settingsRepository = ServiceRegistry.getSettingsRepository(); + return settingsRepository.isUseAggregatesForCoverage(); + } + private volatile float totalCovered = 0.0f; + private volatile float totalMissed = 0.0f; + @Override public float get(final String jacocoFilePath) { final String content; @@ -56,14 +69,43 @@ public float get(final String jacocoFilePath) { "Can't read Jacoco report by path: " + jacocoFilePath); } - final float lineMissed = getByXpath(jacocoFilePath, content, MISSED_XPATH); - final float lineCovered = getByXpath(jacocoFilePath, content, COVERAGE_XPATH); - final float lines = lineCovered + lineMissed; - if (lines == 0) { + final SettingsRepository settingsRepository = ServiceRegistry.getSettingsRepository(); + String missedMetric; + String coverageMetric; + if (settingsRepository.getSonarCoverageMetric() != null && SonarMasterCoverageRepository.SONAR_OVERALL_INSTRUCTION_COVERAGE_METRIC_NAME.equalsIgnoreCase(settingsRepository.getSonarCoverageMetric())) { + missedMetric = MISSED_INSTRUCTION_XPATH; + coverageMetric = COVERAGE_INSTRUCTION_XPATH; + } else if (settingsRepository.getSonarCoverageMetric() != null && SonarMasterCoverageRepository.SONAR_OVERALL_BRANCH_COVERAGE_METRIC_NAME.equalsIgnoreCase(settingsRepository.getSonarCoverageMetric())) { + missedMetric = MISSED_BRANCH_XPATH; + coverageMetric = COVERAGE_BRANCH_XPATH; + } else { + missedMetric = MISSED_LINE_XPATH; + coverageMetric = COVERAGE_LINE_XPATH; + } + + final float countMissed = getByXpath(jacocoFilePath, content, missedMetric); + final float countCovered = getByXpath(jacocoFilePath, content, coverageMetric); + + totalCovered += countCovered; + totalMissed += countMissed; + + final float count = countCovered + countMissed; + if (count == 0) { return 0; } else { - return lineCovered / (lines); + return countCovered / (count); } } + + @Override + public float getAggregate () { + final float count = totalCovered + totalMissed; + if (count == 0) { + return 0; + } else { + return totalCovered / (count); + } + + } } diff --git a/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/Message.java b/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/Message.java index ec8407f..987c350 100644 --- a/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/Message.java +++ b/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/Message.java @@ -34,8 +34,14 @@ class Message { private final float masterCoverage; public Message(float coverage, float masterCoverage) { - this.coverage = Percent.roundFourAfterDigit(coverage); - this.masterCoverage = Percent.roundFourAfterDigit(masterCoverage); + final SettingsRepository settingsRepository = ServiceRegistry.getSettingsRepository(); + if (settingsRepository.getCoverageRoundingDigits() == 0) { + this.coverage = Percent.roundFourAfterDigit(coverage); + this.masterCoverage = Percent.roundFourAfterDigit(masterCoverage); + } else { + this.coverage = Percent.roundCustomAfterDigit(coverage, settingsRepository.getCoverageRoundingDigits()); + this.masterCoverage = Percent.roundCustomAfterDigit(masterCoverage, settingsRepository.getCoverageRoundingDigits()); + } } public String forConsole() { diff --git a/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/Percent.java b/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/Percent.java index e390d4c..0f467a6 100644 --- a/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/Percent.java +++ b/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/Percent.java @@ -42,6 +42,11 @@ public static float roundFourAfterDigit(float value) { return ((float) Math.round(value * 10000)) / 10000; } + public static float roundCustomAfterDigit(float value, int places) { + double scale = Math.pow(10, places); + return (float) (Math.ceil(value * scale) / scale); + } + public static String toWholeString(float value) { return (value < 0 ? "-" : value > 0 ? "+" : "") + toWholeNoSignString(value); } diff --git a/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/SettingsRepository.java b/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/SettingsRepository.java index f8f828a..a0426d4 100644 --- a/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/SettingsRepository.java +++ b/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/SettingsRepository.java @@ -17,8 +17,14 @@ interface SettingsRepository { boolean isUseSonarForMasterCoverage(); boolean isDisableSimpleCov(); + + boolean isUseAggregatesForCoverage(); String getSonarUrl(); String getSonarToken(); + + String getSonarCoverageMetric(); + + int getCoverageRoundingDigits(); } diff --git a/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/SimpleCovParser.java b/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/SimpleCovParser.java index 4ebb1c1..7925307 100644 --- a/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/SimpleCovParser.java +++ b/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/SimpleCovParser.java @@ -13,6 +13,15 @@ public class SimpleCovParser implements CoverageReportParser { private static final String METRIC_PATH = "$.metrics.covered_percent"; + @Override + public boolean canAggregate() { + return false; + } + @Override + public float getAggregate() { + throw new UnsupportedOperationException(); + } + @Override public float get(String simpleCovFilePath) { final String content; diff --git a/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/SonarMasterCoverageRepository.java b/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/SonarMasterCoverageRepository.java index fc49162..cb0acb9 100644 --- a/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/SonarMasterCoverageRepository.java +++ b/src/main/java/com/github/terma/jenkins/githubprcoveragestatus/SonarMasterCoverageRepository.java @@ -40,7 +40,9 @@ public class SonarMasterCoverageRepository implements MasterCoverageRepository { private static final String SONAR_SEARCH_PROJECTS_API_PATH = "/api/projects/index"; private static final String SONAR_COMPONENT_MEASURE_API_PATH = "/api/measures/component"; - public static final String SONAR_OVERALL_LINE_COVERAGE_METRIC_NAME = "coverage"; + public static final String SONAR_OVERALL_INSTRUCTION_COVERAGE_METRIC_NAME = "coverage"; + public static final String SONAR_OVERALL_LINE_COVERAGE_METRIC_NAME = "line_coverage"; + public static final String SONAR_OVERALL_BRANCH_COVERAGE_METRIC_NAME = "branch_coverage"; private final String sonarUrl; private final String login; @@ -92,7 +94,13 @@ private SonarProject getSonarProject(final String repoName) throws SonarProjectR log("Found project for repo name %s - %s", repoName, sonarProjects.get(0)); return sonarProjects.get(0); } else { - log("Found multiple projects for repo name %s - found %s - returning first result", repoName, sonarProjects); + log("Found multiple projects for repo name %s - found %s - looking for repo/key exact match", repoName, sonarProjects); + for (SonarProject project: sonarProjects) { + if (project.getKey().equalsIgnoreCase(repoName)) { + return project; + } + } + log("No repo/key exact match, returning first result"); return sonarProjects.get(0); } } catch (final Exception e) { @@ -107,7 +115,10 @@ private SonarProject getSonarProject(final String repoName) throws SonarProjectR * @throws SonarCoverageMeasureRetrievalException if an error occurred during retrieval of the coverage */ private float getCoverageMeasure(SonarProject project) throws SonarCoverageMeasureRetrievalException { - final String uri = MessageFormat.format("{0}{1}?componentKey={2}&metricKeys={3}", sonarUrl, SONAR_COMPONENT_MEASURE_API_PATH, URLEncoder.encode(project.getKey()), SONAR_OVERALL_LINE_COVERAGE_METRIC_NAME); + final SettingsRepository settingsRepository = ServiceRegistry.getSettingsRepository(); + String metricName = settingsRepository.getSonarCoverageMetric(); + if (metricName == null) metricName = SONAR_OVERALL_INSTRUCTION_COVERAGE_METRIC_NAME; + final String uri = MessageFormat.format("{0}{1}?componentKey={2}&metricKeys={3}", sonarUrl, SONAR_COMPONENT_MEASURE_API_PATH, URLEncoder.encode(project.getKey()), metricName); try { final GetMethod method = executeGetRequest(uri); String value = JsonUtils.findInJson(method.getResponseBodyAsString(), "component.measures[0].value"); diff --git a/src/main/resources/com/github/terma/jenkins/githubprcoveragestatus/Configuration/global.groovy b/src/main/resources/com/github/terma/jenkins/githubprcoveragestatus/Configuration/global.groovy index 356f051..d40a502 100644 --- a/src/main/resources/com/github/terma/jenkins/githubprcoveragestatus/Configuration/global.groovy +++ b/src/main/resources/com/github/terma/jenkins/githubprcoveragestatus/Configuration/global.groovy @@ -48,6 +48,18 @@ f.section(title: descriptor.displayName) { f.password() } + f.entry(field: "sonarCoverageMetric", title: _("Sonar coverage metric")) { + f.textbox() + } + + f.entry(field: "coverageRoundingDigits", title: _("Rounding digits")) { + f.textbox() + } + + f.entry(field: "useAggregatesForCoverage", title: _("Use aggregate calculations for coverage")) { + f.checkbox() + } + f.entry(field: "disableSimpleCov", title: _("Disable SimpleCov coverage parser")) { f.checkbox() } diff --git a/src/main/resources/com/github/terma/jenkins/githubprcoveragestatus/Configuration/help-coverageRoundingDigits.html b/src/main/resources/com/github/terma/jenkins/githubprcoveragestatus/Configuration/help-coverageRoundingDigits.html new file mode 100644 index 0000000..4dbb7f3 --- /dev/null +++ b/src/main/resources/com/github/terma/jenkins/githubprcoveragestatus/Configuration/help-coverageRoundingDigits.html @@ -0,0 +1,3 @@ +
+ Number of digits to round at(Optional). If not specified, no rounding will be performed. In order to match SonarQube, it always rounds up. +
\ No newline at end of file diff --git a/src/main/resources/com/github/terma/jenkins/githubprcoveragestatus/Configuration/help-sonarCoverageMetric.html b/src/main/resources/com/github/terma/jenkins/githubprcoveragestatus/Configuration/help-sonarCoverageMetric.html new file mode 100644 index 0000000..7c3c98f --- /dev/null +++ b/src/main/resources/com/github/terma/jenkins/githubprcoveragestatus/Configuration/help-sonarCoverageMetric.html @@ -0,0 +1,3 @@ +
+ Coverage metric to retrieve from Sonar(Optional). It will use "coverage" if not specified. Known values are "coverage", "line_coverage", and "branch_coverage". +
\ No newline at end of file diff --git a/src/main/resources/com/github/terma/jenkins/githubprcoveragestatus/Configuration/help-useAggregatesForCoverage.html b/src/main/resources/com/github/terma/jenkins/githubprcoveragestatus/Configuration/help-useAggregatesForCoverage.html new file mode 100644 index 0000000..dd89c1e --- /dev/null +++ b/src/main/resources/com/github/terma/jenkins/githubprcoveragestatus/Configuration/help-useAggregatesForCoverage.html @@ -0,0 +1,3 @@ +
+ When enabled compatible plugins will calculate coverage based on aggregate covered/missed amounts, not as an average of all sub-modules. +
\ No newline at end of file diff --git a/src/test/java/com/github/terma/jenkins/githubprcoveragestatus/JacocoParserTest.java b/src/test/java/com/github/terma/jenkins/githubprcoveragestatus/JacocoParserTest.java index 25ef44b..0fc52b7 100644 --- a/src/test/java/com/github/terma/jenkins/githubprcoveragestatus/JacocoParserTest.java +++ b/src/test/java/com/github/terma/jenkins/githubprcoveragestatus/JacocoParserTest.java @@ -18,17 +18,84 @@ package com.github.terma.jenkins.githubprcoveragestatus; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; + +import static org.mockito.Mockito.mock; import java.io.IOException; public class JacocoParserTest { + private SettingsRepository settingsRepository = mock(SettingsRepository.class); + + @Before + public void initMocks() throws IOException { + ServiceRegistry.setSettingsRepository(settingsRepository); + } + + @Test + public void extractCoverageFromJacocoReportDefault() throws IOException { + String filePath = JacocoParserTest.class.getResource( + "/com/github/terma/jenkins/githubprcoveragestatus/JacocoParserTest/jacoco.xml").getFile(); + + Mockito.when(settingsRepository.getSonarCoverageMetric()).thenReturn(null); + Assert.assertEquals(0.22, new JacocoParser().get(filePath), 0.1); + } + + @Test + public void extractCoverageFromJacocoReportInstruction() throws IOException { + String filePath = JacocoParserTest.class.getResource( + "/com/github/terma/jenkins/githubprcoveragestatus/JacocoParserTest/jacoco.xml").getFile(); + + Mockito.when(settingsRepository.getSonarCoverageMetric()).thenReturn(SonarMasterCoverageRepository.SONAR_OVERALL_INSTRUCTION_COVERAGE_METRIC_NAME); + Assert.assertEquals(0.22, new JacocoParser().get(filePath), 0.1); + } + + @Test + public void extractCoverageFromJacocoReportAggregate() throws IOException { + String filePath1 = JacocoParserTest.class.getResource( + "/com/github/terma/jenkins/githubprcoveragestatus/JacocoParserTest/jacoco.xml").getFile(); + String filePath2 = JacocoParserTest.class.getResource( + "/com/github/terma/jenkins/githubprcoveragestatus/JacocoParserTest/jacoco-multiple.xml").getFile(); + + Mockito.when(settingsRepository.isUseAggregatesForCoverage()).thenReturn(true); + JacocoParser parser = new JacocoParser(); + parser.get(filePath1); + parser.get(filePath2); + Assert.assertEquals(0.22, parser.getAggregate(), 0.1); + } + + @Test + public void extractCoverageFromJacocoReportNoAggregate() throws IOException { + String filePath1 = JacocoParserTest.class.getResource( + "/com/github/terma/jenkins/githubprcoveragestatus/JacocoParserTest/jacoco.xml").getFile(); + String filePath2 = JacocoParserTest.class.getResource( + "/com/github/terma/jenkins/githubprcoveragestatus/JacocoParserTest/jacoco-multiple.xml").getFile(); + + Mockito.when(settingsRepository.isUseAggregatesForCoverage()).thenReturn(false); + JacocoParser parser = new JacocoParser(); + Float a = parser.get(filePath1); + Float b = parser.get(filePath2); + Assert.assertEquals(0.61, (a+b)/2, 0.1); + } + + @Test + public void extractCoverageFromJacocoReportLine() throws IOException { + String filePath = JacocoParserTest.class.getResource( + "/com/github/terma/jenkins/githubprcoveragestatus/JacocoParserTest/jacoco.xml").getFile(); + + Mockito.when(settingsRepository.getSonarCoverageMetric()).thenReturn(SonarMasterCoverageRepository.SONAR_OVERALL_LINE_COVERAGE_METRIC_NAME); + Assert.assertEquals(0.22, new JacocoParser().get(filePath), 0.1); + } + @Test - public void extractCoverageFromJacocoReport() throws IOException { + public void extractCoverageFromJacocoReportBranch() throws IOException { String filePath = JacocoParserTest.class.getResource( "/com/github/terma/jenkins/githubprcoveragestatus/JacocoParserTest/jacoco.xml").getFile(); + Mockito.when(settingsRepository.getSonarCoverageMetric()).thenReturn(SonarMasterCoverageRepository.SONAR_OVERALL_BRANCH_COVERAGE_METRIC_NAME); Assert.assertEquals(0.22, new JacocoParser().get(filePath), 0.1); } @@ -59,7 +126,7 @@ public void throwExceptionWhenExtractCoverageFromJacocoAndNoLineTag() throws IOE " \"report.dtd\">\n" + "\n" + "", - messageWithoutAbsolutePath); + messageWithoutAbsolutePath.replace("\r\n", "\n")); } } @@ -83,7 +150,7 @@ public void throwExceptionWhenExtractCoverageFromJacocoAndMissedNotNumber() thro "\n" + " \n" + "", - messageWithoutAbsolutePath); + messageWithoutAbsolutePath.replace("\r\n", "\n")); } } @@ -107,7 +174,7 @@ public void throwExceptionWhenExtractCoverageFromJacocoAndCoveredNotNumber() thr "\n" + " \n" + "", - messageWithoutAbsolutePath); + messageWithoutAbsolutePath.replace("\r\n", "\n")); } } diff --git a/src/test/java/com/github/terma/jenkins/githubprcoveragestatus/MessageTest.java b/src/test/java/com/github/terma/jenkins/githubprcoveragestatus/MessageTest.java index 642e1ac..72c4900 100644 --- a/src/test/java/com/github/terma/jenkins/githubprcoveragestatus/MessageTest.java +++ b/src/test/java/com/github/terma/jenkins/githubprcoveragestatus/MessageTest.java @@ -17,13 +17,26 @@ */ package com.github.terma.jenkins.githubprcoveragestatus; +import static org.mockito.Mockito.mock; + +import java.io.IOException; + import org.junit.Assert; +import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; public class MessageTest { + private SettingsRepository settingsRepository = mock(SettingsRepository.class); + + @Before + public void initMocks() throws IOException { + ServiceRegistry.setSettingsRepository(settingsRepository); + } @Test - public void buildNiceForConsole() { + public void buildNiceForConsoleDefault() { + Mockito.when(settingsRepository.getCoverageRoundingDigits()).thenReturn(0); Assert.assertEquals("Coverage 100% changed 0.0% vs master 100%", new Message(1, 1).forConsole()); Assert.assertEquals("Coverage 0% changed 0.0% vs master 0%", new Message(0, 0).forConsole()); Assert.assertEquals("Coverage 50% changed +50.0% vs master 0%", new Message(0.5f, 0).forConsole()); @@ -33,8 +46,22 @@ public void buildNiceForConsole() { Assert.assertEquals("Coverage 0% changed 0.0% vs master 0%", new Message(0.000007f, 0.000005f).forConsole()); } + @Test + public void buildNiceForConsoleSonar3Rounding() { + Mockito.when(settingsRepository.getCoverageRoundingDigits()).thenReturn(3); + Assert.assertEquals("Coverage 100% changed 0.0% vs master 100%", new Message(1, 1).forConsole()); + Assert.assertEquals("Coverage 0% changed 0.0% vs master 0%", new Message(0, 0).forConsole()); + Assert.assertEquals("Coverage 50% changed +50.0% vs master 0%", new Message(0.5f, 0).forConsole()); + Assert.assertEquals("Coverage 0% changed -50.0% vs master 50%", new Message(0, 0.5f).forConsole()); + Assert.assertEquals("Coverage 70% changed +20.0% vs master 50%", new Message(0.7f, 0.5f).forConsole()); + Assert.assertEquals("Coverage 0% changed 0.0% vs master 0%", new Message(0.0007f, 0.0005f).forConsole()); + Assert.assertEquals("Coverage 0% changed 0.0% vs master 0%", new Message(0.000007f, 0.000005f).forConsole()); + Assert.assertEquals("Coverage 0% changed 0.0% vs master 0%", new Message(0.0041317f, 0.005f).forConsole()); + } + @Test public void buildNiceForIcon() { + Mockito.when(settingsRepository.getCoverageRoundingDigits()).thenReturn(0); Assert.assertEquals("100% (0.0%) vs master 100%", new Message(1, 1).forIcon()); Assert.assertEquals("0% (0.0%) vs master 0%", new Message(0, 0).forIcon()); Assert.assertEquals("50% (+50.0%) vs master 0%", new Message(0.5f, 0).forIcon()); diff --git a/src/test/java/com/github/terma/jenkins/githubprcoveragestatus/SonarMasterCoverageRepositoryTest.java b/src/test/java/com/github/terma/jenkins/githubprcoveragestatus/SonarMasterCoverageRepositoryTest.java index 3e3b964..a0c46e8 100644 --- a/src/test/java/com/github/terma/jenkins/githubprcoveragestatus/SonarMasterCoverageRepositoryTest.java +++ b/src/test/java/com/github/terma/jenkins/githubprcoveragestatus/SonarMasterCoverageRepositoryTest.java @@ -1,21 +1,28 @@ package com.github.terma.jenkins.githubprcoveragestatus; -import com.github.tomakehurst.wiremock.client.MappingBuilder; -import com.github.tomakehurst.wiremock.junit.WireMockRule; -import org.apache.commons.io.IOUtils; -import org.junit.After; -import org.junit.ClassRule; -import org.junit.Test; +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; +import static com.google.common.base.Charsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.mock; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintStream; -import static com.github.tomakehurst.wiremock.client.WireMock.*; -import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; -import static com.google.common.base.Charsets.UTF_8; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; +import org.apache.commons.io.IOUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.mockito.Mockito; + +import com.github.tomakehurst.wiremock.client.MappingBuilder; +import com.github.tomakehurst.wiremock.junit.WireMockRule; public class SonarMasterCoverageRepositoryTest { @@ -27,6 +34,13 @@ public class SonarMasterCoverageRepositoryTest { private SonarMasterCoverageRepository sonarMasterCoverageRepository; private ByteArrayOutputStream buildLogOutputStream; + private SettingsRepository settingsRepository = mock(SettingsRepository.class); + + @Before + public void initMocks() throws IOException { + ServiceRegistry.setSettingsRepository(settingsRepository); + } + @After public void afterTest() throws Exception { System.out.println(buildLogOutputStream.toString()); @@ -34,20 +48,43 @@ public void afterTest() throws Exception { } @Test - public void should_get_coverage() throws IOException { - testCoverage(null, null); - testCoverage("token", ""); - testCoverage("login", "password"); + public void should_get_coverage_default() throws IOException { + testCoverage(null, null, null, 0.953f); + testCoverage("token", "", null, 0.953f); + testCoverage("login", "password", null, 0.953f); + } + + @Test + public void should_get_coverage_branch() throws IOException { + testCoverage(null, null, SonarMasterCoverageRepository.SONAR_OVERALL_BRANCH_COVERAGE_METRIC_NAME, 0.753f); + testCoverage("token", "", SonarMasterCoverageRepository.SONAR_OVERALL_BRANCH_COVERAGE_METRIC_NAME, 0.753f); + testCoverage("login", "password", SonarMasterCoverageRepository.SONAR_OVERALL_BRANCH_COVERAGE_METRIC_NAME, 0.753f); + } + + @Test + public void should_get_coverage_instruction() throws IOException { + testCoverage(null, null, SonarMasterCoverageRepository.SONAR_OVERALL_INSTRUCTION_COVERAGE_METRIC_NAME, 0.953f); + testCoverage("token", "", SonarMasterCoverageRepository.SONAR_OVERALL_INSTRUCTION_COVERAGE_METRIC_NAME, 0.953f); + testCoverage("login", "password", SonarMasterCoverageRepository.SONAR_OVERALL_INSTRUCTION_COVERAGE_METRIC_NAME, 0.953f); } - private void testCoverage(final String login, final String password) throws IOException { + @Test + public void should_get_coverage_line() throws IOException { + testCoverage(null, null, SonarMasterCoverageRepository.SONAR_OVERALL_LINE_COVERAGE_METRIC_NAME, 0.852f); + testCoverage("token", "", SonarMasterCoverageRepository.SONAR_OVERALL_LINE_COVERAGE_METRIC_NAME, 0.852f); + testCoverage("login", "password", SonarMasterCoverageRepository.SONAR_OVERALL_LINE_COVERAGE_METRIC_NAME, 0.852f); + } + + private void testCoverage(final String login, final String password, final String metric, final float expected) throws IOException { givenCoverageRepository(login, password); givenProjectResponseWithSingleMatch(login, password); givenMeasureResponse(); + Mockito.when(settingsRepository.getSonarCoverageMetric()).thenReturn(metric); + final float coverage = sonarMasterCoverageRepository.get(GIT_REPO_URL); - assertThat(coverage, is(0.953f)); + assertThat(coverage, is(expected)); } @Test @@ -57,6 +94,8 @@ public void should_get_coverage_for_multiple_projects_found() throws IOException givenProjectResponseWithMultipleMatches(); givenMeasureResponse(); + Mockito.when(settingsRepository.getSonarCoverageMetric()).thenReturn(null); + final float coverage = sonarMasterCoverageRepository.get(GIT_REPO_URL); assertThat(coverage, is(0.953f)); } @@ -67,6 +106,8 @@ public void should_get_zero_coverage_for_not_found() { givenProjectResponseWithoutMatch(); + Mockito.when(settingsRepository.getSonarCoverageMetric()).thenReturn(null); + assertThat(sonarMasterCoverageRepository.get(GIT_REPO_URL), is(0f)); } @@ -77,6 +118,8 @@ public void should_get_zero_coverage_for_unknown_metric() throws IOException { givenProjectResponseWithSingleMatch(null, null); givenNotFoundMeasureResponse(); + Mockito.when(settingsRepository.getSonarCoverageMetric()).thenReturn("unknown_coverage"); + assertThat(sonarMasterCoverageRepository.get(GIT_REPO_URL), is(0f)); } @@ -122,15 +165,39 @@ private void givenProjectResponseWithoutMatch() { private void givenMeasureResponse() throws IOException { wireMockRule.stubFor(get(urlPathEqualTo("/api/measures/component")) .withQueryParam("componentKey", equalTo("my-project:origin/master")) - .withQueryParam("metricKeys", equalTo(SonarMasterCoverageRepository.SONAR_OVERALL_LINE_COVERAGE_METRIC_NAME)) + .withQueryParam("metricKeys", equalTo(SonarMasterCoverageRepository.SONAR_OVERALL_INSTRUCTION_COVERAGE_METRIC_NAME)) .willReturn(aResponse() .withStatus(200) .withBody(getResponseBodyFromFile("measureFound.json")) ) ); + wireMockRule.stubFor(get(urlPathEqualTo("/api/measures/component")) + .withQueryParam("componentKey", equalTo("my-project:origin/master")) + .withQueryParam("metricKeys", equalTo(SonarMasterCoverageRepository.SONAR_OVERALL_LINE_COVERAGE_METRIC_NAME)) + .willReturn(aResponse() + .withStatus(200) + .withBody(getResponseBodyFromFile("measureFoundLine.json")) + ) + ); + wireMockRule.stubFor(get(urlPathEqualTo("/api/measures/component")) + .withQueryParam("componentKey", equalTo("my-project:origin/master")) + .withQueryParam("metricKeys", equalTo(SonarMasterCoverageRepository.SONAR_OVERALL_BRANCH_COVERAGE_METRIC_NAME)) + .willReturn(aResponse() + .withStatus(200) + .withBody(getResponseBodyFromFile("measureFoundBranch.json")) + ) + ); } private void givenNotFoundMeasureResponse() throws IOException { + wireMockRule.stubFor(get(urlPathEqualTo("/api/measures/component")) + .withQueryParam("componentKey", equalTo("my-project:origin/master")) + .withQueryParam("metricKeys", equalTo(SonarMasterCoverageRepository.SONAR_OVERALL_INSTRUCTION_COVERAGE_METRIC_NAME)) + .willReturn(aResponse() + .withStatus(404) + .withBody(getResponseBodyFromFile("metricNotFound.json")) + ) + ); wireMockRule.stubFor(get(urlPathEqualTo("/api/measures/component")) .withQueryParam("componentKey", equalTo("my-project:origin/master")) .withQueryParam("metricKeys", equalTo(SonarMasterCoverageRepository.SONAR_OVERALL_LINE_COVERAGE_METRIC_NAME)) @@ -139,6 +206,14 @@ private void givenNotFoundMeasureResponse() throws IOException { .withBody(getResponseBodyFromFile("metricNotFound.json")) ) ); + wireMockRule.stubFor(get(urlPathEqualTo("/api/measures/component")) + .withQueryParam("componentKey", equalTo("my-project:origin/master")) + .withQueryParam("metricKeys", equalTo(SonarMasterCoverageRepository.SONAR_OVERALL_BRANCH_COVERAGE_METRIC_NAME)) + .willReturn(aResponse() + .withStatus(404) + .withBody(getResponseBodyFromFile("metricNotFound.json")) + ) + ); } private String getResponseBodyFromFile(String fileName) throws IOException { diff --git a/src/test/resources/com/github/terma/jenkins/githubprcoveragestatus/JacocoParserTest/jacoco-multiple.xml b/src/test/resources/com/github/terma/jenkins/githubprcoveragestatus/JacocoParserTest/jacoco-multiple.xml new file mode 100644 index 0000000..cd651fc --- /dev/null +++ b/src/test/resources/com/github/terma/jenkins/githubprcoveragestatus/JacocoParserTest/jacoco-multiple.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/src/test/resources/com/github/terma/jenkins/githubprcoveragestatus/SonarMasterCoverageRepositoryTest/measureFound.json b/src/test/resources/com/github/terma/jenkins/githubprcoveragestatus/SonarMasterCoverageRepositoryTest/measureFound.json index 18a4e55..6924982 100644 --- a/src/test/resources/com/github/terma/jenkins/githubprcoveragestatus/SonarMasterCoverageRepositoryTest/measureFound.json +++ b/src/test/resources/com/github/terma/jenkins/githubprcoveragestatus/SonarMasterCoverageRepositoryTest/measureFound.json @@ -4,7 +4,7 @@ "key": "my-project:origin/master", "measures": [ { - "metric": "overall_line_coverage", + "metric": "coverage", "periods": [ { "index": 1, diff --git a/src/test/resources/com/github/terma/jenkins/githubprcoveragestatus/SonarMasterCoverageRepositoryTest/measureFoundBranch.json b/src/test/resources/com/github/terma/jenkins/githubprcoveragestatus/SonarMasterCoverageRepositoryTest/measureFoundBranch.json new file mode 100644 index 0000000..39eca67 --- /dev/null +++ b/src/test/resources/com/github/terma/jenkins/githubprcoveragestatus/SonarMasterCoverageRepositoryTest/measureFoundBranch.json @@ -0,0 +1,28 @@ +{ + "component": { + "id": "AVePr_fQwz0tAcNAWEGz", + "key": "my-project:origin/master", + "measures": [ + { + "metric": "branch_coverage", + "periods": [ + { + "index": 1, + "value": "0.0" + }, + { + "index": 2, + "value": "0.0" + }, + { + "index": 3, + "value": "0.0" + } + ], + "value": "75.3" + } + ], + "name": "my-project origin/master", + "qualifier": "TRK" + } +} \ No newline at end of file diff --git a/src/test/resources/com/github/terma/jenkins/githubprcoveragestatus/SonarMasterCoverageRepositoryTest/measureFoundLine.json b/src/test/resources/com/github/terma/jenkins/githubprcoveragestatus/SonarMasterCoverageRepositoryTest/measureFoundLine.json new file mode 100644 index 0000000..d29859a --- /dev/null +++ b/src/test/resources/com/github/terma/jenkins/githubprcoveragestatus/SonarMasterCoverageRepositoryTest/measureFoundLine.json @@ -0,0 +1,28 @@ +{ + "component": { + "id": "AVePr_fQwz0tAcNAWEGz", + "key": "my-project:origin/master", + "measures": [ + { + "metric": "line_coverage", + "periods": [ + { + "index": 1, + "value": "0.0" + }, + { + "index": 2, + "value": "0.0" + }, + { + "index": 3, + "value": "0.0" + } + ], + "value": "85.2" + } + ], + "name": "my-project origin/master", + "qualifier": "TRK" + } +} \ No newline at end of file