aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-batch/src/test/java/org/sonar/batch
diff options
context:
space:
mode:
Diffstat (limited to 'sonar-batch/src/test/java/org/sonar/batch')
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/qualitygate/ConditionUtilsTest.java262
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateProviderTest.java29
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateVerifierTest.java326
3 files changed, 610 insertions, 7 deletions
diff --git a/sonar-batch/src/test/java/org/sonar/batch/qualitygate/ConditionUtilsTest.java b/sonar-batch/src/test/java/org/sonar/batch/qualitygate/ConditionUtilsTest.java
new file mode 100644
index 00000000000..94239a1963e
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/qualitygate/ConditionUtilsTest.java
@@ -0,0 +1,262 @@
+/*
+ * 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.core.qualitygate.db.QualityGateConditionDto;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ConditionUtilsTest {
+
+ private Metric metric;
+ private Measure measure;
+ private ResolvedCondition condition;
+
+ @Before
+ public void setup() {
+ metric = new Metric.Builder("test-metric", "name", Metric.ValueType.FLOAT).create();
+ measure = new Measure();
+ measure.setMetric(metric);
+ condition = mock(ResolvedCondition.class);
+ when(condition.period()).thenReturn(null);
+ }
+
+ @Test
+ public void testInputNumbers() {
+ metric.setType(Metric.ValueType.FLOAT);
+ measure.setValue(10.2d);
+ when(condition.operator()).thenReturn(QualityGateConditionDto.OPERATOR_LESS_THAN);
+ when(condition.metric()).thenReturn(metric);
+
+ try {
+ metric.setType(Metric.ValueType.FLOAT);
+ when(condition.errorThreshold()).thenReturn("20");
+ ConditionUtils.getLevel(condition, measure);
+ } catch (NumberFormatException ex) {
+ Assert.fail();
+ }
+
+ try {
+ metric.setType(Metric.ValueType.INT);
+ when(condition.errorThreshold()).thenReturn("20.1");
+ ConditionUtils.getLevel(condition, measure);
+ } catch (NumberFormatException ex) {
+ Assert.fail();
+ }
+
+ try {
+ metric.setType(Metric.ValueType.PERCENT);
+ when(condition.errorThreshold()).thenReturn("20.1");
+ ConditionUtils.getLevel(condition, measure);
+ } catch (NumberFormatException ex) {
+ Assert.fail();
+ }
+ }
+
+ @Test
+ public void testEquals() {
+
+ metric.setType(Metric.ValueType.FLOAT);
+ measure.setValue(10.2d);
+ when(condition.operator()).thenReturn(QualityGateConditionDto.OPERATOR_EQUALS);
+ when(condition.metric()).thenReturn(metric);
+
+ when(condition.errorThreshold()).thenReturn("10.2");
+ Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+
+ when(condition.errorThreshold()).thenReturn("10.1");
+ Assert.assertEquals(Metric.Level.OK, ConditionUtils.getLevel(condition, measure));
+
+ metric.setType(Metric.ValueType.STRING);
+ measure.setData("TEST");
+ measure.setValue(null);
+
+ when(condition.errorThreshold()).thenReturn("TEST");
+ Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+
+ when(condition.errorThreshold()).thenReturn("TEST2");
+ Assert.assertEquals(Metric.Level.OK, ConditionUtils.getLevel(condition, measure));
+
+ }
+
+ @Test
+ public void testNotEquals() {
+
+ metric.setType(Metric.ValueType.FLOAT);
+ measure.setValue(10.2d);
+ when(condition.operator()).thenReturn(QualityGateConditionDto.OPERATOR_NOT_EQUALS);
+ when(condition.metric()).thenReturn(metric);
+
+ when(condition.errorThreshold()).thenReturn("10.2");
+ Assert.assertEquals(Metric.Level.OK, ConditionUtils.getLevel(condition, measure));
+
+ when(condition.errorThreshold()).thenReturn("10.1");
+ Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+
+ metric.setType(Metric.ValueType.STRING);
+ measure.setData("TEST");
+ measure.setValue(null);
+
+ when(condition.errorThreshold()).thenReturn("TEST");
+ Assert.assertEquals(Metric.Level.OK, ConditionUtils.getLevel(condition, measure));
+
+ when(condition.errorThreshold()).thenReturn("TEST2");
+ Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+
+ }
+
+ @Test
+ public void testGreater() {
+ metric.setType(Metric.ValueType.FLOAT);
+ measure.setValue(10.2d);
+ when(condition.operator()).thenReturn(QualityGateConditionDto.OPERATOR_GREATER_THAN);
+ when(condition.metric()).thenReturn(metric);
+
+ when(condition.errorThreshold()).thenReturn("10.1");
+ Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+
+ when(condition.errorThreshold()).thenReturn("10.3");
+ Assert.assertEquals(Metric.Level.OK, ConditionUtils.getLevel(condition, measure));
+ }
+
+ @Test
+ public void testSmaller() {
+ metric.setType(Metric.ValueType.FLOAT);
+ measure.setValue(10.2d);
+ when(condition.operator()).thenReturn(QualityGateConditionDto.OPERATOR_LESS_THAN);
+ when(condition.metric()).thenReturn(metric);
+
+ when(condition.errorThreshold()).thenReturn("10.1");
+ Assert.assertEquals(Metric.Level.OK, ConditionUtils.getLevel(condition, measure));
+
+ when(condition.errorThreshold()).thenReturn("10.3");
+ Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+ }
+
+ @Test
+ public void testPercent() {
+ metric.setType(Metric.ValueType.PERCENT);
+ measure.setValue(10.2d);
+ when(condition.operator()).thenReturn(QualityGateConditionDto.OPERATOR_EQUALS);
+ when(condition.metric()).thenReturn(metric);
+
+ when(condition.errorThreshold()).thenReturn("10.2");
+ Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+ }
+
+ @Test
+ public void testFloat() {
+ metric.setType(Metric.ValueType.FLOAT);
+ measure.setValue(10.2d);
+ when(condition.operator()).thenReturn(QualityGateConditionDto.OPERATOR_EQUALS);
+ when(condition.metric()).thenReturn(metric);
+
+ when(condition.errorThreshold()).thenReturn("10.2");
+ Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+ }
+
+ @Test
+ public void testInteger() {
+ metric.setType(Metric.ValueType.INT);
+ measure.setValue(10.2d);
+ when(condition.operator()).thenReturn(QualityGateConditionDto.OPERATOR_EQUALS);
+ when(condition.metric()).thenReturn(metric);
+
+ when(condition.errorThreshold()).thenReturn("10");
+ Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+
+ when(condition.errorThreshold()).thenReturn("10.2");
+ Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+ }
+
+ @Test
+ public void testLevel() {
+ metric.setType(Metric.ValueType.LEVEL);
+ measure.setData(Metric.Level.ERROR.toString());
+ when(condition.operator()).thenReturn(QualityGateConditionDto.OPERATOR_EQUALS);
+ when(condition.metric()).thenReturn(metric);
+
+ when(condition.errorThreshold()).thenReturn(Metric.Level.ERROR.toString());
+ Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+
+ when(condition.errorThreshold()).thenReturn(Metric.Level.OK.toString());
+ Assert.assertEquals(Metric.Level.OK, ConditionUtils.getLevel(condition, measure));
+
+ when(condition.operator()).thenReturn(QualityGateConditionDto.OPERATOR_NOT_EQUALS);
+ Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+ }
+
+ @Test
+ public void testBooleans() {
+ metric.setType(Metric.ValueType.BOOL);
+ measure.setValue(0d);
+ when(condition.operator()).thenReturn(QualityGateConditionDto.OPERATOR_EQUALS);
+ when(condition.metric()).thenReturn(metric);
+
+ when(condition.errorThreshold()).thenReturn("1");
+ Assert.assertEquals(Metric.Level.OK, ConditionUtils.getLevel(condition, measure));
+
+ when(condition.errorThreshold()).thenReturn("0");
+ Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+
+ when(condition.operator()).thenReturn(QualityGateConditionDto.OPERATOR_NOT_EQUALS);
+ when(condition.errorThreshold()).thenReturn("1");
+ Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+
+ when(condition.errorThreshold()).thenReturn("0");
+ Assert.assertEquals(Metric.Level.OK, ConditionUtils.getLevel(condition, measure));
+ }
+
+ @Test
+ public void test_work_duration() {
+ metric.setType(Metric.ValueType.WORK_DUR);
+ measure.setValue(60.0d);
+ when(condition.operator()).thenReturn(QualityGateConditionDto.OPERATOR_EQUALS);
+ when(condition.metric()).thenReturn(metric);
+
+ when(condition.errorThreshold()).thenReturn("60");
+ Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+ }
+
+ @Test
+ public void testErrorAndWarningLevel() {
+ metric.setType(Metric.ValueType.FLOAT);
+ measure.setValue(10.2d);
+ when(condition.operator()).thenReturn(QualityGateConditionDto.OPERATOR_EQUALS);
+ when(condition.metric()).thenReturn(metric);
+
+ when(condition.errorThreshold()).thenReturn("10.2");
+ Assert.assertEquals(Metric.Level.ERROR, ConditionUtils.getLevel(condition, measure));
+
+ when(condition.errorThreshold()).thenReturn("10.1");
+ Assert.assertEquals(Metric.Level.OK, ConditionUtils.getLevel(condition, measure));
+
+ when(condition.errorThreshold()).thenReturn("10.3");
+ when(condition.warningThreshold()).thenReturn("10.2");
+ Assert.assertEquals(Metric.Level.WARN, ConditionUtils.getLevel(condition, measure));
+ }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateProviderTest.java b/sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateProviderTest.java
index 746c47f3a58..46729866736 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateProviderTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateProviderTest.java
@@ -19,6 +19,7 @@
*/
package org.sonar.batch.qualitygate;
+import com.google.common.collect.ImmutableList;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -26,14 +27,17 @@ import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.slf4j.Logger;
import org.sonar.api.config.Settings;
+import org.sonar.api.measures.MetricFinder;
import org.sonar.api.utils.MessageException;
import org.sonar.batch.bootstrap.ServerClient;
import org.sonar.wsclient.SonarClient;
import org.sonar.wsclient.base.HttpException;
import org.sonar.wsclient.qualitygate.QualityGateClient;
+import org.sonar.wsclient.qualitygate.QualityGateCondition;
import org.sonar.wsclient.qualitygate.QualityGateDetails;
import java.net.HttpURLConnection;
+import java.util.Collection;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Mockito.mock;
@@ -50,6 +54,9 @@ public class QualityGateProviderTest {
private ServerClient client;
@Mock
+ private MetricFinder metricFinder;
+
+ @Mock
private QualityGateClient qualityGateClient;
@Mock
@@ -64,8 +71,8 @@ public class QualityGateProviderTest {
@Test
public void should_load_empty_quality_gate_from_default_settings() {
- assertThat(new QualityGateProvider().provide(settings, client).conditions()).isEmpty();
- assertThat(new QualityGateProvider().init(settings, client, logger).isEnabled()).isFalse();
+ assertThat(new QualityGateProvider().provide(settings, client, metricFinder).conditions()).isEmpty();
+ assertThat(new QualityGateProvider().init(settings, client, metricFinder, logger).isEnabled()).isFalse();
verify(logger).info("No quality gate is configured.");
}
@@ -76,7 +83,7 @@ public class QualityGateProviderTest {
QualityGateDetails qGate = mock(QualityGateDetails.class);
when(qualityGateClient.show(qGateName)).thenReturn(qGate);
when(qGate.name()).thenReturn(qGateName);
- QualityGate actualGate = new QualityGateProvider().init(settings, client, logger);
+ QualityGate actualGate = new QualityGateProvider().init(settings, client, metricFinder, logger);
assertThat(actualGate.name()).isEqualTo(qGateName);
assertThat(actualGate.isEnabled()).isTrue();
verify(logger).info("Loaded quality gate '{}'", qGateName);
@@ -90,8 +97,18 @@ public class QualityGateProviderTest {
QualityGateDetails qGate = mock(QualityGateDetails.class);
when(qualityGateClient.show(qGateId)).thenReturn(qGate);
when(qGate.name()).thenReturn(qGateName);
- assertThat(new QualityGateProvider().init(settings, client, logger).name()).isEqualTo(qGateName);
+ String metricKey1 = "metric1";
+ QualityGateCondition serverCondition1 = mock(QualityGateCondition.class);
+ when(serverCondition1.metricKey()).thenReturn(metricKey1);
+ String metricKey2 = "metric2";
+ QualityGateCondition serverCondition2 = mock(QualityGateCondition.class);
+ when(serverCondition2.metricKey()).thenReturn(metricKey2);
+ Collection<QualityGateCondition> conditions = ImmutableList.of(serverCondition1, serverCondition2);
+ when(qGate.conditions()).thenReturn(conditions);
+ assertThat(new QualityGateProvider().init(settings, client, metricFinder, logger).name()).isEqualTo(qGateName);
verify(logger).info("Loaded quality gate '{}'", qGateName);
+ verify(metricFinder).findByKey(metricKey1);
+ verify(metricFinder).findByKey(metricKey2);
}
@Test(expected = MessageException.class)
@@ -99,7 +116,7 @@ public class QualityGateProviderTest {
String qGateName = "Sonar way";
when(settings.getString("sonar.qualitygate")).thenReturn(qGateName);
when(qualityGateClient.show(qGateName)).thenThrow(new HttpException("http://server/api/qualitygates/show?name=Sonar%20way", HttpURLConnection.HTTP_NOT_FOUND));
- new QualityGateProvider().provide(settings, client);
+ new QualityGateProvider().provide(settings, client, metricFinder);
}
@Test(expected = HttpException.class)
@@ -107,7 +124,7 @@ public class QualityGateProviderTest {
String qGateName = "Sonar way";
when(settings.getString("sonar.qualitygate")).thenReturn(qGateName);
when(qualityGateClient.show(qGateName)).thenThrow(new HttpException("http://server/api/qualitygates/show?name=Sonar%20way", HttpURLConnection.HTTP_NOT_ACCEPTABLE));
- new QualityGateProvider().provide(settings, client);
+ new QualityGateProvider().provide(settings, client, metricFinder);
}
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateVerifierTest.java b/sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateVerifierTest.java
index 4a56c238971..53977f6b9f7 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateVerifierTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateVerifierTest.java
@@ -19,24 +19,39 @@
*/
package org.sonar.batch.qualitygate;
+import com.google.common.collect.ImmutableList;
+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.resources.File;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
+import org.sonar.api.test.IsMeasure;
+import org.sonar.api.utils.Duration;
+import org.sonar.api.utils.Durations;
+import org.sonar.core.qualitygate.db.QualityGateConditionDto;
import org.sonar.core.timemachine.Periods;
+import java.util.ArrayList;
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.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class QualityGateVerifierTest {
@@ -52,6 +67,7 @@ public class QualityGateVerifierTest {
Snapshot snapshot;
Periods periods;
I18n i18n;
+ Durations durations;
@Before
public void before() {
@@ -59,6 +75,7 @@ public class QualityGateVerifierTest {
periods = mock(Periods.class);
i18n = mock(I18n.class);
when(i18n.message(any(Locale.class), eq("variation"), eq("variation"))).thenReturn("variation");
+ durations = mock(Durations.class);
measureClasses = new Measure(CoreMetrics.CLASSES, 20d);
measureCoverage = new Measure(CoreMetrics.COVERAGE, 35d);
@@ -71,7 +88,7 @@ public class QualityGateVerifierTest {
snapshot = mock(Snapshot.class);
qualityGate = mock(QualityGate.class);
when(qualityGate.isEnabled()).thenReturn(true);
- verifier = new QualityGateVerifier(qualityGate);
+ verifier = new QualityGateVerifier(qualityGate, snapshot, periods, i18n, durations);
project = new Project("foo");
}
@@ -97,4 +114,311 @@ public class QualityGateVerifierTest {
assertThat(verifier.dependsOnVariations()).isEqualTo(DecoratorBarriers.END_OF_TIME_MACHINE);
}
+ @Test
+ public void depends_upon_metrics() {
+ when(qualityGate.conditions()).thenReturn(ImmutableList.of(new ResolvedCondition(null, CoreMetrics.CLASSES)));
+ assertThat(verifier.dependsUponMetrics()).containsOnly(CoreMetrics.CLASSES);
+ }
+
+ @Test
+ public void ok_when_no_alerts() {
+ ArrayList<ResolvedCondition> conditions = Lists.newArrayList(
+ mockCondition(CoreMetrics.CLASSES, QualityGateConditionDto.OPERATOR_GREATER_THAN, null, "20"),
+ mockCondition(CoreMetrics.COVERAGE, QualityGateConditionDto.OPERATOR_GREATER_THAN, null, "35.0"));
+ when(qualityGate.conditions()).thenReturn(conditions);
+
+ verifier.decorate(project, context);
+
+ verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.QUALITY_GATE_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() {
+ ArrayList<ResolvedCondition> conditions = Lists.newArrayList(
+ mockCondition(CoreMetrics.CLASSES, QualityGateConditionDto.OPERATOR_GREATER_THAN, null, "20"),
+ mockCondition(CoreMetrics.COVERAGE, QualityGateConditionDto.OPERATOR_GREATER_THAN, null, "35.0"));
+ when(qualityGate.conditions()).thenReturn(conditions);
+
+ verifier.decorate(File.create("src/Foo.php"), context);
+
+ verify(context, never()).saveMeasure(any(Measure.class));
+ }
+
+ @Test
+ public void generate_warnings() {
+ ArrayList<ResolvedCondition> conditions = Lists.newArrayList(
+ mockCondition(CoreMetrics.CLASSES, QualityGateConditionDto.OPERATOR_GREATER_THAN, null, "100"),
+ mockCondition(CoreMetrics.COVERAGE, QualityGateConditionDto.OPERATOR_LESS_THAN, null, "95.0")); // generates warning because coverage 35% < 95%
+ when(qualityGate.conditions()).thenReturn(conditions);
+
+ verifier.decorate(project, context);
+
+ verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.QUALITY_GATE_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() {
+ ArrayList<ResolvedCondition> conditions = Lists.newArrayList(
+ mockCondition(CoreMetrics.CLASSES, QualityGateConditionDto.OPERATOR_LESS_THAN, null, "100"), // generates warning because classes 20 < 100
+ mockCondition(CoreMetrics.COVERAGE, QualityGateConditionDto.OPERATOR_LESS_THAN, "50.0", "80.0")); // generates error because coverage 35% < 50%
+ when(qualityGate.conditions()).thenReturn(conditions);
+
+ verifier.decorate(project, context);
+
+ verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.QUALITY_GATE_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");
+ ArrayList<ResolvedCondition> conditions = Lists.newArrayList(
+ mockCondition(CoreMetrics.CLASSES, QualityGateConditionDto.OPERATOR_LESS_THAN, null, "10000"), // there are 20 classes, error threshold is higher => alert
+ mockCondition(CoreMetrics.COVERAGE, QualityGateConditionDto.OPERATOR_LESS_THAN, "50.0", "80.0"));// coverage is 35%, warning threshold is higher => alert
+ when(qualityGate.conditions()).thenReturn(conditions);
+
+ verifier.decorate(project, context);
+
+ verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.QUALITY_GATE_STATUS, Metric.Level.ERROR, "Classes LT 10000, Coverages LT 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));
+ ArrayList<ResolvedCondition> conditions = Lists.newArrayList(mockCondition(metric, QualityGateConditionDto.OPERATOR_LESS_THAN, "10", null));
+ when(qualityGate.conditions()).thenReturn(conditions);
+ verifier.decorate(project, context);
+
+ verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.QUALITY_GATE_STATUS, Metric.Level.ERROR, "THE RATING LT 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");
+ ArrayList<ResolvedCondition> conditions = Lists.newArrayList(
+ // there are 20 classes, error threshold is higher => alert
+ mockCondition(CoreMetrics.CLASSES, QualityGateConditionDto.OPERATOR_LESS_THAN, "10000", null)
+ );
+ when(qualityGate.conditions()).thenReturn(conditions);
+
+ verifier.decorate(project, context);
+
+ verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.QUALITY_GATE_STATUS, Metric.Level.ERROR, "Classes LT 10000")));
+ }
+
+ @Test
+ public void shouldBeOkIfPeriodVariationIsEnough() {
+ measureClasses.setVariation1(0d);
+ measureCoverage.setVariation2(50d);
+ measureComplexity.setVariation3(2d);
+
+ ArrayList<ResolvedCondition> conditions = Lists.newArrayList(
+ mockCondition(CoreMetrics.CLASSES, QualityGateConditionDto.OPERATOR_GREATER_THAN, null, "10", 1), // ok because no variation
+ mockCondition(CoreMetrics.COVERAGE, QualityGateConditionDto.OPERATOR_LESS_THAN, null, "40.0", 2), // ok because coverage increases of 50%, which is more
+ // than 40%
+ mockCondition(CoreMetrics.COMPLEXITY, QualityGateConditionDto.OPERATOR_GREATER_THAN, null, "5", 3) // ok because complexity increases of 2, which is less
+ // than 5
+ );
+ when(qualityGate.conditions()).thenReturn(conditions);
+
+ verifier.decorate(project, context);
+
+ verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.QUALITY_GATE_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);
+
+ ArrayList<ResolvedCondition> conditions = Lists.newArrayList(
+ mockCondition(CoreMetrics.CLASSES, QualityGateConditionDto.OPERATOR_GREATER_THAN, null, "30", 1), // generates warning because classes increases of 40,
+ // which is greater than 30
+ mockCondition(CoreMetrics.COVERAGE, QualityGateConditionDto.OPERATOR_LESS_THAN, null, "10.0", 2), // generates warning because coverage increases of 5%,
+ // which is smaller than 10%
+ mockCondition(CoreMetrics.COMPLEXITY, QualityGateConditionDto.OPERATOR_GREATER_THAN, null, "60", 3) // generates warning because complexity increases of
+ // 70, which is smaller than 60
+ );
+ when(qualityGate.conditions()).thenReturn(conditions);
+
+ verifier.decorate(project, context);
+
+ verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.QUALITY_GATE_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);
+
+ ArrayList<ResolvedCondition> conditions = Lists.newArrayList(
+ mockCondition(CoreMetrics.CLASSES, QualityGateConditionDto.OPERATOR_GREATER_THAN, null, "10", 1));
+ when(qualityGate.conditions()).thenReturn(conditions);
+
+ verifier.decorate(project, context);
+
+ verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.QUALITY_GATE_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);
+
+ ArrayList<ResolvedCondition> conditions = Lists.newArrayList(
+ mockCondition(ratingMetric, QualityGateConditionDto.OPERATOR_GREATER_THAN, null, "100", 1)
+ );
+ when(qualityGate.conditions()).thenReturn(conditions);
+
+ verifier.decorate(project, context);
+
+ verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.QUALITY_GATE_STATUS, Metric.Level.OK, null)));
+ //verify(context).saveMeasure(argThat(hasLevel(measureRatingMetric, Metric.Level.OK)));
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void shouldAllowOnlyVariationPeriodOneGlobalPeriods() {
+ measureClasses.setVariation4(40d);
+
+ ArrayList<ResolvedCondition> conditions = Lists.newArrayList(
+ mockCondition(CoreMetrics.CLASSES, QualityGateConditionDto.OPERATOR_GREATER_THAN, null, "30", 4)
+ );
+ when(qualityGate.conditions()).thenReturn(conditions);
+
+ 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);
+
+ ArrayList<ResolvedCondition> conditions = Lists.newArrayList(
+ mockCondition(CoreMetrics.SCM_AUTHORS_BY_LINE, QualityGateConditionDto.OPERATOR_GREATER_THAN, null, "30", 1)
+ );
+ when(qualityGate.conditions()).thenReturn(conditions);
+
+ 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");
+
+ ArrayList<ResolvedCondition> conditions = Lists.newArrayList(
+ mockCondition(CoreMetrics.CLASSES, QualityGateConditionDto.OPERATOR_GREATER_THAN, null, "30", 1) // generates warning because classes increases of 40,
+ // which is greater than 30
+ );
+ when(qualityGate.conditions()).thenReturn(conditions);
+
+ verifier.decorate(project, context);
+
+ verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.QUALITY_GATE_STATUS, Metric.Level.WARN, "Classes variation GT 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");
+
+ ArrayList<ResolvedCondition> conditions = Lists.newArrayList(
+ mockCondition(newMetric, QualityGateConditionDto.OPERATOR_GREATER_THAN, null, "30", 1) // generates warning because classes increases of 40, which is
+ // greater than 30
+ );
+ when(qualityGate.conditions()).thenReturn(conditions);
+
+ verifier.decorate(project, context);
+
+ verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.QUALITY_GATE_STATUS, Metric.Level.WARN, "New Measure GT 30 since someday")));
+ }
+
+ @Test
+ public void alert_on_work_duration() {
+ Metric metric = new Metric.Builder("tech_debt", "Debt", Metric.ValueType.WORK_DUR).create();
+
+ // metric name is declared in l10n bundle
+ when(i18n.message(any(Locale.class), eq("metric.tech_debt.name"), anyString())).thenReturn("The Debt");
+ when(durations.format(any(Locale.class), eq(Duration.create(3600L)), eq(Durations.DurationFormat.SHORT))).thenReturn("1h");
+
+ when(context.getMeasure(metric)).thenReturn(new Measure(metric, 1800d));
+ ArrayList<ResolvedCondition> conditions = Lists.newArrayList(mockCondition(metric, QualityGateConditionDto.OPERATOR_LESS_THAN, "3600", null));
+ when(qualityGate.conditions()).thenReturn(conditions);
+ verifier.decorate(project, context);
+
+ verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.QUALITY_GATE_STATUS, Metric.Level.ERROR, "The Debt LT 1h")));
+ }
+
+ 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);
+ }
+ };
+ }
+
+ private ResolvedCondition mockCondition(Metric metric, String operator, String error, String warning) {
+ return mockCondition(metric, operator, error, warning, null);
+ }
+
+ private ResolvedCondition mockCondition(Metric metric, String operator, String error, String warning, Integer period) {
+ ResolvedCondition cond = mock(ResolvedCondition.class);
+ when(cond.metric()).thenReturn(metric);
+ when(cond.metricKey()).thenReturn(metric.getKey());
+ when(cond.operator()).thenReturn(operator);
+ when(cond.warningThreshold()).thenReturn(warning);
+ when(cond.errorThreshold()).thenReturn(error);
+ when(cond.period()).thenReturn(period);
+ return cond;
+ }
+
}