]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-926 Fix alerts reporting for multi-language projects
authorJulien HENRY <julien.henry@sonarsource.com>
Thu, 20 Feb 2014 10:23:55 +0000 (11:23 +0100)
committerJulien HENRY <julien.henry@sonarsource.com>
Thu, 20 Feb 2014 10:23:55 +0000 (11:23 +0100)
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CheckAlertThresholds.java
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/CheckAlertThresholdsTest.java
sonar-batch/src/main/java/org/sonar/batch/phases/ProfileLogger.java
sonar-batch/src/main/java/org/sonar/batch/rule/ProjectAlerts.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
sonar-batch/src/test/java/org/sonar/batch/phases/ProfileLoggerTest.java

index 8ca7d3d073164f5e4ceb58c29d955ebc55fe0389..1580d14f1edabb7bce550570372a8ac08f81384f 100644 (file)
@@ -21,17 +21,21 @@ package org.sonar.plugins.core.sensors;
 
 import com.google.common.collect.Lists;
 import org.apache.commons.lang.StringUtils;
-import org.sonar.api.batch.*;
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorBarriers;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependedUpon;
+import org.sonar.api.batch.DependsUpon;
 import org.sonar.api.database.model.Snapshot;
 import org.sonar.api.i18n.I18n;
 import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.measures.Measure;
 import org.sonar.api.measures.Metric;
 import org.sonar.api.profiles.Alert;
-import org.sonar.api.profiles.RulesProfile;
 import org.sonar.api.resources.Project;
 import org.sonar.api.resources.Resource;
 import org.sonar.api.resources.ResourceUtils;
+import org.sonar.batch.rule.ProjectAlerts;
 import org.sonar.core.timemachine.Periods;
 
 import java.util.List;
