From: Jean-Baptiste Lievremont Date: Fri, 13 Sep 2013 13:04:41 +0000 (+0200) Subject: SONAR-766 Add exclusions for coverage X-Git-Tag: 4.0~447 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=3af09034934f3f7b3611fdfed6dd217c73c033e9;p=sonarqube.git SONAR-766 Add exclusions for coverage Introduce new API to filter measures before they are saved to context Implement a measure filter for code coverage measures Add configuration property for coverage exclusions --- diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java index 4b32a973eb3..f72400dd0f7 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java @@ -20,7 +20,6 @@ package org.sonar.plugins.core; import org.sonar.api.resources.Qualifiers; - import org.sonar.api.config.PropertyDefinition; import com.google.common.collect.ImmutableList; import org.sonar.api.CoreProperties; @@ -65,6 +64,7 @@ import org.sonar.plugins.core.sensors.BranchCoverageDecorator; import org.sonar.plugins.core.sensors.CheckAlertThresholds; import org.sonar.plugins.core.sensors.CommentDensityDecorator; import org.sonar.plugins.core.sensors.CoverageDecorator; +import org.sonar.plugins.core.sensors.CoverageMeasurementFilter; import org.sonar.plugins.core.sensors.DirectoriesDecorator; import org.sonar.plugins.core.sensors.FilesDecorator; import org.sonar.plugins.core.sensors.GenerateAlertEvents; @@ -463,6 +463,7 @@ public final class CorePlugin extends SonarPlugin { OverallLineCoverageDecorator.class, OverallCoverageDecorator.class, OverallBranchCoverageDecorator.class, + CoverageMeasurementFilter.class, ApplyProjectRolesDecorator.class, CommentDensityDecorator.class, NoSonarFilter.class, @@ -486,6 +487,7 @@ public final class CorePlugin extends SonarPlugin { extensions.addAll(getPropertyDefinitions()); extensions.addAll(IgnoreIssuesPlugin.getExtensions()); + extensions.addAll(CoverageMeasurementFilter.getPropertyDefinitions()); return extensions.build(); } diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CoverageMeasurementFilter.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CoverageMeasurementFilter.java new file mode 100644 index 00000000000..99b60f96157 --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CoverageMeasurementFilter.java @@ -0,0 +1,93 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.plugins.core.sensors; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableList.Builder; +import org.sonar.api.CoreProperties; +import org.sonar.api.PropertyType; +import org.sonar.api.config.PropertyDefinition; +import org.sonar.api.config.Settings; +import org.sonar.api.measures.Measure; +import org.sonar.api.resources.Qualifiers; +import org.sonar.api.resources.Resource; +import org.sonar.api.utils.WildcardPattern; +import org.sonar.core.measure.MeasurementFilter; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +public class CoverageMeasurementFilter implements MeasurementFilter { + + public static final String PROPERTY_COVERAGE_EXCLUSIONS = "sonar.coverage.exclusions"; + public static final String PROPERTY_COVERAGE_INCLUSIONS = "sonar.coverage.inclusions"; + + private Settings settings; + private CoverageDecorator decorator; + private Collection resourcePatterns; + + public CoverageMeasurementFilter(Settings settings, CoverageDecorator decorator) { + this.settings = settings; + this.decorator = decorator; + + initPatterns(); + } + + @Override + public boolean accept(Resource resource, Measure measure) { + if (this.decorator.usedMetrics().contains(measure.getMetric())) { + return !hasMatchingPattern(resource); + } else { + return true; + } + } + + private boolean hasMatchingPattern(Resource resource) { + boolean found = false; + Iterator iterator = resourcePatterns.iterator(); + while (!found && iterator.hasNext()) { + found = resource.matchFilePattern(iterator.next().toString()); + } + return found; + } + + @VisibleForTesting + void initPatterns() { + Builder builder = ImmutableList.builder(); + for (String pattern : settings.getStringArray(PROPERTY_COVERAGE_EXCLUSIONS)) { + builder.add(WildcardPattern.create(pattern)); + } + resourcePatterns = builder.build(); + } + + public static List getPropertyDefinitions() { + return ImmutableList.of( + PropertyDefinition.builder("sonar.coverage.exclusions") + .category(CoreProperties.CATEGORY_EXCLUSIONS) + .subCategory(CoreProperties.CATEGORY_CODE_COVERAGE) // TODO Replace with dedicated subcat + .type(PropertyType.STRING) + .multiValues(true) + .onQualifiers(Qualifiers.PROJECT, Qualifiers.MODULE) + .build() + ); + } +} diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties b/plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties index 616799b7a4b..07b52027cf7 100644 --- a/plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties +++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties @@ -825,6 +825,10 @@ property.category.exclusions.issues=Issues property.category.exclusions.issues.description=Configure which issues should not be reported. property.category.exclusions.duplications=Duplications property.category.exclusions.duplications.description=Configure files which should not be checked for duplicated code. +property.category.exclusions.codeCoverage=Code Coverage +property.category.exclusions.codeCoverage.description=Configure files which should be considered for code coverage. +property.sonar.coverage.exclusions.name=Coverage Exclusions +property.sonar.coverage.exclusions.description=Patterns used to exclude some files from coverage report. property.error.notBoolean=Valid options are "true" and "false" property.error.notInteger=Only digits are allowed property.error.notFloat=Not a floating point number diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/CoverageMeasurementFilterTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/CoverageMeasurementFilterTest.java new file mode 100644 index 00000000000..fd4693e1702 --- /dev/null +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/CoverageMeasurementFilterTest.java @@ -0,0 +1,76 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.plugins.core.sensors; + +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.config.PropertyDefinitions; +import org.sonar.api.config.Settings; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.Measure; +import org.sonar.api.resources.File; +import org.sonar.api.resources.Resource; + +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class CoverageMeasurementFilterTest { + + private Settings settings; + + private CoverageMeasurementFilter filter; + + @Before + public void createFilter() { + settings = new Settings(new PropertyDefinitions(CoverageMeasurementFilter.getPropertyDefinitions())); + filter = new CoverageMeasurementFilter(settings, new CoverageDecorator()); + } + + @Test + public void shouldNotFilterNonCoverageMetrics() { + Measure otherMeasure = mock(Measure.class); + when(otherMeasure.getMetric()).thenReturn(CoreMetrics.LINES); + assertThat(filter.accept(mock(Resource.class), otherMeasure)).isTrue(); + } + + @Test + public void shouldFilterFileBasedOnPattern() { + Resource resource = new File("org/polop/File.php"); + Measure coverageMeasure = mock(Measure.class); + when(coverageMeasure.getMetric()).thenReturn(CoreMetrics.LINES_TO_COVER); + + settings.setProperty(CoverageMeasurementFilter.PROPERTY_COVERAGE_EXCLUSIONS, "org/polop/*"); + filter.initPatterns(); + assertThat(filter.accept(resource, coverageMeasure)).isFalse(); + } + + @Test + public void shouldNotFilterFileBasedOnPattern() { + Resource resource = new File("org/polop/File.php"); + Measure coverageMeasure = mock(Measure.class); + when(coverageMeasure.getMetric()).thenReturn(CoreMetrics.LINES_TO_COVER); + + settings.setProperty(CoverageMeasurementFilter.PROPERTY_COVERAGE_EXCLUSIONS, "org/other/*"); + filter.initPatterns(); + assertThat(filter.accept(resource, coverageMeasure)).isTrue(); + } +} + diff --git a/sonar-batch/src/main/java/org/sonar/batch/DefaultSensorContext.java b/sonar-batch/src/main/java/org/sonar/batch/DefaultSensorContext.java index 57c75b2551f..bc3c0424451 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/DefaultSensorContext.java +++ b/sonar-batch/src/main/java/org/sonar/batch/DefaultSensorContext.java @@ -19,6 +19,8 @@ */ package org.sonar.batch; +import org.sonar.core.measure.MeasurementFilters; + import org.sonar.api.batch.Event; import org.sonar.api.batch.SensorContext; import org.sonar.api.batch.SonarIndex; @@ -40,10 +42,12 @@ public class DefaultSensorContext implements SensorContext { private SonarIndex index; private Project project; + private MeasurementFilters filters; - public DefaultSensorContext(SonarIndex index, Project project) { + public DefaultSensorContext(SonarIndex index, Project project, MeasurementFilters filters) { this.index = index; this.project = project; + this.filters = filters; } public Project getProject() { @@ -115,11 +119,15 @@ public class DefaultSensorContext implements SensorContext { } public Measure saveMeasure(Resource resource, Metric metric, Double value) { - return index.addMeasure(resourceOrProject(resource), new Measure(metric, value)); + return saveMeasure(resource, new Measure(metric, value)); } public Measure saveMeasure(Resource resource, Measure measure) { - return index.addMeasure(resourceOrProject(resource), measure); + if(filters.accept(resource, measure)) { + return index.addMeasure(resourceOrProject(resource), measure); + } else { + return measure; + } } public void saveViolation(Violation violation, boolean force) { diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java index 23a087ce155..0075238fd4e 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java @@ -19,6 +19,8 @@ */ package org.sonar.batch.scan; +import org.sonar.core.measure.MeasurementFilters; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.BatchExtension; @@ -104,6 +106,7 @@ public class ModuleScanContainer extends ComponentContainer { DefaultTimeMachine.class, ViolationFilters.class, IssueFilters.class, + MeasurementFilters.class, ResourceFilters.class, DeprecatedJsonReport.class, JsonReport.class, diff --git a/sonar-core/src/main/java/org/sonar/core/measure/MeasurementFilter.java b/sonar-core/src/main/java/org/sonar/core/measure/MeasurementFilter.java new file mode 100644 index 00000000000..0ff7cb04dfa --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/measure/MeasurementFilter.java @@ -0,0 +1,41 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.core.measure; + +import org.sonar.api.BatchExtension; + +import org.sonar.api.resources.Resource; +import org.sonar.api.measures.Measure; + +/** + * Allows to define filter {@link Measure}s when they are saved on {@link Resource}s + * @since 4.0 + * + */ +public interface MeasurementFilter extends BatchExtension { + + /** + * + * @param resource + * @param measure + * @return true if the given measure can be saved for the given resource + */ + boolean accept(Resource resource, Measure measure); +} diff --git a/sonar-core/src/main/java/org/sonar/core/measure/MeasurementFilters.java b/sonar-core/src/main/java/org/sonar/core/measure/MeasurementFilters.java new file mode 100644 index 00000000000..79896cc0d43 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/measure/MeasurementFilters.java @@ -0,0 +1,55 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.core.measure; + +import com.google.common.collect.ImmutableList; +import org.sonar.api.BatchExtension; +import org.sonar.api.measures.Measure; +import org.sonar.api.resources.Resource; + +import java.util.Iterator; +import java.util.List; + +/** + * + * @since 4.0 + * + */ +public class MeasurementFilters implements BatchExtension { + + private List filters; + + public MeasurementFilters() { + this.filters = ImmutableList.of(); + } + + public MeasurementFilters(MeasurementFilter[] filters) { + this.filters = ImmutableList.copyOf(filters); + } + + public boolean accept(Resource resource, Measure measure) { + boolean accept = true; + Iterator iteratorFilter = filters.iterator(); + while(accept && iteratorFilter.hasNext()) { + accept &= iteratorFilter.next().accept(resource, measure); + } + return accept; + } +}