diff options
Diffstat (limited to 'sonar-batch/src/test/java/org/sonar/batch')
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; + } + } |