@@ -43,14 +47,13 @@ public class CheckAlertThresholds implements Decorator {
   private static final String VARIATION = "variation";
 
   private final Snapshot snapshot;
-  private final RulesProfile profile;
   private final Periods periods;
   private final I18n i18n;
+  private ProjectAlerts projectAlerts;
 
-
-  public CheckAlertThresholds(Snapshot snapshot, RulesProfile profile, Periods periods, I18n i18n) {
+  public CheckAlertThresholds(Snapshot snapshot, ProjectAlerts projectAlerts, Periods periods, I18n i18n) {
     this.snapshot = snapshot;
-    this.profile = profile;
+    this.projectAlerts = projectAlerts;
     this.periods = periods;
     this.i18n = i18n;
   }
@@ -68,18 +71,15 @@ public class CheckAlertThresholds implements Decorator {
   @DependsUpon
   public List<Metric> dependsUponMetrics() {
     List<Metric> metrics = Lists.newLinkedList();
-    for (Alert alert : profile.getAlerts()) {
+    for (Alert alert : projectAlerts.all()) {
       metrics.add(alert.getMetric());
     }
     return metrics;
   }
 
-
   public boolean shouldExecuteOnProject(Project project) {
-    return profile != null
-        && profile.getAlerts() != null
-        && !profile.getAlerts().isEmpty()
-        && ResourceUtils.isRootProject(project);
+    return !projectAlerts.all().isEmpty()
+      && ResourceUtils.isRootProject(project);
   }
 
   public void decorate(final Resource resource, final DecoratorContext context) {
@@ -92,7 +92,7 @@ public class CheckAlertThresholds implements Decorator {
     Metric.Level globalLevel = Metric.Level.OK;
     List<String> labels = Lists.newArrayList();
 
-    for (final Alert alert : profile.getAlerts()) {
+    for (final Alert alert : projectAlerts.all()) {
       Measure measure = context.getMeasure(alert.getMetric());
       if (measure != null) {
         Metric.Level level = AlertUtils.getLevel(alert, measure);
@@ -145,17 +145,16 @@ public class CheckAlertThresholds implements Decorator {
     }
 
     stringBuilder
-        .append(" ").append(alert.getOperator()).append(" ")
-        .append(level.equals(Metric.Level.ERROR) ? alert.getValueError() : alert.getValueWarning());
+      .append(" ").append(alert.getOperator()).append(" ")
+      .append(level.equals(Metric.Level.ERROR) ? alert.getValueError() : alert.getValueWarning());
 
-    if (alertPeriod != null){
+    if (alertPeriod != null) {
       stringBuilder.append(" ").append(periods.label(snapshot, alertPeriod));
     }
 
     return stringBuilder.toString();
   }
 
-
   @Override
   public String toString() {
     return getClass().getSimpleName();
index 46e43b20a410a31f35880772cf88033e1278206d..6154147a1ee2d6c5d9579836785aba4a66954acf 100644 (file)
@@ -23,7 +23,6 @@ import org.apache.commons.lang.NotImplementedException;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentMatcher;
-import org.mockito.Mockito;
 import org.sonar.api.batch.DecoratorBarriers;
 import org.sonar.api.batch.DecoratorContext;
 import org.sonar.api.database.model.Snapshot;
@@ -32,11 +31,11 @@ import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.measures.Measure;
 import org.sonar.api.measures.Metric;
 import org.sonar.api.profiles.Alert;
-import org.sonar.api.profiles.RulesProfile;
 import org.sonar.api.resources.Project;
 import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.resources.Resource;
 import org.sonar.api.test.IsMeasure;
+import org.sonar.batch.rule.ProjectAlerts;
 import org.sonar.core.timemachine.Periods;
 
 import java.util.ArrayList;
@@ -45,15 +44,20 @@ import java.util.Locale;
 
 import static com.google.common.collect.Lists.newArrayList;
 import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.argThat;
-import static org.mockito.Mockito.*;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 public class CheckAlertThresholdsTest {
 
   private CheckAlertThresholds decorator;
   private DecoratorContext context;
-  private RulesProfile profile;
+  private ProjectAlerts projectAlerts;
 
   private Measure measureClasses;
   private Measure measureCoverage;
@@ -80,39 +84,39 @@ public class CheckAlertThresholdsTest {
     when(context.getMeasure(CoreMetrics.COMPLEXITY)).thenReturn(measureComplexity);
 
     snapshot = mock(Snapshot.class);
-    profile = mock(RulesProfile.class);
-    decorator = new CheckAlertThresholds(snapshot, profile, periods, i18n);
+    projectAlerts = mock(ProjectAlerts.class);
+    decorator = new CheckAlertThresholds(snapshot, projectAlerts, periods, i18n);
     project = mock(Resource.class);
     when(project.getQualifier()).thenReturn(Qualifiers.PROJECT);
   }
 
   @Test
-  public void should_generates_alert_status(){
+  public void should_generates_alert_status() {
     assertThat(decorator.generatesAlertStatus()).isEqualTo(CoreMetrics.ALERT_STATUS);
   }
 
   @Test
-  public void should_depends_on_variations(){
+  public void should_depends_on_variations() {
     assertThat(decorator.dependsOnVariations()).isEqualTo(DecoratorBarriers.END_OF_TIME_MACHINE);
   }
 
   @Test
-  public void should_depends_upon_metrics(){
-    when(profile.getAlerts()).thenReturn(newArrayList(new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "20")));
+  public void should_depends_upon_metrics() {
+    when(projectAlerts.all()).thenReturn(newArrayList(new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "20")));
     assertThat(decorator.dependsUponMetrics()).containsOnly(CoreMetrics.CLASSES);
   }
 
   @Test
   public void shouldNotCreateAlertsWhenNoThresholds() {
-    when(profile.getAlerts()).thenReturn(new ArrayList<Alert>());
+    when(projectAlerts.all()).thenReturn(new ArrayList<Alert>());
     assertThat(decorator.shouldExecuteOnProject(new Project("key"))).isFalse();
   }
 
   @Test
   public void shouldBeOkWhenNoAlert() {
-    when(profile.getAlerts()).thenReturn(Arrays.asList(
-        new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "20"),
-        new Alert(null, CoreMetrics.COVERAGE, Alert.OPERATOR_GREATER, null, "35.0")));
+    when(projectAlerts.all()).thenReturn(Arrays.asList(
+      new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "20"),
+      new Alert(null, CoreMetrics.COVERAGE, Alert.OPERATOR_GREATER, null, "35.0")));
 
     decorator.decorate(project, context);
 
@@ -124,7 +128,7 @@ public class CheckAlertThresholdsTest {
   @Test
   public void checkRootProjectsOnly() {
     when(project.getQualifier()).thenReturn(Resource.QUALIFIER_FILE);
-    when(profile.getAlerts()).thenReturn(Arrays.asList(
+    when(projectAlerts.all()).thenReturn(Arrays.asList(
       new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "20"),
       new Alert(null, CoreMetrics.COVERAGE, Alert.OPERATOR_GREATER, null, "35.0")));
 
@@ -135,9 +139,9 @@ public class CheckAlertThresholdsTest {
 
   @Test
   public void shouldGenerateWarnings() {
-    when(profile.getAlerts()).thenReturn(Arrays.asList(
-        new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "100"),
-        new Alert(null, CoreMetrics.COVERAGE, Alert.OPERATOR_SMALLER, null, "95.0"))); // generates warning because coverage 35% < 95%
+    when(projectAlerts.all()).thenReturn(Arrays.asList(
+      new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "100"),
+      new Alert(null, CoreMetrics.COVERAGE, Alert.OPERATOR_SMALLER, null, "95.0"))); // generates warning because coverage 35% < 95%
 
     decorator.decorate(project, context);
 
@@ -150,9 +154,9 @@ public class CheckAlertThresholdsTest {
 
   @Test
   public void globalStatusShouldBeErrorIfWarningsAndErrors() {
-    when(profile.getAlerts()).thenReturn(Arrays.asList(
-        new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_SMALLER, null, "100"), // generates warning because classes 20 < 100
-        new Alert(null, CoreMetrics.COVERAGE, Alert.OPERATOR_SMALLER, "50.0", "80.0"))); // generates error because coverage 35% < 50%
+    when(projectAlerts.all()).thenReturn(Arrays.asList(
+      new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_SMALLER, null, "100"), // generates warning because classes 20 < 100
+      new Alert(null, CoreMetrics.COVERAGE, Alert.OPERATOR_SMALLER, "50.0", "80.0"))); // generates error because coverage 35% < 50%
 
     decorator.decorate(project, context);
 
@@ -166,9 +170,11 @@ public class CheckAlertThresholdsTest {
   public void globalLabelShouldAggregateAllLabels() {
     when(i18n.message(any(Locale.class), eq("metric.classes.name"), anyString())).thenReturn("Classes");
     when(i18n.message(any(Locale.class), eq("metric.coverage.name"), anyString())).thenReturn("Coverages");
-    when(profile.getAlerts()).thenReturn(Arrays.asList(
-        new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_SMALLER, null, "10000"), // there are 20 classes, error threshold is higher => alert
-        new Alert(null, CoreMetrics.COVERAGE, Alert.OPERATOR_SMALLER, "50.0", "80.0"))); // coverage is 35%, warning threshold is higher => alert
+    when(projectAlerts.all()).thenReturn(Arrays.asList(
+      new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_SMALLER, null, "10000"), // there are 20 classes, error threshold is higher =>
+                                                                                   // alert
+      new Alert(null, CoreMetrics.COVERAGE, Alert.OPERATOR_SMALLER, "50.0", "80.0"))); // coverage is 35%, warning threshold is higher =>
+                                                                                       // alert
 
     decorator.decorate(project, context);
 
@@ -183,7 +189,7 @@ public class CheckAlertThresholdsTest {
     when(i18n.message(any(Locale.class), eq("metric.rating.name"), anyString())).thenReturn("THE RATING");
 
     when(context.getMeasure(metric)).thenReturn(new Measure(metric, 4d));
-    when(profile.getAlerts()).thenReturn(Arrays.<Alert>asList(new Alert(null, metric, Alert.OPERATOR_SMALLER, "10", null)));
+    when(projectAlerts.all()).thenReturn(Arrays.<Alert>asList(new Alert(null, metric, Alert.OPERATOR_SMALLER, "10", null)));
     decorator.decorate(project, context);
 
     verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.ERROR, "THE RATING < 10")));
@@ -193,10 +199,10 @@ public class CheckAlertThresholdsTest {
   public void alertLabelUsesMetricNameIfMissingL10nBundle() {
     // the third argument is Metric#getName()
     when(i18n.message(any(Locale.class), eq("metric.classes.name"), eq("Classes"))).thenReturn("Classes");
-    when(profile.getAlerts()).thenReturn(Arrays.<Alert>asList(
+    when(projectAlerts.all()).thenReturn(Arrays.<Alert>asList(
       // there are 20 classes, error threshold is higher => alert
       new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_SMALLER, "10000", null)
-    ));
+      ));
 
     decorator.decorate(project, context);
 
@@ -209,11 +215,13 @@ public class CheckAlertThresholdsTest {
     measureCoverage.setVariation2(50d);
     measureComplexity.setVariation3(2d);
 
-    when(profile.getAlerts()).thenReturn(Arrays.asList(
-        new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "10", 1), // ok because no variation
-        new Alert(null, CoreMetrics.COVERAGE, Alert.OPERATOR_SMALLER, null, "40.0", 2), // ok because coverage increases of 50%, which is more than 40%
-        new Alert(null, CoreMetrics.COMPLEXITY, Alert.OPERATOR_GREATER, null, "5", 3) // ok because complexity increases of 2, which is less than 5
-    ));
+    when(projectAlerts.all()).thenReturn(Arrays.asList(
+      new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "10", 1), // ok because no variation
+      new Alert(null, CoreMetrics.COVERAGE, Alert.OPERATOR_SMALLER, null, "40.0", 2), // ok because coverage increases of 50%, which is more
+                                                                                      // than 40%
+      new Alert(null, CoreMetrics.COMPLEXITY, Alert.OPERATOR_GREATER, null, "5", 3) // ok because complexity increases of 2, which is less
+                                                                                    // than 5
+      ));
 
     decorator.decorate(project, context);
 
@@ -230,11 +238,14 @@ public class CheckAlertThresholdsTest {
     measureCoverage.setVariation2(5d);
     measureComplexity.setVariation3(70d);
 
-    when(profile.getAlerts()).thenReturn(Arrays.asList(
-        new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "30", 1),  // generates warning because classes increases of 40, which is greater than 30
-        new Alert(null, CoreMetrics.COVERAGE, Alert.OPERATOR_SMALLER, null, "10.0", 2), // generates warning because coverage increases of 5%, which is smaller than 10%
-        new Alert(null, CoreMetrics.COMPLEXITY, Alert.OPERATOR_GREATER, null, "60", 3) // generates warning because complexity increases of 70, which is smaller than 60
-    ));
+    when(projectAlerts.all()).thenReturn(Arrays.asList(
+      new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "30", 1), // generates warning because classes increases of 40,
+                                                                                   // which is greater than 30
+      new Alert(null, CoreMetrics.COVERAGE, Alert.OPERATOR_SMALLER, null, "10.0", 2), // generates warning because coverage increases of 5%,
+                                                                                      // which is smaller than 10%
+      new Alert(null, CoreMetrics.COMPLEXITY, Alert.OPERATOR_GREATER, null, "60", 3) // generates warning because complexity increases of
+                                                                                     // 70, which is smaller than 60
+      ));
 
     decorator.decorate(project, context);
 
@@ -249,9 +260,9 @@ public class CheckAlertThresholdsTest {
   public void shouldBeOkIfVariationIsNull() {
     measureClasses.setVariation1(null);
 
-    when(profile.getAlerts()).thenReturn(Arrays.asList(
-        new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "10", 1)
-    ));
+    when(projectAlerts.all()).thenReturn(Arrays.asList(
+      new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "10", 1)
+      ));
 
     decorator.decorate(project, context);
 
@@ -266,9 +277,9 @@ public class CheckAlertThresholdsTest {
     measureRatingMetric.setVariation1(50d);
     when(context.getMeasure(ratingMetric)).thenReturn(measureRatingMetric);
 
-    when(profile.getAlerts()).thenReturn(Arrays.asList(
-        new Alert(null, ratingMetric, Alert.OPERATOR_GREATER, null, "100", 1)
-    ));
+    when(projectAlerts.all()).thenReturn(Arrays.asList(
+      new Alert(null, ratingMetric, Alert.OPERATOR_GREATER, null, "100", 1)
+      ));
 
     decorator.decorate(project, context);
 
@@ -280,9 +291,9 @@ public class CheckAlertThresholdsTest {
   public void shouldAllowOnlyVariationPeriodOneGlobalPeriods() {
     measureClasses.setVariation4(40d);
 
-    when(profile.getAlerts()).thenReturn(Arrays.asList(
-        new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "30", 4)
-    ));
+    when(projectAlerts.all()).thenReturn(Arrays.asList(
+      new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "30", 4)
+      ));
 
     decorator.decorate(project, context);
   }
@@ -293,9 +304,9 @@ public class CheckAlertThresholdsTest {
     measure.setVariation1(50d);
     when(context.getMeasure(CoreMetrics.SCM_AUTHORS_BY_LINE)).thenReturn(measure);
 
-    when(profile.getAlerts()).thenReturn(Arrays.asList(
-        new Alert(null, CoreMetrics.SCM_AUTHORS_BY_LINE, Alert.OPERATOR_GREATER, null, "30", 1)
-    ));
+    when(projectAlerts.all()).thenReturn(Arrays.asList(
+      new Alert(null, CoreMetrics.SCM_AUTHORS_BY_LINE, Alert.OPERATOR_GREATER, null, "30", 1)
+      ));
 
     decorator.decorate(project, context);
   }
@@ -307,9 +318,10 @@ public class CheckAlertThresholdsTest {
     when(i18n.message(any(Locale.class), eq("metric.classes.name"), anyString())).thenReturn("Classes");
     when(periods.label(snapshot, 1)).thenReturn("since someday");
 
-    when(profile.getAlerts()).thenReturn(Arrays.asList(
-        new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "30", 1) // generates warning because classes increases of 40, which is greater than 30
-    ));
+    when(projectAlerts.all()).thenReturn(Arrays.asList(
+      new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "30", 1) // generates warning because classes increases of 40,
+                                                                                  // which is greater than 30
+      ));
 
     decorator.decorate(project, context);
 
@@ -327,9 +339,10 @@ public class CheckAlertThresholdsTest {
     when(i18n.message(any(Locale.class), eq("metric.new_metric_key.name"), anyString())).thenReturn("New Measure");
     when(periods.label(snapshot, 1)).thenReturn("since someday");
 
-    when(profile.getAlerts()).thenReturn(Arrays.asList(
-        new Alert(null, newMetric, Alert.OPERATOR_GREATER, null, "30", 1) // generates warning because classes increases of 40, which is greater than 30
-    ));
+    when(projectAlerts.all()).thenReturn(Arrays.asList(
+      new Alert(null, newMetric, Alert.OPERATOR_GREATER, null, "30", 1) // generates warning because classes increases of 40, which is
+                                                                        // greater than 30
+      ));
 
     decorator.decorate(project, context);
 
index 96d0721420d9d35ca5070e2ab4d01ea8e5c475b7..65c69dd5e9ba15274890c92471a7190396479d72 100644 (file)
@@ -26,10 +26,13 @@ import org.slf4j.LoggerFactory;
 import org.sonar.api.BatchComponent;
 import org.sonar.api.batch.ModuleLanguages;
 import org.sonar.api.config.Settings;
+import org.sonar.api.profiles.Alert;
+import org.sonar.api.profiles.RulesProfile;
 import org.sonar.api.utils.MessageException;
-import org.sonar.api.utils.SonarException;
 import org.sonar.batch.rule.ModuleQProfiles;
 import org.sonar.batch.rule.ModuleQProfiles.QProfile;
+import org.sonar.batch.rule.ProjectAlerts;
+import org.sonar.batch.rule.RulesProfileWrapper;
 
 public class ProfileLogger implements BatchComponent {
 
@@ -38,11 +41,16 @@ public class ProfileLogger implements BatchComponent {
   private final Settings settings;
   private final ModuleLanguages languages;
   private final ModuleQProfiles profiles;
+  private final ProjectAlerts projectAlerts;
 
-  public ProfileLogger(Settings settings, ModuleLanguages languages, ModuleQProfiles profiles) {
+  private final RulesProfile rulesProfile;
+
+  public ProfileLogger(Settings settings, ModuleLanguages languages, ModuleQProfiles profiles, ProjectAlerts projectAlerts, RulesProfile rulesProfile) {
     this.settings = settings;
     this.languages = languages;
     this.profiles = profiles;
+    this.projectAlerts = projectAlerts;
+    this.rulesProfile = rulesProfile;
   }
 
   public void execute() {
@@ -63,6 +71,18 @@ public class ProfileLogger implements BatchComponent {
     if (!defaultNameUsed && !languages.keys().isEmpty()) {
       throw MessageException.of("sonar.profile was set to '" + defaultName + "' but didn't match any profile for any language. Please check your configuration.");
     }
+
+    addModuleAlertsToProjectAlerts();
+  }
+
+  private void addModuleAlertsToProjectAlerts() {
+    RulesProfileWrapper profileWrapper = (RulesProfileWrapper) rulesProfile;
+    for (String lang : languages.keys()) {
+      RulesProfile profile = profileWrapper.getProfileByLanguage(lang);
+      for (Alert alert : profile.getAlerts()) {
+        projectAlerts.add(alert);
+      }
+    }
   }
 
 }
diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/ProjectAlerts.java b/sonar-batch/src/main/java/org/sonar/batch/rule/ProjectAlerts.java
new file mode 100644 (file)
index 0000000..c1d7a11
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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.batch.rule;
+
+import com.google.common.collect.Lists;
+import org.sonar.api.BatchComponent;
+import org.sonar.api.profiles.Alert;
+
+import java.util.List;
+
+/**
+ * Lists the alerts enabled on the current project.
+ */
+public class ProjectAlerts implements BatchComponent {
+
+  private final List<Alert> alerts = Lists.newArrayList();
+
+  public ProjectAlerts() {
+  }
+
+  public void add(Alert alert) {
+    alerts.add(alert);
+  }
+
+  public List<Alert> all() {
+    return alerts;
+  }
+
+}
index b564ea4ca62ad21af1b08b175523973901b9d71c..500b42bf39954d9310cc60a228f500a2a817cd68 100644 (file)
@@ -36,16 +36,39 @@ import org.sonar.batch.DefaultFileLinesContextFactory;
 import org.sonar.batch.DefaultResourceCreationLock;
 import org.sonar.batch.ProjectConfigurator;
 import org.sonar.batch.ProjectTree;
-import org.sonar.batch.bootstrap.*;
+import org.sonar.batch.bootstrap.BootstrapSettings;
+import org.sonar.batch.bootstrap.ExtensionInstaller;
+import org.sonar.batch.bootstrap.ExtensionMatcher;
+import org.sonar.batch.bootstrap.ExtensionUtils;
+import org.sonar.batch.bootstrap.MetricProvider;
 import org.sonar.batch.components.PeriodsDefinition;
 import org.sonar.batch.debt.DebtModelLoader;
 import org.sonar.batch.debt.DebtModelProvider;
 import org.sonar.batch.debt.IssueChangelogDebtCalculator;
 import org.sonar.batch.debt.RuleDebtCalculator;
-import org.sonar.batch.index.*;
-import org.sonar.batch.issue.*;
+import org.sonar.batch.index.Caches;
+import org.sonar.batch.index.ComponentDataCache;
+import org.sonar.batch.index.ComponentDataPersister;
+import org.sonar.batch.index.DefaultIndex;
+import org.sonar.batch.index.DefaultPersistenceManager;
+import org.sonar.batch.index.DefaultResourcePersister;
+import org.sonar.batch.index.DependencyPersister;
+import org.sonar.batch.index.EventPersister;
+import org.sonar.batch.index.LinkPersister;
+import org.sonar.batch.index.MeasurePersister;
+import org.sonar.batch.index.MemoryOptimizer;
+import org.sonar.batch.index.ResourceCache;
+import org.sonar.batch.index.ResourceKeyMigration;
+import org.sonar.batch.index.SnapshotCache;
+import org.sonar.batch.index.SourcePersister;
+import org.sonar.batch.issue.DefaultProjectIssues;
+import org.sonar.batch.issue.DeprecatedViolations;
+import org.sonar.batch.issue.IssueCache;
+import org.sonar.batch.issue.IssuePersister;
+import org.sonar.batch.issue.ScanIssueStorage;
 import org.sonar.batch.phases.GraphPersister;
 import org.sonar.batch.profiling.PhasesSumUpTimeProfiler;
+import org.sonar.batch.rule.ProjectAlerts;
 import org.sonar.batch.scan.filesystem.InputFileCache;
 import org.sonar.batch.scan.maven.FakeMavenPluginExecutor;
 import org.sonar.batch.scan.maven.MavenPluginExecutor;
@@ -164,7 +187,9 @@ public class ProjectScanContainer extends ComponentContainer {
       // Differential periods
       PeriodsDefinition.class,
 
-      ProjectSettingsReady.class);
+      ProjectSettingsReady.class,
+
+      ProjectAlerts.class);
   }
 
   private void fixMavenExecutor() {
index 68d1cde8c4a8c472da843a0dca99dcd80cc75bd5..deb1096215bb015ba7af5e8d5d1e1ab8206c95a6 100644 (file)
@@ -27,22 +27,34 @@ import org.junit.rules.ExpectedException;
 import org.slf4j.Logger;
 import org.sonar.api.batch.ModuleLanguages;
 import org.sonar.api.config.Settings;
+import org.sonar.api.profiles.Alert;
+import org.sonar.api.profiles.RulesProfile;
 import org.sonar.api.utils.MessageException;
 import org.sonar.batch.rule.ModuleQProfiles;
 import org.sonar.batch.rule.ModuleQProfiles.QProfile;
+import org.sonar.batch.rule.ProjectAlerts;
+import org.sonar.batch.rule.RulesProfileWrapper;
 
+import java.util.Arrays;
 import java.util.Collections;
 
-import static org.mockito.Mockito.*;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 public class ProfileLoggerTest {
 
   @Rule
   public ExpectedException thrown = ExpectedException.none();
 
-  ModuleLanguages languages;
-  ModuleQProfiles profiles;
-  Settings settings = new Settings();
+  private ModuleLanguages languages;
+  private ModuleQProfiles profiles;
+  private Settings settings = new Settings();
+  private ProjectAlerts projectAlerts = new ProjectAlerts();
+  private RulesProfileWrapper rulesProfile = mock(RulesProfileWrapper.class);
+  private RulesProfile javaRulesProfile;
+  private RulesProfile cobolRulesProfile;
 
   @Before
   public void before() {
@@ -52,15 +64,19 @@ public class ProfileLoggerTest {
     profiles = mock(ModuleQProfiles.class);
     QProfile javaProfile = mock(QProfile.class);
     when(javaProfile.name()).thenReturn("My Java profile");
+    javaRulesProfile = mock(RulesProfile.class);
+    when(rulesProfile.getProfileByLanguage("java")).thenReturn(javaRulesProfile);
     when(profiles.findByLanguage("java")).thenReturn(javaProfile);
     QProfile cobolProfile = mock(QProfile.class);
     when(cobolProfile.name()).thenReturn("My Cobol profile");
+    cobolRulesProfile = mock(RulesProfile.class);
+    when(rulesProfile.getProfileByLanguage("cobol")).thenReturn(cobolRulesProfile);
     when(profiles.findByLanguage("cobol")).thenReturn(cobolProfile);
   }
 
   @Test
   public void should_log_all_used_profiles() {
-    ProfileLogger profileLogger = new ProfileLogger(settings, languages, profiles);
+    ProfileLogger profileLogger = new ProfileLogger(settings, languages, profiles, projectAlerts, rulesProfile);
     Logger logger = mock(Logger.class);
     profileLogger.execute(logger);
 
@@ -72,7 +88,7 @@ public class ProfileLoggerTest {
   public void should_fail_if_default_profile_not_used() {
     settings.setProperty("sonar.profile", "Unknown");
 
-    ProfileLogger profileLogger = new ProfileLogger(settings, languages, profiles);
+    ProfileLogger profileLogger = new ProfileLogger(settings, languages, profiles, projectAlerts, rulesProfile);
 
     thrown.expect(MessageException.class);
     thrown.expectMessage("sonar.profile was set to 'Unknown' but didn't match any profile for any language. Please check your configuration.");
@@ -85,7 +101,7 @@ public class ProfileLoggerTest {
     settings.setProperty("sonar.profile", "Unknown");
     when(languages.keys()).thenReturn(Collections.<String>emptyList());
 
-    ProfileLogger profileLogger = new ProfileLogger(settings, languages, profiles);
+    ProfileLogger profileLogger = new ProfileLogger(settings, languages, profiles, projectAlerts, rulesProfile);
 
     profileLogger.execute();
 
@@ -95,8 +111,24 @@ public class ProfileLoggerTest {
   public void should_not_fail_if_default_profile_used_at_least_once() {
     settings.setProperty("sonar.profile", "My Java profile");
 
-    ProfileLogger profileLogger = new ProfileLogger(settings, languages, profiles);
+    ProfileLogger profileLogger = new ProfileLogger(settings, languages, profiles, projectAlerts, rulesProfile);
 
     profileLogger.execute();
   }
+
+  @Test
+  public void should_collect_alerts() {
+    Alert javaAlert1 = new Alert();
+    Alert javaAlert2 = new Alert();
+    Alert cobolAlert1 = new Alert();
+    Alert cobolAlert2 = new Alert();
+    when(javaRulesProfile.getAlerts()).thenReturn(Arrays.asList(javaAlert1, javaAlert2));
+    when(cobolRulesProfile.getAlerts()).thenReturn(Arrays.asList(cobolAlert1, cobolAlert2));
+
+    ProfileLogger profileLogger = new ProfileLogger(settings, languages, profiles, projectAlerts, rulesProfile);
+
+    profileLogger.execute();
+
+    assertThat(projectAlerts.all()).containsExactly(javaAlert1, javaAlert2, cobolAlert1, cobolAlert2);
+  }
 }