From 6abce5190a0a399f0b7e0af09cda6e86b2188284 Mon Sep 17 00:00:00 2001 From: =?utf8?q?S=C3=A9bastien=20Lesaint?= Date: Thu, 4 Jun 2015 18:24:02 +0200 Subject: [PATCH] SONAR-6620 remove Dto and BatchReport object from MeasureRepository --- .../container/ComputeEngineContainerImpl.java | 8 +- .../measure/BatchMeasureToMeasure.java | 118 ++++++++ .../server/computation/measure/Measure.java | 176 +++++++++++ .../measure/MeasureDtoToMeasure.java | 121 ++++++++ .../computation/measure/MeasureImpl.java | 148 +++++++++ .../measure/MeasureRepository.java | 38 ++- .../measure/MeasureRepositoryImpl.java | 124 +++++++- .../server/computation/metric/Metric.java | 57 ++++ .../server/computation/metric/MetricImpl.java | 82 +++++ .../computation/metric/MetricRepository.java | 35 +++ .../metric/MetricRepositoryImpl.java | 53 ++++ .../computation/metric/package-info.java | 24 ++ .../step/QualityGateEventsStep.java | 106 +++---- .../step/QualityProfileEventsStep.java | 56 ++-- .../measure/BatchMeasureToMeasureTest.java | 282 ++++++++++++++++++ .../measure/MeasureDtoToMeasureTest.java | 276 +++++++++++++++++ .../computation/measure/MeasureImplTest.java | 260 ++++++++++++++++ .../measure/MeasureRepositoryImplTest.java | 247 +++++++++++++++ .../computation/metric/MetricImplTest.java | 82 +++++ .../metric/MetricRepositoryImplTest.java | 105 +++++++ .../step/QualityGateEventsStepTest.java | 196 ++++++------ .../step/QualityProfileEventsStepTest.java | 36 +-- .../MeasureRepositoryImplTest/shared.xml | 7 + 23 files changed, 2412 insertions(+), 225 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/measure/BatchMeasureToMeasure.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/measure/Measure.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureDtoToMeasure.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureImpl.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/metric/Metric.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/metric/MetricImpl.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/metric/MetricRepository.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/metric/MetricRepositoryImpl.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/metric/package-info.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/measure/BatchMeasureToMeasureTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureDtoToMeasureTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureImplTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureRepositoryImplTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/metric/MetricImplTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/metric/MetricRepositoryImplTest.java create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/computation/measure/MeasureRepositoryImplTest/shared.xml diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java index 160bdac97af..f3386a42786 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java @@ -51,6 +51,7 @@ import org.sonar.server.computation.issue.SourceLinesCache; import org.sonar.server.computation.language.PlatformLanguageRepository; import org.sonar.server.computation.measure.MeasureRepositoryImpl; import org.sonar.server.computation.measure.MetricCache; +import org.sonar.server.computation.metric.MetricRepositoryImpl; import org.sonar.server.computation.period.PeriodsHolderImpl; import org.sonar.server.computation.step.ComputationStep; import org.sonar.server.computation.step.ComputationSteps; @@ -123,18 +124,19 @@ public class ComputeEngineContainerImpl extends ComponentContainer implements Co return Arrays.asList( ActivityManager.class, + // holders + BatchReportDirectoryHolderImpl.class, TreeRootHolderImpl.class, + PeriodsHolderImpl.class, BatchReportReaderImpl.class, - BatchReportDirectoryHolderImpl.class, - // repositories PlatformLanguageRepository.class, + MetricRepositoryImpl.class, MeasureRepositoryImpl.class, EventRepositoryImpl.class, ProjectSettingsRepository.class, - PeriodsHolderImpl.class, DbIdsRepository.class, // issues diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/measure/BatchMeasureToMeasure.java b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/BatchMeasureToMeasure.java new file mode 100644 index 00000000000..35a6966560b --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/BatchMeasureToMeasure.java @@ -0,0 +1,118 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 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.server.computation.measure; + +import com.google.common.base.Optional; +import java.util.Objects; +import javax.annotation.Nullable; +import org.sonar.batch.protocol.output.BatchReport; +import org.sonar.server.computation.metric.Metric; + +public class BatchMeasureToMeasure { + + public Optional toMeasure(@Nullable BatchReport.Measure batchMeasure, Metric metric) { + Objects.requireNonNull(metric); + if (batchMeasure == null) { + return Optional.absent(); + } + + String data = batchMeasure.hasStringValue() ? batchMeasure.getStringValue() : null; + switch (metric.getMetricType().getValueType()) { + case INT: + return toIntegerMeasure(batchMeasure, data); + case LONG: + return toLongMeasure(batchMeasure, data); + case DOUBLE: + return toDoubleMeasure(batchMeasure, data); + case BOOLEAN: + return toBooleanMeasure(batchMeasure, data); + case STRING: + return toStringMeasure(batchMeasure); + case LEVEL: + return toLevelMeasure(batchMeasure); + case NO_VALUE: + return toNoValueMeasure(batchMeasure); + default: + throw new IllegalArgumentException("Unsupported Measure.ValueType " + metric.getMetricType().getValueType()); + } + } + + private static Optional toIntegerMeasure(BatchReport.Measure batchMeasure, @Nullable String data) { + if (!batchMeasure.hasIntValue()) { + return toMeasure(MeasureImpl.createNoValue(), batchMeasure); + } + return toMeasure(MeasureImpl.create(batchMeasure.getIntValue(), data), batchMeasure); + } + + private static Optional toLongMeasure(BatchReport.Measure batchMeasure, @Nullable String data) { + if (!batchMeasure.hasLongValue()) { + return toMeasure(MeasureImpl.createNoValue(), batchMeasure); + } + return toMeasure(MeasureImpl.create(batchMeasure.getLongValue(), data), batchMeasure); + } + + private static Optional toDoubleMeasure(BatchReport.Measure batchMeasure, @Nullable String data) { + if (!batchMeasure.hasDoubleValue()) { + return toMeasure(MeasureImpl.createNoValue(), batchMeasure); + } + return toMeasure(MeasureImpl.create(batchMeasure.getDoubleValue(), data), batchMeasure); + } + + private static Optional toBooleanMeasure(BatchReport.Measure batchMeasure, @Nullable String data) { + if (!batchMeasure.hasBooleanValue()) { + return toMeasure(MeasureImpl.createNoValue(), batchMeasure); + } + return toMeasure(MeasureImpl.create(batchMeasure.getBooleanValue(), data), batchMeasure); + } + + private static Optional toStringMeasure(BatchReport.Measure batchMeasure) { + if (!batchMeasure.hasStringValue()) { + return toMeasure(MeasureImpl.createNoValue(), batchMeasure); + } + return toMeasure(MeasureImpl.create(batchMeasure.getStringValue()), batchMeasure); + } + + private static Optional toLevelMeasure(BatchReport.Measure batchMeasure) { + if (!batchMeasure.hasStringValue()) { + return toMeasure(MeasureImpl.createNoValue(), batchMeasure); + } + Optional level = Measure.Level.toLevel(batchMeasure.getStringValue()); + if (!level.isPresent()) { + return toMeasure(MeasureImpl.createNoValue(), batchMeasure); + } + return toMeasure(MeasureImpl.create(level.get()), batchMeasure); + } + + private static Optional toNoValueMeasure(BatchReport.Measure batchMeasure) { + return toMeasure(MeasureImpl.createNoValue(), batchMeasure); + } + + private static Optional toMeasure(MeasureImpl measure, BatchReport.Measure batchMeasure) { + if (batchMeasure.hasAlertStatus() && !measure.hasQualityGateStatus()) { + Optional qualityGateStatus = Measure.Level.toLevel(batchMeasure.getAlertStatus()); + if (qualityGateStatus.isPresent()) { + String text = batchMeasure.hasAlertText() ? batchMeasure.getAlertText() : null; + measure.setQualityGateStatus(new Measure.QualityGateStatus(qualityGateStatus.get(), text)); + } + } + return Optional.of((Measure) measure); + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/measure/Measure.java b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/Measure.java new file mode 100644 index 00000000000..7d420c8c519 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/Measure.java @@ -0,0 +1,176 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 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.server.computation.measure; + +import com.google.common.base.Objects; +import com.google.common.base.Optional; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +import static java.util.Objects.requireNonNull; + +public interface Measure { + + enum ValueType { + NO_VALUE, BOOLEAN, INT, LONG, DOUBLE, STRING, LEVEL + } + + enum Level { + OK("Green"), + WARN("Orange"), + ERROR("Red"); + + private final String colorName; + + Level(String colorName) { + this.colorName = colorName; + } + + public String getColorName() { + return colorName; + } + + public static Optional toLevel(@Nullable String level) { + if (level == null) { + return Optional.absent(); + } + try { + return Optional.of(Level.valueOf(level)); + } catch (IllegalArgumentException e) { + return Optional.absent(); + } + } + } + + /** + * A QualityGate status has a level and an optional describing text. + */ + @Immutable + final class QualityGateStatus { + private final Level status; + @CheckForNull + private final String text; + + /** + * Creates a QualityGateStatus without a text. + */ + public QualityGateStatus(Level status) { + this(status, null); + } + + /** + * Creates a QualityGateStatus with a optional text. + */ + public QualityGateStatus(Level status, @Nullable String text) { + this.status = requireNonNull(status); + this.text = text; + } + + public Level getStatus() { + return status; + } + + @CheckForNull + public String getText() { + return text; + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("status", status) + .add("text", text) + .toString(); + } + } + + /** + * The type of value stored in the measure. + */ + ValueType getValueType(); + + /** + * The value of this measure as a boolean if the type is {@link Measure.ValueType#BOOLEAN}. + * + * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#BOOLEAN} + */ + boolean getBooleanValue(); + + /** + * The value of this measure as a int if the type is {@link Measure.ValueType#INT}. + * + * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#INT} + */ + int getIntValue(); + + /** + * The value of this measure as a long if the type is {@link Measure.ValueType#LONG}. + * + * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#LONG} + */ + long getLongValue(); + + /** + * The value of this measure as a double if the type is {@link Measure.ValueType#DOUBLE}. + * + * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#DOUBLE} + */ + double getDoubleValue(); + + /** + * The value of this measure as a String if the type is {@link Measure.ValueType#STRING}. + * + * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#STRING} + */ + String getStringValue(); + + /** + * The value of this measure as a Level if the type is {@link Measure.ValueType#LEVEL}. + * + * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#LEVEL} + */ + Level getLevelValue(); + + /** + * The data of this measure if it exists. + *

+ * If the measure type is {@link Measure.ValueType#STRING}, the value returned by this function is the same as {@link #getStringValue()}. + *

+ * + * @throws IllegalStateException if the value type of the measure is not {@link Measure.ValueType#BOOLEAN} + */ + @CheckForNull + String getData(); + + /** + * Any Measure, which ever is its value type, can have a QualityGate status. + */ + boolean hasQualityGateStatus(); + + /** + * The QualityGate status for this measure. + * Don't call this method unless you've checked the result of {@link #hasQualityGateStatus()} first + * + * @throws IllegalStateException if the measure has no QualityGate status + */ + QualityGateStatus getQualityGateStatus(); + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureDtoToMeasure.java b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureDtoToMeasure.java new file mode 100644 index 00000000000..c2dad3d403d --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureDtoToMeasure.java @@ -0,0 +1,121 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 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.server.computation.measure; + +import com.google.common.base.Optional; +import java.util.Objects; +import javax.annotation.Nullable; +import org.sonar.core.measure.db.MeasureDto; +import org.sonar.server.computation.metric.Metric; + +import static org.sonar.server.computation.measure.Measure.Level.toLevel; + +public class MeasureDtoToMeasure { + + public Optional toMeasure(@Nullable MeasureDto measureDto, Metric metric) { + Objects.requireNonNull(metric); + if (measureDto == null) { + return Optional.absent(); + } + + Double value = measureDto.getValue(); + String data = measureDto.getData(); + switch (metric.getMetricType().getValueType()) { + case INT: + return toIntegerMeasure(measureDto, value, data); + case LONG: + return toLongMeasure(measureDto, value, data); + case DOUBLE: + return toDoubleMeasure(measureDto, value, data); + case BOOLEAN: + return toBooleanMeasure(measureDto, value, data); + case STRING: + return toStringMeasure(measureDto, data); + case LEVEL: + return toLevelMeasure(measureDto, data); + case NO_VALUE: + return toNoValueMeasure(measureDto); + default: + throw new IllegalArgumentException("Unsupported Measure.ValueType " + metric.getMetricType().getValueType()); + } + } + + private static Optional toIntegerMeasure(MeasureDto measureDto, @Nullable Double value, String data) { + if (value == null) { + return toMeasure(MeasureImpl.createNoValue(), measureDto); + } + return toMeasure(MeasureImpl.create(value.intValue(), data), measureDto); + } + + private static Optional toLongMeasure(MeasureDto measureDto, @Nullable Double value, String data) { + if (value == null) { + return toMeasure(MeasureImpl.createNoValue(), measureDto); + } + return toMeasure(MeasureImpl.create(value.longValue(), data), measureDto); + } + + private static Optional toDoubleMeasure(MeasureDto measureDto, @Nullable Double value, String data) { + if (value == null) { + return toMeasure(MeasureImpl.createNoValue(), measureDto); + } + return toMeasure(MeasureImpl.create(value.doubleValue(), data), measureDto); + } + + private static Optional toBooleanMeasure(MeasureDto measureDto, @Nullable Double value, String data) { + if (value == null) { + return toMeasure(MeasureImpl.createNoValue(), measureDto); + } + return toMeasure(MeasureImpl.create(value == 1.0d, data), measureDto); + } + + private static Optional toStringMeasure(MeasureDto measureDto, @Nullable String data) { + if (data == null) { + return toMeasure(MeasureImpl.createNoValue(), measureDto); + } + return toMeasure(MeasureImpl.create(data), measureDto); + } + + private static Optional toLevelMeasure(MeasureDto measureDto, @Nullable String data) { + if (data == null) { + return toMeasure(MeasureImpl.createNoValue(), measureDto); + } + Optional level = toLevel(data); + if (!level.isPresent()) { + return toMeasure(MeasureImpl.createNoValue(), measureDto); + } + return toMeasure(MeasureImpl.create(level.get()), measureDto); + } + + private static Optional toNoValueMeasure(MeasureDto measureDto) { + return toMeasure(MeasureImpl.createNoValue(), measureDto); + } + + private static Optional toMeasure(MeasureImpl measure, MeasureDto measureDto) { + if (measureDto.getAlertStatus() != null && !measure.hasQualityGateStatus()) { + Optional qualityGateStatus = toLevel(measureDto.getAlertStatus()); + if (qualityGateStatus.isPresent()) { + measure.setQualityGateStatus(new Measure.QualityGateStatus(qualityGateStatus.get(), measureDto.getAlertText())); + } + } + + return Optional.of((Measure) measure); + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureImpl.java new file mode 100644 index 00000000000..b4471e948a3 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureImpl.java @@ -0,0 +1,148 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 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.server.computation.measure; + +import com.google.common.base.Preconditions; +import java.util.Locale; +import javax.annotation.Nullable; + +import static java.util.Objects.requireNonNull; + +public final class MeasureImpl implements Measure { + + private final ValueType valueType; + @Nullable + private final Double value; + @Nullable + private final String data; + @Nullable + private final Level dataLevel; + @Nullable + private QualityGateStatus qualityGateStatus; + + protected MeasureImpl(ValueType valueType, @Nullable Double value, @Nullable String data, @Nullable Level dataLevel) { + this.valueType = valueType; + this.value = value; + this.data = data; + this.dataLevel = dataLevel; + } + + public static MeasureImpl create(boolean value, @Nullable String data) { + return new MeasureImpl(ValueType.BOOLEAN, value ? 1.0d : 0.0d, data, null); + } + + public static MeasureImpl create(int value, @Nullable String data) { + return new MeasureImpl(ValueType.INT, (double) value, data, null); + } + + public static MeasureImpl create(long value, @Nullable String data) { + return new MeasureImpl(ValueType.LONG, (double) value, data, null); + } + + public static MeasureImpl create(double value, @Nullable String data) { + return new MeasureImpl(ValueType.DOUBLE, value, data, null); + } + + public static MeasureImpl create(String value) { + return new MeasureImpl(ValueType.STRING, null, requireNonNull(value), null); + } + + public static MeasureImpl create(Level level) { + return new MeasureImpl(ValueType.LEVEL, null, null, requireNonNull(level)); + } + + public static MeasureImpl createNoValue() { + return new MeasureImpl(ValueType.NO_VALUE, null, null, null); + } + + @Override + public ValueType getValueType() { + return valueType; + } + + @Override + public boolean getBooleanValue() { + checkValueType(ValueType.BOOLEAN); + return value == 1.0d; + } + + @Override + public int getIntValue() { + checkValueType(ValueType.INT); + return value.intValue(); + } + + @Override + public long getLongValue() { + checkValueType(ValueType.LONG); + return value.longValue(); + } + + @Override + public double getDoubleValue() { + checkValueType(ValueType.DOUBLE); + return value; + } + + @Override + public String getStringValue() { + checkValueType(ValueType.STRING); + return data; + } + + @Override + public Level getLevelValue() { + checkValueType(ValueType.LEVEL); + return dataLevel; + } + + @Override + public String getData() { + return data; + } + + private void checkValueType(ValueType expected) { + if (valueType != expected) { + throw new IllegalStateException( + String.format( + "value can not be converted to %s because current value type is a %s", + expected.toString().toLowerCase(Locale.US), + valueType + )); + } + } + + public MeasureImpl setQualityGateStatus(QualityGateStatus qualityGateStatus) { + this.qualityGateStatus = requireNonNull(qualityGateStatus, "Can not set a null QualityGate status"); + return this; + } + + @Override + public boolean hasQualityGateStatus() { + return this.qualityGateStatus != null; + } + + @Override + public QualityGateStatus getQualityGateStatus() { + Preconditions.checkState(qualityGateStatus != null, "Measure does not have an QualityGate status"); + return this.qualityGateStatus; + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureRepository.java b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureRepository.java index 390e3ff62e6..ff01e2c2271 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureRepository.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureRepository.java @@ -19,17 +19,39 @@ */ package org.sonar.server.computation.measure; -import org.sonar.api.measures.Metric; -import org.sonar.batch.protocol.output.BatchReport; - import com.google.common.base.Optional; -import org.sonar.core.measure.db.MeasureDto; +import java.util.Map; import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.metric.Metric; +import org.sonar.server.computation.metric.MetricImpl; public interface MeasureRepository { - // FIXME should not expose MeasureDto from DAO layer - Optional findPrevious(Component component, Metric metric); - // FIXME should not expose Measure from BatchReport - Optional findCurrent(Component component, Metric metric); + /** + * Retrieves the base measure (ie. the one currently existing in DB) for the specified {@link Component} for + * the specified {@link MetricImpl} if it exists. + * + * @throws NullPointerException if either argument is {@code null} + */ + Optional getBaseMeasure(Component component, Metric metric); + + /** + * Retrieves the measure created during the current analysis for the specified {@link Component} for the specified + * {@link MetricImpl} if it exists (ie. one created by the Compute Engine or the Batch). + */ + Optional getRawMeasure(Component component, Metric metric); + + /** + * @return {@link Measure}s for the specified {@link Component} mapped by their metric key. + */ + Map getRawMeasures(Component component); + + /** + * Adds the specified measure for the specified Component and Metric. There can be no more than one measure for a + * specific combination of Component and Metric. + * + * @throws NullPointerException if any of the argument is null + * @throws UnsupportedOperationException when trying to add a measure when one already exists for the specified Component/Metric paar + */ + void add(Component component, Metric metric, Measure measure); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureRepositoryImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureRepositoryImpl.java index 8e7dd40b4ee..ca88815d23a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureRepositoryImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureRepositoryImpl.java @@ -19,39 +19,77 @@ */ package org.sonar.server.computation.measure; +import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import javax.annotation.Nonnull; -import org.sonar.api.measures.Metric; +import javax.annotation.Nullable; import org.sonar.batch.protocol.output.BatchReport; import org.sonar.core.measure.db.MeasureDto; import org.sonar.core.persistence.DbSession; import org.sonar.server.computation.batch.BatchReportReader; import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.metric.Metric; +import org.sonar.server.computation.metric.MetricRepository; import org.sonar.server.db.DbClient; +import static com.google.common.collect.FluentIterable.from; +import static java.util.Objects.requireNonNull; + public class MeasureRepositoryImpl implements MeasureRepository { private final DbClient dbClient; private final BatchReportReader reportReader; + private final MeasureDtoToMeasure measureDtoToMeasure = new MeasureDtoToMeasure(); + private final BatchMeasureToMeasure batchMeasureToMeasure = new BatchMeasureToMeasure(); + private final Function batchMeasureToMeasureFunction; + private final Map> measures = new HashMap<>(); - public MeasureRepositoryImpl(DbClient dbClient, BatchReportReader reportReader) { + public MeasureRepositoryImpl(DbClient dbClient, BatchReportReader reportReader, final MetricRepository metricRepository) { this.dbClient = dbClient; this.reportReader = reportReader; + + this.batchMeasureToMeasureFunction = new Function() { + @Nullable + @Override + public Measure apply(@Nonnull BatchReport.Measure input) { + return batchMeasureToMeasure.toMeasure(input, metricRepository.getByKey(input.getMetricKey())).get(); + } + }; } @Override - public Optional findPrevious(Component component, Metric metric) { + public Optional getBaseMeasure(Component component, Metric metric) { + // fail fast + requireNonNull(component); + requireNonNull(metric); + try (DbSession dbSession = dbClient.openSession(false)) { - return Optional.fromNullable( - dbClient.measureDao().findByComponentKeyAndMetricKey(dbSession, component.getKey(), metric.getKey()) - ); + MeasureDto measureDto = dbClient.measureDao().findByComponentKeyAndMetricKey(dbSession, component.getKey(), metric.getKey()); + return measureDtoToMeasure.toMeasure(measureDto, metric); } } @Override - public Optional findCurrent(Component component, final Metric metric) { - return Optional.fromNullable(Iterables.find( + public Optional getRawMeasure(final Component component, final Metric metric) { + // fail fast + requireNonNull(component); + requireNonNull(metric); + + Optional local = findLocal(component, metric); + if (local.isPresent()) { + return local; + } + return findInBatch(component, metric); + } + + private Optional findInBatch(Component component, final Metric metric) { + BatchReport.Measure batchMeasure = Iterables.find( reportReader.readComponentMeasures(component.getRef()), new Predicate() { @Override @@ -59,6 +97,74 @@ public class MeasureRepositoryImpl implements MeasureRepository { return input.getMetricKey().equals(metric.getKey()); } } - , null)); + , null); + + return batchMeasureToMeasure.toMeasure(batchMeasure, metric); + } + + @Override + public void add(Component component, Metric metric, Measure measure) { + requireNonNull(component); + requireNonNull(metric); + requireNonNull(measure); + + Optional existingMeasure = findLocal(component, metric); + if (existingMeasure.isPresent()) { + throw new UnsupportedOperationException( + String.format( + "a measure can be set only once for a specific Component (ref=%s) and Metric (key=%s)", + component.getRef(), + metric.getKey() + )); + } + addLocal(component, metric, measure); + } + + @Override + public Map getRawMeasures(Component component) { + Map rawMeasures = measures.get(component.getRef()); + ImmutableMap batchMeasures = from(reportReader.readComponentMeasures(component.getRef())) + .uniqueIndex(BatchMeasureToMetricKey.INSTANCE); + + if (rawMeasures == null && batchMeasures.isEmpty()) { + return Collections.emptyMap(); + } + + Map rawMeasuresFromBatch = Maps.transformValues(batchMeasures, batchMeasureToMeasureFunction); + if (rawMeasures == null) { + return ImmutableMap.copyOf(rawMeasuresFromBatch); + } + + ImmutableMap.Builder builder = ImmutableMap.builder(); + builder.putAll(rawMeasuresFromBatch); + builder.putAll(rawMeasures); + return builder.build(); + } + + private Optional findLocal(Component component, Metric metric) { + Map measuresPerMetric = measures.get(component.getRef()); + if (measuresPerMetric == null) { + return Optional.absent(); + } + return Optional.fromNullable(measuresPerMetric.get(metric.getKey())); + } + + private void addLocal(Component component, Metric metric, Measure measure) { + Map measuresPerMetric = measures.get(component.getRef()); + if (measuresPerMetric == null) { + measuresPerMetric = new HashMap<>(); + measures.put(component.getRef(), measuresPerMetric); + } + measuresPerMetric.put(metric.getKey(), measure); + } + + private enum BatchMeasureToMetricKey implements Function { + INSTANCE; + + @Nullable + @Override + public String apply(@Nonnull BatchReport.Measure input) { + return input.getMetricKey(); + } } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/metric/Metric.java b/server/sonar-server/src/main/java/org/sonar/server/computation/metric/Metric.java new file mode 100644 index 00000000000..52035df4ffa --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/metric/Metric.java @@ -0,0 +1,57 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 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.server.computation.metric; + +import org.sonar.server.computation.measure.Measure; + +public interface Metric { + /** + * The Metric's key is its domain identifier. + */ + String getKey(); + + String getName(); + + MetricType getMetricType(); + + enum MetricType { + INT(Measure.ValueType.INT), + MILLISEC(Measure.ValueType.INT), + RATING(Measure.ValueType.INT), + WORK_DUR(Measure.ValueType.LONG), + FLOAT(Measure.ValueType.DOUBLE), + PERCENT(Measure.ValueType.DOUBLE), + BOOL(Measure.ValueType.BOOLEAN), + STRING(Measure.ValueType.STRING), + DISTRIB(Measure.ValueType.STRING), + DATA(Measure.ValueType.STRING), + LEVEL(Measure.ValueType.LEVEL); + + private final Measure.ValueType valueType; + + MetricType(Measure.ValueType valueType) { + this.valueType = valueType; + } + + public Measure.ValueType getValueType() { + return valueType; + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/metric/MetricImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/metric/MetricImpl.java new file mode 100644 index 00000000000..d28f64a3ecb --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/metric/MetricImpl.java @@ -0,0 +1,82 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 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.server.computation.metric; + +import java.util.Objects; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +import static java.util.Objects.requireNonNull; + +@Immutable +public final class MetricImpl implements Metric { + + private final String key; + private final String name; + private final MetricType metricType; + + public MetricImpl(String key, String name, MetricType metricType) { + this.key = requireNonNull(key); + this.name = requireNonNull(name); + this.metricType = requireNonNull(metricType); + } + + @Override + public String getKey() { + return key; + } + + @Override + public String getName() { + return name; + } + + @Override + public MetricType getMetricType() { + return metricType; + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + MetricImpl metric = (MetricImpl) o; + return Objects.equals(key, metric.key) && + Objects.equals(metricType, metric.metricType); + } + + @Override + public int hashCode() { + return Objects.hash(key, metricType); + } + + @Override + public String toString() { + return com.google.common.base.Objects.toStringHelper(this) + .add("key", key) + .add("name", name) + .add("metricType", metricType) + .toString(); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/metric/MetricRepository.java b/server/sonar-server/src/main/java/org/sonar/server/computation/metric/MetricRepository.java new file mode 100644 index 00000000000..9120ce8fea1 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/metric/MetricRepository.java @@ -0,0 +1,35 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 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.server.computation.metric; + +public interface MetricRepository { + + /** + * Gets the {@link Metric} with the specific key. + *

Since it does not make sense to encounter a reference (ie. a key) to a Metric during processing of + * a new analysis and not finding it in DB (metrics are never deleted), this method will throw an + * IllegalStateException if the metric with the specified key can not be found.

+ * + * @throws IllegalStateException if no Metric with the specified key is found + * @throws NullPointerException if the specified key is {@code null} + */ + Metric getByKey(String key); + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/metric/MetricRepositoryImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/metric/MetricRepositoryImpl.java new file mode 100644 index 00000000000..63766adab99 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/metric/MetricRepositoryImpl.java @@ -0,0 +1,53 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 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.server.computation.metric; + +import org.sonar.core.metric.db.MetricDto; +import org.sonar.core.persistence.DbSession; +import org.sonar.server.db.DbClient; + +import static java.util.Objects.requireNonNull; + +public class MetricRepositoryImpl implements MetricRepository { + private final DbClient dbClient; + + public MetricRepositoryImpl(DbClient dbClient) { + this.dbClient = dbClient; + } + + @Override + public Metric getByKey(String key) { + requireNonNull(key); + + try (DbSession dbSession = dbClient.openSession(false)) { + MetricDto metricDto = dbClient.metricDao().selectNullableByKey(dbSession, key); + if (metricDto == null) { + throw new IllegalStateException(String.format("Metric with key '%s' does not exist", key)); + } + + return toMetric(metricDto); + } + } + + private static Metric toMetric(MetricDto metricDto) { + return new MetricImpl(metricDto.getKey(), metricDto.getShortName(), Metric.MetricType.valueOf(metricDto.getValueType())); + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/metric/package-info.java b/server/sonar-server/src/main/java/org/sonar/server/computation/metric/package-info.java new file mode 100644 index 00000000000..1f06a93cc4a --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/metric/package-info.java @@ -0,0 +1,24 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 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.server.computation.metric; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityGateEventsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityGateEventsStep.java index 51966a692fb..7eaad9ec031 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityGateEventsStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityGateEventsStep.java @@ -25,28 +25,33 @@ import org.sonar.api.measures.CoreMetrics; import org.sonar.api.notifications.Notification; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; -import org.sonar.batch.protocol.output.BatchReport; -import org.sonar.core.measure.db.MeasureDto; import org.sonar.server.computation.component.Component; import org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor; import org.sonar.server.computation.component.TreeRootHolder; import org.sonar.server.computation.event.Event; import org.sonar.server.computation.event.EventRepository; +import org.sonar.server.computation.measure.Measure; import org.sonar.server.computation.measure.MeasureRepository; +import org.sonar.server.computation.metric.Metric; +import org.sonar.server.computation.metric.MetricRepository; import org.sonar.server.notification.NotificationManager; public class QualityGateEventsStep implements ComputationStep { - public static final Logger LOGGER = Loggers.get(QualityGateEventsStep.class); + private static final Logger LOGGER = Loggers.get(QualityGateEventsStep.class); + private final TreeRootHolder treeRootHolder; - private final EventRepository eventRepository; + private final MetricRepository metricRepository; private final MeasureRepository measureRepository; + private final EventRepository eventRepository; private final NotificationManager notificationManager; - public QualityGateEventsStep(TreeRootHolder treeRootHolder, EventRepository eventRepository, - MeasureRepository measureRepository, NotificationManager notificationManager) { - this.eventRepository = eventRepository; - this.measureRepository = measureRepository; + public QualityGateEventsStep(TreeRootHolder treeRootHolder, + MetricRepository metricRepository, MeasureRepository measureRepository, EventRepository eventRepository, + NotificationManager notificationManager) { this.treeRootHolder = treeRootHolder; + this.metricRepository = metricRepository; + this.measureRepository = measureRepository; + this.eventRepository = eventRepository; this.notificationManager = notificationManager; } @@ -61,95 +66,64 @@ public class QualityGateEventsStep implements ComputationStep { } private void executeForProject(Component project) { - Optional statusMeasure = measureRepository.findCurrent(project, CoreMetrics.ALERT_STATUS); - if (!statusMeasure.isPresent()) { - return; - } - Optional status = parse(statusMeasure.get().getAlertStatus()); - if (!status.isPresent()) { + Metric metric = metricRepository.getByKey(CoreMetrics.ALERT_STATUS_KEY); + Optional rawStatus = measureRepository.getRawMeasure(project, metric); + if (!rawStatus.isPresent() || !rawStatus.get().hasQualityGateStatus()) { return; } - checkStatusChange(project, status.get(), statusMeasure.get().getAlertText()); + checkQualityGateStatusChange(project, metric, rawStatus.get().getQualityGateStatus()); } - private void checkStatusChange(Component project, GateStatus status, String description) { - Optional baseMeasure = measureRepository.findPrevious(project, CoreMetrics.ALERT_STATUS); + private void checkQualityGateStatusChange(Component project, Metric metric, Measure.QualityGateStatus rawStatus) { + Optional baseMeasure = measureRepository.getBaseMeasure(project, metric); if (!baseMeasure.isPresent()) { - checkStatus(project, status, description); + checkNewQualityGate(project, rawStatus); return; } - Optional baseStatus = parse(baseMeasure.get().getAlertStatus()); - if (!baseStatus.isPresent()) { - LOGGER.warn(String.format("Base status for project %s is not a supported value. Can not compute Quality Gate event", project.getKey())); - checkStatus(project, status, description); + if (!baseMeasure.get().hasQualityGateStatus()) { + LOGGER.warn(String.format("Previous alterStatus for project %s is not a supported value. Can not compute Quality Gate event", project.getKey())); + checkNewQualityGate(project, rawStatus); return; } - - if (baseStatus.get() != status) { - // The status has changed - String label = String.format("%s (was %s)", status.getColorName(), baseStatus.get().getColorName()); - createEvent(project, label, description); - boolean isNewKo = (baseStatus.get() == GateStatus.OK); - notifyUsers(project, label, description, status, isNewKo); + Measure.QualityGateStatus baseStatus = baseMeasure.get().getQualityGateStatus(); + + if (baseStatus.getStatus() != rawStatus.getStatus()) { + // The QualityGate status has changed + String label = String.format("%s (was %s)", rawStatus.getStatus().getColorName(), baseStatus.getStatus().getColorName()); + createEvent(project, label, rawStatus.getText()); + boolean isNewKo = (rawStatus.getStatus() == Measure.Level.OK); + notifyUsers(project, label, rawStatus, isNewKo); } } - private void checkStatus(Component project, GateStatus status, String description) { - if (status != GateStatus.OK) { + private void checkNewQualityGate(Component project, Measure.QualityGateStatus rawStatus) { + if (rawStatus.getStatus() != Measure.Level.OK) { // There were no defined alerts before, so this one is a new one - createEvent(project, status.getColorName(), description); - notifyUsers(project, status.getColorName(), description, status, true); - } - } - - private static Optional parse(@Nullable String alertStatus) { - if (alertStatus == null) { - return Optional.absent(); - } - - try { - return Optional.of(GateStatus.valueOf(alertStatus)); - } catch (IllegalArgumentException e) { - LOGGER.error(String.format("Unsupported alertStatus value '%s' can not be parsed to AlertStatus", alertStatus)); - return Optional.absent(); - } - } - - private enum GateStatus { - OK("Green"), WARN("Orange"), ERROR("Red"); - - private String colorName; - - GateStatus(String colorName) { - this.colorName = colorName; - } - - public String getColorName() { - return colorName; + createEvent(project, rawStatus.getStatus().getColorName(), rawStatus.getText()); + // notifyUsers(project, alertName, alertText, alertLevel, true); } } /** * @param label "Red (was Orange)" - * @param description text detail, for example "Coverage < 80%" - * @param status OK, WARN or ERROR + * @param rawStatus OK, WARN or ERROR + optional text */ - private void notifyUsers(Component project, String label, String description, GateStatus status, boolean isNewAlert) { + private void notifyUsers(Component project, String label, Measure.QualityGateStatus rawStatus, boolean isNewAlert) { Notification notification = new Notification("alerts") .setDefaultMessage(String.format("Alert on %s: %s", project.getName(), label)) .setFieldValue("projectName", project.getName()) .setFieldValue("projectKey", project.getKey()) .setFieldValue("projectUuid", project.getUuid()) .setFieldValue("alertName", label) - .setFieldValue("alertText", description) - .setFieldValue("alertLevel", status.toString()) + .setFieldValue("alertText", rawStatus.getText()) + .setFieldValue("alertLevel", rawStatus.getStatus().toString()) .setFieldValue("isNewAlert", Boolean.toString(isNewAlert)); notificationManager.scheduleForSending(notification); } - private void createEvent(Component project, String name, String description) { + private void createEvent(Component project, String name, @Nullable String description) { eventRepository.add(project, Event.createAlert(name, null, description)); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityProfileEventsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityProfileEventsStep.java index 8c223e442ef..d02909069d3 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityProfileEventsStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityProfileEventsStep.java @@ -29,16 +29,16 @@ import org.apache.commons.lang.time.DateUtils; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.resources.Language; import org.sonar.api.utils.KeyValueFormat; -import org.sonar.batch.protocol.output.BatchReport; import org.sonar.core.UtcDateUtils; -import org.sonar.core.measure.db.MeasureDto; import org.sonar.server.computation.component.Component; import org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor; import org.sonar.server.computation.component.TreeRootHolder; import org.sonar.server.computation.event.Event; import org.sonar.server.computation.event.EventRepository; import org.sonar.server.computation.language.LanguageRepository; +import org.sonar.server.computation.measure.Measure; import org.sonar.server.computation.measure.MeasureRepository; +import org.sonar.server.computation.metric.MetricRepository; import org.sonar.server.computation.qualityprofile.QPMeasureData; import org.sonar.server.computation.qualityprofile.QualityProfile; @@ -46,18 +46,22 @@ import static org.sonar.server.computation.component.DepthTraversalTypeAwareVisi public class QualityProfileEventsStep implements ComputationStep { private final TreeRootHolder treeRootHolder; + private final MetricRepository metricRepository; private final MeasureRepository measureRepository; private final EventRepository eventRepository; private final LanguageRepository languageRepository; public QualityProfileEventsStep(TreeRootHolder treeRootHolder, - MeasureRepository measureRepository, EventRepository eventRepository, LanguageRepository languageRepository) { + MetricRepository metricRepository, MeasureRepository measureRepository, LanguageRepository languageRepository, + EventRepository eventRepository) { this.treeRootHolder = treeRootHolder; + this.metricRepository = metricRepository; this.measureRepository = measureRepository; this.eventRepository = eventRepository; this.languageRepository = languageRepository; } + @Override public void execute() { new DepthTraversalTypeAwareVisitor(Component.Type.PROJECT, POST_ORDER) { @@ -69,53 +73,53 @@ public class QualityProfileEventsStep implements ComputationStep { } private void executeForProject(Component projectComponent) { - Optional previousMeasure = measureRepository.findPrevious(projectComponent, CoreMetrics.QUALITY_PROFILES); - if (!previousMeasure.isPresent()) { + Optional baseMeasure = measureRepository.getBaseMeasure(projectComponent, metricRepository.getByKey(CoreMetrics.QUALITY_PROFILES_KEY)); + if (!baseMeasure.isPresent()) { // first analysis -> do not generate events return; } - // Load current profiles - Optional currentMeasure = measureRepository.findCurrent(projectComponent, CoreMetrics.QUALITY_PROFILES); - if (!currentMeasure.isPresent()) { + // Load base profiles + Optional rawMeasure = measureRepository.getRawMeasure(projectComponent, metricRepository.getByKey(CoreMetrics.QUALITY_PROFILES_KEY)); + if (!rawMeasure.isPresent()) { throw new IllegalStateException("Missing measure " + CoreMetrics.QUALITY_PROFILES + " for component " + projectComponent.getRef()); } - Map currentProfiles = QPMeasureData.fromJson(currentMeasure.get().getStringValue()).getProfilesByKey(); + Map rawProfiles = QPMeasureData.fromJson(rawMeasure.get().getStringValue()).getProfilesByKey(); - Map previousProfiles = parseJsonData(previousMeasure); - detectNewOrUpdatedProfiles(projectComponent, previousProfiles, currentProfiles); - detectNoMoreUsedProfiles(projectComponent, previousProfiles, currentProfiles); + Map baseProfiles = parseJsonData(baseMeasure); + detectNewOrUpdatedProfiles(projectComponent, baseProfiles, rawProfiles); + detectNoMoreUsedProfiles(projectComponent, baseProfiles, rawProfiles); } - private static Map parseJsonData(Optional previousMeasure) { - String data = previousMeasure.get().getData(); + private static Map parseJsonData(Optional measure) { + String data = measure.get().getStringValue(); if (data == null) { return Collections.emptyMap(); } return QPMeasureData.fromJson(data).getProfilesByKey(); } - private void detectNoMoreUsedProfiles(Component context, Map previousProfiles, Map currentProfiles) { - for (QualityProfile previousProfile : previousProfiles.values()) { - if (!currentProfiles.containsKey(previousProfile.getQpKey())) { - markAsRemoved(context, previousProfile); + private void detectNoMoreUsedProfiles(Component context, Map baseProfiles, Map rawProfiles) { + for (QualityProfile baseProfile : baseProfiles.values()) { + if (!rawProfiles.containsKey(baseProfile.getQpKey())) { + markAsRemoved(context, baseProfile); } } } - private void detectNewOrUpdatedProfiles(Component component, Map previousProfiles, Map currentProfiles) { - for (QualityProfile profile : currentProfiles.values()) { - QualityProfile previousProfile = previousProfiles.get(profile.getQpKey()); - if (previousProfile == null) { + private void detectNewOrUpdatedProfiles(Component component, Map baseProfiles, Map rawProfiles) { + for (QualityProfile profile : rawProfiles.values()) { + QualityProfile baseProfile = baseProfiles.get(profile.getQpKey()); + if (baseProfile == null) { markAsAdded(component, profile); - } else if (profile.getRulesUpdatedAt().after(previousProfile.getRulesUpdatedAt())) { - markAsChanged(component, previousProfile, profile); + } else if (profile.getRulesUpdatedAt().after(baseProfile.getRulesUpdatedAt())) { + markAsChanged(component, baseProfile, profile); } } } - private void markAsChanged(Component component, QualityProfile previousProfile, QualityProfile profile) { - Date from = previousProfile.getRulesUpdatedAt(); + private void markAsChanged(Component component, QualityProfile baseProfile, QualityProfile profile) { + Date from = baseProfile.getRulesUpdatedAt(); String data = KeyValueFormat.format(ImmutableSortedMap.of( "key", profile.getQpKey(), diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/measure/BatchMeasureToMeasureTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/measure/BatchMeasureToMeasureTest.java new file mode 100644 index 00000000000..71055251a82 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/measure/BatchMeasureToMeasureTest.java @@ -0,0 +1,282 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 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.server.computation.measure; + +import com.google.common.base.Optional; +import org.junit.Test; +import org.sonar.batch.protocol.output.BatchReport; +import org.sonar.server.computation.metric.Metric; +import org.sonar.server.computation.metric.MetricImpl; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.guava.api.Assertions.assertThat; + +public class BatchMeasureToMeasureTest { + private static final Metric SOME_INT_METRIC = new MetricImpl("key", "name", Metric.MetricType.INT); + private static final Metric SOME_LONG_METRIC = new MetricImpl("key", "name", Metric.MetricType.WORK_DUR); + private static final Metric SOME_DOUBLE_METRIC = new MetricImpl("key", "name", Metric.MetricType.FLOAT); + private static final Metric SOME_STRING_METRIC = new MetricImpl("key", "name", Metric.MetricType.STRING); + private static final Metric SOME_BOOLEAN_METRIC = new MetricImpl("key", "name", Metric.MetricType.BOOL); + private static final Metric SOME_LEVEL_METRIC = new MetricImpl("key", "name", Metric.MetricType.LEVEL); + + private static final String SOME_DATA = "some_data man!"; + private static final String SOME_ALERT_TEXT = "some alert text_be_careFul!"; + private static final BatchReport.Measure EMPTY_BATCH_MEASURE = BatchReport.Measure.newBuilder().build(); + + private BatchMeasureToMeasure underTest = new BatchMeasureToMeasure(); + + @Test + public void toMeasure_returns_absent_for_null_argument() { + assertThat(underTest.toMeasure(null, SOME_INT_METRIC)).isAbsent(); + } + + @Test(expected = NullPointerException.class) + public void toMeasure_throws_NPE_if_metric_argument_is_null() { + underTest.toMeasure(EMPTY_BATCH_MEASURE, null); + } + + @Test(expected = NullPointerException.class) + public void toMeasure_throws_NPE_if_both_arguments_are_null() { + underTest.toMeasure(null, null); + } + + @Test + public void toMeasure_returns_no_value_if_dto_has_no_string_value_for_LEVEL_Metric() { + Optional measure = underTest.toMeasure(EMPTY_BATCH_MEASURE, SOME_LEVEL_METRIC); + + assertThat(measure).isPresent(); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.NO_VALUE); + } + + @Test + public void toMeasure_returns_no_value_if_dto_has_invalid_string_value_for_LEVEL_Metric() { + Optional measure = underTest.toMeasure(BatchReport.Measure.newBuilder().setStringValue("trololo").build(), SOME_LEVEL_METRIC); + + assertThat(measure).isPresent(); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.NO_VALUE); + } + + @Test + public void toMeasure_returns_no_value_if_dto_has_value_in_wrong_case_for_LEVEL_Metric() { + Optional measure = underTest.toMeasure(BatchReport.Measure.newBuilder().setStringValue("waRn").build(), SOME_LEVEL_METRIC); + + assertThat(measure).isPresent(); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.NO_VALUE); + } + + @Test + public void toMeasure_returns_value_for_LEVEL_Metric() { + for (Measure.Level alertStatus : Measure.Level.values()) { + verify_toMeasure_returns_value_for_LEVEL_Metric(alertStatus); + } + } + + private void verify_toMeasure_returns_value_for_LEVEL_Metric(Measure.Level expectedQualityGateStatus) { + Optional measure = underTest.toMeasure(BatchReport.Measure.newBuilder().setStringValue(expectedQualityGateStatus.name()).build(), SOME_LEVEL_METRIC); + assertThat(measure).isPresent(); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.LEVEL); + assertThat(measure.get().getLevelValue()).isEqualTo(expectedQualityGateStatus); + } + + @Test + public void toMeasure_for_LEVEL_Metric_maps_QualityGateStatus() { + BatchReport.Measure batchMeasure = BatchReport.Measure.newBuilder() + .setStringValue(Measure.Level.OK.name()) + .setAlertStatus(Measure.Level.ERROR.name()) + .setAlertText(SOME_ALERT_TEXT) + .build(); + + Optional measure = underTest.toMeasure(batchMeasure, SOME_LEVEL_METRIC); + + assertThat(measure).isPresent(); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.LEVEL); + assertThat(measure.get().getLevelValue()).isEqualTo(Measure.Level.OK); + assertThat(measure.get().getQualityGateStatus().getStatus()).isEqualTo(Measure.Level.ERROR); + assertThat(measure.get().getQualityGateStatus().getText()).isEqualTo(SOME_ALERT_TEXT); + } + + @Test + public void toMeasure_for_LEVEL_Metric_parses_level_from_data() { + for (Measure.Level level : Measure.Level.values()) { + verify_toMeasure_for_LEVEL_Metric_parses_level_from_data(level); + } + } + + private void verify_toMeasure_for_LEVEL_Metric_parses_level_from_data(Measure.Level expectedLevel) { + BatchReport.Measure batchMeasure = BatchReport.Measure.newBuilder() + .setStringValue(expectedLevel.name()) + .build(); + + Optional measure = underTest.toMeasure(batchMeasure, SOME_LEVEL_METRIC); + + assertThat(measure).isPresent(); + assertThat(measure.get().getLevelValue()).isEqualTo(expectedLevel); + } + + @Test + public void toMeasure_returns_no_value_if_dto_has_no_value_for_Int_Metric() { + Optional measure = underTest.toMeasure(EMPTY_BATCH_MEASURE, SOME_INT_METRIC); + + assertThat(measure.isPresent()); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.NO_VALUE); + } + + @Test + public void toMeasure_maps_data_and_alert_properties_in_dto_for_Int_Metric() { + BatchReport.Measure batchMeasure = BatchReport.Measure.newBuilder() + .setIntValue(10) + .setStringValue(SOME_DATA) + .setAlertStatus(Measure.Level.OK.name()).setAlertText(SOME_ALERT_TEXT) + .build(); + + Optional measure = underTest.toMeasure(batchMeasure, SOME_INT_METRIC); + + assertThat(measure.isPresent()); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.INT); + assertThat(measure.get().getIntValue()).isEqualTo(10); + assertThat(measure.get().getData()).isEqualTo(SOME_DATA); + assertThat(measure.get().getQualityGateStatus().getStatus()).isEqualTo(Measure.Level.OK); + assertThat(measure.get().getQualityGateStatus().getText()).isEqualTo(SOME_ALERT_TEXT); + } + + @Test + public void toMeasure_returns_no_value_if_dto_has_no_value_for_Long_Metric() { + Optional measure = underTest.toMeasure(EMPTY_BATCH_MEASURE, SOME_LONG_METRIC); + + assertThat(measure.isPresent()); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.NO_VALUE); + } + + @Test + public void toMeasure_returns_long_part_of_value_in_dto_for_Long_Metric() { + Optional measure = underTest.toMeasure(BatchReport.Measure.newBuilder().setLongValue(15l).build(), SOME_LONG_METRIC); + + assertThat(measure.isPresent()); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.LONG); + assertThat(measure.get().getLongValue()).isEqualTo(15); + } + + @Test + public void toMeasure_maps_data_and_alert_properties_in_dto_for_Long_Metric() { + BatchReport.Measure batchMeasure = BatchReport.Measure.newBuilder() + .setLongValue(10l) + .setStringValue(SOME_DATA) + .setAlertStatus(Measure.Level.OK.name()).setAlertText(SOME_ALERT_TEXT) + .build(); + + Optional measure = underTest.toMeasure(batchMeasure, SOME_LONG_METRIC); + + assertThat(measure.isPresent()); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.LONG); + assertThat(measure.get().getLongValue()).isEqualTo(10); + assertThat(measure.get().getData()).isEqualTo(SOME_DATA); + assertThat(measure.get().getQualityGateStatus().getStatus()).isEqualTo(Measure.Level.OK); + assertThat(measure.get().getQualityGateStatus().getText()).isEqualTo(SOME_ALERT_TEXT); + } + + @Test + public void toMeasure_returns_no_value_if_dto_has_no_value_for_Double_Metric() { + Optional measure = underTest.toMeasure(EMPTY_BATCH_MEASURE, SOME_DOUBLE_METRIC); + + assertThat(measure.isPresent()); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.NO_VALUE); + } + + @Test + public void toMeasure_maps_data_and_alert_properties_in_dto_for_Double_Metric() { + BatchReport.Measure batchMeasure = BatchReport.Measure.newBuilder() + .setDoubleValue(10.6395d) + .setStringValue(SOME_DATA) + .setAlertStatus(Measure.Level.OK.name()).setAlertText(SOME_ALERT_TEXT) + .build(); + + Optional measure = underTest.toMeasure(batchMeasure, SOME_DOUBLE_METRIC); + + assertThat(measure.isPresent()); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.DOUBLE); + assertThat(measure.get().getDoubleValue()).isEqualTo(10.6395d); + assertThat(measure.get().getData()).isEqualTo(SOME_DATA); + assertThat(measure.get().getQualityGateStatus().getStatus()).isEqualTo(Measure.Level.OK); + assertThat(measure.get().getQualityGateStatus().getText()).isEqualTo(SOME_ALERT_TEXT); + } + + @Test + public void toMeasure_returns_no_value_if_dto_has_no_value_for_Boolean_metric() { + Optional measure = underTest.toMeasure(EMPTY_BATCH_MEASURE, SOME_BOOLEAN_METRIC); + + assertThat(measure.isPresent()); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.NO_VALUE); + } + + @Test + public void toMeasure_returns_false_value_if_dto_has_invalid_value_for_Boolean_metric() { + verify_toMeasure_returns_false_value_if_dto_has_invalid_value_for_Boolean_metric(true); + verify_toMeasure_returns_false_value_if_dto_has_invalid_value_for_Boolean_metric(false); + } + + private void verify_toMeasure_returns_false_value_if_dto_has_invalid_value_for_Boolean_metric(boolean expected) { + Optional measure = underTest.toMeasure(BatchReport.Measure.newBuilder().setBooleanValue(expected).build(), SOME_BOOLEAN_METRIC); + + assertThat(measure.isPresent()); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.BOOLEAN); + assertThat(measure.get().getBooleanValue()).isEqualTo(expected); + } + + @Test + public void toMeasure_maps_data_and_alert_properties_in_dto_for_Boolean_metric() { + BatchReport.Measure batchMeasure = BatchReport.Measure.newBuilder() + .setBooleanValue(true).setStringValue(SOME_DATA).setAlertStatus(Measure.Level.OK.name()).setAlertText(SOME_ALERT_TEXT).build(); + + Optional measure = underTest.toMeasure(batchMeasure, SOME_BOOLEAN_METRIC); + + assertThat(measure.isPresent()); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.BOOLEAN); + assertThat(measure.get().getBooleanValue()).isTrue(); + assertThat(measure.get().getData()).isEqualTo(SOME_DATA); + assertThat(measure.get().getQualityGateStatus().getStatus()).isEqualTo(Measure.Level.OK); + assertThat(measure.get().getQualityGateStatus().getText()).isEqualTo(SOME_ALERT_TEXT); + } + + @Test + public void toMeasure_returns_no_value_if_dto_has_no_value_for_String_Metric() { + Optional measure = underTest.toMeasure(EMPTY_BATCH_MEASURE, SOME_STRING_METRIC); + + assertThat(measure.isPresent()); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.NO_VALUE); + } + + @Test + public void toMeasure_maps_alert_properties_in_dto_for_String_Metric() { + BatchReport.Measure batchMeasure = BatchReport.Measure.newBuilder() + .setStringValue(SOME_DATA) + .setAlertStatus(Measure.Level.OK.name()).setAlertText(SOME_ALERT_TEXT) + .build(); + + Optional measure = underTest.toMeasure(batchMeasure, SOME_STRING_METRIC); + + assertThat(measure.isPresent()); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.STRING); + assertThat(measure.get().getStringValue()).isEqualTo(SOME_DATA); + assertThat(measure.get().getData()).isEqualTo(SOME_DATA); + assertThat(measure.get().getQualityGateStatus().getStatus()).isEqualTo(Measure.Level.OK); + assertThat(measure.get().getQualityGateStatus().getText()).isEqualTo(SOME_ALERT_TEXT); + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureDtoToMeasureTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureDtoToMeasureTest.java new file mode 100644 index 00000000000..ddf953b5bf6 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureDtoToMeasureTest.java @@ -0,0 +1,276 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 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.server.computation.measure; + +import com.google.common.base.Optional; +import org.junit.Test; +import org.sonar.core.measure.db.MeasureDto; +import org.sonar.server.computation.measure.Measure.Level; +import org.sonar.server.computation.metric.Metric; +import org.sonar.server.computation.metric.MetricImpl; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.guava.api.Assertions.assertThat; + +public class MeasureDtoToMeasureTest { + private static final Metric SOME_INT_METRIC = new MetricImpl("key", "name", Metric.MetricType.INT); + private static final Metric SOME_LONG_METRIC = new MetricImpl("key", "name", Metric.MetricType.WORK_DUR); + private static final Metric SOME_DOUBLE_METRIC = new MetricImpl("key", "name", Metric.MetricType.FLOAT); + private static final Metric SOME_STRING_METRIC = new MetricImpl("key", "name", Metric.MetricType.STRING); + private static final Metric SOME_BOOLEAN_METRIC = new MetricImpl("key", "name", Metric.MetricType.BOOL); + private static final Metric SOME_LEVEL_METRIC = new MetricImpl("key", "name", Metric.MetricType.LEVEL); + + private static final String SOME_DATA = "some_data man!"; + private static final String SOME_ALERT_TEXT = "some alert text_be_careFul!"; + private static final MeasureDto EMPTY_MEASURE_DTO = new MeasureDto(); + + private MeasureDtoToMeasure underTest = new MeasureDtoToMeasure(); + + @Test + public void toMeasure_returns_absent_for_null_argument() { + assertThat(underTest.toMeasure(null, SOME_INT_METRIC)).isAbsent(); + } + + @Test(expected = NullPointerException.class) + public void toMeasure_throws_NPE_if_metric_argument_is_null() { + underTest.toMeasure(EMPTY_MEASURE_DTO, null); + } + + @Test(expected = NullPointerException.class) + public void toMeasure_throws_NPE_if_both_arguments_are_null() { + underTest.toMeasure(null, null); + } + + @Test + public void toMeasure_returns_no_value_if_dto_has_no_data_for_Level_Metric() { + Optional measure = underTest.toMeasure(EMPTY_MEASURE_DTO, SOME_LEVEL_METRIC); + assertThat(measure).isPresent(); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.NO_VALUE); + } + + @Test + public void toMeasure_returns_no_value_if_dto_has_invalid_data_for_Level_Metric() { + Optional measure = underTest.toMeasure(new MeasureDto().setData("trololo"), SOME_LEVEL_METRIC); + assertThat(measure).isPresent(); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.NO_VALUE); + } + + @Test + public void toMeasure_returns_no_value_if_dta_has_data_in_wrong_case_for_Level_Metric() { + Optional measure = underTest.toMeasure(new MeasureDto().setData("waRn"), SOME_LEVEL_METRIC); + assertThat(measure).isPresent(); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.NO_VALUE); + } + + @Test + public void toMeasure_returns_no_QualityGateStatus_if_dto_has_no_alertStatus_for_Level_Metric() { + Optional measure = underTest.toMeasure(EMPTY_MEASURE_DTO, SOME_STRING_METRIC); + assertThat(measure).isPresent(); + assertThat(measure.get().hasQualityGateStatus()).isFalse(); + } + + @Test + public void toMeasure_returns_no_QualityGateStatus_if_alertStatus_has_invalid_data_for_Level_Metric() { + Optional measure = underTest.toMeasure(new MeasureDto().setData("trololo"), SOME_STRING_METRIC); + assertThat(measure).isPresent(); + assertThat(measure.get().hasQualityGateStatus()).isFalse(); + } + + @Test + public void toMeasure_returns_no_QualityGateStatus_if_alertStatus_has_data_in_wrong_case_for_Level_Metric() { + Optional measure = underTest.toMeasure(new MeasureDto().setData("waRn"), SOME_STRING_METRIC); + assertThat(measure).isPresent(); + assertThat(measure.get().hasQualityGateStatus()).isFalse(); + } + + @Test + public void toMeasure_returns_value_for_LEVEL_Metric() { + for (Level level : Level.values()) { + verify_toMeasure_returns_value_for_LEVEL_Metric(level); + } + } + + private void verify_toMeasure_returns_value_for_LEVEL_Metric(Level expectedLevel) { + Optional measure = underTest.toMeasure(new MeasureDto().setData(expectedLevel.name()), SOME_LEVEL_METRIC); + assertThat(measure).isPresent(); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.LEVEL); + assertThat(measure.get().getLevelValue()).isEqualTo(expectedLevel); + } + + @Test + public void toMeasure_for_LEVEL_Metric_can_have_an_qualityGateStatus() { + MeasureDto measureDto = new MeasureDto().setData(Level.OK.name()).setAlertStatus(Level.ERROR.name()).setAlertText(SOME_ALERT_TEXT); + + Optional measure = underTest.toMeasure(measureDto, SOME_LEVEL_METRIC); + + assertThat(measure).isPresent(); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.LEVEL); + assertThat(measure.get().getLevelValue()).isEqualTo(Level.OK); + assertThat(measure.get().getQualityGateStatus().getStatus()).isEqualTo(Level.ERROR); + assertThat(measure.get().getQualityGateStatus().getText()).isEqualTo(SOME_ALERT_TEXT); + } + + @Test(expected = IllegalStateException.class) + public void toMeasure_for_LEVEL_Metric_ignores_data() { + MeasureDto measureDto = new MeasureDto().setAlertStatus(Level.ERROR.name()).setData(SOME_DATA); + + Optional measure = underTest.toMeasure(measureDto, SOME_LEVEL_METRIC); + + assertThat(measure).isPresent(); + measure.get().getStringValue(); + } + + @Test + public void toMeasure_returns_no_value_if_dto_has_no_value_for_Int_Metric() { + Optional measure = underTest.toMeasure(EMPTY_MEASURE_DTO, SOME_INT_METRIC); + + assertThat(measure.isPresent()); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.NO_VALUE); + } + + @Test + public void toMeasure_returns_int_part_of_value_in_dto_for_Int_Metric() { + Optional measure = underTest.toMeasure(new MeasureDto().setValue(1.5d), SOME_INT_METRIC); + + assertThat(measure.isPresent()); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.INT); + assertThat(measure.get().getIntValue()).isEqualTo(1); + } + + @Test + public void toMeasure_maps_data_and_alert_properties_in_dto_for_Int_Metric() { + MeasureDto measureDto = new MeasureDto().setValue(10d).setData(SOME_DATA).setAlertStatus(Level.OK.name()).setAlertText(SOME_ALERT_TEXT); + + Optional measure = underTest.toMeasure(measureDto, SOME_INT_METRIC); + + assertThat(measure.isPresent()); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.INT); + assertThat(measure.get().getIntValue()).isEqualTo(10); + assertThat(measure.get().getData()).isEqualTo(SOME_DATA); + assertThat(measure.get().getQualityGateStatus().getStatus()).isEqualTo(Level.OK); + assertThat(measure.get().getQualityGateStatus().getText()).isEqualTo(SOME_ALERT_TEXT); + } + + @Test + public void toMeasure_returns_no_value_if_dto_has_no_value_for_Long_Metric() { + Optional measure = underTest.toMeasure(EMPTY_MEASURE_DTO, SOME_LONG_METRIC); + + assertThat(measure.isPresent()); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.NO_VALUE); + } + + @Test + public void toMeasure_returns_long_part_of_value_in_dto_for_Long_Metric() { + Optional measure = underTest.toMeasure(new MeasureDto().setValue(1.5d), SOME_LONG_METRIC); + + assertThat(measure.isPresent()); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.LONG); + assertThat(measure.get().getLongValue()).isEqualTo(1); + } + + @Test + public void toMeasure_maps_data_and_alert_properties_in_dto_for_Long_Metric() { + MeasureDto measureDto = new MeasureDto().setValue(10d).setData(SOME_DATA).setAlertStatus(Level.OK.name()).setAlertText(SOME_ALERT_TEXT); + + Optional measure = underTest.toMeasure(measureDto, SOME_LONG_METRIC); + + assertThat(measure.isPresent()); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.LONG); + assertThat(measure.get().getLongValue()).isEqualTo(10); + assertThat(measure.get().getData()).isEqualTo(SOME_DATA); + assertThat(measure.get().getQualityGateStatus().getStatus()).isEqualTo(Level.OK); + assertThat(measure.get().getQualityGateStatus().getText()).isEqualTo(SOME_ALERT_TEXT); + } + + @Test + public void toMeasure_returns_no_value_if_dto_has_no_value_for_Double_Metric() { + Optional measure = underTest.toMeasure(EMPTY_MEASURE_DTO, SOME_DOUBLE_METRIC); + + assertThat(measure.isPresent()); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.NO_VALUE); + } + + @Test + public void toMeasure_maps_data_and_alert_properties_in_dto_for_Double_Metric() { + MeasureDto measureDto = new MeasureDto().setValue(10.6395d).setData(SOME_DATA).setAlertStatus(Level.OK.name()).setAlertText(SOME_ALERT_TEXT); + + Optional measure = underTest.toMeasure(measureDto, SOME_DOUBLE_METRIC); + + assertThat(measure.isPresent()); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.DOUBLE); + assertThat(measure.get().getDoubleValue()).isEqualTo(10.6395d); + assertThat(measure.get().getData()).isEqualTo(SOME_DATA); + assertThat(measure.get().getQualityGateStatus().getStatus()).isEqualTo(Level.OK); + assertThat(measure.get().getQualityGateStatus().getText()).isEqualTo(SOME_ALERT_TEXT); + } + + @Test + public void toMeasure_returns_no_value_if_dto_has_no_value_for_Boolean_metric() { + Optional measure = underTest.toMeasure(EMPTY_MEASURE_DTO, SOME_BOOLEAN_METRIC); + + assertThat(measure.isPresent()); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.NO_VALUE); + } + + @Test + public void toMeasure_returns_false_value_if_dto_has_invalid_value_for_Boolean_metric() { + Optional measure = underTest.toMeasure(new MeasureDto().setValue(1.987d), SOME_BOOLEAN_METRIC); + + assertThat(measure.isPresent()); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.BOOLEAN); + assertThat(measure.get().getBooleanValue()).isFalse(); + } + + @Test + public void toMeasure_maps_data_and_alert_properties_in_dto_for_Boolean_metric() { + MeasureDto measureDto = new MeasureDto().setValue(1d).setData(SOME_DATA).setAlertStatus(Level.OK.name()).setAlertText(SOME_ALERT_TEXT); + + Optional measure = underTest.toMeasure(measureDto, SOME_BOOLEAN_METRIC); + + assertThat(measure.isPresent()); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.BOOLEAN); + assertThat(measure.get().getBooleanValue()).isTrue(); + assertThat(measure.get().getData()).isEqualTo(SOME_DATA); + assertThat(measure.get().getQualityGateStatus().getStatus()).isEqualTo(Level.OK); + assertThat(measure.get().getQualityGateStatus().getText()).isEqualTo(SOME_ALERT_TEXT); + } + + @Test + public void toMeasure_returns_no_value_if_dto_has_no_value_for_String_Metric() { + Optional measure = underTest.toMeasure(EMPTY_MEASURE_DTO, SOME_STRING_METRIC); + + assertThat(measure.isPresent()); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.NO_VALUE); + } + + @Test + public void toMeasure_maps_alert_properties_in_dto_for_String_Metric() { + MeasureDto measureDto = new MeasureDto().setData(SOME_DATA).setAlertStatus(Level.OK.name()).setAlertText(SOME_ALERT_TEXT); + + Optional measure = underTest.toMeasure(measureDto, SOME_STRING_METRIC); + + assertThat(measure.isPresent()); + assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.STRING); + assertThat(measure.get().getStringValue()).isEqualTo(SOME_DATA); + assertThat(measure.get().getData()).isEqualTo(SOME_DATA); + assertThat(measure.get().getQualityGateStatus().getStatus()).isEqualTo(Level.OK); + assertThat(measure.get().getQualityGateStatus().getText()).isEqualTo(SOME_ALERT_TEXT); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureImplTest.java new file mode 100644 index 00000000000..c9ddf85e9e1 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureImplTest.java @@ -0,0 +1,260 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 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.server.computation.measure; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableList; +import com.tngtech.java.junit.dataprovider.DataProvider; +import com.tngtech.java.junit.dataprovider.DataProviderRunner; +import com.tngtech.java.junit.dataprovider.UseDataProvider; +import java.util.List; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.sonar.server.computation.measure.Measure.ValueType; + +import static com.google.common.collect.FluentIterable.from; +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(DataProviderRunner.class) +public class MeasureImplTest { + + private static final MeasureImpl INT_MEASURE = MeasureImpl.create((int) 1, null); + private static final MeasureImpl LONG_MEASURE = MeasureImpl.create(1l, null); + private static final MeasureImpl DOUBLE_MEASURE = MeasureImpl.create(1d, null); + private static final MeasureImpl STRING_MEASURE = MeasureImpl.create("some_sT ring"); + private static final MeasureImpl TRUE_MEASURE = MeasureImpl.create(true, null); + private static final MeasureImpl FALSE_MEASURE = MeasureImpl.create(false, null); + private static final MeasureImpl LEVEL_MEASURE = MeasureImpl.create(Measure.Level.OK); + private static final MeasureImpl NO_VALUE_MEASURE = MeasureImpl.createNoValue(); + + private static final List MEASURES = ImmutableList.of( + INT_MEASURE, LONG_MEASURE, DOUBLE_MEASURE, STRING_MEASURE, TRUE_MEASURE, FALSE_MEASURE, NO_VALUE_MEASURE, LEVEL_MEASURE + ); + + @Test(expected = NullPointerException.class) + public void create_from_String_throws_NPE_if_arg_is_null() { + MeasureImpl.create((String) null); + } + + @DataProvider + public static Object[][] all_but_INT_MEASURE() { + return getMeasuresExcept(ValueType.INT); + } + + @DataProvider + public static Object[][] all_but_LONG_MEASURE() { + return getMeasuresExcept(ValueType.LONG); + } + + @DataProvider + public static Object[][] all_but_DOUBLE_MEASURE() { + return getMeasuresExcept(ValueType.DOUBLE); + } + + @DataProvider + public static Object[][] all_but_BOOLEAN_MEASURE() { + return getMeasuresExcept(ValueType.BOOLEAN); + } + + @DataProvider + public static Object[][] all_but_STRING_MEASURE() { + return getMeasuresExcept(ValueType.STRING); + } + + @DataProvider + public static Object[][] all_but_LEVEL_MEASURE() { + return getMeasuresExcept(ValueType.LEVEL); + } + + @DataProvider + public static Object[][] all() { + return from(MEASURES).transform(WrapInArray.INSTANCE).toArray(MeasureImpl[].class); + } + + private static MeasureImpl[][] getMeasuresExcept(final ValueType valueType) { + return from(MEASURES) + .filter(new Predicate() { + @Override + public boolean apply(@Nonnull MeasureImpl input) { + return input.getValueType() != valueType; + } + }).transform(WrapInArray.INSTANCE) + .toArray(MeasureImpl[].class); + } + + @Test + public void create_from_int_has_INT_value_type() { + assertThat(INT_MEASURE.getValueType()).isEqualTo(ValueType.INT); + } + + @Test + public void create_from_long_has_LONG_value_type() { + assertThat(LONG_MEASURE.getValueType()).isEqualTo(ValueType.LONG); + } + + @Test + public void create_from_double_has_DOUBLE_value_type() { + assertThat(DOUBLE_MEASURE.getValueType()).isEqualTo(ValueType.DOUBLE); + } + + @Test + public void create_from_boolean_has_BOOLEAN_value_type() { + assertThat(TRUE_MEASURE.getValueType()).isEqualTo(ValueType.BOOLEAN); + assertThat(FALSE_MEASURE.getValueType()).isEqualTo(ValueType.BOOLEAN); + } + + @Test + public void create_from_String_has_STRING_value_type() { + assertThat(STRING_MEASURE.getValueType()).isEqualTo(ValueType.STRING); + } + + @Test(expected = IllegalStateException.class) + @UseDataProvider("all_but_INT_MEASURE") + public void getIntValue_throws_ISE_for_all_value_types_except_INT(Measure measure) { + measure.getIntValue(); + } + + @Test + public void getIntValue_returns_value_for_INT_value_type() { + assertThat(INT_MEASURE.getIntValue()).isEqualTo(1); + } + + @Test(expected = IllegalStateException.class) + @UseDataProvider("all_but_LONG_MEASURE") + public void getLongValue_throws_ISE_for_all_value_types_except_LONG(Measure measure) { + measure.getLongValue(); + } + + @Test + public void getLongValue_returns_value_for_LONG_value_type() { + assertThat(LONG_MEASURE.getLongValue()).isEqualTo(1); + } + + @Test(expected = IllegalStateException.class) + @UseDataProvider("all_but_DOUBLE_MEASURE") + public void getDoubleValue_throws_ISE_for_all_value_types_except_DOUBLE(Measure measure) { + measure.getDoubleValue(); + } + + @Test + public void getDoubleValue_returns_value_for_DOUBLE_value_type() { + assertThat(DOUBLE_MEASURE.getDoubleValue()).isEqualTo(1d); + } + + @Test(expected = IllegalStateException.class) + @UseDataProvider("all_but_BOOLEAN_MEASURE") + public void getBooleanValue_throws_ISE_for_all_value_types_except_BOOLEAN(Measure measure) { + measure.getBooleanValue(); + } + + @Test + public void getBooleanValue_returns_value_for_BOOLEAN_value_type() { + assertThat(TRUE_MEASURE.getBooleanValue()).isTrue(); + assertThat(FALSE_MEASURE.getBooleanValue()).isFalse(); + } + + @Test(expected = IllegalStateException.class) + @UseDataProvider("all_but_STRING_MEASURE") + public void getStringValue_throws_ISE_for_all_value_types_except_STRING(Measure measure) { + measure.getStringValue(); + } + + @Test(expected = IllegalStateException.class) + @UseDataProvider("all_but_LEVEL_MEASURE") + public void getLevelValue_throws_ISE_for_all_value_types_except_LEVEL(Measure measure) { + measure.getLevelValue(); + } + + @Test + public void getData_returns_null_for_NO_VALUE_value_type() { + assertThat(NO_VALUE_MEASURE.getData()).isNull(); + } + + @Test + @UseDataProvider("all_but_STRING_MEASURE") + public void getData_returns_null_for_all_value_types_but_STRING_when_not_set(Measure measure) { + assertThat(measure.getData()).isNull(); + } + + @Test + public void getData_returns_value_for_STRING_value_type() { + assertThat(STRING_MEASURE.getData()).isEqualTo(STRING_MEASURE.getStringValue()); + } + + @Test + @UseDataProvider("all") + public void hasAlertStatus_returns_false_for_all_value_types_when_not_set(Measure measure) { + assertThat(measure.hasQualityGateStatus()).isFalse(); + } + + @Test(expected = IllegalStateException.class) + @UseDataProvider("all") + public void getAlertStatus_throws_ISE_for_all_value_types_when_not_set(Measure measure) { + measure.getQualityGateStatus(); + } + + @Test + public void getAlertStatus_returns_argument_from_setAlertStatus() { + Measure.QualityGateStatus someStatus = new Measure.QualityGateStatus(Measure.Level.OK); + + assertThat(MeasureImpl.create(true, null).setQualityGateStatus(someStatus).getQualityGateStatus()).isEqualTo(someStatus); + assertThat(MeasureImpl.create(false, null).setQualityGateStatus(someStatus).getQualityGateStatus()).isEqualTo(someStatus); + assertThat(MeasureImpl.create((int) 1, null).setQualityGateStatus(someStatus).getQualityGateStatus()).isEqualTo(someStatus); + assertThat(MeasureImpl.create((long) 1, null).setQualityGateStatus(someStatus).getQualityGateStatus()).isEqualTo(someStatus); + assertThat(MeasureImpl.create((double) 1, null).setQualityGateStatus(someStatus).getQualityGateStatus()).isEqualTo(someStatus); + assertThat(MeasureImpl.create("str").setQualityGateStatus(someStatus).getQualityGateStatus()).isEqualTo(someStatus); + assertThat(MeasureImpl.create(Measure.Level.OK).setQualityGateStatus(someStatus).getQualityGateStatus()).isEqualTo(someStatus); + } + + @Test(expected = NullPointerException.class) + @UseDataProvider("all") + public void setAlertStatus_throws_NPE_if_arg_is_null(MeasureImpl measure) { + measure.setQualityGateStatus(null); + } + + @Test + public void getData_returns_argument_from_factory_method() { + String someData = "lololool"; + + assertThat(MeasureImpl.create(true, someData).getData()).isEqualTo(someData); + assertThat(MeasureImpl.create(false, someData).getData()).isEqualTo(someData); + assertThat(MeasureImpl.create((int) 1, someData).getData()).isEqualTo(someData); + assertThat(MeasureImpl.create((long) 1, someData).getData()).isEqualTo(someData); + assertThat(MeasureImpl.create((double) 1, someData).getData()).isEqualTo(someData); + } + + @Test + public void measure_of_value_type_LEVEL_has_no_data() { + assertThat(LEVEL_MEASURE.getData()).isNull(); + } + + private enum WrapInArray implements Function { + INSTANCE; + + @Nullable + @Override + public MeasureImpl[] apply(@Nonnull MeasureImpl input) { + return new MeasureImpl[] {input}; + } + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureRepositoryImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureRepositoryImplTest.java new file mode 100644 index 00000000000..4a3d0f15859 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureRepositoryImplTest.java @@ -0,0 +1,247 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 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.server.computation.measure; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; +import javax.annotation.CheckForNull; +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.batch.protocol.output.BatchReport; +import org.sonar.core.measure.db.MeasureDto; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.DbTester; +import org.sonar.server.component.db.ComponentDao; +import org.sonar.server.component.db.SnapshotDao; +import org.sonar.server.computation.batch.BatchReportReader; +import org.sonar.server.computation.batch.BatchReportReaderRule; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.DumbComponent; +import org.sonar.server.computation.metric.Metric; +import org.sonar.server.computation.metric.MetricRepository; +import org.sonar.server.db.DbClient; +import org.sonar.server.measure.persistence.MeasureDao; +import org.sonar.server.metric.persistence.MetricDao; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.guava.api.Assertions.assertThat; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +public class MeasureRepositoryImplTest { + @ClassRule + public static final DbTester dbTester = new DbTester(); + public static final String SOME_DATA = "some data"; + @Rule + public BatchReportReaderRule reportReader = new BatchReportReaderRule(); + + private static final String FILE_COMPONENT_KEY = "file cpt key"; + private static final DumbComponent FILE_COMPONENT = DumbComponent.builder(Component.Type.FILE, 1).setKey(FILE_COMPONENT_KEY).build(); + private static final DumbComponent OTHER_COMPONENT = DumbComponent.builder(Component.Type.FILE, 2).setKey("some other key").build(); + private static final String METRIC_KEY_1 = "metric 1"; + private static final int METRIC_ID_1 = 1; + private static final String METRIC_KEY_2 = "metric 2"; + private static final int METRIC_ID_2 = 2; + private final Metric metric1 = mock(Metric.class); + private final Metric metric2 = mock(Metric.class); + private static final long LAST_SNAPSHOT_ID = 123; + private static final long OTHER_SNAPSHOT_ID = 369; + private static final long COMPONENT_ID = 567; + private static final Measure SOME_MEASURE = mock(Measure.class); + + private DbClient dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new MeasureDao(), new SnapshotDao(), new MetricDao(), new ComponentDao()); + private MetricRepository metricRepository = mock(MetricRepository.class); + private MeasureRepositoryImpl underTest = new MeasureRepositoryImpl(dbClient, reportReader, metricRepository); + + private DbClient mockedDbClient = mock(DbClient.class); + private BatchReportReader mockBatchReportReader = mock(BatchReportReader.class); + private MeasureRepositoryImpl underTestWithMock = new MeasureRepositoryImpl(mockedDbClient, mockBatchReportReader, metricRepository); + + @CheckForNull + private DbSession dbSession; + + @Before + public void setUp() throws Exception { + when(metric1.getKey()).thenReturn(METRIC_KEY_1); + when(metric1.getMetricType()).thenReturn(Metric.MetricType.STRING); + when(metric2.getKey()).thenReturn(METRIC_KEY_2); + when(metric2.getMetricType()).thenReturn(Metric.MetricType.STRING); + + // references to metrics are consistent with DB by design + when(metricRepository.getByKey(METRIC_KEY_1)).thenReturn(metric1); + when(metricRepository.getByKey(METRIC_KEY_2)).thenReturn(metric2); + } + + @After + public void tearDown() throws Exception { + if (dbSession != null) { + dbSession.close(); + } + } + + @Test + public void getBaseMeasure_throws_NPE_and_does_not_open_session_if_component_is_null() { + try { + underTestWithMock.getBaseMeasure(null, metric1); + fail("an NPE should have been raised"); + } catch (NullPointerException e) { + verifyZeroInteractions(mockedDbClient); + } + } + + @Test + public void getBaseMeasure_throws_NPE_and_does_not_open_session_if_metric_is_null() { + try { + underTestWithMock.getBaseMeasure(FILE_COMPONENT, null); + fail("an NPE should have been raised"); + } catch (NullPointerException e) { + verifyZeroInteractions(mockedDbClient); + } + } + + @Test + public void getBaseMeasure_returns_absent_if_measure_does_not_exist_in_DB() { + Optional res = underTest.getBaseMeasure(FILE_COMPONENT, metric1); + + assertThat(res).isAbsent(); + } + + @Test + public void getBaseMeasure_returns_Measure_if_measure_of_last_snapshot_only_in_DB() { + dbTester.prepareDbUnit(getClass(), "shared.xml"); + dbSession = dbClient.openSession(false); + dbClient.measureDao().insert(dbSession, createMeasureDto(METRIC_ID_1, LAST_SNAPSHOT_ID)); + dbClient.measureDao().insert(dbSession, createMeasureDto(METRIC_ID_2, OTHER_SNAPSHOT_ID)); + dbSession.commit(); + + // metric 1 is associated to snapshot with "last=true" + Optional res = underTest.getBaseMeasure(FILE_COMPONENT, metric1); + + assertThat(res).isPresent(); + assertThat(res.get().getStringValue()).isEqualTo(SOME_DATA); + + // metric 2 is associated to snapshot with "last=false" => not retrieved + res = underTest.getBaseMeasure(FILE_COMPONENT, metric2); + + assertThat(res).isAbsent(); + } + + @Test(expected = NullPointerException.class) + public void add_throws_NPE_if_Component_argument_is_null() { + underTest.add(null, metric1, mock(Measure.class)); + } + + @Test(expected = NullPointerException.class) + public void add_throws_NPE_if_Component_metric_is_null() { + underTest.add(FILE_COMPONENT, null, mock(Measure.class)); + } + + @Test(expected = NullPointerException.class) + public void add_throws_NPE_if_Component_measure_is_null() { + underTest.add(FILE_COMPONENT, metric1, null); + } + + @Test(expected = UnsupportedOperationException.class) + public void add_throws_UOE_if_measure_already_exists() { + underTest.add(FILE_COMPONENT, metric1, mock(Measure.class)); + underTest.add(FILE_COMPONENT, metric1, mock(Measure.class)); + } + + @Test + public void getRawMeasure_throws_NPE_without_reading_batch_report_if_component_arg_is_null() { + try { + underTestWithMock.getRawMeasure(null, metric1); + fail("an NPE should have been raised"); + } catch (NullPointerException e) { + verifyNoMoreInteractions(mockBatchReportReader); + } + } + + @Test + public void getRawMeasure_throws_NPE_without_reading_batch_report_if_metric_arg_is_null() { + try { + underTestWithMock.getRawMeasure(FILE_COMPONENT, null); + fail("an NPE should have been raised"); + } catch (NullPointerException e) { + verifyNoMoreInteractions(mockBatchReportReader); + } + } + + @Test + public void getRawMeasure_returns_measure_added_through_add_method() { + underTest.add(FILE_COMPONENT, metric1, SOME_MEASURE); + + Optional res = underTest.getRawMeasure(FILE_COMPONENT, metric1); + + assertThat(res).isPresent(); + assertThat(res.get()).isSameAs(SOME_MEASURE); + + // make sure we really match on the specified component and metric + assertThat(underTest.getRawMeasure(OTHER_COMPONENT, metric1)).isAbsent(); + assertThat(underTest.getRawMeasure(FILE_COMPONENT, metric2)).isAbsent(); + } + + @Test + public void getRawMeasure_returns_measure_from_batch_if_not_added_through_add_method() { + String value = "trololo"; + + reportReader.putMeasures(FILE_COMPONENT.getRef(), ImmutableList.of( + BatchReport.Measure.newBuilder().setMetricKey(METRIC_KEY_1).setStringValue(value).build() + )); + + Optional res = underTest.getRawMeasure(FILE_COMPONENT, metric1); + + assertThat(res).isPresent(); + assertThat(res.get().getStringValue()).isEqualTo(value); + + // make sure we really match on the specified component and metric + assertThat(underTest.getRawMeasure(FILE_COMPONENT, metric2)).isAbsent(); + assertThat(underTest.getRawMeasure(OTHER_COMPONENT, metric1)).isAbsent(); + } + + @Test + public void getRawMeasure_retrieves_added_measure_over_batch_measure() { + reportReader.putMeasures(FILE_COMPONENT.getRef(), ImmutableList.of( + BatchReport.Measure.newBuilder().setMetricKey(METRIC_KEY_1).setStringValue("some value").build() + )); + + Measure addedMeasure = mock(Measure.class); + underTest.add(FILE_COMPONENT, metric1, addedMeasure); + + Optional res = underTest.getRawMeasure(FILE_COMPONENT, metric1); + + assertThat(res).isPresent(); + assertThat(res.get()).isSameAs(addedMeasure); + } + + private static MeasureDto createMeasureDto(int metricId, long snapshotId) { + return new MeasureDto() + .setComponentId(COMPONENT_ID) + .setSnapshotId(snapshotId) + .setData(SOME_DATA) + .setMetricId(metricId); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/metric/MetricImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/metric/MetricImplTest.java new file mode 100644 index 00000000000..9ff5e5171ff --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/metric/MetricImplTest.java @@ -0,0 +1,82 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 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.server.computation.metric; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class MetricImplTest { + + private static final String SOME_KEY = "key"; + private static final String SOME_NAME = "name"; + + @Test(expected = NullPointerException.class) + public void constructor_throws_NPE_if_key_arg_is_null() { + new MetricImpl(null, SOME_NAME, Metric.MetricType.BOOL); + } + + @Test(expected = NullPointerException.class) + public void constructor_throws_NPE_if_name_arg_is_null() { + new MetricImpl(SOME_KEY, null, Metric.MetricType.BOOL); + } + + @Test(expected = NullPointerException.class) + public void constructor_throws_NPE_if_valueType_arg_is_null() { + new MetricImpl(SOME_KEY, SOME_NAME, null); + } + + @Test + public void verify_getters() { + MetricImpl metric = new MetricImpl(SOME_KEY, SOME_NAME, Metric.MetricType.FLOAT); + + assertThat(metric.getKey()).isEqualTo(SOME_KEY); + assertThat(metric.getName()).isEqualTo(SOME_NAME); + assertThat(metric.getMetricType()).isEqualTo(Metric.MetricType.FLOAT); + } + + @Test + public void equals_uses_key_and_name() { + MetricImpl expected = new MetricImpl(SOME_KEY, SOME_NAME, Metric.MetricType.FLOAT); + + assertThat(new MetricImpl(SOME_KEY, SOME_NAME, Metric.MetricType.FLOAT)).isEqualTo(expected); + assertThat(new MetricImpl("some other key", SOME_NAME, Metric.MetricType.FLOAT)).isNotEqualTo(expected); + assertThat(new MetricImpl(SOME_KEY, SOME_NAME, Metric.MetricType.STRING)).isNotEqualTo(expected); + } + + @Test + public void equals_ignores_valueType() { + assertThat(new MetricImpl(SOME_KEY, SOME_NAME, Metric.MetricType.FLOAT)).isEqualTo(new MetricImpl(SOME_KEY, "some other name", Metric.MetricType.FLOAT)); + } + + @Test + public void hashcode_uses_only_key_and_valueType() { + int expected = new MetricImpl(SOME_KEY, SOME_NAME, Metric.MetricType.FLOAT).hashCode(); + + assertThat(new MetricImpl(SOME_KEY, "some other name", Metric.MetricType.FLOAT).hashCode()).isEqualTo(expected); + } + + @Test + public void all_fields_are_displayed_in_toString() { + assertThat(new MetricImpl(SOME_KEY, SOME_NAME, Metric.MetricType.FLOAT).toString()) + .isEqualTo("MetricImpl{key=key, name=name, metricType=FLOAT}"); + + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/metric/MetricRepositoryImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/metric/MetricRepositoryImplTest.java new file mode 100644 index 00000000000..fa1e114df8d --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/metric/MetricRepositoryImplTest.java @@ -0,0 +1,105 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 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.server.computation.metric; + +import javax.annotation.CheckForNull; +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.sonar.core.metric.db.MetricDto; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.DbTester; +import org.sonar.server.db.DbClient; +import org.sonar.server.metric.persistence.MetricDao; +import org.sonar.test.DbTests; + +import static org.assertj.core.api.Assertions.assertThat; + +@Category(DbTests.class) +public class MetricRepositoryImplTest { + private static final String SOME_KEY = "some key"; + private static final String SOME_NAME = "the short name"; + + @ClassRule + public static final DbTester dbTester = new DbTester(); + + private DbClient dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new MetricDao()); + private MetricRepository underTest = new MetricRepositoryImpl(dbClient); + + @CheckForNull + private DbSession dbSession; + + @Before + public void setUp() throws Exception { + dbTester.truncateTables(); + } + + @After + public void tearDown() throws Exception { + if (dbSession != null) { + dbSession.close(); + } + } + + @Test(expected = NullPointerException.class) + public void findByKey_throws_NPE_if_arg_is_null() { + underTest.getByKey(null); + } + + @Test(expected = IllegalStateException.class) + public void findByKey_throws_ISE_of_Metric_does_not_exist() { + underTest.getByKey(SOME_KEY); + } + + @Test + public void verify_mapping_and_valueType_conversion_from_DB() { + dbSession = dbClient.openSession(false); + + for (Metric.MetricType metricType : Metric.MetricType.values()) { + verify_mapping_and_valueType_conversion_from_DB_impl(metricType.name(), metricType); + } + } + + private void verify_mapping_and_valueType_conversion_from_DB_impl(String valueType, Metric.MetricType expected) { + MetricDto metricDto = new MetricDto().setKey(SOME_KEY + valueType).setShortName(SOME_NAME).setValueType(valueType); + + dbClient.metricDao().insert(dbSession, metricDto); + dbSession.commit(); + + Metric metric = underTest.getByKey(metricDto.getKey()); + + assertThat(metric.getKey()).isEqualTo(metricDto.getKey()); + assertThat(metric.getName()).isEqualTo(metricDto.getShortName()); + assertThat(metric.getMetricType()).isEqualTo(expected); + } + + @Test(expected = IllegalArgumentException.class) + public void findByKey_throws_IAE_if_valueType_can_not_be_parsed() { + MetricDto metricDto = new MetricDto().setKey(SOME_KEY).setShortName(SOME_NAME).setValueType("trololo"); + + dbSession = dbClient.openSession(false); + dbClient.metricDao().insert(dbSession, metricDto); + dbSession.commit(); + + underTest.getByKey(SOME_KEY); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/QualityGateEventsStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/QualityGateEventsStepTest.java index cf6df3a9bd1..efecd5049b1 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/QualityGateEventsStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/QualityGateEventsStepTest.java @@ -20,18 +20,21 @@ package org.sonar.server.computation.step; import com.google.common.base.Optional; -import javax.annotation.Nullable; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.sonar.api.notifications.Notification; -import org.sonar.batch.protocol.output.BatchReport; -import org.sonar.core.measure.db.MeasureDto; import org.sonar.server.computation.batch.TreeRootHolderRule; +import org.sonar.server.computation.component.Component; +import org.sonar.server.computation.component.DumbComponent; import org.sonar.server.computation.event.Event; import org.sonar.server.computation.event.EventRepository; +import org.sonar.server.computation.measure.Measure; +import org.sonar.server.computation.measure.MeasureImpl; import org.sonar.server.computation.measure.MeasureRepository; +import org.sonar.server.computation.metric.Metric; +import org.sonar.server.computation.metric.MetricRepository; import org.sonar.server.notification.NotificationManager; import static org.assertj.core.api.Assertions.assertThat; @@ -41,189 +44,190 @@ import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS; -import static org.sonar.server.computation.component.DumbComponent.DUMB_PROJECT; +import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY; +import static org.sonar.server.computation.measure.Measure.Level.ERROR; +import static org.sonar.server.computation.measure.Measure.Level.OK; +import static org.sonar.server.computation.measure.Measure.Level.WARN; public class QualityGateEventsStepTest { - - static final String INVALID_STATUS = "trololo"; - static final String DESCRIPTION = "gate errors"; + private static final DumbComponent PROJECT_COMPONENT = DumbComponent.builder(Component.Type.PROJECT, 1).setUuid("uuid 1").setKey("key 1") + .addChildren(DumbComponent.builder(Component.Type.MODULE, 2).build()) + .build(); + private static final String INVALID_ALERT_STATUS = "trololo"; + private static final String ALERT_TEXT = "alert text"; + private static final Measure.QualityGateStatus OK_QUALITY_GATE_STATUS = new Measure.QualityGateStatus(OK, ALERT_TEXT); + private static final Measure.QualityGateStatus WARN_QUALITY_GATE_STATUS = new Measure.QualityGateStatus(WARN, ALERT_TEXT); + private static final Measure.QualityGateStatus ERROR_QUALITY_GATE_STATUS = new Measure.QualityGateStatus(ERROR, ALERT_TEXT); @Rule public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); - ArgumentCaptor eventArgumentCaptor = ArgumentCaptor.forClass(Event.class); - ArgumentCaptor notificationArgumentCaptor = ArgumentCaptor.forClass(Notification.class); + private ArgumentCaptor eventArgumentCaptor = ArgumentCaptor.forClass(Event.class); + private ArgumentCaptor notificationArgumentCaptor = ArgumentCaptor.forClass(Notification.class); + + private Metric alertStatusMetric = mock(Metric.class); - EventRepository eventRepository = mock(EventRepository.class); - MeasureRepository measureRepository = mock(MeasureRepository.class); - NotificationManager notificationManager = mock(NotificationManager.class); - QualityGateEventsStep underTest = new QualityGateEventsStep(treeRootHolder, eventRepository, measureRepository, notificationManager); + private MetricRepository metricRepository = mock(MetricRepository.class); + private MeasureRepository measureRepository = mock(MeasureRepository.class); + private EventRepository eventRepository = mock(EventRepository.class); + private NotificationManager notificationManager = mock(NotificationManager.class); + private QualityGateEventsStep underTest = new QualityGateEventsStep(treeRootHolder, metricRepository, measureRepository, eventRepository, notificationManager); @Before public void setUp() throws Exception { - treeRootHolder.setRoot(DUMB_PROJECT); + when(metricRepository.getByKey(ALERT_STATUS_KEY)).thenReturn(alertStatusMetric); + treeRootHolder.setRoot(PROJECT_COMPONENT); } @Test - public void no_event_if_no_status_measure() { - when(measureRepository.findCurrent(DUMB_PROJECT, ALERT_STATUS)).thenReturn(Optional.absent()); + public void no_event_if_no_raw_ALERT_STATUS_measure() { + when(measureRepository.getRawMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(Optional.absent()); underTest.execute(); - verify(measureRepository).findCurrent(DUMB_PROJECT, ALERT_STATUS); - verifyNoMoreInteractions(measureRepository, eventRepository, notificationManager); + verify(measureRepository).getRawMeasure(PROJECT_COMPONENT, alertStatusMetric); + verifyNoMoreInteractions(measureRepository, eventRepository); } @Test - public void no_event_created_if_status_measure_is_null() { - when(measureRepository.findCurrent(DUMB_PROJECT, ALERT_STATUS)).thenReturn(Optional.of(BatchReport.Measure.newBuilder().build())); + public void no_event_created_if_raw_ALERT_STATUS_measure_is_null() { + when(measureRepository.getRawMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(of(MeasureImpl.createNoValue())); underTest.execute(); - verify(measureRepository).findCurrent(DUMB_PROJECT, ALERT_STATUS); - verifyNoMoreInteractions(measureRepository, eventRepository, notificationManager); + verify(measureRepository).getRawMeasure(PROJECT_COMPONENT, alertStatusMetric); + verifyNoMoreInteractions(measureRepository, eventRepository); + } + + private static Optional of(MeasureImpl measure) { + return Optional.of((Measure) measure); } @Test - public void no_event_created_if_status_measure_has_unsupported_value() { - when(measureRepository.findCurrent(DUMB_PROJECT, ALERT_STATUS)).thenReturn(Optional.of(BatchReport.Measure.newBuilder().setAlertStatus(INVALID_STATUS).build())); + public void no_event_created_if_raw_ALERT_STATUS_measure_is_unsupported_value() { + when(measureRepository.getRawMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(of(MeasureImpl.create(INVALID_ALERT_STATUS))); underTest.execute(); - verify(measureRepository).findCurrent(DUMB_PROJECT, ALERT_STATUS); - verifyNoMoreInteractions(measureRepository, eventRepository, notificationManager); + verify(measureRepository).getRawMeasure(PROJECT_COMPONENT, alertStatusMetric); + verifyNoMoreInteractions(measureRepository, eventRepository); } @Test - public void no_event_created_if_OK_and_no_base_status() { - String alertStatus = "OK"; + public void no_event_created_if_no_base_ALERT_STATUS_and_raw_is_OK() { + Measure.QualityGateStatus someQGStatus = new Measure.QualityGateStatus(Measure.Level.OK); - when(measureRepository.findCurrent(DUMB_PROJECT, ALERT_STATUS)).thenReturn(createBatchReportMeasure(alertStatus, null)); - when(measureRepository.findPrevious(DUMB_PROJECT, ALERT_STATUS)).thenReturn(Optional.absent()); + when(measureRepository.getRawMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(of(MeasureImpl.createNoValue().setQualityGateStatus(someQGStatus))); + when(measureRepository.getBaseMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(of(MeasureImpl.createNoValue())); underTest.execute(); - verify(measureRepository).findCurrent(DUMB_PROJECT, ALERT_STATUS); - verify(measureRepository).findPrevious(DUMB_PROJECT, ALERT_STATUS); - verifyNoMoreInteractions(measureRepository, eventRepository, notificationManager); + verify(measureRepository).getRawMeasure(PROJECT_COMPONENT, alertStatusMetric); + verify(measureRepository).getBaseMeasure(PROJECT_COMPONENT, alertStatusMetric); + verifyNoMoreInteractions(measureRepository, eventRepository); } @Test - public void event_created_if_WARN_and_no_base_status() { - verify_event_created_if_no_base_status("WARN", "Orange", null); + public void event_created_if_no_base_ALERT_STATUS_and_raw_is_WARN() { + verify_event_created_if_no_base_ALERT_STATUS_measure(WARN, "Orange"); } @Test - public void event_created_if_ERROR_and_no_base_status() { - verify_event_created_if_no_base_status("ERROR", "Red", null); + public void event_created_if_base_ALERT_STATUS_and_raw_is_ERROR() { + verify_event_created_if_no_base_ALERT_STATUS_measure(ERROR, "Red"); } @Test - public void event_created_if_ERROR_and_base_measure_has_no_status() { - verify_event_created_if_no_base_status("ERROR", "Red", new MeasureDto()); + public void event_created_if_base_ALERT_STATUS_has_no_alertStatus_and_raw_is_ERROR() { + verify_event_created_if_no_base_ALERT_STATUS_measure(ERROR, "Red"); } @Test - public void event_created_if_WARN_and_base_measure_has_no_status() { - verify_event_created_if_no_base_status("WARN", "Orange", new MeasureDto()); + public void event_created_if_base_ALERT_STATUS_has_no_alertStatus_and_raw_is_WARN() { + verify_event_created_if_no_base_ALERT_STATUS_measure(WARN, "Orange"); } @Test - public void event_created_if_ERROR_and_base_status_has_invalid_value() { - verify_event_created_if_no_base_status("ERROR", "Red", new MeasureDto().setAlertStatus(INVALID_STATUS)); + public void event_created_if_base_ALERT_STATUS_has_invalid_alertStatus_and_raw_is_ERROR() { + verify_event_created_if_no_base_ALERT_STATUS_measure(ERROR, "Red"); } @Test - public void event_created_if_WARN_and_base_status_has_invalid_value() { - verify_event_created_if_no_base_status("WARN", "Orange", new MeasureDto().setAlertStatus(INVALID_STATUS)); + public void event_created_if_base_ALERT_STATUS_has_invalid_alertStatus_and_raw_is_WARN() { + verify_event_created_if_no_base_ALERT_STATUS_measure(WARN, "Orange"); } - private void verify_event_created_if_no_base_status(String status, String expectedLabel, @Nullable MeasureDto measureDto) { - when(measureRepository.findCurrent(DUMB_PROJECT, ALERT_STATUS)).thenReturn(createBatchReportMeasure(status, DESCRIPTION)); - when(measureRepository.findPrevious(DUMB_PROJECT, ALERT_STATUS)).thenReturn(Optional.fromNullable(measureDto)); + private void verify_event_created_if_no_base_ALERT_STATUS_measure(Measure.Level rawAlterStatus, String expectedEventName) { + Measure.QualityGateStatus someQGStatus = new Measure.QualityGateStatus(rawAlterStatus, ALERT_TEXT); + + when(measureRepository.getRawMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(of(MeasureImpl.createNoValue().setQualityGateStatus(someQGStatus))); + when(measureRepository.getBaseMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(of(MeasureImpl.createNoValue())); underTest.execute(); - verify(measureRepository).findCurrent(DUMB_PROJECT, ALERT_STATUS); - verify(measureRepository).findPrevious(DUMB_PROJECT, ALERT_STATUS); - verify(eventRepository).add(eq(DUMB_PROJECT), eventArgumentCaptor.capture()); + verify(measureRepository).getRawMeasure(PROJECT_COMPONENT, alertStatusMetric); + verify(measureRepository).getBaseMeasure(PROJECT_COMPONENT, alertStatusMetric); + verify(eventRepository).add(eq(PROJECT_COMPONENT), eventArgumentCaptor.capture()); verifyNoMoreInteractions(measureRepository, eventRepository); Event event = eventArgumentCaptor.getValue(); assertThat(event.getCategory()).isEqualTo(Event.Category.ALERT); - assertThat(event.getName()).isEqualTo(expectedLabel); - assertThat(event.getDescription()).isEqualTo(DESCRIPTION); + assertThat(event.getName()).isEqualTo(expectedEventName); + assertThat(event.getDescription()).isEqualTo(ALERT_TEXT); assertThat(event.getData()).isNull(); - - verify(notificationManager).scheduleForSending(notificationArgumentCaptor.capture()); - Notification notification = notificationArgumentCaptor.getValue(); - assertThat(notification.getType()).isEqualTo("alerts"); - assertThat(notification.getFieldValue("projectKey")).isEqualTo(DUMB_PROJECT.getKey()); - assertThat(notification.getFieldValue("projectUuid")).isEqualTo(DUMB_PROJECT.getUuid()); - assertThat(notification.getFieldValue("projectName")).isEqualTo(DUMB_PROJECT.getName()); - assertThat(notification.getFieldValue("alertLevel")).isEqualTo(status); - assertThat(notification.getFieldValue("alertName")).isEqualTo(expectedLabel); } @Test - public void no_event_created_if_status_same_as_base() { - String alertStatus = "OK"; - - when(measureRepository.findCurrent(DUMB_PROJECT, ALERT_STATUS)).thenReturn(createBatchReportMeasure(alertStatus, DESCRIPTION)); - when(measureRepository.findPrevious(DUMB_PROJECT, ALERT_STATUS)).thenReturn(Optional.of(new MeasureDto().setAlertStatus(alertStatus))); + public void no_event_created_if_base_ALERT_STATUS_measure_but_status_is_the_same() { + when(measureRepository.getRawMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(of(MeasureImpl.createNoValue().setQualityGateStatus(OK_QUALITY_GATE_STATUS))); + when(measureRepository.getBaseMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(of(MeasureImpl.createNoValue().setQualityGateStatus(OK_QUALITY_GATE_STATUS))); underTest.execute(); - verify(measureRepository).findCurrent(DUMB_PROJECT, ALERT_STATUS); - verify(measureRepository).findPrevious(DUMB_PROJECT, ALERT_STATUS); - verifyNoMoreInteractions(measureRepository, eventRepository, notificationManager); + verify(measureRepository).getRawMeasure(PROJECT_COMPONENT, alertStatusMetric); + verify(measureRepository).getBaseMeasure(PROJECT_COMPONENT, alertStatusMetric); + verifyNoMoreInteractions(measureRepository, eventRepository); } @Test - public void event_created_if_status_changed() { - verify_event_created_if_status_changed("OK", "WARN", "Orange (was Green)"); - verify_event_created_if_status_changed("OK", "ERROR", "Red (was Green)"); - verify_event_created_if_status_changed("WARN", "OK", "Green (was Orange)"); - verify_event_created_if_status_changed("WARN", "ERROR", "Red (was Orange)"); - verify_event_created_if_status_changed("ERROR", "OK", "Green (was Red)"); - verify_event_created_if_status_changed("ERROR", "WARN", "Orange (was Red)"); + public void event_created_if_base_ALERT_STATUS_measure_exists_and_status_has_changed() { + verify_event_created_if_base_ALERT_STATUS_measure_exists_and_status_has_changed(OK, WARN_QUALITY_GATE_STATUS, "Orange (was Green)"); + verify_event_created_if_base_ALERT_STATUS_measure_exists_and_status_has_changed(OK, ERROR_QUALITY_GATE_STATUS, "Red (was Green)"); + verify_event_created_if_base_ALERT_STATUS_measure_exists_and_status_has_changed(WARN, OK_QUALITY_GATE_STATUS, "Green (was Orange)"); + verify_event_created_if_base_ALERT_STATUS_measure_exists_and_status_has_changed(WARN, ERROR_QUALITY_GATE_STATUS, "Red (was Orange)"); + verify_event_created_if_base_ALERT_STATUS_measure_exists_and_status_has_changed(ERROR, OK_QUALITY_GATE_STATUS, "Green (was Red)"); + verify_event_created_if_base_ALERT_STATUS_measure_exists_and_status_has_changed(ERROR, WARN_QUALITY_GATE_STATUS, "Orange (was Red)"); } - private void verify_event_created_if_status_changed(String baseStatus, String status, String expectedLabel) { - when(measureRepository.findCurrent(DUMB_PROJECT, ALERT_STATUS)).thenReturn(createBatchReportMeasure(status, DESCRIPTION)); - when(measureRepository.findPrevious(DUMB_PROJECT, ALERT_STATUS)).thenReturn(Optional.of(new MeasureDto().setAlertStatus(baseStatus))); + private void verify_event_created_if_base_ALERT_STATUS_measure_exists_and_status_has_changed(Measure.Level previousAlertStatus, + Measure.QualityGateStatus newQualityGateStatus, String expectedLabel) { + when(measureRepository.getRawMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn(of(MeasureImpl.createNoValue().setQualityGateStatus(newQualityGateStatus))); + when(measureRepository.getBaseMeasure(PROJECT_COMPONENT, alertStatusMetric)).thenReturn( + of(MeasureImpl.createNoValue().setQualityGateStatus(new Measure.QualityGateStatus(previousAlertStatus)))); underTest.execute(); - verify(measureRepository).findCurrent(DUMB_PROJECT, ALERT_STATUS); - verify(measureRepository).findPrevious(DUMB_PROJECT, ALERT_STATUS); - verify(eventRepository).add(eq(DUMB_PROJECT), eventArgumentCaptor.capture()); + verify(measureRepository).getRawMeasure(PROJECT_COMPONENT, alertStatusMetric); + verify(measureRepository).getBaseMeasure(PROJECT_COMPONENT, alertStatusMetric); + verify(eventRepository).add(eq(PROJECT_COMPONENT), eventArgumentCaptor.capture()); verifyNoMoreInteractions(measureRepository, eventRepository); Event event = eventArgumentCaptor.getValue(); assertThat(event.getCategory()).isEqualTo(Event.Category.ALERT); assertThat(event.getName()).isEqualTo(expectedLabel); - assertThat(event.getDescription()).isEqualTo(DESCRIPTION); + assertThat(event.getDescription()).isEqualTo(ALERT_TEXT); assertThat(event.getData()).isNull(); verify(notificationManager).scheduleForSending(notificationArgumentCaptor.capture()); Notification notification = notificationArgumentCaptor.getValue(); assertThat(notification.getType()).isEqualTo("alerts"); - assertThat(notification.getFieldValue("projectKey")).isEqualTo(DUMB_PROJECT.getKey()); - assertThat(notification.getFieldValue("projectUuid")).isEqualTo(DUMB_PROJECT.getUuid()); - assertThat(notification.getFieldValue("projectName")).isEqualTo(DUMB_PROJECT.getName()); - assertThat(notification.getFieldValue("alertLevel")).isEqualTo(status); + assertThat(notification.getFieldValue("projectKey")).isEqualTo(PROJECT_COMPONENT.getKey()); + assertThat(notification.getFieldValue("projectUuid")).isEqualTo(PROJECT_COMPONENT.getUuid()); + assertThat(notification.getFieldValue("projectName")).isEqualTo(PROJECT_COMPONENT.getName()); + assertThat(notification.getFieldValue("alertLevel")).isEqualTo(newQualityGateStatus.getStatus().name()); assertThat(notification.getFieldValue("alertName")).isEqualTo(expectedLabel); reset(measureRepository, eventRepository, notificationManager); } - private static Optional createBatchReportMeasure(String status, @Nullable String description) { - BatchReport.Measure.Builder builder = BatchReport.Measure.newBuilder().setAlertStatus(status); - if (description != null) { - builder.setAlertText(description); - } - return Optional.of(builder.build()); - } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/QualityProfileEventsStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/QualityProfileEventsStepTest.java index d491d4a581f..2876c95c2f6 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/QualityProfileEventsStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/QualityProfileEventsStepTest.java @@ -36,16 +36,18 @@ import org.mockito.stubbing.Answer; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.resources.AbstractLanguage; import org.sonar.api.resources.Language; -import org.sonar.batch.protocol.output.BatchReport; import org.sonar.core.UtcDateUtils; -import org.sonar.core.measure.db.MeasureDto; import org.sonar.server.computation.batch.TreeRootHolderRule; import org.sonar.server.computation.component.Component; import org.sonar.server.computation.component.DumbComponent; import org.sonar.server.computation.event.Event; import org.sonar.server.computation.event.EventRepository; import org.sonar.server.computation.language.LanguageRepository; +import org.sonar.server.computation.measure.Measure; +import org.sonar.server.computation.measure.MeasureImpl; import org.sonar.server.computation.measure.MeasureRepository; +import org.sonar.server.computation.metric.Metric; +import org.sonar.server.computation.metric.MetricRepository; import org.sonar.server.computation.qualityprofile.QPMeasureData; import org.sonar.server.computation.qualityprofile.QualityProfile; @@ -71,21 +73,25 @@ public class QualityProfileEventsStepTest { private static final String LANGUAGE_KEY_2 = "language_key_2"; private static final String LANGUAGE_KEY_3 = "languageKey3"; + private MetricRepository metricRepository = mock(MetricRepository.class); private MeasureRepository measureRepository = mock(MeasureRepository.class); private LanguageRepository languageRepository = mock(LanguageRepository.class); private EventRepository eventRepository = mock(EventRepository.class); private ArgumentCaptor eventArgumentCaptor = ArgumentCaptor.forClass(Event.class); - private QualityProfileEventsStep underTest = new QualityProfileEventsStep(treeRootHolder, measureRepository, eventRepository, languageRepository); + private Metric qualityProfileMetric = mock(Metric.class); + + private QualityProfileEventsStep underTest = new QualityProfileEventsStep(treeRootHolder, metricRepository, measureRepository, languageRepository, eventRepository); @Before public void setUp() throws Exception { + when(metricRepository.getByKey(CoreMetrics.QUALITY_PROFILES_KEY)).thenReturn(qualityProfileMetric); treeRootHolder.setRoot(DumbComponent.builder(Component.Type.PROJECT, 1).setUuid("uuid").setKey("key").build()); } @Test - public void no_effect_if_no_previous_measure() { - when(measureRepository.findPrevious(treeRootHolder.getRoot(), CoreMetrics.QUALITY_PROFILES)).thenReturn(Optional.absent()); + public void no_effect_if_no_base_measure() { + when(measureRepository.getBaseMeasure(treeRootHolder.getRoot(), qualityProfileMetric)).thenReturn(Optional.absent()); underTest.execute(); @@ -93,15 +99,15 @@ public class QualityProfileEventsStepTest { } @Test(expected = IllegalStateException.class) - public void ISE_if_no_current_measure() { - when(measureRepository.findPrevious(treeRootHolder.getRoot(), CoreMetrics.QUALITY_PROFILES)).thenReturn(Optional.of(newMeasureDto())); - when(measureRepository.findCurrent(treeRootHolder.getRoot(), CoreMetrics.QUALITY_PROFILES)).thenReturn(Optional.absent()); + public void ISE_if_no_raw_measure() { + when(measureRepository.getBaseMeasure(treeRootHolder.getRoot(), qualityProfileMetric)).thenReturn(Optional.of(newMeasure())); + when(measureRepository.getRawMeasure(treeRootHolder.getRoot(), qualityProfileMetric)).thenReturn(Optional.absent()); underTest.execute(); } @Test - public void no_event_if_no_qp_now_nor_before() { + public void no_event_if_no_base_nor_row_QualityProfile_measure() { mockMeasures(treeRootHolder.getRoot(), null, null); underTest.execute(); @@ -248,8 +254,8 @@ public class QualityProfileEventsStepTest { } private void mockMeasures(Component component, @Nullable QualityProfile[] previous, @Nullable QualityProfile[] current) { - when(measureRepository.findPrevious(component, CoreMetrics.QUALITY_PROFILES)).thenReturn(Optional.of(newMeasureDto(previous))); - when(measureRepository.findCurrent(component, CoreMetrics.QUALITY_PROFILES)).thenReturn(Optional.of(newQPBatchMeasure(current))); + when(measureRepository.getBaseMeasure(component, qualityProfileMetric)).thenReturn(Optional.of(newMeasure(previous))); + when(measureRepository.getRawMeasure(component, qualityProfileMetric)).thenReturn(Optional.of(newMeasure(current))); } private static void verifyEvent(Event event, String expectedName, @Nullable String expectedData) { @@ -274,12 +280,8 @@ public class QualityProfileEventsStepTest { return qps; } - private static MeasureDto newMeasureDto(@Nullable QualityProfile... qps) { - return new MeasureDto().setData(toJson(qps)); - } - - private static BatchReport.Measure newQPBatchMeasure(@Nullable QualityProfile... qps) { - return BatchReport.Measure.newBuilder().setStringValue(toJson(qps)).build(); + private static Measure newMeasure(@Nullable QualityProfile... qps) { + return MeasureImpl.create(toJson(qps)); } private static String toJson(@Nullable QualityProfile... qps) { diff --git a/server/sonar-server/src/test/resources/org/sonar/server/computation/measure/MeasureRepositoryImplTest/shared.xml b/server/sonar-server/src/test/resources/org/sonar/server/computation/measure/MeasureRepositoryImplTest/shared.xml new file mode 100644 index 00000000000..19ce3154111 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/computation/measure/MeasureRepositoryImplTest/shared.xml @@ -0,0 +1,7 @@ + + + + + + + -- 2.39.5