]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-766 Add exclusions for coverage
authorJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>
Fri, 13 Sep 2013 13:04:41 +0000 (15:04 +0200)
committerJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>
Fri, 13 Sep 2013 13:08:55 +0000 (15:08 +0200)
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

plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CoverageMeasurementFilter.java [new file with mode: 0644]
plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/CoverageMeasurementFilterTest.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/DefaultSensorContext.java
sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java
sonar-core/src/main/java/org/sonar/core/measure/MeasurementFilter.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/measure/MeasurementFilters.java [new file with mode: 0644]

index 4b32a973eb356bdef21b7cafdc86dec5eeee7279..f72400dd0f7d4b0df6924f3feeda2a156d4d3112 100644 (file)
@@ -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 (file)
index 0000000..99b60f9
--- /dev/null
@@ -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<WildcardPattern> 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<WildcardPattern> iterator = resourcePatterns.iterator();
+    while (!found && iterator.hasNext()) {
+      found = resource.matchFilePattern(iterator.next().toString());
+    }
+    return found;
+  }
+
+  @VisibleForTesting
+  void initPatterns() {
+    Builder<WildcardPattern> builder = ImmutableList.<WildcardPattern>builder();
+    for (String pattern : settings.getStringArray(PROPERTY_COVERAGE_EXCLUSIONS)) {
+      builder.add(WildcardPattern.create(pattern));
+    }
+    resourcePatterns = builder.build();
+  }
+
+  public static List<? extends PropertyDefinition> 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()
+      );
+  }
+}
index 616799b7a4b6e94f765f41de7b0f68be91c4fdc2..07b52027cf782e7d0b5f5a9054c64041a4d2edec 100644 (file)
@@ -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 (file)
index 0000000..fd4693e
--- /dev/null
@@ -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();
+  }
+}
+
index 57c75b2551f7797e9887b4b66e515ff6e802ddea..bc3c04244512cb248305bf1da1d6bc592fee0654 100644 (file)
@@ -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) {
index 23a087ce1554bd871a0f043550ba60aecdfa8415..0075238fd4edda6aad46b5a5709a4b47dfa219c0 100644 (file)
@@ -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 (file)
index 0000000..0ff7cb0
--- /dev/null
@@ -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 <code>true</code> 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 (file)
index 0000000..79896cc
--- /dev/null
@@ -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<MeasurementFilter> 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<MeasurementFilter> iteratorFilter = filters.iterator();
+    while(accept && iteratorFilter.hasNext()) {
+      accept &= iteratorFilter.next().accept(resource, measure);
+    }
+    return accept;
+  }
+}