From 40df046f0c54e77eb3e40d5e9dd5791337a5db89 Mon Sep 17 00:00:00 2001 From: Julien HENRY Date: Thu, 20 Feb 2014 11:23:55 +0100 Subject: [PATCH] SONAR-926 Fix alerts reporting for multi-language projects --- .../core/sensors/CheckAlertThresholds.java | 33 +++-- .../sensors/CheckAlertThresholdsTest.java | 123 ++++++++++-------- .../org/sonar/batch/phases/ProfileLogger.java | 24 +++- .../org/sonar/batch/rule/ProjectAlerts.java | 46 +++++++ .../batch/scan/ProjectScanContainer.java | 33 ++++- .../sonar/batch/phases/ProfileLoggerTest.java | 48 +++++-- 6 files changed, 221 insertions(+), 86 deletions(-) create mode 100644 sonar-batch/src/main/java/org/sonar/batch/rule/ProjectAlerts.java diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CheckAlertThresholds.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CheckAlertThresholds.java index 8ca7d3d0731..1580d14f1ed 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CheckAlertThresholds.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CheckAlertThresholds.java @@ -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 dependsUponMetrics() { List 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 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(); diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/CheckAlertThresholdsTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/CheckAlertThresholdsTest.java index 46e43b20a41..6154147a1ee 100644 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/CheckAlertThresholdsTest.java +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/CheckAlertThresholdsTest.java @@ -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()); + when(projectAlerts.all()).thenReturn(new ArrayList()); 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.asList(new Alert(null, metric, Alert.OPERATOR_SMALLER, "10", null))); + when(projectAlerts.all()).thenReturn(Arrays.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.asList( + when(projectAlerts.all()).thenReturn(Arrays.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); diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/ProfileLogger.java b/sonar-batch/src/main/java/org/sonar/batch/phases/ProfileLogger.java index 96d0721420d..65c69dd5e9b 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/phases/ProfileLogger.java +++ b/sonar-batch/src/main/java/org/sonar/batch/phases/ProfileLogger.java @@ -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 index 00000000000..c1d7a116a19 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/rule/ProjectAlerts.java @@ -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 alerts = Lists.newArrayList(); + + public ProjectAlerts() { + } + + public void add(Alert alert) { + alerts.add(alert); + } + + public List all() { + return alerts; + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java index b564ea4ca62..500b42bf399 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java @@ -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() { diff --git a/sonar-batch/src/test/java/org/sonar/batch/phases/ProfileLoggerTest.java b/sonar-batch/src/test/java/org/sonar/batch/phases/ProfileLoggerTest.java index 68d1cde8c4a..deb1096215b 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/phases/ProfileLoggerTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/phases/ProfileLoggerTest.java @@ -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.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); + } } -- 2.39.5