*/
package org.sonar.plugins.core;
-import org.sonar.batch.issue.ignore.IssueExclusionsConfiguration;
-
import com.google.common.collect.ImmutableList;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.Properties;
-import org.sonar.api.Property;
-import org.sonar.api.PropertyType;
-import org.sonar.api.SonarPlugin;
+import org.sonar.api.*;
import org.sonar.api.checks.NoSonarFilter;
import org.sonar.api.config.PropertyDefinition;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.utils.WorkDurationFactory;
import org.sonar.batch.components.PastSnapshotFinder;
import org.sonar.batch.debt.IssueChangelogDebtCalculator;
+import org.sonar.batch.issue.ignore.IssueExclusionsConfiguration;
import org.sonar.core.timemachine.Periods;
import org.sonar.plugins.core.batch.IndexProjectPostJob;
import org.sonar.plugins.core.charts.DistributionAreaChart;
import org.sonar.plugins.core.charts.DistributionBarChart;
import org.sonar.plugins.core.charts.XradarChart;
import org.sonar.plugins.core.colorizers.JavaColorizerFormat;
-import org.sonar.plugins.core.dashboards.GlobalDefaultDashboard;
-import org.sonar.plugins.core.dashboards.ProjectDefaultDashboard;
-import org.sonar.plugins.core.dashboards.ProjectHotspotDashboard;
-import org.sonar.plugins.core.dashboards.ProjectIssuesDashboard;
-import org.sonar.plugins.core.dashboards.ProjectTimeMachineDashboard;
-import org.sonar.plugins.core.issue.CountFalsePositivesDecorator;
-import org.sonar.plugins.core.issue.CountUnresolvedIssuesDecorator;
-import org.sonar.plugins.core.issue.InitialOpenIssuesSensor;
-import org.sonar.plugins.core.issue.InitialOpenIssuesStack;
-import org.sonar.plugins.core.issue.IssueHandlers;
-import org.sonar.plugins.core.issue.IssueTracking;
-import org.sonar.plugins.core.issue.IssueTrackingDecorator;
-import org.sonar.plugins.core.issue.IssuesDensityDecorator;
-import org.sonar.plugins.core.issue.WeightedIssuesDecorator;
-import org.sonar.plugins.core.issue.notification.ChangesOnMyIssueNotificationDispatcher;
-import org.sonar.plugins.core.issue.notification.IssueChangesEmailTemplate;
-import org.sonar.plugins.core.issue.notification.NewFalsePositiveNotificationDispatcher;
-import org.sonar.plugins.core.issue.notification.NewIssuesEmailTemplate;
-import org.sonar.plugins.core.issue.notification.NewIssuesNotificationDispatcher;
-import org.sonar.plugins.core.issue.notification.SendIssueNotificationsPostJob;
+import org.sonar.plugins.core.dashboards.*;
+import org.sonar.plugins.core.issue.*;
+import org.sonar.plugins.core.issue.notification.*;
import org.sonar.plugins.core.measurefilters.MyFavouritesFilter;
import org.sonar.plugins.core.measurefilters.ProjectFilter;
import org.sonar.plugins.core.notifications.alerts.NewAlerts;
import org.sonar.plugins.core.security.ApplyProjectRolesDecorator;
-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.FileHashSensor;
-import org.sonar.plugins.core.sensors.FilesDecorator;
-import org.sonar.plugins.core.sensors.GenerateAlertEvents;
-import org.sonar.plugins.core.sensors.ItBranchCoverageDecorator;
-import org.sonar.plugins.core.sensors.ItCoverageDecorator;
-import org.sonar.plugins.core.sensors.ItLineCoverageDecorator;
-import org.sonar.plugins.core.sensors.LineCoverageDecorator;
-import org.sonar.plugins.core.sensors.ManualMeasureDecorator;
-import org.sonar.plugins.core.sensors.OverallBranchCoverageDecorator;
-import org.sonar.plugins.core.sensors.OverallCoverageDecorator;
-import org.sonar.plugins.core.sensors.OverallLineCoverageDecorator;
-import org.sonar.plugins.core.sensors.ProfileEventsSensor;
-import org.sonar.plugins.core.sensors.ProjectLinksSensor;
-import org.sonar.plugins.core.sensors.UnitTestDecorator;
-import org.sonar.plugins.core.sensors.VersionEventsSensor;
+import org.sonar.plugins.core.sensors.*;
import org.sonar.plugins.core.technicaldebt.NewTechnicalDebtDecorator;
import org.sonar.plugins.core.technicaldebt.TechnicalDebtDecorator;
-import org.sonar.plugins.core.timemachine.NewCoverageAggregator;
-import org.sonar.plugins.core.timemachine.NewCoverageFileAnalyzer;
-import org.sonar.plugins.core.timemachine.NewItCoverageFileAnalyzer;
-import org.sonar.plugins.core.timemachine.NewOverallCoverageFileAnalyzer;
-import org.sonar.plugins.core.timemachine.TendencyDecorator;
-import org.sonar.plugins.core.timemachine.TimeMachineConfigurationPersister;
-import org.sonar.plugins.core.timemachine.VariationDecorator;
+import org.sonar.plugins.core.timemachine.*;
import org.sonar.plugins.core.web.TestsViewer;
-import org.sonar.plugins.core.widgets.AlertsWidget;
-import org.sonar.plugins.core.widgets.BubbleChartWidget;
-import org.sonar.plugins.core.widgets.ComplexityWidget;
-import org.sonar.plugins.core.widgets.CoverageWidget;
-import org.sonar.plugins.core.widgets.CustomMeasuresWidget;
-import org.sonar.plugins.core.widgets.DescriptionWidget;
-import org.sonar.plugins.core.widgets.DocumentationCommentsWidget;
-import org.sonar.plugins.core.widgets.DuplicationsWidget;
-import org.sonar.plugins.core.widgets.EventsWidget;
-import org.sonar.plugins.core.widgets.HotspotMetricWidget;
-import org.sonar.plugins.core.widgets.HotspotMostViolatedResourcesWidget;
-import org.sonar.plugins.core.widgets.HotspotMostViolatedRulesWidget;
-import org.sonar.plugins.core.widgets.ItCoverageWidget;
-import org.sonar.plugins.core.widgets.SizeWidget;
-import org.sonar.plugins.core.widgets.TechnicalDebtPyramidWidget;
-import org.sonar.plugins.core.widgets.TimeMachineWidget;
-import org.sonar.plugins.core.widgets.TimelineWidget;
-import org.sonar.plugins.core.widgets.TreemapWidget;
-import org.sonar.plugins.core.widgets.WelcomeWidget;
-import org.sonar.plugins.core.widgets.issues.ActionPlansWidget;
-import org.sonar.plugins.core.widgets.issues.FalsePositiveIssuesWidget;
-import org.sonar.plugins.core.widgets.issues.IssueFilterWidget;
-import org.sonar.plugins.core.widgets.issues.IssuesWidget;
-import org.sonar.plugins.core.widgets.issues.MyUnresolvedIssuesWidget;
-import org.sonar.plugins.core.widgets.issues.UnresolvedIssuesPerAssigneeWidget;
-import org.sonar.plugins.core.widgets.issues.UnresolvedIssuesStatusesWidget;
-import org.sonar.plugins.core.widgets.measures.MeasureFilterAsBubbleChartWidget;
-import org.sonar.plugins.core.widgets.measures.MeasureFilterAsHistogramWidget;
-import org.sonar.plugins.core.widgets.measures.MeasureFilterAsPieChartWidget;
-import org.sonar.plugins.core.widgets.measures.MeasureFilterListWidget;
-import org.sonar.plugins.core.widgets.measures.MeasureFilterTreemapWidget;
+import org.sonar.plugins.core.widgets.*;
+import org.sonar.plugins.core.widgets.issues.*;
+import org.sonar.plugins.core.widgets.measures.*;
import java.util.Arrays;
import java.util.List;
ProjectLinksSensor.class,
UnitTestDecorator.class,
VersionEventsSensor.class,
- CheckAlertThresholds.class,
GenerateAlertEvents.class,
LineCoverageDecorator.class,
CoverageDecorator.class,
+++ /dev/null
-/*
- * 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.apache.commons.lang.NotImplementedException;
-import org.apache.commons.lang.StringUtils;
-import org.sonar.api.measures.Measure;
-import org.sonar.api.measures.Metric;
-import org.sonar.api.profiles.Alert;
-
-public final class AlertUtils {
-
- private AlertUtils() {
- }
-
- /**
- * Get the matching alert level for the given measure
- */
- public static Metric.Level getLevel(Alert alert, Measure measure) {
- if (evaluateAlert(alert, measure, Metric.Level.ERROR)) {
- return Metric.Level.ERROR;
- }
- if (evaluateAlert(alert, measure, Metric.Level.WARN)) {
- return Metric.Level.WARN;
- }
- return Metric.Level.OK;
- }
-
- private static boolean evaluateAlert(Alert alert, Measure measure, Metric.Level alertLevel) {
- String valueToEval = getValueToEval(alert, alertLevel);
- if (StringUtils.isEmpty(valueToEval)) {
- return false;
- }
-
- Comparable criteriaValue = getValueForComparison(alert.getMetric(), valueToEval);
- Comparable measureValue = getMeasureValue(alert, measure);
- if (measureValue != null) {
- return doesReachThresholds(measureValue, criteriaValue, alert);
- }
- return false;
- }
-
- private static boolean doesReachThresholds(Comparable measureValue, Comparable criteriaValue, Alert alert) {
- int comparison = measureValue.compareTo(criteriaValue);
- return !(isNotEquals(comparison, alert)
- || isGreater(comparison, alert)
- || isSmaller(comparison, alert)
- || isEquals(comparison, alert));
- }
-
- private static boolean isNotEquals(int comparison, Alert alert) {
- return alert.isNotEqualsOperator() && comparison == 0;
- }
-
- private static boolean isGreater(int comparison, Alert alert) {
- return alert.isGreaterOperator() && comparison != 1;
- }
-
- private static boolean isSmaller(int comparison, Alert alert) {
- return alert.isSmallerOperator() && comparison != -1;
- }
-
- private static boolean isEquals(int comparison, Alert alert) {
- return alert.isEqualsOperator() && comparison != 0;
- }
-
- private static String getValueToEval(Alert alert, Metric.Level alertLevel) {
- if (alertLevel.equals(Metric.Level.ERROR)) {
- return alert.getValueError();
- } else if (alertLevel.equals(Metric.Level.WARN)) {
- return alert.getValueWarning();
- } else {
- throw new IllegalStateException(alertLevel.toString());
- }
- }
-
- private static Comparable getValueForComparison(Metric metric, String value) {
- if (isADouble(metric)) {
- return Double.parseDouble(value);
- }
- if (isAInteger(metric)) {
- return parseInteger(value);
- }
- if (isAString(metric)) {
- return value;
- }
- if (isABoolean(metric)) {
- return Integer.parseInt(value);
- }
- throw new NotImplementedException(metric.getType().toString());
- }
-
- private static Comparable<Integer> parseInteger(String value) {
- return value.contains(".") ? Integer.parseInt(value.substring(0, value.indexOf('.'))) : Integer.parseInt(value);
- }
-
- private static Comparable getMeasureValue(Alert alert, Measure measure) {
- Metric metric = alert.getMetric();
- if (isADouble(metric)) {
- return getValue(alert, measure);
- }
- if (isAInteger(metric)) {
- return parseInteger(alert, measure);
- }
- if (alert.getPeriod() == null) {
- return getMeasureValueForStringOrBoolean(metric, measure);
- }
- throw new NotImplementedException(metric.getType().toString());
- }
-
- private static Comparable getMeasureValueForStringOrBoolean(Metric metric, Measure measure) {
- if (isAString(metric)) {
- return measure.getData();
- }
- if (isABoolean(metric)) {
- return measure.getValue().intValue();
- }
- throw new NotImplementedException(metric.getType().toString());
- }
-
- private static Comparable<Integer> parseInteger(Alert alert, Measure measure) {
- Double value = getValue(alert, measure);
- return value != null ? value.intValue() : null;
- }
-
- private static boolean isADouble(Metric metric) {
- return metric.getType() == Metric.ValueType.FLOAT ||
- metric.getType() == Metric.ValueType.PERCENT ||
- metric.getType() == Metric.ValueType.RATING;
- }
-
- private static boolean isAInteger(Metric metric) {
- return metric.getType() == Metric.ValueType.INT ||
- metric.getType() == Metric.ValueType.MILLISEC;
- }
-
- private static boolean isAString(Metric metric) {
- return metric.getType() == Metric.ValueType.STRING ||
- metric.getType() == Metric.ValueType.LEVEL;
- }
-
- private static boolean isABoolean(Metric metric) {
- return metric.getType() == Metric.ValueType.BOOL;
- }
-
- private static Double getValue(Alert alert, Measure measure) {
- if (alert.getPeriod() == null) {
- return measure.getValue();
- } else if (alert.getPeriod() == 1) {
- return measure.getVariation1();
- } else if (alert.getPeriod() == 2) {
- return measure.getVariation2();
- } else if (alert.getPeriod() == 3) {
- return measure.getVariation3();
- } else {
- throw new IllegalStateException("Following index period is not allowed : " + Double.toString(alert.getPeriod()));
- }
- }
-}
+++ /dev/null
-/*
- * 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.collect.Lists;
-import org.apache.commons.lang.StringUtils;
-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.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;
-import java.util.Locale;
-
-public class CheckAlertThresholds implements Decorator {
-
- private static final String VARIATION_METRIC_PREFIX = "new_";
- private static final String VARIATION = "variation";
-
- private final Snapshot snapshot;
- private final Periods periods;
- private final I18n i18n;
- private ProjectAlerts projectAlerts;
-
- public CheckAlertThresholds(Snapshot snapshot, ProjectAlerts projectAlerts, Periods periods, I18n i18n) {
- this.snapshot = snapshot;
- this.projectAlerts = projectAlerts;
- this.periods = periods;
- this.i18n = i18n;
- }
-
- @DependedUpon
- public Metric generatesAlertStatus() {
- return CoreMetrics.ALERT_STATUS;
- }
-
- @DependsUpon
- public String dependsOnVariations() {
- return DecoratorBarriers.END_OF_TIME_MACHINE;
- }
-
- @DependsUpon
- public List<Metric> dependsUponMetrics() {
- List<Metric> metrics = Lists.newLinkedList();
- for (Alert alert : projectAlerts.all()) {
- metrics.add(alert.getMetric());
- }
- return metrics;
- }
-
- public boolean shouldExecuteOnProject(Project project) {
- return !projectAlerts.all().isEmpty()
- && ResourceUtils.isRootProject(project);
- }
-
- public void decorate(final Resource resource, final DecoratorContext context) {
- if (shouldDecorateResource(resource)) {
- decorateResource(context);
- }
- }
-
- private void decorateResource(DecoratorContext context) {
- Metric.Level globalLevel = Metric.Level.OK;
- List<String> labels = Lists.newArrayList();
-
- for (final Alert alert : projectAlerts.all()) {
- Measure measure = context.getMeasure(alert.getMetric());
- if (measure != null) {
- Metric.Level level = AlertUtils.getLevel(alert, measure);
-
- measure.setAlertStatus(level);
- String text = getText(alert, level);
- if (!StringUtils.isBlank(text)) {
- measure.setAlertText(text);
- labels.add(text);
- }
-
- context.saveMeasure(measure);
-
- if (Metric.Level.WARN == level && globalLevel != Metric.Level.ERROR) {
- globalLevel = Metric.Level.WARN;
-
- } else if (Metric.Level.ERROR == level) {
- globalLevel = Metric.Level.ERROR;
- }
- }
- }
-
- Measure globalMeasure = new Measure(CoreMetrics.ALERT_STATUS, globalLevel);
- globalMeasure.setAlertStatus(globalLevel);
- globalMeasure.setAlertText(StringUtils.join(labels, ", "));
- context.saveMeasure(globalMeasure);
- }
-
- private boolean shouldDecorateResource(final Resource resource) {
- return ResourceUtils.isRootProject(resource);
- }
-
- private String getText(Alert alert, Metric.Level level) {
- if (level == Metric.Level.OK) {
- return null;
- }
- return getAlertLabel(alert, level);
- }
-
- private String getAlertLabel(Alert alert, Metric.Level level) {
- Integer alertPeriod = alert.getPeriod();
- String metric = i18n.message(getLocale(), "metric." + alert.getMetric().getKey() + ".name", alert.getMetric().getName());
-
- StringBuilder stringBuilder = new StringBuilder();
- stringBuilder.append(metric);
-
- if (alertPeriod != null && !alert.getMetric().getKey().startsWith(VARIATION_METRIC_PREFIX)) {
- String variation = i18n.message(getLocale(), VARIATION, VARIATION).toLowerCase();
- stringBuilder.append(" ").append(variation);
- }
-
- stringBuilder
- .append(" ").append(alert.getOperator()).append(" ")
- .append(level.equals(Metric.Level.ERROR) ? alert.getValueError() : alert.getValueWarning());
-
- if (alertPeriod != null) {
- stringBuilder.append(" ").append(periods.label(snapshot, alertPeriod));
- }
-
- return stringBuilder.toString();
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName();
- }
-
- private Locale getLocale() {
- return Locale.ENGLISH;
- }
-
-}
+++ /dev/null
-/*
- * 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.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.sonar.api.measures.Measure;
-import org.sonar.api.measures.Metric;
-import org.sonar.api.profiles.Alert;
-
-public class AlertUtilsTest {
- private Metric metric;
- private Measure measure;
- private Metric metric2;
- private Measure measure2;
- private Alert alert;
-
- @Before
- public void setup() {
- metric = new Metric.Builder("test-metric", "name", Metric.ValueType.FLOAT).create();
- measure = new Measure();
- measure.setMetric(metric);
-
- metric2 = new Metric.Builder("test-metric2", "name2", Metric.ValueType.FLOAT).create();
- measure2 = new Measure();
- measure2.setMetric(metric2);
-
- alert = new Alert();
- }
-
- @Test
- public void testInputNumbers() {
- metric.setType(Metric.ValueType.FLOAT);
- measure.setValue(10.2d);
- alert.setOperator(Alert.OPERATOR_SMALLER);
- alert.setMetric(metric);
-
- try {
- metric.setType(Metric.ValueType.FLOAT);
- alert.setValueError("20");
- AlertUtils.getLevel(alert, measure);
- } catch (NumberFormatException ex) {
- Assert.fail();
- }
-
- try {
- metric.setType(Metric.ValueType.INT);
- alert.setValueError("20.1");
- AlertUtils.getLevel(alert, measure);
- } catch (NumberFormatException ex) {
- Assert.fail();
- }
-
- try {
- metric.setType(Metric.ValueType.PERCENT);
- alert.setValueError("20.1");
- AlertUtils.getLevel(alert, measure);
- } catch (NumberFormatException ex) {
- Assert.fail();
- }
- }
-
- @Test
- public void testEquals() {
-
- metric.setType(Metric.ValueType.FLOAT);
- measure.setValue(10.2d);
- alert.setOperator(Alert.OPERATOR_EQUALS);
- alert.setMetric(metric);
-
- alert.setValueError("10.2");
- Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
-
- alert.setValueError("10.1");
- Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
-
- metric.setType(Metric.ValueType.STRING);
- measure.setData("TEST");
- measure.setValue(null);
-
- alert.setValueError("TEST");
- Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
-
- alert.setValueError("TEST2");
- Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
-
- }
-
- @Test
- public void testNotEquals() {
-
- metric.setType(Metric.ValueType.FLOAT);
- measure.setValue(10.2d);
- alert.setOperator(Alert.OPERATOR_NOT_EQUALS);
- alert.setMetric(metric);
-
- alert.setValueError("10.2");
- Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
-
- alert.setValueError("10.1");
- Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
-
- metric.setType(Metric.ValueType.STRING);
- measure.setData("TEST");
- measure.setValue(null);
-
- alert.setValueError("TEST");
- Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
-
- alert.setValueError("TEST2");
- Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
-
- }
-
- @Test
- public void testGreater() {
- metric.setType(Metric.ValueType.FLOAT);
- measure.setValue(10.2d);
- alert.setOperator(Alert.OPERATOR_GREATER);
- alert.setMetric(metric);
-
- alert.setValueError("10.1");
- Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
-
- alert.setValueError("10.3");
- Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
- }
-
- @Test
- public void testSmaller() {
- metric.setType(Metric.ValueType.FLOAT);
- measure.setValue(10.2d);
- alert.setOperator(Alert.OPERATOR_SMALLER);
- alert.setMetric(metric);
-
- alert.setValueError("10.1");
- Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
-
- alert.setValueError("10.3");
- Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
- }
-
- @Test
- public void testPercent() {
- metric.setType(Metric.ValueType.PERCENT);
- measure.setValue(10.2d);
- alert.setOperator(Alert.OPERATOR_EQUALS);
- alert.setMetric(metric);
-
- alert.setValueError("10.2");
- Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
- }
-
- @Test
- public void testFloat() {
- metric.setType(Metric.ValueType.FLOAT);
- measure.setValue(10.2d);
- alert.setOperator(Alert.OPERATOR_EQUALS);
- alert.setMetric(metric);
-
- alert.setValueError("10.2");
- Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
- }
-
- @Test
- public void testInteger() {
- metric.setType(Metric.ValueType.INT);
- measure.setValue(10.2d);
- alert.setOperator(Alert.OPERATOR_EQUALS);
- alert.setMetric(metric);
-
- alert.setValueError("10");
- Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
-
- alert.setValueError("10.2");
- Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
- }
-
- @Test
- public void testLevel() {
- metric.setType(Metric.ValueType.LEVEL);
- measure.setData(Metric.Level.ERROR.toString());
- alert.setOperator(Alert.OPERATOR_EQUALS);
- alert.setMetric(metric);
-
- alert.setValueError(Metric.Level.ERROR.toString());
- Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
-
- alert.setValueError(Metric.Level.OK.toString());
- Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
-
- alert.setOperator(Alert.OPERATOR_NOT_EQUALS);
- Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
- }
-
- @Test
- public void testBooleans() {
- metric.setType(Metric.ValueType.BOOL);
- measure.setValue(0d);
- alert.setOperator(Alert.OPERATOR_EQUALS);
- alert.setMetric(metric);
-
- alert.setValueError("1");
- Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
-
- alert.setValueError("0");
- Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
-
- alert.setOperator(Alert.OPERATOR_NOT_EQUALS);
- alert.setValueError("1");
- Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
-
- alert.setValueError("0");
- Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
- }
-
- @Test
- public void testErrorAndWarningLevel() {
-
- metric.setType(Metric.ValueType.FLOAT);
- measure.setValue(10.2d);
- alert.setOperator(Alert.OPERATOR_EQUALS);
- alert.setMetric(metric);
-
- alert.setValueError("10.2");
- Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
-
- alert.setValueError("10.1");
- Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
-
- alert.setValueError("10.3");
- alert.setValueWarning("10.2");
- Assert.assertEquals(Metric.Level.WARN, AlertUtils.getLevel(alert, measure));
- }
-}
+++ /dev/null
-/*
- * 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.apache.commons.lang.NotImplementedException;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentMatcher;
-import org.sonar.api.batch.DecoratorBarriers;
-import org.sonar.api.batch.DecoratorContext;
-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.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;
-import java.util.Arrays;
-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.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 ProjectAlerts projectAlerts;
-
- private Measure measureClasses;
- private Measure measureCoverage;
- private Measure measureComplexity;
-
- private Resource project;
- private Snapshot snapshot;
- private Periods periods;
- private I18n i18n;
-
- @Before
- public void setup() {
- context = mock(DecoratorContext.class);
- periods = mock(Periods.class);
- i18n = mock(I18n.class);
- when(i18n.message(any(Locale.class), eq("variation"), eq("variation"))).thenReturn("variation");
-
- measureClasses = new Measure(CoreMetrics.CLASSES, 20d);
- measureCoverage = new Measure(CoreMetrics.COVERAGE, 35d);
- measureComplexity = new Measure(CoreMetrics.COMPLEXITY, 50d);
-
- when(context.getMeasure(CoreMetrics.CLASSES)).thenReturn(measureClasses);
- when(context.getMeasure(CoreMetrics.COVERAGE)).thenReturn(measureCoverage);
- when(context.getMeasure(CoreMetrics.COMPLEXITY)).thenReturn(measureComplexity);
-
- snapshot = mock(Snapshot.class);
- 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() {
- assertThat(decorator.generatesAlertStatus()).isEqualTo(CoreMetrics.ALERT_STATUS);
- }
-
- @Test
- public void should_depends_on_variations() {
- assertThat(decorator.dependsOnVariations()).isEqualTo(DecoratorBarriers.END_OF_TIME_MACHINE);
- }
-
- @Test
- 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(projectAlerts.all()).thenReturn(new ArrayList<Alert>());
- assertThat(decorator.shouldExecuteOnProject(new Project("key"))).isFalse();
- }
-
- @Test
- public void shouldBeOkWhenNoAlert() {
- 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);
-
- verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.ALERT_STATUS, Metric.Level.OK.toString())));
- verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.OK)));
- verify(context).saveMeasure(argThat(hasLevel(measureCoverage, Metric.Level.OK)));
- }
-
- @Test
- public void checkRootProjectsOnly() {
- when(project.getQualifier()).thenReturn(Resource.QUALIFIER_FILE);
- 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);
-
- verify(context, never()).saveMeasure(any(Measure.class));
- }
-
- @Test
- public void shouldGenerateWarnings() {
- 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);
-
- verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.WARN, null)));
-
- verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.OK)));
- verify(context).saveMeasure(argThat(hasLevel(measureCoverage, Metric.Level.WARN)));
-
- }
-
- @Test
- public void globalStatusShouldBeErrorIfWarningsAndErrors() {
- 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);
-
- verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.ERROR, null)));
-
- verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.WARN)));
- verify(context).saveMeasure(argThat(hasLevel(measureCoverage, Metric.Level.ERROR)));
- }
-
- @Test
- 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(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);
-
- verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.ERROR, "Classes < 10000, Coverages < 50.0")));
- }
-
- @Test
- public void alertLabelUsesL10nMetricName() {
- Metric metric = new Metric.Builder("rating", "Rating", Metric.ValueType.INT).create();
-
- // metric name is declared in l10n bundle
- when(i18n.message(any(Locale.class), eq("metric.rating.name"), anyString())).thenReturn("THE RATING");
-
- when(context.getMeasure(metric)).thenReturn(new Measure(metric, 4d));
- 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")));
- }
-
- @Test
- public void alertLabelUsesMetricNameIfMissingL10nBundle() {
- // the third argument is Metric#getName()
- when(i18n.message(any(Locale.class), eq("metric.classes.name"), eq("Classes"))).thenReturn("Classes");
- 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);
-
- verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.ERROR, "Classes < 10000")));
- }
-
- @Test
- public void shouldBeOkIfPeriodVariationIsEnough() {
- measureClasses.setVariation1(0d);
- measureCoverage.setVariation2(50d);
- measureComplexity.setVariation3(2d);
-
- 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);
-
- verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.OK, null)));
-
- verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.OK)));
- verify(context).saveMeasure(argThat(hasLevel(measureCoverage, Metric.Level.OK)));
- verify(context).saveMeasure(argThat(hasLevel(measureComplexity, Metric.Level.OK)));
- }
-
- @Test
- public void shouldGenerateWarningIfPeriodVariationIsNotEnough() {
- measureClasses.setVariation1(40d);
- measureCoverage.setVariation2(5d);
- measureComplexity.setVariation3(70d);
-
- 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);
-
- verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.WARN, null)));
-
- verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.WARN)));
- verify(context).saveMeasure(argThat(hasLevel(measureCoverage, Metric.Level.WARN)));
- verify(context).saveMeasure(argThat(hasLevel(measureComplexity, Metric.Level.WARN)));
- }
-
- @Test
- public void shouldBeOkIfVariationIsNull() {
- measureClasses.setVariation1(null);
-
- when(projectAlerts.all()).thenReturn(Arrays.asList(
- new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "10", 1)
- ));
-
- decorator.decorate(project, context);
-
- verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.OK, null)));
- verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.OK)));
- }
-
- @Test
- public void shouldVariationPeriodValueCouldBeUsedForRatingMetric() {
- Metric ratingMetric = new Metric.Builder("key_rating_metric", "Rating metric", Metric.ValueType.RATING).create();
- Measure measureRatingMetric = new Measure(ratingMetric, 150d);
- measureRatingMetric.setVariation1(50d);
- when(context.getMeasure(ratingMetric)).thenReturn(measureRatingMetric);
-
- when(projectAlerts.all()).thenReturn(Arrays.asList(
- new Alert(null, ratingMetric, Alert.OPERATOR_GREATER, null, "100", 1)
- ));
-
- decorator.decorate(project, context);
-
- verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.OK, null)));
- verify(context).saveMeasure(argThat(hasLevel(measureRatingMetric, Metric.Level.OK)));
- }
-
- @Test(expected = IllegalStateException.class)
- public void shouldAllowOnlyVariationPeriodOneGlobalPeriods() {
- measureClasses.setVariation4(40d);
-
- when(projectAlerts.all()).thenReturn(Arrays.asList(
- new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "30", 4)
- ));
-
- decorator.decorate(project, context);
- }
-
- @Test(expected = NotImplementedException.class)
- public void shouldNotAllowPeriodVariationAlertOnStringMetric() {
- Measure measure = new Measure(CoreMetrics.SCM_AUTHORS_BY_LINE, 100d);
- measure.setVariation1(50d);
- when(context.getMeasure(CoreMetrics.SCM_AUTHORS_BY_LINE)).thenReturn(measure);
-
- when(projectAlerts.all()).thenReturn(Arrays.asList(
- new Alert(null, CoreMetrics.SCM_AUTHORS_BY_LINE, Alert.OPERATOR_GREATER, null, "30", 1)
- ));
-
- decorator.decorate(project, context);
- }
-
- @Test
- public void shouldLabelAlertContainsPeriod() {
- measureClasses.setVariation1(40d);
-
- when(i18n.message(any(Locale.class), eq("metric.classes.name"), anyString())).thenReturn("Classes");
- when(periods.label(snapshot, 1)).thenReturn("since someday");
-
- 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);
-
- verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.WARN, "Classes variation > 30 since someday")));
- }
-
- @Test
- public void shouldLabelAlertForNewMetricDoNotContainsVariationWord() {
- Metric newMetric = new Metric.Builder("new_metric_key", "New Metric", Metric.ValueType.INT).create();
- Measure measure = new Measure(newMetric, 15d);
- measure.setVariation1(50d);
- when(context.getMeasure(newMetric)).thenReturn(measure);
- measureClasses.setVariation1(40d);
-
- 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(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);
-
- verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.WARN, "New Measure > 30 since someday")));
- }
-
- private ArgumentMatcher<Measure> matchesMetric(final Metric metric, final Metric.Level alertStatus, final String alertText) {
- return new ArgumentMatcher<Measure>() {
- @Override
- public boolean matches(Object arg) {
- boolean result = ((Measure) arg).getMetric().equals(metric) && ((Measure) arg).getAlertStatus() == alertStatus;
- if (result && alertText != null) {
- result = alertText.equals(((Measure) arg).getAlertText());
- }
- return result;
- }
- };
- }
-
- private ArgumentMatcher<Measure> hasLevel(final Measure measure, final Metric.Level alertStatus) {
- return new ArgumentMatcher<Measure>() {
- @Override
- public boolean matches(Object arg) {
- return arg == measure && ((Measure) arg).getAlertStatus().equals(alertStatus);
- }
- };
- }
-
-}
+++ /dev/null
-/*
- * 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.
- */
-
-/**
- * This package is a part of bootstrap process, so we should take care about backward compatibility.
- */
-@ParametersAreNonnullByDefault
-package org.sonar.batch.fs;
-
-import javax.annotation.ParametersAreNonnullByDefault;
import org.sonar.batch.index.PersistenceManager;
import org.sonar.batch.index.ScanPersister;
import org.sonar.batch.issue.ignore.scanner.IssueExclusionsLoader;
+import org.sonar.batch.rule.QProfileVerifier;
import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
import org.sonar.batch.scan.filesystem.FileSystemLogger;
import org.sonar.batch.scan.maven.MavenPhaseExecutor;
private final FileSystemLogger fsLogger;
private final JsonReport jsonReport;
private final DefaultModuleFileSystem fs;
- private final ProfileLogger profileLogger;
+ private final QProfileVerifier profileVerifier;
private final IssueExclusionsLoader issueExclusionsLoader;
public PhaseExecutor(Phases phases, DecoratorsExecutor decoratorsExecutor, MavenPhaseExecutor mavenPhaseExecutor,
PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor,
PersistenceManager persistenceManager, SensorContext sensorContext, DefaultIndex index,
EventBus eventBus, UpdateStatusJob updateStatusJob, ProjectInitializer pi,
- ScanPersister[] persisters, FileSystemLogger fsLogger, JsonReport jsonReport, DefaultModuleFileSystem fs, ProfileLogger profileLogger,
+ ScanPersister[] persisters, FileSystemLogger fsLogger, JsonReport jsonReport, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier,
IssueExclusionsLoader issueExclusionsLoader) {
this.phases = phases;
this.decoratorsExecutor = decoratorsExecutor;
this.fsLogger = fsLogger;
this.jsonReport = jsonReport;
this.fs = fs;
- this.profileLogger = profileLogger;
+ this.profileVerifier = profileVerifier;
this.issueExclusionsLoader = issueExclusionsLoader;
}
PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor,
PersistenceManager persistenceManager, SensorContext sensorContext, DefaultIndex index,
EventBus eventBus, ProjectInitializer pi, ScanPersister[] persisters, FileSystemLogger fsLogger, JsonReport jsonReport,
- DefaultModuleFileSystem fs, ProfileLogger profileLogger, IssueExclusionsLoader issueExclusionsLoader) {
+ DefaultModuleFileSystem fs, QProfileVerifier profileVerifier, IssueExclusionsLoader issueExclusionsLoader) {
this(phases, decoratorsExecutor, mavenPhaseExecutor, mavenPluginsConfigurator, initializersExecutor, postJobsExecutor,
- sensorsExecutor, persistenceManager, sensorContext, index, eventBus, null, pi, persisters, fsLogger, jsonReport, fs, profileLogger, issueExclusionsLoader);
+ sensorsExecutor, persistenceManager, sensorContext, index, eventBus, null, pi, persisters, fsLogger, jsonReport, fs, profileVerifier, issueExclusionsLoader);
}
/**
fs.index();
// Log detected languages and their profiles after FS is indexed and languages detected
- profileLogger.execute();
+ profileVerifier.execute();
// Initialize issue exclusions
issueExclusionsLoader.execute();
+++ /dev/null
-/*
- * 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.phases;
-
-import com.google.common.annotations.VisibleForTesting;
-import org.apache.commons.lang.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.sonar.api.BatchComponent;
-import org.sonar.api.batch.fs.FileSystem;
-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;
-
-public class ProfileLogger implements BatchComponent {
-
- private static final Logger LOG = LoggerFactory.getLogger(ProfileLogger.class);
-
- private final Settings settings;
- private final FileSystem fs;
- private final ModuleQProfiles profiles;
- private final ProjectAlerts projectAlerts;
-
- private final RulesProfile rulesProfile;
-
- public ProfileLogger(Settings settings, FileSystem fs, ModuleQProfiles profiles, ProjectAlerts projectAlerts, RulesProfile rulesProfile) {
- this.settings = settings;
- this.fs = fs;
- this.profiles = profiles;
- this.projectAlerts = projectAlerts;
- this.rulesProfile = rulesProfile;
- }
-
- public void execute() {
- execute(LOG);
- }
-
- @VisibleForTesting
- void execute(Logger logger) {
- String defaultName = settings.getString(ModuleQProfiles.SONAR_PROFILE_PROP);
- boolean defaultNameUsed = StringUtils.isBlank(defaultName);
- for (String lang : fs.languages()) {
- QProfile profile = profiles.findByLanguage(lang);
- if (profile == null) {
- logger.warn("No Quality profile found for language " + lang);
- } else {
- logger.info("Quality profile for {}: {}", lang, profile.name());
- if (StringUtils.isNotBlank(defaultName) && defaultName.equals(profile.name())) {
- defaultNameUsed = true;
- }
- }
- }
- if (!defaultNameUsed && !fs.languages().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 : fs.languages()) {
- RulesProfile profile = profileWrapper.getProfileByLanguage(lang);
- for (Alert alert : profile.getAlerts()) {
- projectAlerts.add(alert);
- }
- }
- }
-
-}
--- /dev/null
+/*
+ * 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.qualitygate;
+
+import org.apache.commons.lang.NotImplementedException;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.profiles.Alert;
+
+class AlertUtils {
+
+ private AlertUtils() {
+ // only static stuff
+ }
+
+ /**
+ * Get the matching alert level for the given measure
+ */
+ static Metric.Level getLevel(Alert alert, Measure measure) {
+ if (evaluateAlert(alert, measure, Metric.Level.ERROR)) {
+ return Metric.Level.ERROR;
+ }
+ if (evaluateAlert(alert, measure, Metric.Level.WARN)) {
+ return Metric.Level.WARN;
+ }
+ return Metric.Level.OK;
+ }
+
+ private static boolean evaluateAlert(Alert alert, Measure measure, Metric.Level alertLevel) {
+ String valueToEval = getValueToEval(alert, alertLevel);
+ if (StringUtils.isEmpty(valueToEval)) {
+ return false;
+ }
+
+ Comparable criteriaValue = getValueForComparison(alert.getMetric(), valueToEval);
+ Comparable measureValue = getMeasureValue(alert, measure);
+ if (measureValue != null) {
+ return doesReachThresholds(measureValue, criteriaValue, alert);
+ }
+ return false;
+ }
+
+ private static boolean doesReachThresholds(Comparable measureValue, Comparable criteriaValue, Alert alert) {
+ int comparison = measureValue.compareTo(criteriaValue);
+ return !(isNotEquals(comparison, alert)
+ || isGreater(comparison, alert)
+ || isSmaller(comparison, alert)
+ || isEquals(comparison, alert));
+ }
+
+ private static boolean isNotEquals(int comparison, Alert alert) {
+ return alert.isNotEqualsOperator() && comparison == 0;
+ }
+
+ private static boolean isGreater(int comparison, Alert alert) {
+ return alert.isGreaterOperator() && comparison != 1;
+ }
+
+ private static boolean isSmaller(int comparison, Alert alert) {
+ return alert.isSmallerOperator() && comparison != -1;
+ }
+
+ private static boolean isEquals(int comparison, Alert alert) {
+ return alert.isEqualsOperator() && comparison != 0;
+ }
+
+ private static String getValueToEval(Alert alert, Metric.Level alertLevel) {
+ if (alertLevel.equals(Metric.Level.ERROR)) {
+ return alert.getValueError();
+ } else if (alertLevel.equals(Metric.Level.WARN)) {
+ return alert.getValueWarning();
+ } else {
+ throw new IllegalStateException(alertLevel.toString());
+ }
+ }
+
+ private static Comparable getValueForComparison(Metric metric, String value) {
+ if (isADouble(metric)) {
+ return Double.parseDouble(value);
+ }
+ if (isAInteger(metric)) {
+ return parseInteger(value);
+ }
+ if (isAString(metric)) {
+ return value;
+ }
+ if (isABoolean(metric)) {
+ return Integer.parseInt(value);
+ }
+ throw new NotImplementedException(metric.getType().toString());
+ }
+
+ private static Comparable<Integer> parseInteger(String value) {
+ return value.contains(".") ? Integer.parseInt(value.substring(0, value.indexOf('.'))) : Integer.parseInt(value);
+ }
+
+ private static Comparable getMeasureValue(Alert alert, Measure measure) {
+ Metric metric = alert.getMetric();
+ if (isADouble(metric)) {
+ return getValue(alert, measure);
+ }
+ if (isAInteger(metric)) {
+ return parseInteger(alert, measure);
+ }
+ if (alert.getPeriod() == null) {
+ return getMeasureValueForStringOrBoolean(metric, measure);
+ }
+ throw new NotImplementedException(metric.getType().toString());
+ }
+
+ private static Comparable getMeasureValueForStringOrBoolean(Metric metric, Measure measure) {
+ if (isAString(metric)) {
+ return measure.getData();
+ }
+ if (isABoolean(metric)) {
+ return measure.getValue().intValue();
+ }
+ throw new NotImplementedException(metric.getType().toString());
+ }
+
+ private static Comparable<Integer> parseInteger(Alert alert, Measure measure) {
+ Double value = getValue(alert, measure);
+ return value != null ? value.intValue() : null;
+ }
+
+ private static boolean isADouble(Metric metric) {
+ return metric.getType() == Metric.ValueType.FLOAT ||
+ metric.getType() == Metric.ValueType.PERCENT ||
+ metric.getType() == Metric.ValueType.RATING;
+ }
+
+ private static boolean isAInteger(Metric metric) {
+ return metric.getType() == Metric.ValueType.INT ||
+ metric.getType() == Metric.ValueType.MILLISEC;
+ }
+
+ private static boolean isAString(Metric metric) {
+ return metric.getType() == Metric.ValueType.STRING ||
+ metric.getType() == Metric.ValueType.LEVEL;
+ }
+
+ private static boolean isABoolean(Metric metric) {
+ return metric.getType() == Metric.ValueType.BOOL;
+ }
+
+ private static Double getValue(Alert alert, Measure measure) {
+ if (alert.getPeriod() == null) {
+ return measure.getValue();
+ } else if (alert.getPeriod() == 1) {
+ return measure.getVariation1();
+ } else if (alert.getPeriod() == 2) {
+ return measure.getVariation2();
+ } else if (alert.getPeriod() == 3) {
+ return measure.getVariation3();
+ } else {
+ throw new IllegalStateException("Following index period is not allowed : " + Double.toString(alert.getPeriod()));
+ }
+ }
+}
--- /dev/null
+/*
+ * 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.qualitygate;
+
+import com.google.common.collect.Sets;
+import org.sonar.api.BatchComponent;
+import org.sonar.api.profiles.Alert;
+
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * Lists the alerts enabled on the current project.
+ */
+public class ProjectAlerts implements BatchComponent {
+
+ private final Set<Alert> alerts = Sets.newHashSet();
+
+ void addAll(Collection<Alert> alerts) {
+ this.alerts.addAll(alerts);
+ }
+
+ Set<Alert> all() {
+ return alerts;
+ }
+
+}
--- /dev/null
+/*
+ * 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.qualitygate;
+
+import org.sonar.api.batch.Sensor;
+import org.sonar.api.batch.SensorContext;
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.profiles.RulesProfile;
+import org.sonar.api.resources.Project;
+import org.sonar.batch.rule.RulesProfileWrapper;
+
+/**
+ * Executed on every module to feed {@link org.sonar.batch.qualitygate.ProjectAlerts}
+ */
+public class QualityGateLoader implements Sensor {
+
+ private final FileSystem fs;
+ private final RulesProfileWrapper qProfile;
+ private final ProjectAlerts projectAlerts;
+
+ public QualityGateLoader(FileSystem fs, RulesProfileWrapper qProfile, ProjectAlerts projectAlerts) {
+ this.fs = fs;
+ this.qProfile = qProfile;
+ this.projectAlerts = projectAlerts;
+ }
+
+ @Override
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ @Override
+ public void analyse(Project module, SensorContext context) {
+ for (String lang : fs.languages()) {
+ RulesProfile profile = qProfile.getProfileByLanguage(lang);
+ if (profile != null) {
+ projectAlerts.addAll(profile.getAlerts());
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Quality gate loader";
+ }
+}
--- /dev/null
+/*
+ * 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.qualitygate;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.batch.*;
+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.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.ResourceUtils;
+import org.sonar.core.timemachine.Periods;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+public class QualityGateVerifier implements Decorator {
+
+ private static final String VARIATION_METRIC_PREFIX = "new_";
+ private static final String VARIATION = "variation";
+
+ private final Snapshot snapshot;
+ private final Periods periods;
+ private final I18n i18n;
+ private ProjectAlerts projectAlerts;
+
+ public QualityGateVerifier(Snapshot snapshot, ProjectAlerts projectAlerts, Periods periods, I18n i18n) {
+ this.snapshot = snapshot;
+ this.projectAlerts = projectAlerts;
+ this.periods = periods;
+ this.i18n = i18n;
+ }
+
+ @DependedUpon
+ public Metric generatesAlertStatus() {
+ return CoreMetrics.ALERT_STATUS;
+ }
+
+ @DependsUpon
+ public String dependsOnVariations() {
+ return DecoratorBarriers.END_OF_TIME_MACHINE;
+ }
+
+ @DependsUpon
+ public Collection<Metric> dependsUponMetrics() {
+ Set<Metric> metrics = Sets.newHashSet();
+ for (Alert alert : projectAlerts.all()) {
+ metrics.add(alert.getMetric());
+ }
+ return metrics;
+ }
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ public void decorate(final Resource resource, final DecoratorContext context) {
+ if (ResourceUtils.isRootProject(resource) && !projectAlerts.all().isEmpty()) {
+ checkProjectAlerts(context);
+ }
+ }
+
+ private void checkProjectAlerts(DecoratorContext context) {
+ Metric.Level globalLevel = Metric.Level.OK;
+ List<String> labels = Lists.newArrayList();
+
+ for (Alert alert : projectAlerts.all()) {
+ Measure measure = context.getMeasure(alert.getMetric());
+ if (measure != null) {
+ Metric.Level level = AlertUtils.getLevel(alert, measure);
+
+ measure.setAlertStatus(level);
+ String text = getText(alert, level);
+ if (!StringUtils.isBlank(text)) {
+ measure.setAlertText(text);
+ labels.add(text);
+ }
+
+ context.saveMeasure(measure);
+
+ if (Metric.Level.WARN == level && globalLevel != Metric.Level.ERROR) {
+ globalLevel = Metric.Level.WARN;
+
+ } else if (Metric.Level.ERROR == level) {
+ globalLevel = Metric.Level.ERROR;
+ }
+ }
+ }
+
+ Measure globalMeasure = new Measure(CoreMetrics.ALERT_STATUS, globalLevel);
+ globalMeasure.setAlertStatus(globalLevel);
+ globalMeasure.setAlertText(StringUtils.join(labels, ", "));
+ context.saveMeasure(globalMeasure);
+ }
+
+ private String getText(Alert alert, Metric.Level level) {
+ if (level == Metric.Level.OK) {
+ return null;
+ }
+ return getAlertLabel(alert, level);
+ }
+
+ private String getAlertLabel(Alert alert, Metric.Level level) {
+ Integer alertPeriod = alert.getPeriod();
+ String metric = i18n.message(Locale.ENGLISH, "metric." + alert.getMetric().getKey() + ".name", alert.getMetric().getName());
+
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append(metric);
+
+ if (alertPeriod != null && !alert.getMetric().getKey().startsWith(VARIATION_METRIC_PREFIX)) {
+ String variation = i18n.message(Locale.ENGLISH, VARIATION, VARIATION).toLowerCase();
+ stringBuilder.append(" ").append(variation);
+ }
+
+ stringBuilder
+ .append(" ").append(alert.getOperator()).append(" ")
+ .append(level.equals(Metric.Level.ERROR) ? alert.getValueError() : alert.getValueWarning());
+
+ if (alertPeriod != null) {
+ stringBuilder.append(" ").append(periods.label(snapshot, alertPeriod));
+ }
+
+ return stringBuilder.toString();
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+}
--- /dev/null
+/*
+ * 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.batch.qualitygate;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+++ /dev/null
-/*
- * 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;
- }
-
-}
--- /dev/null
+/*
+ * 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.annotations.VisibleForTesting;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.BatchComponent;
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.config.Settings;
+import org.sonar.api.utils.MessageException;
+import org.sonar.batch.rule.ModuleQProfiles.QProfile;
+
+public class QProfileVerifier implements BatchComponent {
+
+ private static final Logger LOG = LoggerFactory.getLogger(QProfileVerifier.class);
+
+ private final Settings settings;
+ private final FileSystem fs;
+ private final ModuleQProfiles profiles;
+
+ public QProfileVerifier(Settings settings, FileSystem fs, ModuleQProfiles profiles) {
+ this.settings = settings;
+ this.fs = fs;
+ this.profiles = profiles;
+ }
+
+ public void execute() {
+ execute(LOG);
+ }
+
+ @VisibleForTesting
+ void execute(Logger logger) {
+ String defaultName = settings.getString(ModuleQProfiles.SONAR_PROFILE_PROP);
+ boolean defaultNameUsed = StringUtils.isBlank(defaultName);
+ for (String lang : fs.languages()) {
+ QProfile profile = profiles.findByLanguage(lang);
+ if (profile == null) {
+ logger.warn("No Quality profile found for language " + lang);
+ } else {
+ logger.info("Quality profile for {}: {}", lang, profile.name());
+ if (StringUtils.isNotBlank(defaultName) && defaultName.equals(profile.name())) {
+ defaultNameUsed = true;
+ }
+ }
+ }
+ if (!defaultNameUsed && !fs.languages().isEmpty()) {
+ throw MessageException.of("sonar.profile was set to '" + defaultName + "' but didn't match any profile for any language. Please check your configuration.");
+ }
+ }
+}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.fs.internal.DefaultFileSystem;
import org.sonar.api.config.Settings;
import org.sonar.api.resources.Language;
import org.sonar.api.resources.Languages;
private final Settings settings;
private final Languages languages;
+ private final DefaultFileSystem fs;
- public LanguageVerifier(Settings settings, Languages languages) {
+ public LanguageVerifier(Settings settings, Languages languages, DefaultFileSystem fs) {
this.settings = settings;
this.languages = languages;
+ this.fs = fs;
}
if (language == null) {
throw MessageException.of("You must install a plugin that supports the language '" + languageKey + "'");
}
+
+ // force the registration of the language, even if there are no related source files
+ fs.addLanguages(languageKey);
}
}
import org.sonar.batch.issue.ModuleIssues;
import org.sonar.batch.phases.PhaseExecutor;
import org.sonar.batch.phases.PhasesTimeProfiler;
-import org.sonar.batch.phases.ProfileLogger;
+import org.sonar.batch.rule.QProfileVerifier;
+import org.sonar.batch.qualitygate.QualityGateLoader;
+import org.sonar.batch.qualitygate.QualityGateVerifier;
import org.sonar.batch.rule.ActiveRulesProvider;
import org.sonar.batch.rule.ModuleQProfiles;
import org.sonar.batch.rule.QProfileSensor;
DefaultModuleFileSystem.class,
ModuleFileSystemInitializer.class,
ProjectFileSystemAdapter.class,
- ProfileLogger.class,
+ QProfileVerifier.class,
// the Snapshot component will be removed when asynchronous measures are improved (required for AsynchronousMeasureSensor)
getComponentByType(ResourcePersister.class).getSnapshot(module),
MeasurementFilters.class,
ResourceFilters.class,
+ // quality gates
+ QualityGateLoader.class,
+ QualityGateVerifier.class,
+
// rules
ModuleQProfiles.class,
new ActiveRulesProvider(),
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.qualitygate.ProjectAlerts;
import org.sonar.batch.scan.filesystem.InputFileCache;
import org.sonar.batch.scan.maven.FakeMavenPluginExecutor;
import org.sonar.batch.scan.maven.MavenPluginExecutor;
ProjectSettingsReady.class,
+ // quality gates
ProjectAlerts.class);
}
+++ /dev/null
-/*
- * 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.phases;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.slf4j.Logger;
-import org.sonar.api.batch.fs.internal.DefaultFileSystem;
-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.fest.assertions.Assertions.assertThat;
-import static org.mockito.Mockito.*;
-
-public class ProfileLoggerTest {
-
- @Rule
- public ExpectedException thrown = ExpectedException.none();
-
- DefaultFileSystem fs = new DefaultFileSystem();
- ModuleQProfiles profiles;
- Settings settings = new Settings();
- ProjectAlerts projectAlerts = new ProjectAlerts();
- RulesProfileWrapper rulesProfile = mock(RulesProfileWrapper.class);
- RulesProfile javaRulesProfile;
- RulesProfile cobolRulesProfile;
-
- @Before
- public void before() {
- 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() {
- fs.addLanguages("java", "cobol");
- ProfileLogger profileLogger = new ProfileLogger(settings, fs, profiles, projectAlerts, rulesProfile);
- Logger logger = mock(Logger.class);
- profileLogger.execute(logger);
-
- verify(logger).info("Quality profile for {}: {}", "java", "My Java profile");
- verify(logger).info("Quality profile for {}: {}", "cobol", "My Cobol profile");
- }
-
- @Test
- public void should_fail_if_default_profile_not_used() {
- fs.addLanguages("java", "cobol");
- settings.setProperty("sonar.profile", "Unknown");
-
- ProfileLogger profileLogger = new ProfileLogger(settings, fs, 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.");
-
- profileLogger.execute();
- }
-
- @Test
- public void should_not_fail_if_no_language_on_project() {
- settings.setProperty("sonar.profile", "Unknown");
-
- ProfileLogger profileLogger = new ProfileLogger(settings, fs, profiles, projectAlerts, rulesProfile);
-
- profileLogger.execute();
-
- }
-
- @Test
- public void should_not_fail_if_default_profile_used_at_least_once() {
- fs.addLanguages("java", "cobol");
- settings.setProperty("sonar.profile", "My Java profile");
-
- ProfileLogger profileLogger = new ProfileLogger(settings, fs, profiles, projectAlerts, rulesProfile);
-
- profileLogger.execute();
- }
-
- @Test
- public void should_collect_alerts() {
- fs.addLanguages("java", "cobol");
- 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, fs, profiles, projectAlerts, rulesProfile);
-
- profileLogger.execute();
-
- assertThat(projectAlerts.all()).containsExactly(cobolAlert1, cobolAlert2, javaAlert1, javaAlert2);
- }
-}
--- /dev/null
+/*
+ * 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.qualitygate;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.profiles.Alert;
+
+public class AlertUtilsTest {
+ private Metric metric;
+ private Measure measure;
+ private Metric metric2;
+ private Measure measure2;
+ private Alert alert;
+
+ @Before
+ public void setup() {
+ metric = new Metric.Builder("test-metric", "name", Metric.ValueType.FLOAT).create();
+ measure = new Measure();
+ measure.setMetric(metric);
+
+ metric2 = new Metric.Builder("test-metric2", "name2", Metric.ValueType.FLOAT).create();
+ measure2 = new Measure();
+ measure2.setMetric(metric2);
+
+ alert = new Alert();
+ }
+
+ @Test
+ public void testInputNumbers() {
+ metric.setType(Metric.ValueType.FLOAT);
+ measure.setValue(10.2d);
+ alert.setOperator(Alert.OPERATOR_SMALLER);
+ alert.setMetric(metric);
+
+ try {
+ metric.setType(Metric.ValueType.FLOAT);
+ alert.setValueError("20");
+ AlertUtils.getLevel(alert, measure);
+ } catch (NumberFormatException ex) {
+ Assert.fail();
+ }
+
+ try {
+ metric.setType(Metric.ValueType.INT);
+ alert.setValueError("20.1");
+ AlertUtils.getLevel(alert, measure);
+ } catch (NumberFormatException ex) {
+ Assert.fail();
+ }
+
+ try {
+ metric.setType(Metric.ValueType.PERCENT);
+ alert.setValueError("20.1");
+ AlertUtils.getLevel(alert, measure);
+ } catch (NumberFormatException ex) {
+ Assert.fail();
+ }
+ }
+
+ @Test
+ public void testEquals() {
+
+ metric.setType(Metric.ValueType.FLOAT);
+ measure.setValue(10.2d);
+ alert.setOperator(Alert.OPERATOR_EQUALS);
+ alert.setMetric(metric);
+
+ alert.setValueError("10.2");
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+
+ alert.setValueError("10.1");
+ Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
+
+ metric.setType(Metric.ValueType.STRING);
+ measure.setData("TEST");
+ measure.setValue(null);
+
+ alert.setValueError("TEST");
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+
+ alert.setValueError("TEST2");
+ Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
+
+ }
+
+ @Test
+ public void testNotEquals() {
+
+ metric.setType(Metric.ValueType.FLOAT);
+ measure.setValue(10.2d);
+ alert.setOperator(Alert.OPERATOR_NOT_EQUALS);
+ alert.setMetric(metric);
+
+ alert.setValueError("10.2");
+ Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
+
+ alert.setValueError("10.1");
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+
+ metric.setType(Metric.ValueType.STRING);
+ measure.setData("TEST");
+ measure.setValue(null);
+
+ alert.setValueError("TEST");
+ Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
+
+ alert.setValueError("TEST2");
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+
+ }
+
+ @Test
+ public void testGreater() {
+ metric.setType(Metric.ValueType.FLOAT);
+ measure.setValue(10.2d);
+ alert.setOperator(Alert.OPERATOR_GREATER);
+ alert.setMetric(metric);
+
+ alert.setValueError("10.1");
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+
+ alert.setValueError("10.3");
+ Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
+ }
+
+ @Test
+ public void testSmaller() {
+ metric.setType(Metric.ValueType.FLOAT);
+ measure.setValue(10.2d);
+ alert.setOperator(Alert.OPERATOR_SMALLER);
+ alert.setMetric(metric);
+
+ alert.setValueError("10.1");
+ Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
+
+ alert.setValueError("10.3");
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+ }
+
+ @Test
+ public void testPercent() {
+ metric.setType(Metric.ValueType.PERCENT);
+ measure.setValue(10.2d);
+ alert.setOperator(Alert.OPERATOR_EQUALS);
+ alert.setMetric(metric);
+
+ alert.setValueError("10.2");
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+ }
+
+ @Test
+ public void testFloat() {
+ metric.setType(Metric.ValueType.FLOAT);
+ measure.setValue(10.2d);
+ alert.setOperator(Alert.OPERATOR_EQUALS);
+ alert.setMetric(metric);
+
+ alert.setValueError("10.2");
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+ }
+
+ @Test
+ public void testInteger() {
+ metric.setType(Metric.ValueType.INT);
+ measure.setValue(10.2d);
+ alert.setOperator(Alert.OPERATOR_EQUALS);
+ alert.setMetric(metric);
+
+ alert.setValueError("10");
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+
+ alert.setValueError("10.2");
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+ }
+
+ @Test
+ public void testLevel() {
+ metric.setType(Metric.ValueType.LEVEL);
+ measure.setData(Metric.Level.ERROR.toString());
+ alert.setOperator(Alert.OPERATOR_EQUALS);
+ alert.setMetric(metric);
+
+ alert.setValueError(Metric.Level.ERROR.toString());
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+
+ alert.setValueError(Metric.Level.OK.toString());
+ Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
+
+ alert.setOperator(Alert.OPERATOR_NOT_EQUALS);
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+ }
+
+ @Test
+ public void testBooleans() {
+ metric.setType(Metric.ValueType.BOOL);
+ measure.setValue(0d);
+ alert.setOperator(Alert.OPERATOR_EQUALS);
+ alert.setMetric(metric);
+
+ alert.setValueError("1");
+ Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
+
+ alert.setValueError("0");
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+
+ alert.setOperator(Alert.OPERATOR_NOT_EQUALS);
+ alert.setValueError("1");
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+
+ alert.setValueError("0");
+ Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
+ }
+
+ @Test
+ public void testErrorAndWarningLevel() {
+ metric.setType(Metric.ValueType.FLOAT);
+ measure.setValue(10.2d);
+ alert.setOperator(Alert.OPERATOR_EQUALS);
+ alert.setMetric(metric);
+
+ alert.setValueError("10.2");
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+
+ alert.setValueError("10.1");
+ Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
+
+ alert.setValueError("10.3");
+ alert.setValueWarning("10.2");
+ Assert.assertEquals(Metric.Level.WARN, AlertUtils.getLevel(alert, measure));
+ }
+}
--- /dev/null
+/*
+ * 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.qualitygate;
+
+import com.google.common.collect.Lists;
+import org.junit.Test;
+import org.sonar.api.profiles.Alert;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class ProjectAlertsTest {
+ @Test
+ public void test() throws Exception {
+ ProjectAlerts alerts = new ProjectAlerts();
+ assertThat(alerts.all()).isEmpty();
+
+ alerts.addAll(Lists.newArrayList(new Alert()));
+ assertThat(alerts.all()).hasSize(1);
+ }
+}
--- /dev/null
+/*
+ * 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.qualitygate;
+
+import com.google.common.collect.Lists;
+import org.junit.Test;
+import org.sonar.api.batch.fs.internal.DefaultFileSystem;
+import org.sonar.api.profiles.Alert;
+import org.sonar.api.profiles.RulesProfile;
+import org.sonar.api.resources.Project;
+import org.sonar.batch.rule.RulesProfileWrapper;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class QualityGateLoaderTest {
+
+ DefaultFileSystem fs = new DefaultFileSystem();
+ RulesProfileWrapper qProfile = mock(RulesProfileWrapper.class);
+ ProjectAlerts alerts = new ProjectAlerts();
+ QualityGateLoader loader = new QualityGateLoader(fs, qProfile, alerts);
+
+ @Test
+ public void should_always_be_executed() throws Exception {
+ assertThat(loader.shouldExecuteOnProject(new Project("struts"))).isTrue();
+ }
+
+ @Test
+ public void test_toString() throws Exception {
+ assertThat(loader.toString()).isEqualTo("Quality gate loader");
+ }
+
+ @Test
+ public void register_project_alerts() throws Exception {
+ fs.addLanguages("java", "php");
+
+ RulesProfile javaProfile = new RulesProfile();
+ javaProfile.setAlerts(Lists.newArrayList(new Alert()));
+ when(qProfile.getProfileByLanguage("java")).thenReturn(javaProfile);
+
+ loader.analyse(null, null);
+
+ assertThat(alerts.all()).hasSize(1);
+ }
+}
--- /dev/null
+
+/*
+ * 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.qualitygate;
+
+import com.google.common.collect.Lists;
+import org.apache.commons.lang.NotImplementedException;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+import org.sonar.api.batch.DecoratorBarriers;
+import org.sonar.api.batch.DecoratorContext;
+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.resources.File;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.test.IsMeasure;
+import org.sonar.core.timemachine.Periods;
+
+import java.util.Locale;
+
+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.Matchers.eq;
+import static org.mockito.Mockito.*;
+
+public class QualityGateVerifierTest {
+
+ QualityGateVerifier verifier;
+ DecoratorContext context;
+ ProjectAlerts projectAlerts;
+
+ Measure measureClasses;
+ Measure measureCoverage;
+ Measure measureComplexity;
+ Resource project;
+ Snapshot snapshot;
+ Periods periods;
+ I18n i18n;
+
+ @Before
+ public void before() {
+ context = mock(DecoratorContext.class);
+ periods = mock(Periods.class);
+ i18n = mock(I18n.class);
+ when(i18n.message(any(Locale.class), eq("variation"), eq("variation"))).thenReturn("variation");
+
+ measureClasses = new Measure(CoreMetrics.CLASSES, 20d);
+ measureCoverage = new Measure(CoreMetrics.COVERAGE, 35d);
+ measureComplexity = new Measure(CoreMetrics.COMPLEXITY, 50d);
+
+ when(context.getMeasure(CoreMetrics.CLASSES)).thenReturn(measureClasses);
+ when(context.getMeasure(CoreMetrics.COVERAGE)).thenReturn(measureCoverage);
+ when(context.getMeasure(CoreMetrics.COMPLEXITY)).thenReturn(measureComplexity);
+
+ snapshot = mock(Snapshot.class);
+ projectAlerts = new ProjectAlerts();
+ verifier = new QualityGateVerifier(snapshot, projectAlerts, periods, i18n);
+ project = new Project("foo");
+ }
+
+ @Test
+ public void should_always_be_executed() {
+ assertThat(verifier.shouldExecuteOnProject(new Project("foo"))).isTrue();
+ }
+
+ @Test
+ public void test_toString() {
+ assertThat(verifier.toString()).isEqualTo("QualityGateVerifier");
+ }
+
+ @Test
+ public void generates_alert_status() {
+ assertThat(verifier.generatesAlertStatus()).isEqualTo(CoreMetrics.ALERT_STATUS);
+ }
+
+ @Test
+ public void depends_on_variations() {
+ assertThat(verifier.dependsOnVariations()).isEqualTo(DecoratorBarriers.END_OF_TIME_MACHINE);
+ }
+
+ @Test
+ public void depends_upon_metrics() {
+ projectAlerts.addAll(Lists.newArrayList(new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "20")));
+ assertThat(verifier.dependsUponMetrics()).containsOnly(CoreMetrics.CLASSES);
+ }
+
+ @Test
+ public void ok_when_no_alerts() {
+ projectAlerts.addAll(Lists.newArrayList(
+ new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "20"),
+ new Alert(null, CoreMetrics.COVERAGE, Alert.OPERATOR_GREATER, null, "35.0")));
+
+ verifier.decorate(project, context);
+
+ verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.ALERT_STATUS, Metric.Level.OK.toString())));
+ verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.OK)));
+ verify(context).saveMeasure(argThat(hasLevel(measureCoverage, Metric.Level.OK)));
+ }
+
+ @Test
+ public void check_root_modules_only() {
+ projectAlerts.addAll(Lists.newArrayList(
+ new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "20"),
+ new Alert(null, CoreMetrics.COVERAGE, Alert.OPERATOR_GREATER, null, "35.0")));
+
+ verifier.decorate(File.create("src/Foo.php"), context);
+
+ verify(context, never()).saveMeasure(any(Measure.class));
+ }
+
+ @Test
+ public void generate_warnings() {
+ projectAlerts.addAll(Lists.newArrayList(
+ 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%
+
+ verifier.decorate(project, context);
+
+ verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.WARN, null)));
+
+ verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.OK)));
+ verify(context).saveMeasure(argThat(hasLevel(measureCoverage, Metric.Level.WARN)));
+
+ }
+
+ @Test
+ public void globalStatusShouldBeErrorIfWarningsAndErrors() {
+ projectAlerts.addAll(Lists.newArrayList(
+ 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%
+
+ verifier.decorate(project, context);
+
+ verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.ERROR, null)));
+
+ verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.WARN)));
+ verify(context).saveMeasure(argThat(hasLevel(measureCoverage, Metric.Level.ERROR)));
+ }
+
+ @Test
+ 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");
+ projectAlerts.addAll(Lists.newArrayList(
+ 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
+
+ verifier.decorate(project, context);
+
+ verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.ERROR, "Classes < 10000, Coverages < 50.0")));
+ }
+
+ @Test
+ public void alertLabelUsesL10nMetricName() {
+ Metric metric = new Metric.Builder("rating", "Rating", Metric.ValueType.INT).create();
+
+ // metric name is declared in l10n bundle
+ when(i18n.message(any(Locale.class), eq("metric.rating.name"), anyString())).thenReturn("THE RATING");
+
+ when(context.getMeasure(metric)).thenReturn(new Measure(metric, 4d));
+ projectAlerts.addAll(Lists.newArrayList(new Alert(null, metric, Alert.OPERATOR_SMALLER, "10", null)));
+ verifier.decorate(project, context);
+
+ verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.ERROR, "THE RATING < 10")));
+ }
+
+ @Test
+ public void alertLabelUsesMetricNameIfMissingL10nBundle() {
+ // the third argument is Metric#getName()
+ when(i18n.message(any(Locale.class), eq("metric.classes.name"), eq("Classes"))).thenReturn("Classes");
+ projectAlerts.addAll(Lists.newArrayList(
+ // there are 20 classes, error threshold is higher => alert
+ new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_SMALLER, "10000", null)
+ ));
+
+ verifier.decorate(project, context);
+
+ verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.ERROR, "Classes < 10000")));
+ }
+
+ @Test
+ public void shouldBeOkIfPeriodVariationIsEnough() {
+ measureClasses.setVariation1(0d);
+ measureCoverage.setVariation2(50d);
+ measureComplexity.setVariation3(2d);
+
+ projectAlerts.addAll(Lists.newArrayList(
+ 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
+ ));
+
+ verifier.decorate(project, context);
+
+ verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.OK, null)));
+
+ verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.OK)));
+ verify(context).saveMeasure(argThat(hasLevel(measureCoverage, Metric.Level.OK)));
+ verify(context).saveMeasure(argThat(hasLevel(measureComplexity, Metric.Level.OK)));
+ }
+
+ @Test
+ public void shouldGenerateWarningIfPeriodVariationIsNotEnough() {
+ measureClasses.setVariation1(40d);
+ measureCoverage.setVariation2(5d);
+ measureComplexity.setVariation3(70d);
+
+ projectAlerts.addAll(Lists.newArrayList(
+ 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
+ ));
+
+ verifier.decorate(project, context);
+
+ verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.WARN, null)));
+
+ verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.WARN)));
+ verify(context).saveMeasure(argThat(hasLevel(measureCoverage, Metric.Level.WARN)));
+ verify(context).saveMeasure(argThat(hasLevel(measureComplexity, Metric.Level.WARN)));
+ }
+
+ @Test
+ public void shouldBeOkIfVariationIsNull() {
+ measureClasses.setVariation1(null);
+
+ projectAlerts.addAll(Lists.newArrayList(
+ new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "10", 1)));
+
+ verifier.decorate(project, context);
+
+ verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.OK, null)));
+ verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.OK)));
+ }
+
+ @Test
+ public void shouldVariationPeriodValueCouldBeUsedForRatingMetric() {
+ Metric ratingMetric = new Metric.Builder("key_rating_metric", "Rating metric", Metric.ValueType.RATING).create();
+ Measure measureRatingMetric = new Measure(ratingMetric, 150d);
+ measureRatingMetric.setVariation1(50d);
+ when(context.getMeasure(ratingMetric)).thenReturn(measureRatingMetric);
+
+ projectAlerts.addAll(Lists.newArrayList(
+ new Alert(null, ratingMetric, Alert.OPERATOR_GREATER, null, "100", 1)
+ ));
+
+ verifier.decorate(project, context);
+
+ verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.OK, null)));
+ verify(context).saveMeasure(argThat(hasLevel(measureRatingMetric, Metric.Level.OK)));
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void shouldAllowOnlyVariationPeriodOneGlobalPeriods() {
+ measureClasses.setVariation4(40d);
+
+ projectAlerts.addAll(Lists.newArrayList(
+ new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "30", 4)
+ ));
+
+ verifier.decorate(project, context);
+ }
+
+ @Test(expected = NotImplementedException.class)
+ public void shouldNotAllowPeriodVariationAlertOnStringMetric() {
+ Measure measure = new Measure(CoreMetrics.SCM_AUTHORS_BY_LINE, 100d);
+ measure.setVariation1(50d);
+ when(context.getMeasure(CoreMetrics.SCM_AUTHORS_BY_LINE)).thenReturn(measure);
+
+ projectAlerts.addAll(Lists.newArrayList(
+ new Alert(null, CoreMetrics.SCM_AUTHORS_BY_LINE, Alert.OPERATOR_GREATER, null, "30", 1)
+ ));
+
+ verifier.decorate(project, context);
+ }
+
+ @Test
+ public void shouldLabelAlertContainsPeriod() {
+ measureClasses.setVariation1(40d);
+
+ when(i18n.message(any(Locale.class), eq("metric.classes.name"), anyString())).thenReturn("Classes");
+ when(periods.label(snapshot, 1)).thenReturn("since someday");
+
+ projectAlerts.addAll(Lists.newArrayList(
+ new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "30", 1) // generates warning because classes increases of 40,
+ // which is greater than 30
+ ));
+
+ verifier.decorate(project, context);
+
+ verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.WARN, "Classes variation > 30 since someday")));
+ }
+
+ @Test
+ public void shouldLabelAlertForNewMetricDoNotContainsVariationWord() {
+ Metric newMetric = new Metric.Builder("new_metric_key", "New Metric", Metric.ValueType.INT).create();
+ Measure measure = new Measure(newMetric, 15d);
+ measure.setVariation1(50d);
+ when(context.getMeasure(newMetric)).thenReturn(measure);
+ measureClasses.setVariation1(40d);
+
+ when(i18n.message(any(Locale.class), eq("metric.new_metric_key.name"), anyString())).thenReturn("New Measure");
+ when(periods.label(snapshot, 1)).thenReturn("since someday");
+
+ projectAlerts.addAll(Lists.newArrayList(
+ new Alert(null, newMetric, Alert.OPERATOR_GREATER, null, "30", 1) // generates warning because classes increases of 40, which is
+ // greater than 30
+ ));
+
+ verifier.decorate(project, context);
+
+ verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.WARN, "New Measure > 30 since someday")));
+ }
+
+ private ArgumentMatcher<Measure> matchesMetric(final Metric metric, final Metric.Level alertStatus, final String alertText) {
+ return new ArgumentMatcher<Measure>() {
+ @Override
+ public boolean matches(Object arg) {
+ boolean result = ((Measure) arg).getMetric().equals(metric) && ((Measure) arg).getAlertStatus() == alertStatus;
+ if (result && alertText != null) {
+ result = alertText.equals(((Measure) arg).getAlertText());
+ }
+ return result;
+ }
+ };
+ }
+
+ private ArgumentMatcher<Measure> hasLevel(final Measure measure, final Metric.Level alertStatus) {
+ return new ArgumentMatcher<Measure>() {
+ @Override
+ public boolean matches(Object arg) {
+ return arg == measure && ((Measure) arg).getAlertStatus().equals(alertStatus);
+ }
+ };
+ }
+
+}
--- /dev/null
+/*
+ * 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 org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.slf4j.Logger;
+import org.sonar.api.batch.fs.internal.DefaultFileSystem;
+import org.sonar.api.config.Settings;
+import org.sonar.api.profiles.RulesProfile;
+import org.sonar.api.utils.MessageException;
+import org.sonar.batch.rule.ModuleQProfiles.QProfile;
+
+import static org.mockito.Mockito.*;
+
+public class QProfileVerifierTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ DefaultFileSystem fs = new DefaultFileSystem();
+ ModuleQProfiles profiles;
+ Settings settings = new Settings();
+ RulesProfile javaRulesProfile;
+ RulesProfile cobolRulesProfile;
+
+ @Before
+ public void before() {
+ profiles = mock(ModuleQProfiles.class);
+ QProfile javaProfile = mock(QProfile.class);
+ when(javaProfile.name()).thenReturn("My Java profile");
+ javaRulesProfile = mock(RulesProfile.class);
+ when(profiles.findByLanguage("java")).thenReturn(javaProfile);
+ QProfile cobolProfile = mock(QProfile.class);
+ when(cobolProfile.name()).thenReturn("My Cobol profile");
+ cobolRulesProfile = mock(RulesProfile.class);
+ when(profiles.findByLanguage("cobol")).thenReturn(cobolProfile);
+ }
+
+ @Test
+ public void should_log_all_used_profiles() {
+ fs.addLanguages("java", "cobol");
+ QProfileVerifier profileLogger = new QProfileVerifier(settings, fs, profiles);
+ Logger logger = mock(Logger.class);
+ profileLogger.execute(logger);
+
+ verify(logger).info("Quality profile for {}: {}", "java", "My Java profile");
+ verify(logger).info("Quality profile for {}: {}", "cobol", "My Cobol profile");
+ }
+
+ @Test
+ public void should_fail_if_default_profile_not_used() {
+ fs.addLanguages("java", "cobol");
+ settings.setProperty("sonar.profile", "Unknown");
+
+ QProfileVerifier profileLogger = new QProfileVerifier(settings, fs, profiles);
+
+ 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.");
+
+ profileLogger.execute();
+ }
+
+ @Test
+ public void should_not_fail_if_no_language_on_project() {
+ settings.setProperty("sonar.profile", "Unknown");
+
+ QProfileVerifier profileLogger = new QProfileVerifier(settings, fs, profiles);
+
+ profileLogger.execute();
+
+ }
+
+ @Test
+ public void should_not_fail_if_default_profile_used_at_least_once() {
+ fs.addLanguages("java", "cobol");
+ settings.setProperty("sonar.profile", "My Java profile");
+
+ QProfileVerifier profileLogger = new QProfileVerifier(settings, fs, profiles);
+
+ profileLogger.execute();
+ }
+}
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import org.sonar.api.batch.fs.internal.DefaultFileSystem;
import org.sonar.api.config.Settings;
import org.sonar.api.resources.Java;
import org.sonar.api.resources.Languages;
import org.sonar.api.utils.MessageException;
+import static org.fest.assertions.Assertions.assertThat;
+
public class LanguageVerifierTest {
Settings settings = new Settings();
Languages languages = new Languages(Java.INSTANCE);
+ DefaultFileSystem fs = new DefaultFileSystem();
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void language_is_not_set() throws Exception {
- LanguageVerifier verifier = new LanguageVerifier(settings, languages);
+ LanguageVerifier verifier = new LanguageVerifier(settings, languages, fs);
verifier.start();
- // no failure
+ // no failure and no language is forced
+ assertThat(fs.languages()).isEmpty();
verifier.stop();
}
public void language_is_valid() throws Exception {
settings.setProperty("sonar.language", "java");
- LanguageVerifier verifier = new LanguageVerifier(settings, languages);
+ LanguageVerifier verifier = new LanguageVerifier(settings, languages, fs);
verifier.start();
- // no failure
+ // no failure and language is hardly registered
+ assertThat(fs.languages()).contains("java");
verifier.stop();
}
thrown.expectMessage("You must install a plugin that supports the language 'php'");
settings.setProperty("sonar.language", "php");
- LanguageVerifier verifier = new LanguageVerifier(settings, languages);
+ LanguageVerifier verifier = new LanguageVerifier(settings, languages, fs);
verifier.start();
}
}
return new Alert(getRulesProfile(), getMetric(), getOperator(), getValueError(), getValueWarning(), getPeriod());
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Alert alert = (Alert) o;
+ if (metric != null ? !metric.equals(alert.metric) : alert.metric != null) {
+ return false;
+ }
+ if (period != null ? !period.equals(alert.period) : alert.period != null) {
+ return false;
+ }
+ return !(rulesProfile != null ? !rulesProfile.equals(alert.rulesProfile) : alert.rulesProfile != null);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = rulesProfile != null ? rulesProfile.hashCode() : 0;
+ result = 31 * result + (metric != null ? metric.hashCode() : 0);
+ result = 31 * result + (period != null ? period.hashCode() : 0);
+ return result;
+ }
}