From: Julien HENRY Date: Wed, 19 Oct 2016 14:50:16 +0000 (+0200) Subject: SONAR-8310, SONAR-8314 Make generic coverage plugin a core feature X-Git-Tag: 6.2-RC1~307 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=9ddf6fda885838c8a3a12b5ade1cf9a32df95763;p=sonarqube.git SONAR-8310, SONAR-8314 Make generic coverage plugin a core feature --- diff --git a/it/it-projects/testing/xoo-sample-with-tests-execution-details/sonar-project.properties b/it/it-projects/testing/xoo-sample-with-tests-execution-details/sonar-project.properties new file mode 100644 index 00000000000..0f2415d0320 --- /dev/null +++ b/it/it-projects/testing/xoo-sample-with-tests-execution-details/sonar-project.properties @@ -0,0 +1,6 @@ +sonar.projectKey=sample-with-tests +sonar.projectName=Sample with tests +sonar.projectVersion=1.0-SNAPSHOT +sonar.sources=src/main/xoo +sonar.tests=src/test/xoo +sonar.language=xoo \ No newline at end of file diff --git a/it/it-projects/testing/xoo-sample-with-tests-execution-details/src/main/xoo/sample/Sample.xoo b/it/it-projects/testing/xoo-sample-with-tests-execution-details/src/main/xoo/sample/Sample.xoo new file mode 100644 index 00000000000..b1210973dd9 --- /dev/null +++ b/it/it-projects/testing/xoo-sample-with-tests-execution-details/src/main/xoo/sample/Sample.xoo @@ -0,0 +1,12 @@ +package sample; + +public class Sample { + + public Sample(int i) { + int j = i++; + } + + private String myMethod() { + return "hello"; + } +} diff --git a/it/it-projects/testing/xoo-sample-with-tests-execution-details/src/test/xoo/sample/SampleTest.xoo b/it/it-projects/testing/xoo-sample-with-tests-execution-details/src/test/xoo/sample/SampleTest.xoo new file mode 100644 index 00000000000..fe2368fefaa --- /dev/null +++ b/it/it-projects/testing/xoo-sample-with-tests-execution-details/src/test/xoo/sample/SampleTest.xoo @@ -0,0 +1,32 @@ +package sample; + +import org.hamcrest.CoreMatchers; +import org.junit.Test; + +import static org.junit.Assert.assertThat; + +public class SampleTest { + + @Test + @Ignore + public void skipped() { + Sample sample = new Sample(1); + assertThat(sample.getI(), CoreMatchers.is(1)); + } + + @Test + public void failure() { + fail(); + } + + @Test + public void error() { + throw new IllegalStateException("Foo"); + } + + @Test + public void success() { + System.out.println("OK"); + } + +} diff --git a/it/it-projects/testing/xoo-sample-with-tests-execution-details/src/test/xoo/sample/SampleTest.xoo.test b/it/it-projects/testing/xoo-sample-with-tests-execution-details/src/test/xoo/sample/SampleTest.xoo.test new file mode 100644 index 00000000000..b0259693199 --- /dev/null +++ b/it/it-projects/testing/xoo-sample-with-tests-execution-details/src/test/xoo/sample/SampleTest.xoo.test @@ -0,0 +1,4 @@ +skipped::::SKIPPED:UNIT +failure:2:Failure::FAILURE:UNIT +error:2:Error:The stack:ERROR:UNIT +success:4:::OK:INTEGRATION \ No newline at end of file diff --git a/it/it-projects/testing/xoo-sample-with-tests-execution-measures/sonar-project.properties b/it/it-projects/testing/xoo-sample-with-tests-execution-measures/sonar-project.properties new file mode 100644 index 00000000000..0f2415d0320 --- /dev/null +++ b/it/it-projects/testing/xoo-sample-with-tests-execution-measures/sonar-project.properties @@ -0,0 +1,6 @@ +sonar.projectKey=sample-with-tests +sonar.projectName=Sample with tests +sonar.projectVersion=1.0-SNAPSHOT +sonar.sources=src/main/xoo +sonar.tests=src/test/xoo +sonar.language=xoo \ No newline at end of file diff --git a/it/it-projects/testing/xoo-sample-with-tests-execution-measures/src/main/xoo/sample/Sample.xoo b/it/it-projects/testing/xoo-sample-with-tests-execution-measures/src/main/xoo/sample/Sample.xoo new file mode 100644 index 00000000000..b1210973dd9 --- /dev/null +++ b/it/it-projects/testing/xoo-sample-with-tests-execution-measures/src/main/xoo/sample/Sample.xoo @@ -0,0 +1,12 @@ +package sample; + +public class Sample { + + public Sample(int i) { + int j = i++; + } + + private String myMethod() { + return "hello"; + } +} diff --git a/it/it-projects/testing/xoo-sample-with-tests-execution-measures/src/test/xoo/sample/SampleTest.xoo b/it/it-projects/testing/xoo-sample-with-tests-execution-measures/src/test/xoo/sample/SampleTest.xoo new file mode 100644 index 00000000000..fe2368fefaa --- /dev/null +++ b/it/it-projects/testing/xoo-sample-with-tests-execution-measures/src/test/xoo/sample/SampleTest.xoo @@ -0,0 +1,32 @@ +package sample; + +import org.hamcrest.CoreMatchers; +import org.junit.Test; + +import static org.junit.Assert.assertThat; + +public class SampleTest { + + @Test + @Ignore + public void skipped() { + Sample sample = new Sample(1); + assertThat(sample.getI(), CoreMatchers.is(1)); + } + + @Test + public void failure() { + fail(); + } + + @Test + public void error() { + throw new IllegalStateException("Foo"); + } + + @Test + public void success() { + System.out.println("OK"); + } + +} diff --git a/it/it-projects/testing/xoo-sample-with-tests-execution-measures/src/test/xoo/sample/SampleTest.xoo.measures b/it/it-projects/testing/xoo-sample-with-tests-execution-measures/src/test/xoo/sample/SampleTest.xoo.measures new file mode 100644 index 00000000000..de1549480b5 --- /dev/null +++ b/it/it-projects/testing/xoo-sample-with-tests-execution-measures/src/test/xoo/sample/SampleTest.xoo.measures @@ -0,0 +1,5 @@ +tests:3 +test_execution_time:8 +skipped_tests:1 +test_errors:1 +test_failures:1 \ No newline at end of file diff --git a/it/it-projects/testing/xoo-sample-with-tests-execution/sonar-project.properties b/it/it-projects/testing/xoo-sample-with-tests-execution/sonar-project.properties deleted file mode 100644 index 0f2415d0320..00000000000 --- a/it/it-projects/testing/xoo-sample-with-tests-execution/sonar-project.properties +++ /dev/null @@ -1,6 +0,0 @@ -sonar.projectKey=sample-with-tests -sonar.projectName=Sample with tests -sonar.projectVersion=1.0-SNAPSHOT -sonar.sources=src/main/xoo -sonar.tests=src/test/xoo -sonar.language=xoo \ No newline at end of file diff --git a/it/it-projects/testing/xoo-sample-with-tests-execution/src/main/xoo/sample/Sample.xoo b/it/it-projects/testing/xoo-sample-with-tests-execution/src/main/xoo/sample/Sample.xoo deleted file mode 100644 index b1210973dd9..00000000000 --- a/it/it-projects/testing/xoo-sample-with-tests-execution/src/main/xoo/sample/Sample.xoo +++ /dev/null @@ -1,12 +0,0 @@ -package sample; - -public class Sample { - - public Sample(int i) { - int j = i++; - } - - private String myMethod() { - return "hello"; - } -} diff --git a/it/it-projects/testing/xoo-sample-with-tests-execution/src/test/xoo/sample/SampleTest.xoo b/it/it-projects/testing/xoo-sample-with-tests-execution/src/test/xoo/sample/SampleTest.xoo deleted file mode 100644 index fe2368fefaa..00000000000 --- a/it/it-projects/testing/xoo-sample-with-tests-execution/src/test/xoo/sample/SampleTest.xoo +++ /dev/null @@ -1,32 +0,0 @@ -package sample; - -import org.hamcrest.CoreMatchers; -import org.junit.Test; - -import static org.junit.Assert.assertThat; - -public class SampleTest { - - @Test - @Ignore - public void skipped() { - Sample sample = new Sample(1); - assertThat(sample.getI(), CoreMatchers.is(1)); - } - - @Test - public void failure() { - fail(); - } - - @Test - public void error() { - throw new IllegalStateException("Foo"); - } - - @Test - public void success() { - System.out.println("OK"); - } - -} diff --git a/it/it-projects/testing/xoo-sample-with-tests-execution/src/test/xoo/sample/SampleTest.xoo.measures b/it/it-projects/testing/xoo-sample-with-tests-execution/src/test/xoo/sample/SampleTest.xoo.measures deleted file mode 100644 index 095af4b5e7b..00000000000 --- a/it/it-projects/testing/xoo-sample-with-tests-execution/src/test/xoo/sample/SampleTest.xoo.measures +++ /dev/null @@ -1,5 +0,0 @@ -tests:4 -test_execution_time:8 -skipped_tests:1 -test_errors:1 -test_failures:1 \ No newline at end of file diff --git a/it/it-projects/testing/xoo-sample-with-tests-execution/src/test/xoo/sample/SampleTest.xoo.test b/it/it-projects/testing/xoo-sample-with-tests-execution/src/test/xoo/sample/SampleTest.xoo.test deleted file mode 100644 index b0259693199..00000000000 --- a/it/it-projects/testing/xoo-sample-with-tests-execution/src/test/xoo/sample/SampleTest.xoo.test +++ /dev/null @@ -1,4 +0,0 @@ -skipped::::SKIPPED:UNIT -failure:2:Failure::FAILURE:UNIT -error:2:Error:The stack:ERROR:UNIT -success:4:::OK:INTEGRATION \ No newline at end of file diff --git a/it/it-tests/src/test/java/it/test/TestExecutionTest.java b/it/it-tests/src/test/java/it/test/TestExecutionTest.java index f0d11de0533..34c75e01a47 100644 --- a/it/it-tests/src/test/java/it/test/TestExecutionTest.java +++ b/it/it-tests/src/test/java/it/test/TestExecutionTest.java @@ -44,19 +44,33 @@ public class TestExecutionTest { } @Test - public void test_execution() throws Exception { - orchestrator.executeBuilds(SonarScanner.create(projectDir("testing/xoo-sample-with-tests-execution"))); + public void test_execution_details() throws Exception { + orchestrator.executeBuilds(SonarScanner.create(projectDir("testing/xoo-sample-with-tests-execution-details"))); Resource project = orchestrator.getServer().getWsClient() .find(ResourceQuery.createForMetrics("sample-with-tests", "test_success_density", "test_failures", "test_errors", "tests", "skipped_tests", "test_execution_time")); - assertThat(project.getMeasureValue("test_success_density")).isEqualTo(50.0); + assertThat(project.getMeasureValue("test_success_density")).isEqualTo(33.3); assertThat(project.getMeasureIntValue("test_failures")).isEqualTo(1); assertThat(project.getMeasureIntValue("test_errors")).isEqualTo(1); - assertThat(project.getMeasureIntValue("tests")).isEqualTo(4); + assertThat(project.getMeasureIntValue("tests")).isEqualTo(3); assertThat(project.getMeasureIntValue("skipped_tests")).isEqualTo(1); assertThat(project.getMeasureIntValue("test_execution_time")).isEqualTo(8); String json = orchestrator.getServer().adminWsClient().get("api/tests/list", "testFileKey", "sample-with-tests:src/test/xoo/sample/SampleTest.xoo"); JSONAssert.assertEquals(IOUtils.toString(this.getClass().getResourceAsStream("/test/TestExecutionTest/expected.json"), "UTF-8"), json, false); } + + @Test + public void test_execution_measures() throws Exception { + orchestrator.executeBuilds(SonarScanner.create(projectDir("testing/xoo-sample-with-tests-execution-measures"))); + + Resource project = orchestrator.getServer().getWsClient() + .find(ResourceQuery.createForMetrics("sample-with-tests", "test_success_density", "test_failures", "test_errors", "tests", "skipped_tests", "test_execution_time")); + assertThat(project.getMeasureValue("test_success_density")).isEqualTo(33.3); + assertThat(project.getMeasureIntValue("test_failures")).isEqualTo(1); + assertThat(project.getMeasureIntValue("test_errors")).isEqualTo(1); + assertThat(project.getMeasureIntValue("tests")).isEqualTo(3); + assertThat(project.getMeasureIntValue("skipped_tests")).isEqualTo(1); + assertThat(project.getMeasureIntValue("test_execution_time")).isEqualTo(8); + } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/BatchComponents.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/BatchComponents.java index 51dc68f024c..d619a516cac 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/BatchComponents.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/BatchComponents.java @@ -27,6 +27,8 @@ import org.sonar.core.component.DefaultResourceTypes; import org.sonar.core.config.CorePropertyDefinitions; import org.sonar.core.issue.tracking.Tracker; import org.sonar.scanner.cpd.CpdComponents; +import org.sonar.scanner.genericcoverage.GenericCoverageSensor; +import org.sonar.scanner.genericcoverage.GenericTestExecutionSensor; import org.sonar.scanner.issue.tracking.ServerIssueFromWs; import org.sonar.scanner.issue.tracking.TrackedIssue; import org.sonar.scanner.scan.report.ConsoleReport; @@ -71,6 +73,13 @@ public class BatchComponents { // CPD components.addAll(CpdComponents.all()); + + // Generic coverage + components.add(GenericCoverageSensor.class); + components.addAll(GenericCoverageSensor.properties()); + components.add(GenericTestExecutionSensor.class); + components.addAll(GenericTestExecutionSensor.properties()); + } else { // Issues tracking components.add(new Tracker()); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/perspectives/BatchPerspectives.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/perspectives/BatchPerspectives.java deleted file mode 100644 index 1e89551a32f..00000000000 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/perspectives/BatchPerspectives.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.scanner.deprecated.perspectives; - -import com.google.common.collect.Maps; -import java.util.Map; -import javax.annotation.CheckForNull; -import org.sonar.api.batch.fs.InputPath; -import org.sonar.api.component.Perspective; -import org.sonar.api.component.ResourcePerspectives; -import org.sonar.api.resources.Resource; -import org.sonar.scanner.index.BatchComponentCache; -import org.sonar.scanner.index.DefaultIndex; - -public class BatchPerspectives implements ResourcePerspectives { - - private final Map, PerspectiveBuilder> builders = Maps.newHashMap(); - private final DefaultIndex resourceIndex; - private final BatchComponentCache componentCache; - - public BatchPerspectives(PerspectiveBuilder[] builders, DefaultIndex resourceIndex, BatchComponentCache componentCache) { - this.resourceIndex = resourceIndex; - this.componentCache = componentCache; - for (PerspectiveBuilder builder : builders) { - this.builders.put(builder.getPerspectiveClass(), builder); - } - } - - @Override - @CheckForNull - public

P as(Class

perspectiveClass, Resource resource) { - Resource indexedResource = resource; - if (resource.getEffectiveKey() == null) { - indexedResource = resourceIndex.getResource(resource); - } - if (indexedResource != null) { - PerspectiveBuilder

builder = builderFor(perspectiveClass); - return builder.loadPerspective(perspectiveClass, componentCache.get(indexedResource)); - } - return null; - } - - @Override - public

P as(Class

perspectiveClass, InputPath inputPath) { - PerspectiveBuilder

builder = builderFor(perspectiveClass); - return builder.loadPerspective(perspectiveClass, componentCache.get(inputPath)); - } - - private PerspectiveBuilder builderFor(Class clazz) { - PerspectiveBuilder builder = (PerspectiveBuilder) builders.get(clazz); - if (builder == null) { - throw new PerspectiveNotFoundException("Perspective class is not registered: " + clazz); - } - return builder; - } -} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/perspectives/PerspectiveBuilder.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/perspectives/PerspectiveBuilder.java index fd1632b9172..255968e45f2 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/perspectives/PerspectiveBuilder.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/perspectives/PerspectiveBuilder.java @@ -21,8 +21,8 @@ package org.sonar.scanner.deprecated.perspectives; import javax.annotation.CheckForNull; import org.sonar.api.batch.ScannerSide; +import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.component.Perspective; -import org.sonar.scanner.index.BatchComponent; @ScannerSide public abstract class PerspectiveBuilder { @@ -38,5 +38,5 @@ public abstract class PerspectiveBuilder { } @CheckForNull - public abstract T loadPerspective(Class perspectiveClass, BatchComponent component); + public abstract T loadPerspective(Class perspectiveClass, InputComponent component); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/perspectives/ScannerPerspectives.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/perspectives/ScannerPerspectives.java new file mode 100644 index 00000000000..1903ef38b12 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/perspectives/ScannerPerspectives.java @@ -0,0 +1,73 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.scanner.deprecated.perspectives; + +import com.google.common.collect.Maps; +import java.util.Map; +import javax.annotation.CheckForNull; +import org.sonar.api.batch.fs.InputPath; +import org.sonar.api.component.Perspective; +import org.sonar.api.component.ResourcePerspectives; +import org.sonar.api.resources.Resource; +import org.sonar.scanner.index.BatchComponentCache; +import org.sonar.scanner.index.DefaultIndex; + +public class ScannerPerspectives implements ResourcePerspectives { + + private final Map, PerspectiveBuilder> builders = Maps.newHashMap(); + private final DefaultIndex resourceIndex; + private final BatchComponentCache componentCache; + + public ScannerPerspectives(PerspectiveBuilder[] builders, DefaultIndex resourceIndex, BatchComponentCache componentCache) { + this.resourceIndex = resourceIndex; + this.componentCache = componentCache; + for (PerspectiveBuilder builder : builders) { + this.builders.put(builder.getPerspectiveClass(), builder); + } + } + + @Override + @CheckForNull + public

P as(Class

perspectiveClass, Resource resource) { + Resource indexedResource = resource; + if (resource.getEffectiveKey() == null) { + indexedResource = resourceIndex.getResource(resource); + } + if (indexedResource != null) { + PerspectiveBuilder

builder = builderFor(perspectiveClass); + return builder.loadPerspective(perspectiveClass, componentCache.get(indexedResource).inputComponent()); + } + return null; + } + + @Override + public

P as(Class

perspectiveClass, InputPath inputPath) { + PerspectiveBuilder

builder = builderFor(perspectiveClass); + return builder.loadPerspective(perspectiveClass, inputPath); + } + + private PerspectiveBuilder builderFor(Class clazz) { + PerspectiveBuilder builder = (PerspectiveBuilder) builders.get(clazz); + if (builder == null) { + throw new PerspectiveNotFoundException("Perspective class is not registered: " + clazz); + } + return builder; + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/test/TestPlanBuilder.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/test/TestPlanBuilder.java index 94fbfd0ef18..580b0b10944 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/test/TestPlanBuilder.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/test/TestPlanBuilder.java @@ -22,11 +22,11 @@ package org.sonar.scanner.deprecated.test; import java.util.HashMap; import java.util.Map; import javax.annotation.CheckForNull; +import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputFile.Type; import org.sonar.api.test.MutableTestPlan; import org.sonar.scanner.deprecated.perspectives.PerspectiveBuilder; -import org.sonar.scanner.index.BatchComponent; public class TestPlanBuilder extends PerspectiveBuilder { @@ -38,9 +38,9 @@ public class TestPlanBuilder extends PerspectiveBuilder { @CheckForNull @Override - public MutableTestPlan loadPerspective(Class perspectiveClass, BatchComponent component) { + public MutableTestPlan loadPerspective(Class perspectiveClass, InputComponent component) { if (component.isFile()) { - InputFile inputFile = (InputFile) component.inputComponent(); + InputFile inputFile = (InputFile) component; if (inputFile.type() == Type.TEST) { if (!testPlanByFile.containsKey(inputFile)) { testPlanByFile.put(inputFile, new DefaultTestPlan()); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/test/TestableBuilder.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/test/TestableBuilder.java index 4c9c3de46fb..a03c3514f4f 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/test/TestableBuilder.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/test/TestableBuilder.java @@ -20,12 +20,12 @@ package org.sonar.scanner.deprecated.test; import javax.annotation.CheckForNull; +import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputFile.Type; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.test.MutableTestable; import org.sonar.scanner.deprecated.perspectives.PerspectiveBuilder; -import org.sonar.scanner.index.BatchComponent; public class TestableBuilder extends PerspectiveBuilder { @@ -35,9 +35,9 @@ public class TestableBuilder extends PerspectiveBuilder { @CheckForNull @Override - public MutableTestable loadPerspective(Class perspectiveClass, BatchComponent component) { + public MutableTestable loadPerspective(Class perspectiveClass, InputComponent component) { if (component.isFile()) { - InputFile inputFile = (InputFile) component.inputComponent(); + InputFile inputFile = (InputFile) component; if (inputFile.type() == Type.MAIN) { return new DefaultTestable((DefaultInputFile) inputFile); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/genericcoverage/GenericCoverageReportParser.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/genericcoverage/GenericCoverageReportParser.java new file mode 100644 index 00000000000..2ccd7a0abaa --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/genericcoverage/GenericCoverageReportParser.java @@ -0,0 +1,194 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.scanner.genericcoverage; + +import com.google.common.base.Preconditions; +import java.io.FileInputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.xml.stream.XMLStreamException; +import org.codehaus.staxmate.in.SMHierarchicCursor; +import org.codehaus.staxmate.in.SMInputCursor; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.coverage.NewCoverage; +import org.sonar.api.utils.StaxParser; + +public class GenericCoverageReportParser { + + private static final String LINE_NUMBER_ATTR = "lineNumber"; + private static final String COVERED_ATTR = "covered"; + private static final String BRANCHES_TO_COVER_ATTR = "branchesToCover"; + private static final String COVERED_BRANCHES_ATTR = "coveredBranches"; + + private static final int MAX_STORED_UNKNOWN_FILE_PATHS = 5; + + private int numberOfUnknownFiles; + private final List firstUnknownFiles = new ArrayList<>(); + private final Set matchedFileKeys = new HashSet<>(); + + public void parse(java.io.File reportFile, SensorContext context) { + try (InputStream inputStream = new FileInputStream(reportFile)) { + parse(inputStream, context); + } catch (Exception e) { + throw new IllegalStateException("Error during parsing of coverage report " + reportFile, e); + } + } + + void parse(InputStream inputStream, SensorContext context) throws XMLStreamException { + new StaxParser(rootCursor -> { + rootCursor.advance(); + parseRootNode(rootCursor, context); + }).parse(inputStream); + } + + private void parseRootNode(SMHierarchicCursor rootCursor, SensorContext context) throws XMLStreamException { + checkElementName(rootCursor, "coverage"); + String version = rootCursor.getAttrValue("version"); + if (!"1".equals(version)) { + throw new IllegalStateException("Unknown report version: " + version + ". This parser only handles version 1."); + } + parseFiles(rootCursor.childElementCursor(), context); + } + + private void parseFiles(SMInputCursor fileCursor, SensorContext context) throws XMLStreamException { + while (fileCursor.getNext() != null) { + checkElementName(fileCursor, "file"); + String filePath = mandatoryAttribute(fileCursor, "path"); + InputFile inputFile = context.fileSystem().inputFile(context.fileSystem().predicates().hasPath(filePath)); + if (inputFile == null) { + numberOfUnknownFiles++; + if (numberOfUnknownFiles <= MAX_STORED_UNKNOWN_FILE_PATHS) { + firstUnknownFiles.add(filePath); + } + continue; + } + Preconditions.checkState( + inputFile.language() != null, + "Line %s of report refers to a file with an unknown language: %s", + fileCursor.getCursorLocation().getLineNumber(), + filePath); + matchedFileKeys.add(inputFile.absolutePath()); + + NewCoverage newCoverage = context.newCoverage().onFile(inputFile); + SMInputCursor lineToCoverCursor = fileCursor.childElementCursor(); + while (lineToCoverCursor.getNext() != null) { + parseLineToCover(lineToCoverCursor, newCoverage); + } + newCoverage.save(); + } + } + + private static void parseLineToCover(SMInputCursor cursor, NewCoverage newCoverage) + throws XMLStreamException { + checkElementName(cursor, "lineToCover"); + String lineNumberAsString = mandatoryAttribute(cursor, LINE_NUMBER_ATTR); + int lineNumber = intValue(lineNumberAsString, cursor, LINE_NUMBER_ATTR, 1); + + boolean covered = getCoveredValue(cursor); + newCoverage.lineHits(lineNumber, covered ? 1 : 0); + + String branchesToCoverAsString = cursor.getAttrValue(BRANCHES_TO_COVER_ATTR); + if (branchesToCoverAsString != null) { + int branchesToCover = intValue(branchesToCoverAsString, cursor, BRANCHES_TO_COVER_ATTR, 0); + String coveredBranchesAsString = cursor.getAttrValue(COVERED_BRANCHES_ATTR); + int coveredBranches = 0; + if (coveredBranchesAsString != null) { + coveredBranches = intValue(coveredBranchesAsString, cursor, COVERED_BRANCHES_ATTR, 0); + if (coveredBranches > branchesToCover) { + throw new IllegalStateException("\"coveredBranches\" should not be greater than \"branchesToCover\" on line " + cursor.getCursorLocation().getLineNumber()); + } + } + newCoverage.conditions(lineNumber, branchesToCover, coveredBranches); + } + } + + private static boolean getCoveredValue(SMInputCursor cursor) throws XMLStreamException { + String coveredAsString = mandatoryAttribute(cursor, COVERED_ATTR); + if (!"true".equalsIgnoreCase(coveredAsString) && !"false".equalsIgnoreCase(coveredAsString)) { + throw new IllegalStateException(expectedMessage("boolean value", COVERED_ATTR, coveredAsString, cursor.getCursorLocation().getLineNumber())); + } + return Boolean.parseBoolean(coveredAsString); + } + + static void checkElementName(SMInputCursor cursor, String expectedName) throws XMLStreamException { + String elementName = cursor.getLocalName(); + if (!expectedName.equals(elementName)) { + throw new IllegalStateException("Unknown XML node, expected \"" + expectedName + "\" but got \"" + elementName + "\" at line " + cursor.getCursorLocation().getLineNumber()); + } + } + + static String mandatoryAttribute(SMInputCursor cursor, String attributeName) throws XMLStreamException { + String attributeValue = cursor.getAttrValue(attributeName); + if (attributeValue == null) { + throw new IllegalStateException( + "Missing attribute \"" + attributeName + "\" in element \"" + cursor.getLocalName() + "\" at line " + cursor.getCursorLocation().getLineNumber()); + } + return attributeValue; + } + + static int intValue(String stringValue, SMInputCursor cursor, String attributeName, int minimum) throws XMLStreamException { + int intValue; + try { + intValue = Integer.valueOf(stringValue); + } catch (NumberFormatException e) { + throw new IllegalStateException(expectedMessage("integer value", attributeName, stringValue, cursor.getCursorLocation().getLineNumber()), e); + } + if (intValue < minimum) { + throw new IllegalStateException("Value of attribute \"" + attributeName + "\" at line " + cursor.getCursorLocation().getLineNumber() + " is \"" + intValue + + "\" but it should be greater than or equal to " + minimum); + } + return intValue; + } + + static long longValue(String stringValue, SMInputCursor cursor, String attributeName, long minimum) throws XMLStreamException { + long longValue; + try { + longValue = Long.valueOf(stringValue); + } catch (NumberFormatException e) { + throw new IllegalStateException(expectedMessage("long value", attributeName, stringValue, cursor.getCursorLocation().getLineNumber()), e); + } + if (longValue < minimum) { + throw new IllegalStateException("Value of attribute \"" + attributeName + "\" at line " + cursor.getCursorLocation().getLineNumber() + " is \"" + longValue + + "\" but it should be greater than or equal to " + minimum); + } + return longValue; + } + + private static String expectedMessage(String expected, String attributeName, String stringValue, int line) { + return "Expected " + expected + " for attribute \"" + attributeName + "\" at line " + line + " but got \"" + stringValue + "\""; + } + + public int numberOfMatchedFiles() { + return matchedFileKeys.size(); + } + + public int numberOfUnknownFiles() { + return numberOfUnknownFiles; + } + + public List firstUnknownFiles() { + return firstUnknownFiles; + } + +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/genericcoverage/GenericCoverageSensor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/genericcoverage/GenericCoverageSensor.java new file mode 100644 index 00000000000..4a4c6012c4f --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/genericcoverage/GenericCoverageSensor.java @@ -0,0 +1,126 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.scanner.genericcoverage; + +import com.google.common.collect.ImmutableList; +import java.io.File; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.stream.Collectors; +import org.sonar.api.batch.Initializer; +import org.sonar.api.batch.sensor.Sensor; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.SensorDescriptor; +import org.sonar.api.config.PropertyDefinition; +import org.sonar.api.config.Settings; +import org.sonar.api.resources.Qualifiers; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; + +import static org.sonar.api.CoreProperties.CATEGORY_CODE_COVERAGE; + +public class GenericCoverageSensor extends Initializer implements Sensor { + + private static final Logger LOG = Loggers.get(GenericCoverageSensor.class); + + private static final String REPORT_PATH_PROPERTY_KEY = "sonar.coverageReportPaths"; + /** + * @deprecated since 6.2 + */ + @Deprecated + private static final String OLD_REPORT_PATH_PROPERTY_KEY = "sonar.genericcoverage.reportPath"; + /** + * @deprecated since 6.2 + */ + @Deprecated + private static final String OLD_COVERAGE_REPORT_PATHS_PROPERTY_KEY = "sonar.genericcoverage.reportPaths"; + /** + * @deprecated since 6.2 + */ + @Deprecated + private static final String OLD_IT_COVERAGE_REPORT_PATHS_PROPERTY_KEY = "sonar.genericcoverage.itReportPaths"; + /** + * @deprecated since 6.2 + */ + @Deprecated + private static final String OLD_OVERALL_COVERAGE_REPORT_PATHS_PROPERTY_KEY = "sonar.genericcoverage.overallReportPaths"; + + private final Settings settings; + + public GenericCoverageSensor(Settings settings) { + this.settings = settings; + } + + public static ImmutableList properties() { + return ImmutableList.of( + + PropertyDefinition.builder(REPORT_PATH_PROPERTY_KEY) + .name("Coverage report paths") + .description("List of comma-separated paths (absolute or relative) containing coverage report.") + .category(CATEGORY_CODE_COVERAGE) + .onQualifiers(Qualifiers.PROJECT) + .deprecatedKey(OLD_COVERAGE_REPORT_PATHS_PROPERTY_KEY) + .build()); + + } + + @Override + public void execute() { + Set reportPaths = new LinkedHashSet<>(); + reportPaths.addAll(Arrays.asList(settings.getStringArray(REPORT_PATH_PROPERTY_KEY))); + loadDeprecated(reportPaths, OLD_REPORT_PATH_PROPERTY_KEY); + loadDeprecated(reportPaths, OLD_IT_COVERAGE_REPORT_PATHS_PROPERTY_KEY); + loadDeprecated(reportPaths, OLD_OVERALL_COVERAGE_REPORT_PATHS_PROPERTY_KEY); + if (!reportPaths.isEmpty()) { + settings.setProperty(REPORT_PATH_PROPERTY_KEY, reportPaths.stream().collect(Collectors.joining(","))); + } + } + + private void loadDeprecated(Set reportPaths, String propertyKey) { + if (settings.hasKey(propertyKey)) { + LOG.warn("Property '{}' is deprecated. Please use '{}' instead.", propertyKey, REPORT_PATH_PROPERTY_KEY); + reportPaths.addAll(Arrays.asList(settings.getStringArray(propertyKey))); + } + } + + @Override + public void describe(SensorDescriptor descriptor) { + descriptor.name("Generic Coverage Report") + .requireProperty(REPORT_PATH_PROPERTY_KEY); + } + + @Override + public void execute(SensorContext context) { + for (String reportPath : settings.getStringArray(REPORT_PATH_PROPERTY_KEY)) { + File reportFile = context.fileSystem().resolvePath(reportPath); + LOG.info("Parsing {}", reportFile); + GenericCoverageReportParser parser = new GenericCoverageReportParser(); + parser.parse(reportFile, context); + LOG.info("Imported coverage data for {} files", parser.numberOfMatchedFiles()); + int numberOfUnknownFiles = parser.numberOfUnknownFiles(); + if (numberOfUnknownFiles > 0) { + LOG.info("Coverage data ignored for " + numberOfUnknownFiles + " unknown files, including:\n" + parser.firstUnknownFiles().stream().collect(Collectors.joining("\n"))); + } + } + + } + +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/genericcoverage/GenericTestExecutionReportParser.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/genericcoverage/GenericTestExecutionReportParser.java new file mode 100644 index 00000000000..2762ec37506 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/genericcoverage/GenericTestExecutionReportParser.java @@ -0,0 +1,159 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.scanner.genericcoverage; + +import com.google.common.base.Preconditions; +import java.io.FileInputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.xml.stream.XMLStreamException; +import org.codehaus.staxmate.in.SMHierarchicCursor; +import org.codehaus.staxmate.in.SMInputCursor; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.test.MutableTestCase; +import org.sonar.api.test.MutableTestPlan; +import org.sonar.api.test.TestCase; +import org.sonar.api.utils.StaxParser; +import org.sonar.scanner.deprecated.test.TestPlanBuilder; + +import static org.sonar.scanner.genericcoverage.GenericCoverageReportParser.checkElementName; +import static org.sonar.scanner.genericcoverage.GenericCoverageReportParser.longValue; +import static org.sonar.scanner.genericcoverage.GenericCoverageReportParser.mandatoryAttribute; + +public class GenericTestExecutionReportParser { + + private static final String NAME_ATTR = "name"; + private static final String DURATION_ATTR = "duration"; + private static final String MESSAGE_ATTR = "message"; + public static final String OK = "ok"; + public static final String ERROR = "error"; + public static final String FAILURE = "failure"; + public static final String SKIPPED = "skipped"; + + private static final int MAX_STORED_UNKNOWN_FILE_PATHS = 5; + + private final TestPlanBuilder testPlanBuilder; + + private int numberOfUnknownFiles; + private final List firstUnknownFiles = new ArrayList<>(); + private final Set matchedFileKeys = new HashSet<>(); + + public GenericTestExecutionReportParser(TestPlanBuilder testPlanBuilder) { + this.testPlanBuilder = testPlanBuilder; + } + + public void parse(java.io.File reportFile, SensorContext context) { + try (InputStream inputStream = new FileInputStream(reportFile)) { + parse(inputStream, context); + } catch (Exception e) { + throw new IllegalStateException("Error during parsing of test execution report " + reportFile, e); + } + } + + public void parse(InputStream inputStream, SensorContext context) throws XMLStreamException { + new StaxParser(rootCursor -> { + rootCursor.advance(); + parseRootNode(rootCursor, context); + }).parse(inputStream); + } + + private void parseRootNode(SMHierarchicCursor rootCursor, SensorContext context) throws XMLStreamException { + checkElementName(rootCursor, "unitTest"); + String version = rootCursor.getAttrValue("version"); + if (!"1".equals(version)) { + throw new IllegalStateException("Unknown report version: " + version + ". This parser only handles version 1."); + } + parseFiles(rootCursor.childElementCursor(), context); + } + + private void parseFiles(SMInputCursor fileCursor, SensorContext context) throws XMLStreamException { + while (fileCursor.getNext() != null) { + checkElementName(fileCursor, "file"); + String filePath = mandatoryAttribute(fileCursor, "path"); + InputFile inputFile = context.fileSystem().inputFile(context.fileSystem().predicates().hasPath(filePath)); + if (inputFile == null) { + numberOfUnknownFiles++; + if (numberOfUnknownFiles <= MAX_STORED_UNKNOWN_FILE_PATHS) { + firstUnknownFiles.add(filePath); + } + continue; + } + Preconditions.checkState( + inputFile.language() != null, + "Line %s of report refers to a file with an unknown language: %s", + fileCursor.getCursorLocation().getLineNumber(), + filePath); + Preconditions.checkState( + inputFile.type() != InputFile.Type.MAIN, + "Line %s of report refers to a file which is not configured as a test file: %s", + fileCursor.getCursorLocation().getLineNumber(), + filePath); + matchedFileKeys.add(inputFile.absolutePath()); + + MutableTestPlan testPlan = testPlanBuilder.loadPerspective(MutableTestPlan.class, inputFile); + SMInputCursor testCaseCursor = fileCursor.childElementCursor(); + while (testCaseCursor.getNext() != null) { + parseTestCase(testCaseCursor, testPlan); + } + } + } + + private void parseTestCase(SMInputCursor cursor, MutableTestPlan testPlan) throws XMLStreamException { + checkElementName(cursor, "testCase"); + MutableTestCase testCase = testPlan.addTestCase(mandatoryAttribute(cursor, NAME_ATTR)); + TestCase.Status status = TestCase.Status.OK; + testCase.setDurationInMs(longValue(mandatoryAttribute(cursor, DURATION_ATTR), cursor, DURATION_ATTR, 0)); + + SMInputCursor child = cursor.descendantElementCursor(); + if (child.getNext() != null) { + String elementName = child.getLocalName(); + if (SKIPPED.equals(elementName)) { + status = TestCase.Status.SKIPPED; + } else if (FAILURE.equals(elementName)) { + status = TestCase.Status.FAILURE; + } else if (ERROR.equals(elementName)) { + status = TestCase.Status.ERROR; + } + testCase.setStatus(status); + if (TestCase.Status.OK != status) { + testCase.setMessage(mandatoryAttribute(child, MESSAGE_ATTR)); + testCase.setStackTrace(child.collectDescendantText()); + } + } + + } + + public int numberOfMatchedFiles() { + return matchedFileKeys.size(); + } + + public int numberOfUnknownFiles() { + return numberOfUnknownFiles; + } + + public List firstUnknownFiles() { + return firstUnknownFiles; + } + +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/genericcoverage/GenericTestExecutionSensor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/genericcoverage/GenericTestExecutionSensor.java new file mode 100644 index 00000000000..a4db6902285 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/genericcoverage/GenericTestExecutionSensor.java @@ -0,0 +1,88 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.scanner.genericcoverage; + +import com.google.common.collect.ImmutableList; +import java.io.File; +import java.util.stream.Collectors; +import org.sonar.api.batch.sensor.Sensor; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.SensorDescriptor; +import org.sonar.api.config.PropertyDefinition; +import org.sonar.api.resources.Qualifiers; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.scanner.deprecated.test.TestPlanBuilder; + +import static org.sonar.api.CoreProperties.CATEGORY_CODE_COVERAGE; + +public class GenericTestExecutionSensor implements Sensor { + + private static final Logger LOG = Loggers.get(GenericTestExecutionSensor.class); + + private static final String REPORT_PATHS_PROPERTY_KEY = "sonar.testExecutionReportPaths"; + /** + * @deprecated since 6.2 + */ + @Deprecated + private static final String OLD_UNIT_TEST_REPORT_PATHS_PROPERTY_KEY = "sonar.genericcoverage.unitTestReportPaths"; + + private final TestPlanBuilder testPlanBuilder; + + public GenericTestExecutionSensor(TestPlanBuilder testPlanBuilder) { + this.testPlanBuilder = testPlanBuilder; + } + + public static ImmutableList properties() { + return ImmutableList.of( + + PropertyDefinition.builder(REPORT_PATHS_PROPERTY_KEY) + .name("Unit tests results report paths") + .description("List of comma-separated paths (absolute or relative) containing unit tests results report.") + .category(CATEGORY_CODE_COVERAGE) + .onQualifiers(Qualifiers.PROJECT) + .deprecatedKey(OLD_UNIT_TEST_REPORT_PATHS_PROPERTY_KEY) + .build()); + + } + + @Override + public void describe(SensorDescriptor descriptor) { + descriptor.name("Generic Tests Excution Report") + .requireProperty(REPORT_PATHS_PROPERTY_KEY); + } + + @Override + public void execute(SensorContext context) { + for (String reportPath : context.settings().getStringArray(REPORT_PATHS_PROPERTY_KEY)) { + File reportFile = context.fileSystem().resolvePath(reportPath); + LOG.info("Parsing {}", reportFile); + GenericTestExecutionReportParser parser = new GenericTestExecutionReportParser(testPlanBuilder); + parser.parse(reportFile, context); + LOG.info("Imported coverage data for {} files", parser.numberOfMatchedFiles()); + int numberOfUnknownFiles = parser.numberOfUnknownFiles(); + if (numberOfUnknownFiles > 0) { + LOG.info("Coverage data ignored for " + numberOfUnknownFiles + " unknown files, including:\n" + parser.firstUnknownFiles().stream().collect(Collectors.joining("\n"))); + } + } + + } + +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/genericcoverage/package-info.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/genericcoverage/package-info.java new file mode 100644 index 00000000000..46f0286f222 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/genericcoverage/package-info.java @@ -0,0 +1,24 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +@ParametersAreNonnullByDefault +package org.sonar.scanner.genericcoverage; + +import javax.annotation.ParametersAreNonnullByDefault; + diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultIssuable.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultIssuable.java index 2a449596484..28c2502430a 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultIssuable.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultIssuable.java @@ -21,21 +21,21 @@ package org.sonar.scanner.issue; import java.util.Collections; import java.util.List; +import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.issue.internal.DefaultIssue; import org.sonar.api.issue.Issuable; import org.sonar.api.issue.Issue; -import org.sonar.scanner.index.BatchComponent; /** * @since 3.6 */ public class DefaultIssuable implements Issuable { - private final BatchComponent component; + private final InputComponent component; private final SensorContext sensorContext; - DefaultIssuable(BatchComponent component, SensorContext sensorContext) { + DefaultIssuable(InputComponent component, SensorContext sensorContext) { this.component = component; this.sensorContext = sensorContext; } @@ -43,7 +43,7 @@ public class DefaultIssuable implements Issuable { @Override public IssueBuilder newIssueBuilder() { DefaultIssue newIssue = (DefaultIssue) sensorContext.newIssue(); - return new DeprecatedIssueBuilderWrapper(component.inputComponent(), newIssue); + return new DeprecatedIssueBuilderWrapper(component, newIssue); } @Override diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssuableFactory.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssuableFactory.java index 85177696c53..47f2ea3bcd3 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssuableFactory.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssuableFactory.java @@ -19,10 +19,10 @@ */ package org.sonar.scanner.issue; +import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.issue.Issuable; import org.sonar.scanner.deprecated.perspectives.PerspectiveBuilder; -import org.sonar.scanner.index.BatchComponent; import org.sonar.scanner.sensor.DefaultSensorContext; /** @@ -39,7 +39,7 @@ public class IssuableFactory extends PerspectiveBuilder { } @Override - public Issuable loadPerspective(Class perspectiveClass, BatchComponent component) { + public Issuable loadPerspective(Class perspectiveClass, InputComponent component) { return new DefaultIssuable(component, sensorContext); } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MeasuresPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MeasuresPublisher.java index 8eb6fe00251..4686aaea47a 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MeasuresPublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MeasuresPublisher.java @@ -20,14 +20,21 @@ package org.sonar.scanner.report; import com.google.common.base.Function; +import com.google.common.collect.Iterables; import java.io.Serializable; import java.util.Collections; import java.util.Map; +import java.util.stream.StreamSupport; import javax.annotation.Nonnull; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.InputFile.Type; import org.sonar.api.batch.measure.Metric; import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.test.MutableTestPlan; +import org.sonar.api.test.TestCase.Status; import org.sonar.api.utils.KeyValueFormat; +import org.sonar.scanner.deprecated.test.TestPlanBuilder; import org.sonar.scanner.index.BatchComponent; import org.sonar.scanner.index.BatchComponentCache; import org.sonar.scanner.protocol.output.ScannerReport; @@ -44,6 +51,16 @@ import static org.sonar.api.measures.CoreMetrics.CONDITIONS_TO_COVER; import static org.sonar.api.measures.CoreMetrics.CONDITIONS_TO_COVER_KEY; import static org.sonar.api.measures.CoreMetrics.LINES_TO_COVER; import static org.sonar.api.measures.CoreMetrics.LINES_TO_COVER_KEY; +import static org.sonar.api.measures.CoreMetrics.SKIPPED_TESTS; +import static org.sonar.api.measures.CoreMetrics.SKIPPED_TESTS_KEY; +import static org.sonar.api.measures.CoreMetrics.TESTS; +import static org.sonar.api.measures.CoreMetrics.TESTS_KEY; +import static org.sonar.api.measures.CoreMetrics.TEST_ERRORS; +import static org.sonar.api.measures.CoreMetrics.TEST_ERRORS_KEY; +import static org.sonar.api.measures.CoreMetrics.TEST_EXECUTION_TIME; +import static org.sonar.api.measures.CoreMetrics.TEST_EXECUTION_TIME_KEY; +import static org.sonar.api.measures.CoreMetrics.TEST_FAILURES; +import static org.sonar.api.measures.CoreMetrics.TEST_FAILURES_KEY; import static org.sonar.api.measures.CoreMetrics.UNCOVERED_CONDITIONS; import static org.sonar.api.measures.CoreMetrics.UNCOVERED_CONDITIONS_KEY; import static org.sonar.api.measures.CoreMetrics.UNCOVERED_LINES; @@ -100,47 +117,76 @@ public class MeasuresPublisher implements ReportPublisherStep { private final BatchComponentCache componentCache; private final MeasureCache measureCache; + private final TestPlanBuilder testPlanBuilder; - public MeasuresPublisher(BatchComponentCache resourceCache, MeasureCache measureCache) { + public MeasuresPublisher(BatchComponentCache resourceCache, MeasureCache measureCache, TestPlanBuilder testPlanBuilder) { this.componentCache = resourceCache; this.measureCache = measureCache; + this.testPlanBuilder = testPlanBuilder; } @Override public void publish(ScannerReportWriter writer) { for (final BatchComponent component : componentCache.all()) { // Recompute all coverage measures from line data to take into account the possible merge of several reports - DefaultMeasure lineHitsMeasure = (DefaultMeasure) measureCache.byMetric(component.key(), CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY); - if (lineHitsMeasure != null) { - Map lineHits = KeyValueFormat.parseIntInt(lineHitsMeasure.value()); - measureCache.put(component.key(), LINES_TO_COVER_KEY, new DefaultMeasure().forMetric(LINES_TO_COVER).withValue(lineHits.keySet().size())); - measureCache.put(component.key(), UNCOVERED_LINES_KEY, - new DefaultMeasure().forMetric(UNCOVERED_LINES).withValue((int) lineHits.values() - .stream() - .filter(hit -> hit == 0) - .count())); - } - DefaultMeasure conditionsMeasure = (DefaultMeasure) measureCache.byMetric(component.key(), CoreMetrics.CONDITIONS_BY_LINE_KEY); - DefaultMeasure coveredConditionsMeasure = (DefaultMeasure) measureCache.byMetric(component.key(), CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY); - if (conditionsMeasure != null) { - Map conditions = KeyValueFormat.parseIntInt(conditionsMeasure.value()); - Map coveredConditions = coveredConditionsMeasure != null ? KeyValueFormat.parseIntInt(coveredConditionsMeasure.value()) : Collections.emptyMap(); - measureCache.put(component.key(), CONDITIONS_TO_COVER_KEY, new DefaultMeasure().forMetric(CONDITIONS_TO_COVER).withValue(conditions - .values() - .stream() - .mapToInt(Integer::intValue) - .sum())); - measureCache.put(component.key(), UNCOVERED_CONDITIONS_KEY, - new DefaultMeasure().forMetric(UNCOVERED_CONDITIONS) - .withValue((int) conditions.keySet() - .stream() - .mapToInt(line -> conditions.get(line) - coveredConditions.get(line)) - .sum())); - } + updateCoverageFromLineData(component); + // Recompute test execution measures from MutableTestPlan to take into account the possible merge of several reports + updateTestExecutionFromTestPlan(component); + Iterable> scannerMeasures = measureCache.byComponentKey(component.key()); Iterable reportMeasures = transform(scannerMeasures, new MeasureToReportMeasure(component)); writer.writeComponentMeasures(component.batchId(), reportMeasures); } } + private void updateTestExecutionFromTestPlan(final BatchComponent component) { + final MutableTestPlan testPlan = testPlanBuilder.loadPerspective(MutableTestPlan.class, component.inputComponent()); + if (testPlan == null || Iterables.isEmpty(testPlan.testCases())) { + return; + } + long nonSkippedTests = StreamSupport.stream(testPlan.testCases().spliterator(), false).filter(t -> t.status() != Status.SKIPPED).count(); + measureCache.put(component.key(), TESTS_KEY, new DefaultMeasure().forMetric(TESTS).withValue((int) nonSkippedTests)); + long executionTime = StreamSupport.stream(testPlan.testCases().spliterator(), false).mapToLong(t -> t.durationInMs() != null ? t.durationInMs().longValue() : 0L).sum(); + measureCache.put(component.key(), TEST_EXECUTION_TIME_KEY, new DefaultMeasure().forMetric(TEST_EXECUTION_TIME).withValue(executionTime)); + long errorTests = StreamSupport.stream(testPlan.testCases().spliterator(), false).filter(t -> t.status() == Status.ERROR).count(); + measureCache.put(component.key(), TEST_ERRORS_KEY, new DefaultMeasure().forMetric(TEST_ERRORS).withValue((int) errorTests)); + long skippedTests = StreamSupport.stream(testPlan.testCases().spliterator(), false).filter(t -> t.status() == Status.SKIPPED).count(); + measureCache.put(component.key(), SKIPPED_TESTS_KEY, new DefaultMeasure().forMetric(SKIPPED_TESTS).withValue((int) skippedTests)); + long failedTests = StreamSupport.stream(testPlan.testCases().spliterator(), false).filter(t -> t.status() == Status.FAILURE).count(); + measureCache.put(component.key(), TEST_FAILURES_KEY, new DefaultMeasure().forMetric(TEST_FAILURES).withValue((int) failedTests)); + } + + private void updateCoverageFromLineData(final BatchComponent component) { + if (!component.isFile() || ((InputFile) component.inputComponent()).type() != Type.MAIN) { + return; + } + DefaultMeasure lineHitsMeasure = (DefaultMeasure) measureCache.byMetric(component.key(), CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY); + if (lineHitsMeasure != null) { + Map lineHits = KeyValueFormat.parseIntInt(lineHitsMeasure.value()); + measureCache.put(component.key(), LINES_TO_COVER_KEY, new DefaultMeasure().forMetric(LINES_TO_COVER).withValue(lineHits.keySet().size())); + measureCache.put(component.key(), UNCOVERED_LINES_KEY, + new DefaultMeasure().forMetric(UNCOVERED_LINES).withValue((int) lineHits.values() + .stream() + .filter(hit -> hit == 0) + .count())); + } + DefaultMeasure conditionsMeasure = (DefaultMeasure) measureCache.byMetric(component.key(), CoreMetrics.CONDITIONS_BY_LINE_KEY); + DefaultMeasure coveredConditionsMeasure = (DefaultMeasure) measureCache.byMetric(component.key(), CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY); + if (conditionsMeasure != null) { + Map conditions = KeyValueFormat.parseIntInt(conditionsMeasure.value()); + Map coveredConditions = coveredConditionsMeasure != null ? KeyValueFormat.parseIntInt(coveredConditionsMeasure.value()) : Collections.emptyMap(); + measureCache.put(component.key(), CONDITIONS_TO_COVER_KEY, new DefaultMeasure().forMetric(CONDITIONS_TO_COVER).withValue(conditions + .values() + .stream() + .mapToInt(Integer::intValue) + .sum())); + measureCache.put(component.key(), UNCOVERED_CONDITIONS_KEY, + new DefaultMeasure().forMetric(UNCOVERED_CONDITIONS) + .withValue((int) conditions.keySet() + .stream() + .mapToInt(line -> conditions.get(line) - coveredConditions.get(line)) + .sum())); + } + } + } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java index cb341a96a3b..7cb5611cdba 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java @@ -60,7 +60,7 @@ public class MetadataPublisher implements ReportPublisherStep { builder.setBranch(branch); } for (QProfile qp : qProfiles.findAll()) { - builder.getMutableQprofilesPerLanguage().put(qp.getLanguage(), org.sonar.scanner.protocol.output.ScannerReport.Metadata.QProfile.newBuilder() + builder.getMutableQprofilesPerLanguage().put(qp.getLanguage(), ScannerReport.Metadata.QProfile.newBuilder() .setKey(qp.getKey()) .setLanguage(qp.getLanguage()) .setName(qp.getName()) diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/TestExecutionAndCoveragePublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/TestExecutionAndCoveragePublisher.java index e5f7cc0cbe1..d37c9439e8b 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/TestExecutionAndCoveragePublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/TestExecutionAndCoveragePublisher.java @@ -24,8 +24,6 @@ import com.google.common.collect.Iterables; import java.util.HashSet; import java.util.Set; import javax.annotation.Nonnull; -import org.sonar.api.batch.fs.InputFile.Type; -import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.test.CoverageBlock; import org.sonar.api.test.MutableTestCase; import org.sonar.api.test.MutableTestPlan; @@ -115,16 +113,7 @@ public class TestExecutionAndCoveragePublisher implements ReportPublisherStep { @Override public void publish(ScannerReportWriter writer) { for (final BatchComponent component : componentCache.all()) { - if (!component.isFile()) { - continue; - } - - DefaultInputFile inputFile = (DefaultInputFile) component.inputComponent(); - if (inputFile.type() != Type.TEST) { - continue; - } - - final MutableTestPlan testPlan = testPlanBuilder.loadPerspective(MutableTestPlan.class, component); + final MutableTestPlan testPlan = testPlanBuilder.loadPerspective(MutableTestPlan.class, component.inputComponent()); if (testPlan == null || Iterables.isEmpty(testPlan.testCases())) { continue; } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleScanContainer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleScanContainer.java index 72f95401c53..5ab42b57c2e 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleScanContainer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleScanContainer.java @@ -35,7 +35,7 @@ import org.sonar.scanner.bootstrap.BatchExtensionDictionnary; import org.sonar.scanner.bootstrap.ExtensionInstaller; import org.sonar.scanner.bootstrap.ExtensionUtils; import org.sonar.scanner.deprecated.DeprecatedSensorContext; -import org.sonar.scanner.deprecated.perspectives.BatchPerspectives; +import org.sonar.scanner.deprecated.perspectives.ScannerPerspectives; import org.sonar.scanner.events.EventBus; import org.sonar.scanner.index.BatchComponentCache; import org.sonar.scanner.index.DefaultIndex; @@ -162,7 +162,7 @@ public class ModuleScanContainer extends ComponentContainer { IgnoreIssuesFilter.class, // Perspectives - BatchPerspectives.class, + ScannerPerspectives.class, HighlightableBuilder.class, SymbolizableBuilder.class, diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/source/HighlightableBuilder.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/source/HighlightableBuilder.java index 5f062b22946..da793d3e981 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/source/HighlightableBuilder.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/source/HighlightableBuilder.java @@ -21,12 +21,12 @@ package org.sonar.scanner.source; import javax.annotation.CheckForNull; import org.sonar.api.batch.AnalysisMode; +import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.sensor.internal.SensorStorage; import org.sonar.api.source.Highlightable; import org.sonar.scanner.deprecated.perspectives.PerspectiveBuilder; -import org.sonar.scanner.index.BatchComponent; public class HighlightableBuilder extends PerspectiveBuilder { @@ -41,9 +41,9 @@ public class HighlightableBuilder extends PerspectiveBuilder { @CheckForNull @Override - public Highlightable loadPerspective(Class perspectiveClass, BatchComponent component) { + public Highlightable loadPerspective(Class perspectiveClass, InputComponent component) { if (component.isFile()) { - InputFile path = (InputFile) component.inputComponent(); + InputFile path = (InputFile) component; return new DefaultHighlightable((DefaultInputFile) path, sensorStorage, analysisMode); } return null; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/source/SymbolizableBuilder.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/source/SymbolizableBuilder.java index e805c6dbcc4..947670bf769 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/source/SymbolizableBuilder.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/source/SymbolizableBuilder.java @@ -21,11 +21,11 @@ package org.sonar.scanner.source; import javax.annotation.CheckForNull; import org.sonar.api.batch.AnalysisMode; +import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.source.Symbolizable; import org.sonar.scanner.deprecated.perspectives.PerspectiveBuilder; -import org.sonar.scanner.index.BatchComponent; import org.sonar.scanner.sensor.DefaultSensorStorage; public class SymbolizableBuilder extends PerspectiveBuilder { @@ -41,9 +41,9 @@ public class SymbolizableBuilder extends PerspectiveBuilder { @CheckForNull @Override - public Symbolizable loadPerspective(Class perspectiveClass, BatchComponent component) { + public Symbolizable loadPerspective(Class perspectiveClass, InputComponent component) { if (component.isFile()) { - InputFile path = (InputFile) component.inputComponent(); + InputFile path = (InputFile) component; return new DefaultSymbolizable((DefaultInputFile) path, sensorStorage, analysisMode); } return null; diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/deprecated/perspectives/PerspectiveBuilderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/deprecated/perspectives/PerspectiveBuilderTest.java index 791db326ae7..934141f862d 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/deprecated/perspectives/PerspectiveBuilderTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/deprecated/perspectives/PerspectiveBuilderTest.java @@ -20,18 +20,18 @@ package org.sonar.scanner.deprecated.perspectives; import org.junit.Test; +import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.component.Perspective; -import org.sonar.scanner.deprecated.perspectives.PerspectiveBuilder; -import org.sonar.scanner.index.BatchComponent; import static org.assertj.core.api.Assertions.assertThat; public class PerspectiveBuilderTest { + @Test public void testGetPerspectiveClass() throws Exception { PerspectiveBuilder builder = new PerspectiveBuilder(FakePerspective.class) { @Override - public FakePerspective loadPerspective(Class perspectiveClass, BatchComponent component) { + public FakePerspective loadPerspective(Class perspectiveClass, InputComponent component) { return null; } }; diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/genericcoverage/GenericCoverageReportParserTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/genericcoverage/GenericCoverageReportParserTest.java new file mode 100644 index 00000000000..fb41e6b2ad9 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/genericcoverage/GenericCoverageReportParserTest.java @@ -0,0 +1,224 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.scanner.genericcoverage; + +import java.io.ByteArrayInputStream; +import java.io.File; +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.sensor.internal.SensorContextTester; + +import static org.assertj.core.api.Assertions.assertThat; + +public class GenericCoverageReportParserTest { + private DefaultInputFile fileWithBranches; + private DefaultInputFile fileWithoutBranch; + private DefaultInputFile emptyFile; + private SensorContextTester context; + + @Before + public void before() { + context = SensorContextTester.create(new File("")); + fileWithBranches = setupFile("src/main/java/com/example/ClassWithBranches.java"); + fileWithoutBranch = setupFile("src/main/java/com/example/ClassWithoutBranch.java"); + emptyFile = setupFile("src/main/java/com/example/EmptyClass.java"); + } + + @Test + public void empty_file() throws Exception { + addFileToFs(emptyFile); + GenericCoverageReportParser parser = new GenericCoverageReportParser(); + parser.parse(this.getClass().getResourceAsStream("coverage.xml"), context); + assertThat(parser.numberOfMatchedFiles()).isEqualTo(1); + assertThat(parser.numberOfUnknownFiles()).isEqualTo(3); + assertThat(parser.firstUnknownFiles()).hasSize(3); + } + + @Test + public void file_without_branch() throws Exception { + addFileToFs(fileWithoutBranch); + GenericCoverageReportParser parser = new GenericCoverageReportParser(); + parser.parse(this.getClass().getResourceAsStream("coverage.xml"), context); + assertThat(parser.numberOfMatchedFiles()).isEqualTo(1); + + assertThat(context.lineHits(fileWithoutBranch.key(), 2)).isEqualTo(0); + assertThat(context.lineHits(fileWithoutBranch.key(), 3)).isEqualTo(1); + assertThat(context.lineHits(fileWithoutBranch.key(), 4)).isNull(); + assertThat(context.lineHits(fileWithoutBranch.key(), 5)).isEqualTo(1); + assertThat(context.lineHits(fileWithoutBranch.key(), 6)).isEqualTo(0); + } + + @Test + public void file_with_branches() throws Exception { + addFileToFs(fileWithBranches); + GenericCoverageReportParser parser = new GenericCoverageReportParser(); + parser.parse(this.getClass().getResourceAsStream("coverage.xml"), context); + assertThat(parser.numberOfMatchedFiles()).isEqualTo(1); + + assertThat(context.lineHits(fileWithBranches.key(), 3)).isEqualTo(1); + assertThat(context.lineHits(fileWithBranches.key(), 4)).isEqualTo(1); + + assertThat(context.conditions(fileWithBranches.key(), 3)).isEqualTo(8); + assertThat(context.conditions(fileWithBranches.key(), 4)).isEqualTo(2); + + assertThat(context.coveredConditions(fileWithBranches.key(), 3)).isEqualTo(5); + assertThat(context.coveredConditions(fileWithBranches.key(), 4)).isEqualTo(0); + } + + @Test(expected = IllegalStateException.class) + public void coverage_invalid_root_node_name() throws Exception { + new GenericCoverageReportParser().parse(new ByteArrayInputStream("".getBytes()), context); + } + + @Test(expected = IllegalStateException.class) + public void coverage_invalid_report_version() throws Exception { + parseCoverageReport(""); + } + + @Test(expected = IllegalStateException.class) + public void coverage_no_report_version() throws Exception { + parseCoverageReport(""); + } + + @Test(expected = IllegalStateException.class) + public void coverage_invalid_file_node_name() throws Exception { + parseCoverageReport(""); + } + + @Test(expected = IllegalStateException.class) + public void unitTest_invalid_file_node_name() throws Exception { + parseCoverageReport(""); + } + + @Test(expected = IllegalStateException.class) + public void coverage_missing_path_attribute() throws Exception { + parseCoverageReport(""); + } + + @Test(expected = IllegalStateException.class) + public void unitTest_missing_path_attribute() throws Exception { + parseCoverageReport(""); + } + + @Test(expected = IllegalStateException.class) + public void coverage_invalid_lineToCover_node_name() throws Exception { + addFileToFs(setupFile("file1")); + parseCoverageReport(""); + } + + @Test(expected = IllegalStateException.class) + public void coverage_missing_lineNumber_in_lineToCover() throws Exception { + addFileToFs(setupFile("file1")); + parseCoverageReport(""); + } + + @Test(expected = IllegalStateException.class) + public void coverage_lineNumber_in_lineToCover_should_be_a_number() throws Exception { + addFileToFs(setupFile("file1")); + parseCoverageReport(""); + } + + @Test(expected = IllegalStateException.class) + public void coverage_lineNumber_in_lineToCover_should_be_positive() throws Exception { + addFileToFs(setupFile("file1")); + parseCoverageReport(""); + } + + @Test + public void coverage_lineNumber_in_lineToCover_can_appear_several_times_for_same_file() throws Exception { + addFileToFs(setupFile("file1")); + parseCoverageReport("" + + "" + + ""); + } + + @Test(expected = IllegalStateException.class) + public void coverage_missing_covered_in_lineToCover() throws Exception { + addFileToFs(setupFile("file1")); + parseCoverageReport(""); + } + + @Test(expected = IllegalStateException.class) + public void coverage_covered_in_lineToCover_should_be_a_boolean() throws Exception { + addFileToFs(setupFile("file1")); + parseCoverageReport(""); + } + + @Test(expected = IllegalStateException.class) + public void coverage_branchesToCover_in_lineToCover_should_be_a_number() throws Exception { + addFileToFs(setupFile("file1")); + parseCoverageReport("" + + ""); + } + + @Test(expected = IllegalStateException.class) + public void coverage_branchesToCover_in_lineToCover_should_not_be_negative() throws Exception { + addFileToFs(setupFile("file1")); + parseCoverageReport("" + + ""); + } + + @Test(expected = IllegalStateException.class) + public void coverage_coveredBranches_in_lineToCover_should_be_a_number() throws Exception { + addFileToFs(setupFile("file1")); + parseCoverageReport("" + + ""); + } + + @Test(expected = IllegalStateException.class) + public void coverage_coveredBranches_in_lineToCover_should_not_be_negative() throws Exception { + addFileToFs(setupFile("file1")); + parseCoverageReport("" + + ""); + } + + @Test(expected = IllegalStateException.class) + public void coverage_coveredBranches_should_not_be_greater_than_branchesToCover() throws Exception { + addFileToFs(setupFile("file1")); + parseCoverageReport("" + + ""); + } + + @Test(expected = IllegalStateException.class) + public void testUnknownFile() throws Exception { + parseCoverageReportFile("xxx.xml"); + } + + private void addFileToFs(DefaultInputFile inputFile) { + context.fileSystem().add(inputFile); + } + + private void parseCoverageReport(String string) throws Exception { + new GenericCoverageReportParser().parse(new ByteArrayInputStream(string.getBytes()), context); + } + + private void parseCoverageReportFile(String reportLocation) throws Exception { + new GenericCoverageReportParser().parse(new File(reportLocation), context); + } + + private DefaultInputFile setupFile(String path) { + return new DefaultInputFile(context.module().key(), path) + .setLanguage("bla") + .setType(InputFile.Type.TEST) + .initMetadata("1\n2\n3\n4\n5\n6"); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/genericcoverage/GenericTestExecutionReportParserTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/genericcoverage/GenericTestExecutionReportParserTest.java new file mode 100644 index 00000000000..3ee34b3f4f0 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/genericcoverage/GenericTestExecutionReportParserTest.java @@ -0,0 +1,167 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.scanner.genericcoverage; + +import java.io.ByteArrayInputStream; +import java.io.File; +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.sensor.internal.SensorContextTester; +import org.sonar.api.test.MutableTestCase; +import org.sonar.api.test.MutableTestPlan; +import org.sonar.scanner.deprecated.test.TestPlanBuilder; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class GenericTestExecutionReportParserTest { + + private TestPlanBuilder testPlanBuilder; + private DefaultInputFile fileWithBranches; + private DefaultInputFile emptyFile; + private SensorContextTester context; + private MutableTestPlan testPlan; + + @Before + public void before() { + context = SensorContextTester.create(new File("")); + fileWithBranches = setupFile("src/main/java/com/example/ClassWithBranches.java"); + emptyFile = setupFile("src/main/java/com/example/EmptyClass.java"); + testPlanBuilder = mock(TestPlanBuilder.class); + + MutableTestCase testCase = mockMutableTestCase(); + testPlan = mockMutableTestPlan(testCase); + + when(testPlanBuilder.loadPerspective(eq(MutableTestPlan.class), any(InputFile.class))).thenReturn(testPlan); + } + + @Test + public void ut_empty_file() throws Exception { + addFileToFs(emptyFile); + GenericTestExecutionReportParser parser = parseReportFile("unittest.xml"); + assertThat(parser.numberOfMatchedFiles()).isEqualTo(1); + assertThat(parser.numberOfUnknownFiles()).isEqualTo(1); + assertThat(parser.firstUnknownFiles()).hasSize(1); + } + + @Test + public void file_with_unittests() throws Exception { + addFileToFs(fileWithBranches); + GenericTestExecutionReportParser parser = parseReportFile("unittest2.xml"); + assertThat(parser.numberOfMatchedFiles()).isEqualTo(1); + + verify(testPlan).addTestCase("test1"); + verify(testPlan).addTestCase("test2"); + verify(testPlan).addTestCase("test3"); + } + + @Test(expected = IllegalStateException.class) + public void unittest_invalid_root_node_name() throws Exception { + parseUnitTestReport(""); + } + + @Test(expected = IllegalStateException.class) + public void unittest_invalid_report_version() throws Exception { + parseUnitTestReport(""); + } + + @Test(expected = IllegalStateException.class) + public void unittest_duration_in_testCase_should_be_a_number() throws Exception { + addFileToFs(setupFile("file1")); + parseUnitTestReport("" + + ""); + } + + @Test(expected = IllegalStateException.class) + public void unittest_failure_should_have_a_message() throws Exception { + addFileToFs(setupFile("file1")); + parseUnitTestReport("" + + ""); + } + + @Test(expected = IllegalStateException.class) + public void unittest_error_should_have_a_message() throws Exception { + addFileToFs(setupFile("file1")); + parseUnitTestReport("" + + ""); + } + + @Test(expected = IllegalStateException.class) + public void unittest_skipped_should_have_a_message() throws Exception { + addFileToFs(setupFile("file1")); + parseUnitTestReport("" + + ""); + } + + @Test(expected = IllegalStateException.class) + public void unittest_duration_in_testCase_should_not_be_negative() throws Exception { + addFileToFs(setupFile("file1")); + parseUnitTestReport("" + + ""); + } + + private void addFileToFs(DefaultInputFile inputFile) { + context.fileSystem().add(inputFile); + } + + private GenericTestExecutionReportParser parseUnitTestReport(String string) throws Exception { + GenericTestExecutionReportParser parser = new GenericTestExecutionReportParser(testPlanBuilder); + parser.parse(new ByteArrayInputStream(string.getBytes()), context); + return parser; + } + + private GenericTestExecutionReportParser parseReportFile(String reportLocation) throws Exception { + GenericTestExecutionReportParser parser = new GenericTestExecutionReportParser(testPlanBuilder); + parser.parse(this.getClass().getResourceAsStream(reportLocation), context); + return parser; + } + + private DefaultInputFile setupFile(String path) { + return new DefaultInputFile(context.module().key(), path) + .setLanguage("bla") + .setType(InputFile.Type.TEST) + .initMetadata("1\n2\n3\n4\n5\n6"); + } + + private MutableTestPlan mockMutableTestPlan(MutableTestCase testCase) { + MutableTestPlan testPlan = mock(MutableTestPlan.class); + when(testPlan.addTestCase(anyString())).thenReturn(testCase); + return testPlan; + } + + private MutableTestCase mockMutableTestCase() { + MutableTestCase testCase = mock(MutableTestCase.class); + when(testCase.setDurationInMs(anyLong())).thenReturn(testCase); + when(testCase.setStatus(any(org.sonar.api.test.TestCase.Status.class))).thenReturn(testCase); + when(testCase.setMessage(anyString())).thenReturn(testCase); + when(testCase.setStackTrace(anyString())).thenReturn(testCase); + when(testCase.setType(anyString())).thenReturn(testCase); + return testCase; + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/IssuableFactoryTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/IssuableFactoryTest.java index 07038c5832f..996e5066856 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/IssuableFactoryTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/IssuableFactoryTest.java @@ -20,13 +20,10 @@ package org.sonar.scanner.issue; import org.junit.Test; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.issue.Issuable; -import org.sonar.api.resources.File; -import org.sonar.api.resources.Project; import org.sonar.scanner.DefaultProjectTree; -import org.sonar.scanner.index.BatchComponent; -import org.sonar.scanner.issue.IssuableFactory; -import org.sonar.scanner.issue.ModuleIssues; import org.sonar.scanner.sensor.DefaultSensorContext; import static org.assertj.core.api.Assertions.assertThat; @@ -40,8 +37,7 @@ public class IssuableFactoryTest { @Test public void file_should_be_issuable() { IssuableFactory factory = new IssuableFactory(mock(DefaultSensorContext.class)); - BatchComponent component = new BatchComponent(1, File.create("foo/bar.c").setEffectiveKey("foo/bar.c"), null); - Issuable issuable = factory.loadPerspective(Issuable.class, component); + Issuable issuable = factory.loadPerspective(Issuable.class, new DefaultInputFile("foo", "src/Foo.java")); assertThat(issuable).isNotNull(); assertThat(issuable.issues()).isEmpty(); @@ -50,8 +46,7 @@ public class IssuableFactoryTest { @Test public void project_should_be_issuable() { IssuableFactory factory = new IssuableFactory(mock(DefaultSensorContext.class)); - BatchComponent component = new BatchComponent(1, new Project("Foo").setEffectiveKey("foo"), null); - Issuable issuable = factory.loadPerspective(Issuable.class, component); + Issuable issuable = factory.loadPerspective(Issuable.class, new DefaultInputModule("foo")); assertThat(issuable).isNotNull(); assertThat(issuable.issues()).isEmpty(); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/coverage/GenericCoverageMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/coverage/GenericCoverageMediumTest.java new file mode 100644 index 00000000000..f14e6c73286 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/coverage/GenericCoverageMediumTest.java @@ -0,0 +1,139 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.scanner.mediumtest.coverage; + +import java.io.File; +import java.io.IOException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.scanner.mediumtest.BatchMediumTester; +import org.sonar.scanner.mediumtest.TaskResult; +import org.sonar.xoo.XooPlugin; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + +public class GenericCoverageMediumTest { + + public BatchMediumTester tester = BatchMediumTester.builder() + .registerPlugin("xoo", new XooPlugin()) + .addDefaultQProfile("xoo", "Sonar Way") + .build(); + + @Before + public void prepare() { + tester.start(); + } + + @After + public void stop() { + tester.stop(); + } + + @Test + public void singleReport() throws IOException { + + File projectDir = new File("src/test/resources/mediumtest/xoo/sample-generic-coverage"); + + TaskResult result = tester + .newScanTask(new File(projectDir, "sonar-project.properties")) + .property("sonar.coverageReportPaths", "coverage.xml") + .start(); + + InputFile noConditions = result.inputFile("xources/hello/NoConditions.xoo"); + assertThat(result.coverageFor(noConditions, 6).getHits()).isTrue(); + assertThat(result.coverageFor(noConditions, 6).getConditions()).isEqualTo(0); + assertThat(result.coverageFor(noConditions, 6).getCoveredConditions()).isEqualTo(0); + + assertThat(result.coverageFor(noConditions, 7).getHits()).isFalse(); + + assertThat(result.allMeasures().get(noConditions.key())).extracting("metricKey", "intValue.value", "stringValue.value") + .containsOnly( + tuple(CoreMetrics.LINES_KEY, 8, ""), + tuple(CoreMetrics.LINES_TO_COVER_KEY, 2, ""), + tuple(CoreMetrics.UNCOVERED_LINES_KEY, 1, ""), + tuple(CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY, 0, "6=1;7=0")); + + InputFile withConditions = result.inputFile("xources/hello/WithConditions.xoo"); + assertThat(result.coverageFor(withConditions, 3).getHits()).isTrue(); + assertThat(result.coverageFor(withConditions, 3).getConditions()).isEqualTo(2); + assertThat(result.coverageFor(withConditions, 3).getCoveredConditions()).isEqualTo(1); + + assertThat(result.allMeasures().get(withConditions.key())).extracting("metricKey", "intValue.value", "stringValue.value") + .containsOnly( + tuple(CoreMetrics.LINES_KEY, 6, ""), + tuple(CoreMetrics.LINES_TO_COVER_KEY, 1, ""), + tuple(CoreMetrics.UNCOVERED_LINES_KEY, 0, ""), + tuple(CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY, 0, "3=1"), + tuple(CoreMetrics.CONDITIONS_TO_COVER_KEY, 2, ""), + tuple(CoreMetrics.UNCOVERED_CONDITIONS_KEY, 1, ""), + tuple(CoreMetrics.CONDITIONS_BY_LINE_KEY, 0, "3=2"), + tuple(CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY, 0, "3=1") + + ); + } + + @Test + public void twoReports() throws IOException { + + File projectDir = new File("src/test/resources/mediumtest/xoo/sample-generic-coverage"); + + TaskResult result = tester + .newScanTask(new File(projectDir, "sonar-project.properties")) + .property("sonar.coverageReportPaths", "coverage.xml,coverage2.xml") + .start(); + + InputFile noConditions = result.inputFile("xources/hello/NoConditions.xoo"); + assertThat(result.coverageFor(noConditions, 6).getHits()).isTrue(); + assertThat(result.coverageFor(noConditions, 6).getConditions()).isEqualTo(0); + assertThat(result.coverageFor(noConditions, 6).getCoveredConditions()).isEqualTo(0); + + assertThat(result.coverageFor(noConditions, 7).getHits()).isTrue(); + + assertThat(result.allMeasures().get(noConditions.key())).extracting("metricKey", "intValue.value", "stringValue.value") + .containsOnly( + tuple(CoreMetrics.LINES_KEY, 8, ""), + tuple(CoreMetrics.LINES_TO_COVER_KEY, 2, ""), + tuple(CoreMetrics.UNCOVERED_LINES_KEY, 0, ""), + tuple(CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY, 0, "6=1;7=1")); + + InputFile withConditions = result.inputFile("xources/hello/WithConditions.xoo"); + assertThat(result.coverageFor(withConditions, 3).getHits()).isTrue(); + assertThat(result.coverageFor(withConditions, 3).getConditions()).isEqualTo(2); + assertThat(result.coverageFor(withConditions, 3).getCoveredConditions()).isEqualTo(2); + + assertThat(result.allMeasures().get(withConditions.key())).extracting("metricKey", "intValue.value", "stringValue.value") + .containsOnly( + tuple(CoreMetrics.LINES_KEY, 6, ""), + tuple(CoreMetrics.LINES_TO_COVER_KEY, 1, ""), + tuple(CoreMetrics.UNCOVERED_LINES_KEY, 0, ""), + tuple(CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY, 0, "3=2"), + tuple(CoreMetrics.CONDITIONS_TO_COVER_KEY, 2, ""), + tuple(CoreMetrics.UNCOVERED_CONDITIONS_KEY, 0, ""), + tuple(CoreMetrics.CONDITIONS_BY_LINE_KEY, 0, "3=2"), + tuple(CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY, 0, "3=2") + + ); + } + +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MeasuresPublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MeasuresPublisherTest.java index 37edc1a0389..190907e001d 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MeasuresPublisherTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MeasuresPublisherTest.java @@ -28,10 +28,13 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.resources.Project; import org.sonar.core.util.CloseableIterator; +import org.sonar.scanner.deprecated.test.TestPlanBuilder; import org.sonar.scanner.index.BatchComponentCache; import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.scanner.protocol.output.ScannerReportReader; @@ -65,11 +68,11 @@ public class MeasuresPublisherTest { Project p = new Project("foo").setAnalysisDate(new Date(1234567L)); BatchComponentCache resourceCache = new BatchComponentCache(); sampleFile = org.sonar.api.resources.File.create("src/Foo.php").setEffectiveKey(FILE_KEY); - resourceCache.add(p, null); - resourceCache.add(sampleFile, null); + resourceCache.add(p, null).setInputComponent(new DefaultInputModule("foo")); + resourceCache.add(sampleFile, null).setInputComponent(new DefaultInputFile("foo", "src/Foo.php")); measureCache = mock(MeasureCache.class); when(measureCache.byComponentKey(anyString())).thenReturn(Collections.>emptyList()); - publisher = new MeasuresPublisher(resourceCache, measureCache); + publisher = new MeasuresPublisher(resourceCache, measureCache, mock(TestPlanBuilder.class)); } @Test diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/source/HighlightableBuilderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/source/HighlightableBuilderTest.java index fd07607430d..f68fe762e42 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/source/HighlightableBuilderTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/source/HighlightableBuilderTest.java @@ -24,13 +24,7 @@ import org.sonar.api.batch.AnalysisMode; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.batch.sensor.internal.SensorStorage; -import org.sonar.api.resources.File; -import org.sonar.api.resources.Project; -import org.sonar.api.resources.Resource; import org.sonar.api.source.Highlightable; -import org.sonar.scanner.index.BatchComponent; -import org.sonar.scanner.source.DefaultHighlightable; -import org.sonar.scanner.source.HighlightableBuilder; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -39,21 +33,16 @@ public class HighlightableBuilderTest { @Test public void should_load_default_perspective() { - Resource file = File.create("foo.c").setEffectiveKey("myproject:path/to/foo.c"); - BatchComponent component = new BatchComponent(1, file, null).setInputComponent(new DefaultInputFile("foo", "foo.c")); - HighlightableBuilder builder = new HighlightableBuilder(mock(SensorStorage.class), mock(AnalysisMode.class)); - Highlightable perspective = builder.loadPerspective(Highlightable.class, component); + Highlightable perspective = builder.loadPerspective(Highlightable.class, new DefaultInputFile("foo", "foo.c")); assertThat(perspective).isNotNull().isInstanceOf(DefaultHighlightable.class); } @Test public void project_should_not_be_highlightable() { - BatchComponent component = new BatchComponent(1, new Project("struts").setEffectiveKey("org.struts"), null).setInputComponent(new DefaultInputModule("struts")); - HighlightableBuilder builder = new HighlightableBuilder(mock(SensorStorage.class), mock(AnalysisMode.class)); - Highlightable perspective = builder.loadPerspective(Highlightable.class, component); + Highlightable perspective = builder.loadPerspective(Highlightable.class, new DefaultInputModule("struts")); assertThat(perspective).isNull(); } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/source/SymbolizableBuilderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/source/SymbolizableBuilderTest.java index 9f50909105c..9a5e2edbc9f 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/source/SymbolizableBuilderTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/source/SymbolizableBuilderTest.java @@ -24,11 +24,7 @@ import org.sonar.api.batch.AnalysisMode; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.component.Perspective; -import org.sonar.api.resources.File; -import org.sonar.api.resources.Project; -import org.sonar.api.resources.Resource; import org.sonar.api.source.Symbolizable; -import org.sonar.scanner.index.BatchComponent; import org.sonar.scanner.sensor.DefaultSensorStorage; import static org.assertj.core.api.Assertions.assertThat; @@ -38,21 +34,16 @@ public class SymbolizableBuilderTest { @Test public void should_load_perspective() { - Resource file = File.create("foo.c").setEffectiveKey("myproject:path/to/foo.c"); - BatchComponent component = new BatchComponent(1, file, null).setInputComponent(new DefaultInputFile("foo", "foo.c")); - SymbolizableBuilder perspectiveBuilder = new SymbolizableBuilder(mock(DefaultSensorStorage.class), mock(AnalysisMode.class)); - Perspective perspective = perspectiveBuilder.loadPerspective(Symbolizable.class, component); + Perspective perspective = perspectiveBuilder.loadPerspective(Symbolizable.class, new DefaultInputFile("foo", "foo.c")); assertThat(perspective).isInstanceOf(Symbolizable.class); } @Test public void project_should_not_be_highlightable() { - BatchComponent component = new BatchComponent(1, new Project("struts").setEffectiveKey("org.struts"), null).setInputComponent(new DefaultInputModule("struts")); - SymbolizableBuilder builder = new SymbolizableBuilder(mock(DefaultSensorStorage.class), mock(AnalysisMode.class)); - Perspective perspective = builder.loadPerspective(Symbolizable.class, component); + Perspective perspective = builder.loadPerspective(Symbolizable.class, new DefaultInputModule("struts")); assertThat(perspective).isNull(); } diff --git a/sonar-scanner-engine/src/test/resources/mediumtest/xoo/sample-generic-coverage/coverage.xml b/sonar-scanner-engine/src/test/resources/mediumtest/xoo/sample-generic-coverage/coverage.xml new file mode 100644 index 00000000000..e12af5e2fb2 --- /dev/null +++ b/sonar-scanner-engine/src/test/resources/mediumtest/xoo/sample-generic-coverage/coverage.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/sonar-scanner-engine/src/test/resources/mediumtest/xoo/sample-generic-coverage/coverage2.xml b/sonar-scanner-engine/src/test/resources/mediumtest/xoo/sample-generic-coverage/coverage2.xml new file mode 100644 index 00000000000..1a1e02b0f7b --- /dev/null +++ b/sonar-scanner-engine/src/test/resources/mediumtest/xoo/sample-generic-coverage/coverage2.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/sonar-scanner-engine/src/test/resources/mediumtest/xoo/sample-generic-coverage/sonar-project.properties b/sonar-scanner-engine/src/test/resources/mediumtest/xoo/sample-generic-coverage/sonar-project.properties new file mode 100644 index 00000000000..ac18398a604 --- /dev/null +++ b/sonar-scanner-engine/src/test/resources/mediumtest/xoo/sample-generic-coverage/sonar-project.properties @@ -0,0 +1,3 @@ +sonar.projectKey=sample-generic-coverage +sonar.sources=xources +sonar.language=xoo diff --git a/sonar-scanner-engine/src/test/resources/mediumtest/xoo/sample-generic-coverage/xources/hello/NoConditions.xoo b/sonar-scanner-engine/src/test/resources/mediumtest/xoo/sample-generic-coverage/xources/hello/NoConditions.xoo new file mode 100644 index 00000000000..1d9c60d56b7 --- /dev/null +++ b/sonar-scanner-engine/src/test/resources/mediumtest/xoo/sample-generic-coverage/xources/hello/NoConditions.xoo @@ -0,0 +1,8 @@ +package hello; + +public class HelloJava { + + public static void main(String[] args) { + System.out.println("Hello"); + } +} \ No newline at end of file diff --git a/sonar-scanner-engine/src/test/resources/mediumtest/xoo/sample-generic-coverage/xources/hello/WithConditions.xoo b/sonar-scanner-engine/src/test/resources/mediumtest/xoo/sample-generic-coverage/xources/hello/WithConditions.xoo new file mode 100644 index 00000000000..8f469103183 --- /dev/null +++ b/sonar-scanner-engine/src/test/resources/mediumtest/xoo/sample-generic-coverage/xources/hello/WithConditions.xoo @@ -0,0 +1,6 @@ + object HelloWorld { + def main(args: Array[String]) { + args.isEmpty ? println("Hello, world of xoo!") : println("Hello, world of empty!") + } + } + \ No newline at end of file diff --git a/sonar-scanner-engine/src/test/resources/org/sonar/scanner/genericcoverage/coverage.xml b/sonar-scanner-engine/src/test/resources/org/sonar/scanner/genericcoverage/coverage.xml new file mode 100644 index 00000000000..74dd213c413 --- /dev/null +++ b/sonar-scanner-engine/src/test/resources/org/sonar/scanner/genericcoverage/coverage.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/sonar-scanner-engine/src/test/resources/org/sonar/scanner/genericcoverage/unittest.xml b/sonar-scanner-engine/src/test/resources/org/sonar/scanner/genericcoverage/unittest.xml new file mode 100644 index 00000000000..f1d12ec0f50 --- /dev/null +++ b/sonar-scanner-engine/src/test/resources/org/sonar/scanner/genericcoverage/unittest.xml @@ -0,0 +1,15 @@ + + + + + + other + + + stacktrace + + + stacktrace + + + diff --git a/sonar-scanner-engine/src/test/resources/org/sonar/scanner/genericcoverage/unittest2.xml b/sonar-scanner-engine/src/test/resources/org/sonar/scanner/genericcoverage/unittest2.xml new file mode 100644 index 00000000000..35ff4ad5912 --- /dev/null +++ b/sonar-scanner-engine/src/test/resources/org/sonar/scanner/genericcoverage/unittest2.xml @@ -0,0 +1,15 @@ + + + + + other + + + stacktrace + + + + long + + +