diff options
author | Sébastien Lesaint <sebastien.lesaint@sonarsource.com> | 2016-04-01 18:33:08 +0200 |
---|---|---|
committer | Sébastien Lesaint <sebastien.lesaint@sonarsource.com> | 2016-04-06 15:42:53 +0200 |
commit | a1418d7865db81d7b56acd00bc4dc3194df9b45d (patch) | |
tree | d61cec7eb4efe498eb39b561a2a24f4ab94d07cd | |
parent | 1f634cd0eef4659086318e17f97a198bce02b821 (diff) | |
download | sonarqube-a1418d7865db81d7b56acd00bc4dc3194df9b45d.tar.gz sonarqube-a1418d7865db81d7b56acd00bc4dc3194df9b45d.zip |
SONAR-7488 support PostProjectAnalysisTask in report CE task
29 files changed, 2168 insertions, 34 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulator.java b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulator.java index 99a8e0a3078..e78c44c8e79 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulator.java @@ -77,9 +77,11 @@ import org.sonar.server.computation.measure.MeasureRepositoryImpl; import org.sonar.server.computation.measure.MeasureToMeasureDto; import org.sonar.server.computation.metric.MetricModule; import org.sonar.server.computation.period.PeriodsHolderImpl; +import org.sonar.server.computation.posttask.PostProjectAnalysisTasksExecutor; import org.sonar.server.computation.qualitygate.EvaluationResultTextConverterImpl; import org.sonar.server.computation.qualitygate.QualityGateHolderImpl; import org.sonar.server.computation.qualitygate.QualityGateServiceImpl; +import org.sonar.server.computation.qualitygate.QualityGateStatusHolderImpl; import org.sonar.server.computation.qualitymodel.NewQualityModelMeasuresVisitor; import org.sonar.server.computation.qualitymodel.QualityModelMeasuresVisitor; import org.sonar.server.computation.qualitymodel.RatingSettings; @@ -124,6 +126,7 @@ public final class ReportComputeEngineContainerPopulator implements ContainerPop */ private static List componentClasses() { return Arrays.asList( + PostProjectAnalysisTasksExecutor.class, ComputationStepExecutor.class, // File System @@ -138,6 +141,7 @@ public final class ReportComputeEngineContainerPopulator implements ContainerPop TreeRootHolderImpl.class, PeriodsHolderImpl.class, QualityGateHolderImpl.class, + QualityGateStatusHolderImpl.class, RatingSettings.class, ActiveRulesHolderImpl.class, MeasureComputersHolderImpl.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/posttask/CeTaskImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/posttask/CeTaskImpl.java new file mode 100644 index 00000000000..c95e52b705c --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/posttask/CeTaskImpl.java @@ -0,0 +1,54 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program 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. + * + * This program 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.posttask; + +import javax.annotation.concurrent.Immutable; +import org.sonar.api.ce.posttask.CeTask; + +import static java.util.Objects.requireNonNull; + +@Immutable +class CeTaskImpl implements CeTask { + private final String id; + private final Status status; + + CeTaskImpl(String id, Status status) { + this.id = requireNonNull(id, "uuid can not be null"); + this.status = requireNonNull(status, "status can not be null"); + } + + @Override + public String getId() { + return id; + } + + @Override + public Status getStatus() { + return status; + } + + @Override + public String toString() { + return "CeTaskImpl{" + + "id='" + id + '\'' + + ", status=" + status + + '}'; + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/posttask/ConditionImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/posttask/ConditionImpl.java new file mode 100644 index 00000000000..8cd92a9b67b --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/posttask/ConditionImpl.java @@ -0,0 +1,183 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program 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. + * + * This program 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.posttask; + +import javax.annotation.CheckForNull; +import javax.annotation.concurrent.Immutable; +import org.sonar.api.ce.posttask.QualityGate; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; +import static org.sonar.api.ce.posttask.QualityGate.EvaluationStatus.NO_VALUE; + +@Immutable +class ConditionImpl implements QualityGate.Condition { + private final QualityGate.EvaluationStatus status; + private final String metricKey; + private final QualityGate.Operator operator; + @CheckForNull + private final String errorThreshold; + @CheckForNull + private final String warningThreshold; + private final boolean onLeakPeriod; + @CheckForNull + private final String value; + + private ConditionImpl(Builder builder) { + requireNonNull(builder.status, "status can not be null"); + requireNonNull(builder.metricKey, "metricKey can not be null"); + requireNonNull(builder.operator, "operator can not be null"); + verifyThresholds(builder); + verifyValue(builder); + + this.status = builder.status; + this.metricKey = builder.metricKey; + this.operator = builder.operator; + this.errorThreshold = builder.errorThreshold; + this.warningThreshold = builder.warningThreshold; + this.onLeakPeriod = builder.onLeakPeriod; + this.value = builder.value; + } + + private static void verifyThresholds(Builder builder) { + checkArgument( + builder.errorThreshold != null || builder.warningThreshold != null, + "At least one of errorThreshold and warningThreshold must be non null"); + } + + private static void verifyValue(Builder builder) { + if (builder.status == NO_VALUE) { + checkArgument(builder.value == null, "value must be null when status is %s", NO_VALUE); + } else { + checkArgument(builder.value != null, "value can not be null when status is not %s", NO_VALUE); + } + } + + public static Builder newBuilder() { + return new Builder(); + } + + public static class Builder { + private String metricKey; + private QualityGate.Operator operator; + @CheckForNull + private String errorThreshold; + @CheckForNull + private String warningThreshold; + private boolean onLeakPeriod; + @CheckForNull + private String value; + private QualityGate.EvaluationStatus status; + + private Builder() { + // enforce use of static method + } + + public Builder setMetricKey(String metricKey) { + this.metricKey = metricKey; + return this; + } + + public Builder setOperator(QualityGate.Operator operator) { + this.operator = operator; + return this; + } + + public Builder setErrorThreshold(String errorThreshold) { + this.errorThreshold = errorThreshold; + return this; + } + + public Builder setWarningThreshold(String warningThreshold) { + this.warningThreshold = warningThreshold; + return this; + } + + public Builder setOnLeakPeriod(boolean onLeakPeriod) { + this.onLeakPeriod = onLeakPeriod; + return this; + } + + public Builder setValue(String value) { + this.value = value; + return this; + } + + public Builder setStatus(QualityGate.EvaluationStatus status) { + this.status = status; + return this; + } + + public ConditionImpl build() { + return new ConditionImpl(this); + } + } + + @Override + public QualityGate.EvaluationStatus getStatus() { + return status; + } + + @Override + public String getMetricKey() { + return metricKey; + } + + @Override + public QualityGate.Operator getOperator() { + return operator; + } + + @Override + public String getErrorThreshold() { + return errorThreshold; + } + + @Override + public String getWarningThreshold() { + return warningThreshold; + } + + @Override + public boolean isOnLeakPeriod() { + return onLeakPeriod; + } + + @Override + public String getValue() { + checkState(status != NO_VALUE, "There is no value when status is " + NO_VALUE); + + return value; + } + + @Override + public String toString() { + return "ConditionImpl{" + + "status=" + status + + ", metricKey='" + metricKey + '\'' + + ", operator=" + operator + + ", errorThreshold='" + errorThreshold + '\'' + + ", warningThreshold='" + warningThreshold + '\'' + + ", onLeakPeriod=" + onLeakPeriod + + ", value='" + value + '\'' + + '}'; + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/posttask/ConditionToCondition.java b/server/sonar-server/src/main/java/org/sonar/server/computation/posttask/ConditionToCondition.java new file mode 100644 index 00000000000..55df858f496 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/posttask/ConditionToCondition.java @@ -0,0 +1,94 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program 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. + * + * This program 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.posttask; + +import com.google.common.base.Function; +import java.util.Map; +import javax.annotation.Nonnull; +import org.sonar.api.ce.posttask.QualityGate; +import org.sonar.server.computation.qualitygate.Condition; +import org.sonar.server.computation.qualitygate.ConditionStatus; + +import static com.google.common.base.Preconditions.checkState; +import static java.lang.String.format; + +/** + * Converts a {@link Condition} from the Compute Engine internal API to a {@link org.sonar.api.ce.posttask.QualityGate.Condition} + * of the public API. + */ +class ConditionToCondition implements Function<Condition, QualityGate.Condition> { + private final ConditionImpl.Builder builder = ConditionImpl.newBuilder(); + private final Map<Condition, ConditionStatus> statusPerConditions; + + public ConditionToCondition(Map<Condition, ConditionStatus> statusPerConditions) { + this.statusPerConditions = statusPerConditions; + } + + @Override + @Nonnull + public QualityGate.Condition apply(@Nonnull Condition input) { + String metricKey = input.getMetric().getKey(); + ConditionStatus conditionStatus = statusPerConditions.get(input); + checkState(conditionStatus != null, "Missing ConditionStatus for condition on metric key %s", metricKey); + return builder + .setStatus(convert(conditionStatus.getStatus())) + .setMetricKey(metricKey) + .setOperator(convert(input.getOperator())) + .setErrorThreshold(input.getErrorThreshold()) + .setWarningThreshold(input.getWarningThreshold()) + .setOnLeakPeriod(input.getPeriod() != null) + .setValue(conditionStatus.getValue()) + .build(); + } + + private static QualityGate.EvaluationStatus convert(ConditionStatus.EvaluationStatus status) { + switch (status) { + case NO_VALUE: + return QualityGate.EvaluationStatus.NO_VALUE; + case OK: + return QualityGate.EvaluationStatus.OK; + case WARN: + return QualityGate.EvaluationStatus.WARN; + case ERROR: + return QualityGate.EvaluationStatus.ERROR; + default: + throw new IllegalArgumentException(format( + "Unsupported value '%s' of ConditionStatus.EvaluationStatus can not be converted to QualityGate.EvaluationStatus", + status)); + } + } + + private static QualityGate.Operator convert(Condition.Operator operator) { + switch (operator) { + case EQUALS: + return QualityGate.Operator.EQUALS; + case NOT_EQUALS: + return QualityGate.Operator.NOT_EQUALS; + case GREATER_THAN: + return QualityGate.Operator.GREATER_THAN; + case LESS_THAN: + return QualityGate.Operator.LESS_THAN; + default: + throw new IllegalArgumentException(format( + "Unsupported value '%s' of Condition.Operation can not be converted to QualityGate.Operator", + operator)); + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/posttask/PostProjectAnalysisTasksExecutor.java b/server/sonar-server/src/main/java/org/sonar/server/computation/posttask/PostProjectAnalysisTasksExecutor.java new file mode 100644 index 00000000000..d89b5718707 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/posttask/PostProjectAnalysisTasksExecutor.java @@ -0,0 +1,192 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program 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. + * + * This program 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.posttask; + +import com.google.common.base.Optional; +import java.util.Collection; +import java.util.Date; +import java.util.Map; +import java.util.Set; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.sonar.api.ce.posttask.CeTask; +import org.sonar.api.ce.posttask.PostProjectAnalysisTask; +import org.sonar.api.ce.posttask.Project; +import org.sonar.api.ce.posttask.QualityGate; +import org.sonar.server.computation.analysis.AnalysisMetadataHolder; +import org.sonar.server.computation.qualitygate.Condition; +import org.sonar.server.computation.qualitygate.ConditionStatus; +import org.sonar.server.computation.qualitygate.QualityGateHolder; +import org.sonar.server.computation.qualitygate.QualityGateStatus; +import org.sonar.server.computation.qualitygate.QualityGateStatusHolder; +import org.sonar.server.computation.step.ComputationStepExecutor; + +import static com.google.common.collect.FluentIterable.from; +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; +import static org.sonar.api.ce.posttask.CeTask.Status.FAILED; +import static org.sonar.api.ce.posttask.CeTask.Status.SUCCESS; + +/** + * Responsible for calling {@link PostProjectAnalysisTask} implementations (if any). + */ +public class PostProjectAnalysisTasksExecutor implements ComputationStepExecutor.Listener { + private static final PostProjectAnalysisTask[] NO_POST_PROJECT_ANALYSIS_TASKS = new PostProjectAnalysisTask[0]; + + private final org.sonar.ce.queue.CeTask ceTask; + private final AnalysisMetadataHolder analysisMetadataHolder; + private final QualityGateHolder qualityGateHolder; + private final QualityGateStatusHolder qualityGateStatusHolder; + private final PostProjectAnalysisTask[] postProjectAnalysisTasks; + + /** + * Constructor used by Pico when there is no {@link PostProjectAnalysisTask} in the container. + */ + public PostProjectAnalysisTasksExecutor(org.sonar.ce.queue.CeTask ceTask, + AnalysisMetadataHolder analysisMetadataHolder, + QualityGateHolder qualityGateHolder, QualityGateStatusHolder qualityGateStatusHolder) { + this(ceTask, analysisMetadataHolder, qualityGateHolder, qualityGateStatusHolder, null); + } + + public PostProjectAnalysisTasksExecutor(org.sonar.ce.queue.CeTask ceTask, + AnalysisMetadataHolder analysisMetadataHolder, + QualityGateHolder qualityGateHolder, QualityGateStatusHolder qualityGateStatusHolder, + @Nullable PostProjectAnalysisTask[] postProjectAnalysisTasks) { + this.analysisMetadataHolder = analysisMetadataHolder; + this.qualityGateHolder = qualityGateHolder; + this.qualityGateStatusHolder = qualityGateStatusHolder; + this.ceTask = ceTask; + this.postProjectAnalysisTasks = postProjectAnalysisTasks == null ? NO_POST_PROJECT_ANALYSIS_TASKS : postProjectAnalysisTasks; + } + + @Override + public void finished(boolean allStepsExecuted) { + if (postProjectAnalysisTasks.length == 0) { + return; + } + + ProjectAnalysis projectAnalysis = createProjectAnalysis(allStepsExecuted ? SUCCESS : FAILED); + for (PostProjectAnalysisTask postProjectAnalysisTask : postProjectAnalysisTasks) { + postProjectAnalysisTask.finished(projectAnalysis); + } + } + + private ProjectAnalysis createProjectAnalysis(CeTask.Status status) { + return new ProjectAnalysis( + new CeTaskImpl(this.ceTask.getUuid(), status), + createProject(this.ceTask), + getAnalysisDate(), + status == SUCCESS ? createQualityGate(this.qualityGateHolder) : null); + } + + private static Project createProject(org.sonar.ce.queue.CeTask ceTask) { + return new ProjectImpl( + ceTask.getComponentUuid(), + ceTask.getComponentKey(), + ceTask.getComponentName()); + } + + private Date getAnalysisDate() { + return new Date(this.analysisMetadataHolder.getAnalysisDate()); + } + + @CheckForNull + private QualityGateImpl createQualityGate(QualityGateHolder qualityGateHolder) { + Optional<org.sonar.server.computation.qualitygate.QualityGate> qualityGateOptional = qualityGateHolder.getQualityGate(); + if (qualityGateOptional.isPresent()) { + org.sonar.server.computation.qualitygate.QualityGate qualityGate = qualityGateOptional.get(); + + return new QualityGateImpl( + String.valueOf(qualityGate.getId()), + qualityGate.getName(), + convert(qualityGateStatusHolder.getStatus()), + convert(qualityGate.getConditions(), qualityGateStatusHolder.getStatusPerConditions())); + } + return null; + } + + private static QualityGate.Status convert(QualityGateStatus status) { + switch (status) { + case OK: + return QualityGate.Status.OK; + case WARN: + return QualityGate.Status.WARN; + case ERROR: + return QualityGate.Status.ERROR; + default: + throw new IllegalArgumentException(format( + "Unsupported value '%s' of QualityGateStatus can not be converted to QualityGate.Status", + status)); + } + } + + private static Collection<QualityGate.Condition> convert(Set<Condition> conditions, Map<Condition, ConditionStatus> statusPerConditions) { + return from(conditions) + .transform(new ConditionToCondition(statusPerConditions)) + .toList(); + } + + private static class ProjectAnalysis implements PostProjectAnalysisTask.ProjectAnalysis { + private final CeTask ceTask; + private final Project project; + private final Date date; + @CheckForNull + private final QualityGate qualityGate; + + private ProjectAnalysis(CeTask ceTask, Project project, Date date, @Nullable QualityGate qualityGate) { + this.ceTask = requireNonNull(ceTask, "ceTask can not be null"); + this.project = requireNonNull(project, "project can not be null"); + this.date = requireNonNull(date, "date can not be null"); + this.qualityGate = qualityGate; + } + + @Override + public CeTask getCeTask() { + return ceTask; + } + + @Override + public Project getProject() { + return project; + } + + @Override + @CheckForNull + public QualityGate getQualityGate() { + return qualityGate; + } + + @Override + public Date getDate() { + return date; + } + + @Override + public String toString() { + return "ProjectAnalysis{" + + "ceTask=" + ceTask + + ", project=" + project + + ", date=" + date + + ", qualityGate=" + qualityGate + + '}'; + } + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/posttask/ProjectImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/posttask/ProjectImpl.java new file mode 100644 index 00000000000..128a665948a --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/posttask/ProjectImpl.java @@ -0,0 +1,62 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program 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. + * + * This program 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.posttask; + +import javax.annotation.concurrent.Immutable; +import org.sonar.api.ce.posttask.Project; + +import static java.util.Objects.requireNonNull; + +@Immutable +class ProjectImpl implements Project { + private final String uuid; + private final String key; + private final String name; + + ProjectImpl(String uuid, String key, String name) { + this.uuid = requireNonNull(uuid, "uuid can not be null"); + this.key = requireNonNull(key, "key can not be null"); + this.name = requireNonNull(name, "name can not be null"); + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public String getKey() { + return key; + } + + @Override + public String getName() { + return name; + } + + @Override + public String toString() { + return "ProjectImpl{" + + "uuid='" + uuid + '\'' + + ", key='" + key + '\'' + + ", name='" + name + '\'' + + '}'; + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/posttask/QualityGateImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/posttask/QualityGateImpl.java new file mode 100644 index 00000000000..376327f0a8d --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/posttask/QualityGateImpl.java @@ -0,0 +1,72 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program 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. + * + * This program 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.posttask; + +import com.google.common.collect.ImmutableList; +import java.util.Collection; +import javax.annotation.concurrent.Immutable; +import org.sonar.api.ce.posttask.QualityGate; + +import static java.util.Objects.requireNonNull; + +@Immutable +class QualityGateImpl implements QualityGate { + private final String id; + private final String name; + private final Status status; + private final Collection<Condition> conditions; + + public QualityGateImpl(String id, String name, Status status, Collection<Condition> conditions) { + this.id = requireNonNull(id, "id can not be null"); + this.name = requireNonNull(name, "name can not be null"); + this.status = requireNonNull(status, "status can not be null"); + this.conditions = ImmutableList.copyOf(requireNonNull(conditions, "conditions can not be null")); + } + + @Override + public String getId() { + return id; + } + + @Override + public String getName() { + return name; + } + + @Override + public Status getStatus() { + return status; + } + + @Override + public Collection<Condition> getConditions() { + return conditions; + } + + @Override + public String toString() { + return "QualityGateImpl{" + + "id='" + id + '\'' + + ", name='" + name + '\'' + + ", status=" + status + + ", conditions=" + conditions + + '}'; + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/posttask/package-info.java b/server/sonar-server/src/main/java/org/sonar/server/computation/posttask/package-info.java new file mode 100644 index 00000000000..a01c923d669 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/posttask/package-info.java @@ -0,0 +1,23 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program 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. + * + * This program 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.posttask; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/ConditionStatus.java b/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/ConditionStatus.java new file mode 100644 index 00000000000..8e41bb52ee8 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/ConditionStatus.java @@ -0,0 +1,73 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program 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. + * + * This program 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.qualitygate; + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; + +@Immutable +public class ConditionStatus { + public static final ConditionStatus NO_VALUE_STATUS = new ConditionStatus(EvaluationStatus.NO_VALUE, null); + + private final EvaluationStatus status; + @CheckForNull + private final String value; + + private ConditionStatus(EvaluationStatus status, @Nullable String value) { + this.status = requireNonNull(status, "status can not be null"); + this.value = value; + } + + public static ConditionStatus create(EvaluationStatus status, String value) { + requireNonNull(status, "status can not be null"); + checkArgument(status != EvaluationStatus.NO_VALUE, "EvaluationStatus 'NO_VALUE' can not be used with this method, use constant ConditionStatus.NO_VALUE_STATUS instead."); + requireNonNull(value, "value can not be null"); + return new ConditionStatus(status, value); + } + + public EvaluationStatus getStatus() { + return status; + } + + /** + * @return {@code null} when {@link #getStatus()} is {@link EvaluationStatus#NO_VALUE}, otherwise non {@code null} + */ + @CheckForNull + public String getValue() { + return value; + } + + @Override + public String toString() { + return "ConditionStatus{" + + "status=" + status + + ", value='" + value + '\'' + + '}'; + } + + public enum EvaluationStatus { + NO_VALUE, OK, WARN, ERROR + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/MutableQualityGateStatusHolder.java b/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/MutableQualityGateStatusHolder.java new file mode 100644 index 00000000000..3e0067c8a0b --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/MutableQualityGateStatusHolder.java @@ -0,0 +1,33 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program 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. + * + * This program 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.qualitygate; + +import java.util.Map; + +public interface MutableQualityGateStatusHolder extends QualityGateStatusHolder { + /** + * Sets the status of the quality gate and its conditions in the holder. + * + * @throws NullPointerException if either {@code globalStatus} or {@code statusPerCondition} is {@code null} + * @throws IllegalArgumentException if {@code statusPerCondition} is empty + * @throws IllegalStateException if the status has already been set in the holder + */ + void setStatus(QualityGateStatus globalStatus, Map<Condition, ConditionStatus> statusPerCondition); +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/QualityGate.java b/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/QualityGate.java index 565c388a10b..fe4dcfad111 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/QualityGate.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/QualityGate.java @@ -28,14 +28,20 @@ import static com.google.common.collect.FluentIterable.from; @Immutable public class QualityGate { + private final long id; private final String name; private final Set<Condition> conditions; - public QualityGate(String name, Iterable<Condition> conditions) { + public QualityGate(long id, String name, Iterable<Condition> conditions) { + this.id = id; this.name = Objects.requireNonNull(name); this.conditions = from(conditions).filter(notNull()).toSet(); } + public long getId() { + return id; + } + public String getName() { return name; } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/QualityGateServiceImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/QualityGateServiceImpl.java index e419e9bc1b6..5d6e7156063 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/QualityGateServiceImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/QualityGateServiceImpl.java @@ -61,7 +61,7 @@ public class QualityGateServiceImpl implements QualityGateService { private QualityGate toQualityGate(QualityGateDto qualityGateDto) { Iterable<Condition> conditions = from(conditionDao.selectForQualityGate(qualityGateDto.getId())).transform(conditionDtoToBean); - return new QualityGate(qualityGateDto.getName(), conditions); + return new QualityGate(qualityGateDto.getId(), qualityGateDto.getName(), conditions); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/QualityGateStatus.java b/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/QualityGateStatus.java new file mode 100644 index 00000000000..7517e95cadd --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/QualityGateStatus.java @@ -0,0 +1,24 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program 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. + * + * This program 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.qualitygate; + +public enum QualityGateStatus { + OK, WARN, ERROR +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/QualityGateStatusHolder.java b/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/QualityGateStatusHolder.java new file mode 100644 index 00000000000..cf212aaa2cc --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/QualityGateStatusHolder.java @@ -0,0 +1,41 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program 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. + * + * This program 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.qualitygate; + +import java.util.Map; + +public interface QualityGateStatusHolder { + + /** + * The global status of the quality gate (if there is a quality gate on the project). + * + * @throws IllegalStateException if status has not yet been set in the holder + * @see QualityGateHolder#getQualityGate() + */ + QualityGateStatus getStatus(); + + /** + * The status per condition of the quality gate (if there is a quality gate on the project). + * + * @throws IllegalStateException if status has not yet been set in the holder + * @see QualityGateHolder#getQualityGate() + */ + Map<Condition, ConditionStatus> getStatusPerConditions(); +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/QualityGateStatusHolderImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/QualityGateStatusHolderImpl.java new file mode 100644 index 00000000000..92a89dd44fe --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/qualitygate/QualityGateStatusHolderImpl.java @@ -0,0 +1,62 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program 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. + * + * This program 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.qualitygate; + +import com.google.common.collect.ImmutableMap; +import java.util.Map; +import javax.annotation.CheckForNull; + +import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; + +public class QualityGateStatusHolderImpl implements MutableQualityGateStatusHolder { + @CheckForNull + private QualityGateStatus status; + @CheckForNull + private Map<Condition, ConditionStatus> statusPerCondition; + + @Override + public QualityGateStatus getStatus() { + checkInitialized(); + + return status; + } + + @Override + public Map<Condition, ConditionStatus> getStatusPerConditions() { + checkInitialized(); + + return statusPerCondition; + } + + private void checkInitialized() { + checkState(status != null, "Quality gate status has not been set yet"); + } + + @Override + public void setStatus(QualityGateStatus globalStatus, Map<Condition, ConditionStatus> statusPerCondition) { + checkState(status == null, "Quality gate status has already been set in the holder"); + requireNonNull(globalStatus, "global status can not be null"); + requireNonNull(statusPerCondition, "status per condition can not be null"); + + this.status = globalStatus; + this.statusPerCondition = ImmutableMap.copyOf(statusPerCondition); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityGateMeasuresStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityGateMeasuresStep.java index 4fb734d7eb6..affc20eec8a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityGateMeasuresStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/QualityGateMeasuresStep.java @@ -19,10 +19,14 @@ */ package org.sonar.server.computation.step; +import com.google.common.base.Function; import com.google.common.base.Optional; +import com.google.common.collect.ImmutableMap; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Set; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.apache.commons.lang.StringUtils; import org.sonar.api.measures.CoreMetrics; @@ -40,12 +44,18 @@ import org.sonar.server.computation.metric.Metric; import org.sonar.server.computation.metric.MetricRepository; import org.sonar.server.computation.qualitygate.Condition; import org.sonar.server.computation.qualitygate.ConditionEvaluator; +import org.sonar.server.computation.qualitygate.ConditionStatus; import org.sonar.server.computation.qualitygate.EvaluationResult; import org.sonar.server.computation.qualitygate.EvaluationResultTextConverter; +import org.sonar.server.computation.qualitygate.MutableQualityGateStatusHolder; import org.sonar.server.computation.qualitygate.QualityGate; import org.sonar.server.computation.qualitygate.QualityGateHolder; +import static com.google.common.collect.FluentIterable.from; +import static java.lang.String.format; import static org.sonar.server.computation.component.ComponentVisitor.Order.PRE_ORDER; +import static org.sonar.server.computation.qualitygate.ConditionStatus.NO_VALUE_STATUS; +import static org.sonar.server.computation.qualitygate.ConditionStatus.create; /** * This step: @@ -62,15 +72,18 @@ public class QualityGateMeasuresStep implements ComputationStep { private final TreeRootHolder treeRootHolder; private final QualityGateHolder qualityGateHolder; + private final MutableQualityGateStatusHolder qualityGateStatusHolder; private final MeasureRepository measureRepository; private final MetricRepository metricRepository; private final EvaluationResultTextConverter evaluationResultTextConverter; - public QualityGateMeasuresStep(TreeRootHolder treeRootHolder, QualityGateHolder qualityGateHolder, + public QualityGateMeasuresStep(TreeRootHolder treeRootHolder, + QualityGateHolder qualityGateHolder, MutableQualityGateStatusHolder qualityGateStatusHolder, MeasureRepository measureRepository, MetricRepository metricRepository, EvaluationResultTextConverter evaluationResultTextConverter) { this.treeRootHolder = treeRootHolder; this.qualityGateHolder = qualityGateHolder; + this.qualityGateStatusHolder = qualityGateStatusHolder; this.evaluationResultTextConverter = evaluationResultTextConverter; this.measureRepository = measureRepository; this.metricRepository = metricRepository; @@ -88,16 +101,68 @@ public class QualityGateMeasuresStep implements ComputationStep { } private void executeForProject(Component project) { - QualityGateDetailsDataBuilder builder = new QualityGateDetailsDataBuilder(); - Optional<QualityGate> qualityGate = qualityGateHolder.getQualityGate(); if (qualityGate.isPresent()) { + QualityGateDetailsDataBuilder builder = new QualityGateDetailsDataBuilder(); updateMeasures(project, qualityGate.get().getConditions(), builder); addProjectMeasure(project, builder); + + updateQualityGateStatusHolder(qualityGate.get(), builder); } } + private void updateQualityGateStatusHolder(QualityGate qualityGate, QualityGateDetailsDataBuilder builder) { + qualityGateStatusHolder.setStatus(convert(builder.getGlobalLevel()), createStatusPerCondition(qualityGate.getConditions(), builder.getEvaluatedConditions())); + } + + private static ConditionStatus.EvaluationStatus toEvaluationStatus(Measure.Level globalLevel) { + switch (globalLevel) { + case OK: + return ConditionStatus.EvaluationStatus.OK; + case WARN: + return ConditionStatus.EvaluationStatus.WARN; + case ERROR: + return ConditionStatus.EvaluationStatus.ERROR; + default: + throw new IllegalArgumentException(format( + "Unsupported value '%s' of Measure.Level can not be converted to EvaluationStatus", + globalLevel)); + } + } + + private static org.sonar.server.computation.qualitygate.QualityGateStatus convert(Measure.Level globalLevel) { + switch (globalLevel) { + case OK: + return org.sonar.server.computation.qualitygate.QualityGateStatus.OK; + case WARN: + return org.sonar.server.computation.qualitygate.QualityGateStatus.WARN; + case ERROR: + return org.sonar.server.computation.qualitygate.QualityGateStatus.ERROR; + default: + throw new IllegalArgumentException(format( + "Unsupported value '%s' of Measure.Level can not be converted to QualityGateStatus", + globalLevel)); + } + } + + private static Map<Condition, ConditionStatus> createStatusPerCondition(Iterable<Condition> conditions, Iterable<EvaluatedCondition> evaluatedConditions) { + Map<Condition, EvaluatedCondition> evaluatedConditionPerCondition = from(evaluatedConditions) + .uniqueIndex(EvaluatedConditionToCondition.INSTANCE); + + ImmutableMap.Builder<Condition, ConditionStatus> builder = ImmutableMap.builder(); + for (Condition condition : conditions) { + EvaluatedCondition evaluatedCondition = evaluatedConditionPerCondition.get(condition); + + if (evaluatedCondition == null) { + builder.put(condition, NO_VALUE_STATUS); + } else { + builder.put(condition, create(toEvaluationStatus(evaluatedCondition.getLevel()), evaluatedCondition.getActualValue())); + } + } + return builder.build(); + } + private void updateMeasures(Component project, Set<Condition> conditions, QualityGateDetailsDataBuilder builder) { for (Condition condition : conditions) { Optional<Measure> measure = measureRepository.getRawMeasure(project, condition.getMetric()); @@ -170,4 +235,14 @@ public class QualityGateMeasuresStep implements ComputationStep { return evaluatedConditions; } } + + private enum EvaluatedConditionToCondition implements Function<EvaluatedCondition, Condition> { + INSTANCE; + + @Override + @Nonnull + public Condition apply(@Nonnull EvaluatedCondition input) { + return input.getCondition(); + } + } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/posttask/CeTaskImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/posttask/CeTaskImplTest.java new file mode 100644 index 00000000000..785441dc7ca --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/posttask/CeTaskImplTest.java @@ -0,0 +1,63 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program 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. + * + * This program 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.posttask; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.ce.posttask.CeTask; + +import static org.assertj.core.api.Assertions.assertThat; + +public class CeTaskImplTest { + private static final String SOME_ID = "some id"; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void constructor_throws_NPE_if_id_is_null() { + expectedException.expect(NullPointerException.class); + expectedException.expectMessage("id can not be null"); + + new CeTaskImpl(null, CeTask.Status.SUCCESS); + } + + @Test + public void constructor_throws_NPE_if_status_is_null() { + expectedException.expect(NullPointerException.class); + expectedException.expectMessage("status can not be null"); + + new CeTaskImpl(SOME_ID, null); + } + + @Test + public void verify_getters() { + CeTaskImpl underTest = new CeTaskImpl(SOME_ID, CeTask.Status.FAILED); + + assertThat(underTest.getId()).isEqualTo(SOME_ID); + assertThat(underTest.getStatus()).isEqualTo(CeTask.Status.FAILED); + } + + @Test + public void verify_toString() { + assertThat(new CeTaskImpl(SOME_ID, CeTask.Status.SUCCESS).toString()).isEqualTo("CeTaskImpl{id='some id', status=SUCCESS}"); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/posttask/ConditionImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/posttask/ConditionImplTest.java new file mode 100644 index 00000000000..577fa19053a --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/posttask/ConditionImplTest.java @@ -0,0 +1,168 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program 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. + * + * This program 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.posttask; + +import com.tngtech.java.junit.dataprovider.DataProvider; +import com.tngtech.java.junit.dataprovider.DataProviderRunner; +import com.tngtech.java.junit.dataprovider.UseDataProvider; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.sonar.api.ce.posttask.QualityGate; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(DataProviderRunner.class) +public class ConditionImplTest { + private static final String METRIC_KEY = "metricKey"; + private static final String ERROR_THRESHOLD = "error threshold"; + private static final String WARN_THRESHOLD = "warn threshold"; + private static final String VALUE = "value"; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private ConditionImpl.Builder builder = ConditionImpl.newBuilder() + .setStatus(QualityGate.EvaluationStatus.OK) + .setMetricKey(METRIC_KEY) + .setOperator(QualityGate.Operator.GREATER_THAN) + .setErrorThreshold(ERROR_THRESHOLD) + .setWarningThreshold(WARN_THRESHOLD) + .setOnLeakPeriod(true) + .setValue(VALUE); + + @Test + public void build_throws_NPE_if_status_is_null() { + builder.setStatus(null); + + expectedException.expect(NullPointerException.class); + expectedException.expectMessage("status can not be null"); + + builder.build(); + } + + @Test + public void build_throws_NPE_if_metricKey_is_null() { + builder.setMetricKey(null); + + expectedException.expect(NullPointerException.class); + expectedException.expectMessage("metricKey can not be null"); + + builder.build(); + } + + @Test + public void build_throws_NPE_if_operator_is_null() { + builder.setOperator(null); + + expectedException.expect(NullPointerException.class); + expectedException.expectMessage("operator can not be null"); + + builder.build(); + } + + @Test + public void build_throws_IAE_if_both_thresholds_are_null() { + builder.setWarningThreshold(null) + .setErrorThreshold(null); + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("At least one of errorThreshold and warningThreshold must be non null"); + + builder.build(); + } + + @Test + public void getValue_throws_ISE_when_condition_type_is_NO_VALUE() { + builder.setStatus(QualityGate.EvaluationStatus.NO_VALUE).setValue(null); + ConditionImpl condition = builder.build(); + + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("There is no value when status is NO_VALUE"); + + condition.getValue(); + } + + @DataProvider + public static Object[][] allStatusesButNO_VALUE() { + Object[][] res = new Object[QualityGate.EvaluationStatus.values().length - 1][1]; + int i = 0; + for (QualityGate.EvaluationStatus status : QualityGate.EvaluationStatus.values()) { + if (status != QualityGate.EvaluationStatus.NO_VALUE) { + res[i][0] = status; + i++; + } + } + return res; + } + + @Test + @UseDataProvider("allStatusesButNO_VALUE") + public void build_throws_IAE_if_value_is_null_but_status_is_not_NO_VALUE(QualityGate.EvaluationStatus status) { + builder.setStatus(status) + .setValue(null); + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("value can not be null when status is not NO_VALUE"); + + builder.build(); + } + + @Test + public void build_throws_IAE_if_value_is_not_null_but_status_is_NO_VALUE() { + builder.setStatus(QualityGate.EvaluationStatus.NO_VALUE); + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("value must be null when status is NO_VALUE"); + + builder.build(); + } + + @Test + public void toString_ConditionImpl_of_type_different_from_NO_VALUE() { + assertThat(builder.build().toString()) + .isEqualTo( + "ConditionImpl{status=OK, metricKey='metricKey', operator=GREATER_THAN, errorThreshold='error threshold', warningThreshold='warn threshold', onLeakPeriod=true, value='value'}"); + } + + @Test + public void toString_ConditionImpl_of_type_NO_VALUE() { + builder.setStatus(QualityGate.EvaluationStatus.NO_VALUE) + .setValue(null); + + assertThat(builder.build().toString()) + .isEqualTo( + "ConditionImpl{status=NO_VALUE, metricKey='metricKey', operator=GREATER_THAN, errorThreshold='error threshold', warningThreshold='warn threshold', onLeakPeriod=true, value='null'}"); + } + + @Test + public void verify_getters() { + ConditionImpl underTest = builder.build(); + + assertThat(underTest.getStatus()).isEqualTo(QualityGate.EvaluationStatus.OK); + assertThat(underTest.getMetricKey()).isEqualTo(METRIC_KEY); + assertThat(underTest.getOperator()).isEqualTo(QualityGate.Operator.GREATER_THAN); + assertThat(underTest.getErrorThreshold()).isEqualTo(ERROR_THRESHOLD); + assertThat(underTest.getWarningThreshold()).isEqualTo(WARN_THRESHOLD); + assertThat(underTest.isOnLeakPeriod()).isEqualTo(true); + assertThat(underTest.getValue()).isEqualTo(VALUE); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/posttask/ConditionToConditionTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/posttask/ConditionToConditionTest.java new file mode 100644 index 00000000000..0cc156504cb --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/posttask/ConditionToConditionTest.java @@ -0,0 +1,163 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program 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. + * + * This program 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.posttask; + +import com.tngtech.java.junit.dataprovider.DataProvider; +import com.tngtech.java.junit.dataprovider.DataProviderRunner; +import com.tngtech.java.junit.dataprovider.UseDataProvider; +import java.util.Collections; +import java.util.Map; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.sonar.api.ce.posttask.QualityGate; +import org.sonar.server.computation.metric.Metric; +import org.sonar.server.computation.qualitygate.Condition; +import org.sonar.server.computation.qualitygate.ConditionStatus; + +import static com.google.common.collect.ImmutableMap.of; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(DataProviderRunner.class) +public class ConditionToConditionTest { + private static final String METRIC_KEY = "metricKey"; + private static final String ERROR_THRESHOLD = "error threshold"; + private static final String WARN_THRESHOLD = "warn threshold"; + private static final Map<Condition, ConditionStatus> NO_STATUS_PER_CONDITIONS = Collections.emptyMap(); + private static final String SOME_VALUE = "some value"; + private static final ConditionStatus SOME_CONDITION_STATUS = ConditionStatus.create(ConditionStatus.EvaluationStatus.OK, SOME_VALUE); + private static final Condition SOME_CONDITION = new Condition(newMetric(METRIC_KEY), Condition.Operator.EQUALS.getDbValue(), ERROR_THRESHOLD, WARN_THRESHOLD, 1); + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void apply_throws_NPE_if_Condition_argument_is_null() { + ConditionToCondition underTest = new ConditionToCondition(NO_STATUS_PER_CONDITIONS); + + expectedException.expect(NullPointerException.class); + + underTest.apply(null); + } + + @Test + public void apply_throws_ISE_if_there_is_no_ConditionStatus_for_Condition_argument() { + ConditionToCondition underTest = new ConditionToCondition(NO_STATUS_PER_CONDITIONS); + + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("Missing ConditionStatus for condition on metric key " + METRIC_KEY); + + underTest.apply(SOME_CONDITION); + } + + @Test + @UseDataProvider("allEvaluationStatuses") + public void apply_converts_all_values_of_status(ConditionStatus.EvaluationStatus status) { + ConditionToCondition underTest = new ConditionToCondition(of( + SOME_CONDITION, + status == ConditionStatus.EvaluationStatus.NO_VALUE ? ConditionStatus.NO_VALUE_STATUS : ConditionStatus.create(status, SOME_VALUE))); + + assertThat(underTest.apply(SOME_CONDITION).getStatus().name()).isEqualTo(status.name()); + } + + @Test + public void apply_converts_key_from_metric() { + ConditionToCondition underTest = new ConditionToCondition(of(SOME_CONDITION, SOME_CONDITION_STATUS)); + + assertThat(underTest.apply(SOME_CONDITION).getMetricKey()).isEqualTo(METRIC_KEY); + } + + @Test + public void apply_copies_thresholds() { + ConditionToCondition underTest = new ConditionToCondition(of(SOME_CONDITION, SOME_CONDITION_STATUS)); + + assertThat(underTest.apply(SOME_CONDITION).getErrorThreshold()).isEqualTo(ERROR_THRESHOLD); + assertThat(underTest.apply(SOME_CONDITION).getWarningThreshold()).isEqualTo(WARN_THRESHOLD); + } + + @Test + @UseDataProvider("allOperatorValues") + public void apply_converts_all_values_of_operator(Condition.Operator operator) { + Condition condition = new Condition(newMetric(METRIC_KEY), operator.getDbValue(), ERROR_THRESHOLD, WARN_THRESHOLD, 1); + ConditionToCondition underTest = new ConditionToCondition(of(condition, SOME_CONDITION_STATUS)); + + assertThat(underTest.apply(condition).getOperator().name()).isEqualTo(operator.name()); + } + + @Test + public void apply_sets_onLeakPeriod_flag_when_Condition_has_non_null_Period() { + Condition noPeriodCondition = new Condition(newMetric(METRIC_KEY), Condition.Operator.NOT_EQUALS.getDbValue(), ERROR_THRESHOLD, WARN_THRESHOLD, null); + ConditionToCondition underTest = new ConditionToCondition(of( + SOME_CONDITION, SOME_CONDITION_STATUS, + noPeriodCondition, SOME_CONDITION_STATUS)); + + assertThat(underTest.apply(SOME_CONDITION).isOnLeakPeriod()).isTrue(); + assertThat(underTest.apply(noPeriodCondition).isOnLeakPeriod()).isFalse(); + } + + @Test + public void apply_copies_value() { + Condition otherCondition = new Condition(newMetric(METRIC_KEY), Condition.Operator.NOT_EQUALS.getDbValue(), ERROR_THRESHOLD, WARN_THRESHOLD, null); + ConditionToCondition underTest = new ConditionToCondition(of( + SOME_CONDITION, SOME_CONDITION_STATUS, + otherCondition, ConditionStatus.NO_VALUE_STATUS + )); + + assertThat(underTest.apply(SOME_CONDITION).getValue()).isEqualTo(SOME_VALUE); + + QualityGate.Condition res = underTest.apply(otherCondition); + + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("There is no value when status is NO_VALUE"); + + res.getValue(); + } + + @DataProvider + public static Object[][] allEvaluationStatuses() { + Object[][] res = new Object[ConditionStatus.EvaluationStatus.values().length][1]; + int i = 0; + for (ConditionStatus.EvaluationStatus status : ConditionStatus.EvaluationStatus.values()) { + res[i][0] = status; + i++; + } + return res; + } + + @DataProvider + public static Object[][] allOperatorValues() { + Object[][] res = new Object[Condition.Operator.values().length][1]; + int i = 0; + for (Condition.Operator operator : Condition.Operator.values()) { + res[i][0] = operator; + i++; + } + return res; + } + + private static Metric newMetric(String metricKey) { + Metric metric = mock(Metric.class); + when(metric.getKey()).thenReturn(metricKey); + return metric; + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/posttask/PostProjectAnalysisTasksExecutorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/posttask/PostProjectAnalysisTasksExecutorTest.java new file mode 100644 index 00000000000..60642737e6a --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/posttask/PostProjectAnalysisTasksExecutorTest.java @@ -0,0 +1,197 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program 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. + * + * This program 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.posttask; + +import com.google.common.collect.ImmutableMap; +import com.tngtech.java.junit.dataprovider.DataProvider; +import com.tngtech.java.junit.dataprovider.DataProviderRunner; +import com.tngtech.java.junit.dataprovider.UseDataProvider; +import java.util.Date; +import java.util.List; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; +import org.sonar.api.ce.posttask.PostProjectAnalysisTask; +import org.sonar.api.ce.posttask.Project; +import org.sonar.ce.queue.CeTask; +import org.sonar.server.computation.analysis.AnalysisMetadataHolderRule; +import org.sonar.server.computation.metric.Metric; +import org.sonar.server.computation.qualitygate.Condition; +import org.sonar.server.computation.qualitygate.ConditionStatus; +import org.sonar.server.computation.qualitygate.MutableQualityGateHolderRule; +import org.sonar.server.computation.qualitygate.MutableQualityGateStatusHolderRule; +import org.sonar.server.computation.qualitygate.QualityGate; +import org.sonar.server.computation.qualitygate.QualityGateStatus; + +import static com.google.common.collect.ImmutableList.of; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(DataProviderRunner.class) +public class PostProjectAnalysisTasksExecutorTest { + private static final long QUALITY_GATE_ID = 98451; + private static final String QUALITY_GATE_NAME = "qualityGate name"; + private static final Condition CONDITION_1 = createCondition("metric key 1"); + private static final Condition CONDITION_2 = createCondition("metric key 2"); + + @Rule + public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule() + .setAnalysisDate(8465132498L); + @Rule + public MutableQualityGateHolderRule qualityGateHolder = new MutableQualityGateHolderRule(); + @Rule + public MutableQualityGateStatusHolderRule qualityGateStatusHolder = new MutableQualityGateStatusHolderRule(); + + private ArgumentCaptor<PostProjectAnalysisTask.ProjectAnalysis> projectAnalysisArgumentCaptor = ArgumentCaptor.forClass(PostProjectAnalysisTask.ProjectAnalysis.class); + private CeTask ceTask = new CeTask.Builder() + .setType("type") + .setUuid("uuid") + .setComponentKey("component key") + .setComponentName("component name") + .setComponentUuid("component uuid") + .build(); + private PostProjectAnalysisTask postProjectAnalysisTask = mock(PostProjectAnalysisTask.class); + private PostProjectAnalysisTasksExecutor underTest = new PostProjectAnalysisTasksExecutor( + ceTask, analysisMetadataHolder, qualityGateHolder, qualityGateStatusHolder, + new PostProjectAnalysisTask[] {postProjectAnalysisTask}); + + @Before + public void setUp() throws Exception { + qualityGateHolder.setQualityGate(new QualityGate(QUALITY_GATE_ID, QUALITY_GATE_NAME, of(CONDITION_1, CONDITION_2))); + qualityGateStatusHolder.setStatus(QualityGateStatus.OK, ImmutableMap.of( + CONDITION_1, ConditionStatus.create(ConditionStatus.EvaluationStatus.OK, "value"), + CONDITION_2, ConditionStatus.NO_VALUE_STATUS)); + } + + @Test + @UseDataProvider("booleanValues") + public void does_not_fail_when_there_is_no_PostProjectAnalysisTasksExecutor(boolean allStepsExecuted) { + new PostProjectAnalysisTasksExecutor(ceTask, analysisMetadataHolder, qualityGateHolder, qualityGateStatusHolder) + .finished(allStepsExecuted); + } + + @Test + @UseDataProvider("booleanValues") + public void finished_calls_all_PostProjectAnalysisTask_in_order_of_the_array_and_passes_the_same_object_to_all(boolean allStepsExecuted) { + PostProjectAnalysisTask postProjectAnalysisTask1 = mock(PostProjectAnalysisTask.class); + PostProjectAnalysisTask postProjectAnalysisTask2 = mock(PostProjectAnalysisTask.class); + InOrder inOrder = inOrder(postProjectAnalysisTask1, postProjectAnalysisTask2); + + new PostProjectAnalysisTasksExecutor( + ceTask, analysisMetadataHolder, qualityGateHolder, qualityGateStatusHolder, + new PostProjectAnalysisTask[] {postProjectAnalysisTask1, postProjectAnalysisTask2}) + .finished(allStepsExecuted); + + inOrder.verify(postProjectAnalysisTask1).finished(projectAnalysisArgumentCaptor.capture()); + inOrder.verify(postProjectAnalysisTask2).finished(projectAnalysisArgumentCaptor.capture()); + inOrder.verifyNoMoreInteractions(); + + List<PostProjectAnalysisTask.ProjectAnalysis> allValues = projectAnalysisArgumentCaptor.getAllValues(); + assertThat(allValues).hasSize(2); + assertThat(allValues.get(0)).isSameAs(allValues.get(1)); + } + + @Test + @UseDataProvider("booleanValues") + public void CeTask_status_depends_on_finished_method_argument_is_true_or_false(boolean allStepsExecuted) { + underTest.finished(allStepsExecuted); + + verify(postProjectAnalysisTask).finished(projectAnalysisArgumentCaptor.capture()); + + assertThat(projectAnalysisArgumentCaptor.getValue().getCeTask().getStatus()) + .isEqualTo( + allStepsExecuted ? org.sonar.api.ce.posttask.CeTask.Status.SUCCESS : org.sonar.api.ce.posttask.CeTask.Status.FAILED); + } + + @Test + public void ceTask_uuid_is_UUID_of_CeTask() { + underTest.finished(true); + + verify(postProjectAnalysisTask).finished(projectAnalysisArgumentCaptor.capture()); + + assertThat(projectAnalysisArgumentCaptor.getValue().getCeTask().getId()) + .isEqualTo(ceTask.getUuid()); + } + + @Test + public void project_uuid_key_and_name_come_from_CeTask() { + underTest.finished(true); + + verify(postProjectAnalysisTask).finished(projectAnalysisArgumentCaptor.capture()); + + Project project = projectAnalysisArgumentCaptor.getValue().getProject(); + assertThat(project.getUuid()).isEqualTo(ceTask.getComponentUuid()); + assertThat(project.getKey()).isEqualTo(ceTask.getComponentKey()); + assertThat(project.getName()).isEqualTo(ceTask.getComponentName()); + } + + @Test + public void analysisDate_comes_from_AnalysisMetadataHolder() { + underTest.finished(true); + + verify(postProjectAnalysisTask).finished(projectAnalysisArgumentCaptor.capture()); + + assertThat(projectAnalysisArgumentCaptor.getValue().getDate()) + .isEqualTo(new Date(analysisMetadataHolder.getAnalysisDate())); + } + + @Test + public void qualityGate_is_null_when_finished_method_argument_is_false() { + underTest.finished(false); + + verify(postProjectAnalysisTask).finished(projectAnalysisArgumentCaptor.capture()); + + assertThat(projectAnalysisArgumentCaptor.getValue().getQualityGate()).isNull(); + } + + @Test + public void qualityGate_is_populated_when_finished_method_argument_is_true() { + underTest.finished(true); + + verify(postProjectAnalysisTask).finished(projectAnalysisArgumentCaptor.capture()); + + org.sonar.api.ce.posttask.QualityGate qualityGate = projectAnalysisArgumentCaptor.getValue().getQualityGate(); + assertThat(qualityGate.getStatus()).isEqualTo(org.sonar.api.ce.posttask.QualityGate.Status.OK); + assertThat(qualityGate.getId()).isEqualTo(String.valueOf(QUALITY_GATE_ID)); + assertThat(qualityGate.getName()).isEqualTo(QUALITY_GATE_NAME); + assertThat(qualityGate.getConditions()).hasSize(2); + } + + @DataProvider + public static Object[][] booleanValues() { + return new Object[][] { + {true}, + {false} + }; + } + + private static Condition createCondition(String metricKey) { + Metric metric = mock(Metric.class); + when(metric.getKey()).thenReturn(metricKey); + return new Condition(metric, Condition.Operator.EQUALS.getDbValue(), "error threshold", "warn threshold", null); + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/posttask/ProjectImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/posttask/ProjectImplTest.java new file mode 100644 index 00000000000..716627ac89e --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/posttask/ProjectImplTest.java @@ -0,0 +1,75 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program 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. + * + * This program 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.posttask; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ProjectImplTest { + private static final String SOME_UUID = "some uuid"; + private static final String SOME_KEY = "some key"; + private static final String SOME_NAME = "some name"; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void constructor_throws_NPE_if_uuid_is_null() { + expectedException.expect(NullPointerException.class); + expectedException.expectMessage("uuid can not be null"); + + new ProjectImpl(null, SOME_KEY, SOME_NAME); + } + + @Test + public void constructor_throws_NPE_if_key_is_null() { + expectedException.expect(NullPointerException.class); + expectedException.expectMessage("key can not be null"); + + new ProjectImpl(SOME_UUID, null, SOME_NAME); + } + + @Test + public void constructor_throws_NPE_if_name_is_null() { + expectedException.expect(NullPointerException.class); + expectedException.expectMessage("name can not be null"); + + new ProjectImpl(SOME_UUID, SOME_KEY, null); + } + + @Test + public void verify_getters() { + ProjectImpl underTest = new ProjectImpl(SOME_UUID, SOME_KEY, SOME_NAME); + + assertThat(underTest.getUuid()).isEqualTo(SOME_UUID); + assertThat(underTest.getKey()).isEqualTo(SOME_KEY); + assertThat(underTest.getName()).isEqualTo(SOME_NAME); + } + + @Test + public void verify_toString() { + assertThat(new ProjectImpl(SOME_UUID, SOME_KEY, SOME_NAME).toString()) + .isEqualTo("ProjectImpl{uuid='some uuid', key='some key', name='some name'}"); + + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/posttask/QualityGateImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/posttask/QualityGateImplTest.java new file mode 100644 index 00000000000..e8c8bda7e62 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/posttask/QualityGateImplTest.java @@ -0,0 +1,96 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program 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. + * + * This program 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.posttask; + +import com.google.common.collect.ImmutableList; +import java.util.Collections; +import java.util.List; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.ce.posttask.QualityGate; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class QualityGateImplTest { + private static final String SOME_ID = "some id"; + private static final String SOME_NAME = "some name"; + private static final QualityGate.Status SOME_STATUS = QualityGate.Status.OK; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private QualityGate.Condition condition = mock(QualityGate.Condition.class); + private QualityGateImpl underTest = new QualityGateImpl(SOME_ID, SOME_NAME, SOME_STATUS, ImmutableList.of(condition)); + + @Test + public void constructor_throws_NPE_if_id_argument_is_null() { + expectedException.expect(NullPointerException.class); + expectedException.expectMessage("id can not be null"); + + new QualityGateImpl(null, SOME_NAME, SOME_STATUS, Collections.<QualityGate.Condition>emptyList()); + } + + @Test + public void constructor_throws_NPE_if_name_argument_is_null() { + expectedException.expect(NullPointerException.class); + expectedException.expectMessage("name can not be null"); + + new QualityGateImpl(SOME_ID, null, SOME_STATUS, Collections.<QualityGate.Condition>emptyList()); + } + + @Test + public void constructor_throws_NPE_if_status_argument_is_null() { + expectedException.expect(NullPointerException.class); + expectedException.expectMessage("status can not be null"); + + new QualityGateImpl(SOME_ID, SOME_NAME, null, Collections.<QualityGate.Condition>emptyList()); + } + + @Test + public void constructor_throws_NPE_if_conditions_argument_is_null() { + expectedException.expect(NullPointerException.class); + expectedException.expectMessage("conditions can not be null"); + + new QualityGateImpl(SOME_ID, SOME_NAME, SOME_STATUS, null); + } + + @Test + public void verify_getters() { + List<QualityGate.Condition> conditions = ImmutableList.of(condition); + + QualityGateImpl underTest = new QualityGateImpl(SOME_ID, SOME_NAME, SOME_STATUS, conditions); + + assertThat(underTest.getId()).isEqualTo(SOME_ID); + assertThat(underTest.getName()).isEqualTo(SOME_NAME); + assertThat(underTest.getStatus()).isEqualTo(SOME_STATUS); + assertThat(underTest.getConditions()).isEqualTo(conditions); + } + + @Test + public void verify_toString() { + when(condition.toString()).thenReturn("{Condition}"); + + assertThat(underTest.toString()) + .isEqualTo("QualityGateImpl{id='some id', name='some name', status=OK, conditions=[{Condition}]}"); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/qualitygate/ConditionStatusTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/qualitygate/ConditionStatusTest.java new file mode 100644 index 00000000000..630f65442ae --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/qualitygate/ConditionStatusTest.java @@ -0,0 +1,99 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program 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. + * + * This program 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.qualitygate; + +import com.tngtech.java.junit.dataprovider.DataProvider; +import com.tngtech.java.junit.dataprovider.DataProviderRunner; +import com.tngtech.java.junit.dataprovider.UseDataProvider; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.server.computation.qualitygate.ConditionStatus.EvaluationStatus.NO_VALUE; +import static org.sonar.server.computation.qualitygate.ConditionStatus.EvaluationStatus.OK; +import static org.sonar.server.computation.qualitygate.ConditionStatus.EvaluationStatus.values; + +@RunWith(DataProviderRunner.class) +public class ConditionStatusTest { + private static final String SOME_VALUE = "value"; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void create_throws_NPE_if_status_argument_is_null() { + expectedException.expect(NullPointerException.class); + expectedException.expectMessage("status can not be null"); + + ConditionStatus.create(null, SOME_VALUE); + } + + @Test + public void create_throws_IAE_if_status_argument_is_NO_VALUE() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("EvaluationStatus 'NO_VALUE' can not be used with this method, use constant ConditionStatus.NO_VALUE_STATUS instead."); + + ConditionStatus.create(NO_VALUE, SOME_VALUE); + } + + @Test + @UseDataProvider("allStatusesButNO_VALUE") + public void create_throws_NPE_if_value_is_null_and_status_argument_is_not_NO_VALUE(ConditionStatus.EvaluationStatus status) { + expectedException.expect(NullPointerException.class); + expectedException.expectMessage("value can not be null"); + + ConditionStatus.create(status, null); + } + + @Test + public void verify_getters() { + ConditionStatus underTest = ConditionStatus.create(OK, SOME_VALUE); + + assertThat(underTest.getStatus()).isEqualTo(OK); + assertThat(underTest.getValue()).isEqualTo(SOME_VALUE); + } + + @Test + public void verify_toString() { + assertThat(ConditionStatus.create(OK, SOME_VALUE).toString()).isEqualTo("ConditionStatus{status=OK, value='value'}"); + assertThat(ConditionStatus.NO_VALUE_STATUS.toString()).isEqualTo("ConditionStatus{status=NO_VALUE, value='null'}"); + } + + @Test + public void constant_NO_VALUE_STATUS_has_status_NO_VALUE_and_null_value() { + assertThat(ConditionStatus.NO_VALUE_STATUS.getStatus()).isEqualTo(NO_VALUE); + assertThat(ConditionStatus.NO_VALUE_STATUS.getValue()).isNull(); + } + + @DataProvider + public static Object[][] allStatusesButNO_VALUE() { + Object[][] res = new Object[values().length - 1][1]; + int i = 0; + for (ConditionStatus.EvaluationStatus status : values()) { + if (status != NO_VALUE) { + res[i][0] = status; + i++; + } + } + return res; + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/qualitygate/MutableQualityGateStatusHolderRule.java b/server/sonar-server/src/test/java/org/sonar/server/computation/qualitygate/MutableQualityGateStatusHolderRule.java new file mode 100644 index 00000000000..415389748de --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/qualitygate/MutableQualityGateStatusHolderRule.java @@ -0,0 +1,51 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program 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. + * + * This program 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.qualitygate; + +import java.util.Map; +import org.junit.rules.ExternalResource; + +public class MutableQualityGateStatusHolderRule extends ExternalResource implements MutableQualityGateStatusHolder { + private MutableQualityGateStatusHolder delegate = new QualityGateStatusHolderImpl(); + + @Override + public void setStatus(QualityGateStatus globalStatus, Map<Condition, ConditionStatus> statusPerCondition) { + delegate.setStatus(globalStatus, statusPerCondition); + } + + @Override + public QualityGateStatus getStatus() { + return delegate.getStatus(); + } + + @Override + public Map<Condition, ConditionStatus> getStatusPerConditions() { + return delegate.getStatusPerConditions(); + } + + @Override + protected void after() { + reset(); + } + + public void reset() { + this.delegate = new QualityGateStatusHolderImpl(); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/qualitygate/QualityGateHolderImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/qualitygate/QualityGateHolderImplTest.java index ee861e7935a..11ba2a4460e 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/qualitygate/QualityGateHolderImplTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/qualitygate/QualityGateHolderImplTest.java @@ -27,7 +27,7 @@ import static org.assertj.guava.api.Assertions.assertThat; public class QualityGateHolderImplTest { - public static final QualityGate QUALITY_GATE = new QualityGate("name", Collections.<Condition>emptyList()); + public static final QualityGate QUALITY_GATE = new QualityGate(4612, "name", Collections.<Condition>emptyList()); @Test(expected = IllegalStateException.class) public void getQualityGate_throws_ISE_if_QualityGate_not_set() { diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/qualitygate/QualityGateServiceImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/qualitygate/QualityGateServiceImplTest.java index da6af29e9a1..31df844a5eb 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/qualitygate/QualityGateServiceImplTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/qualitygate/QualityGateServiceImplTest.java @@ -64,6 +64,7 @@ public class QualityGateServiceImplTest { Optional<QualityGate> res = underTest.findById(SOME_ID); assertThat(res).isPresent(); + assertThat(res.get().getId()).isEqualTo(SOME_ID); assertThat(res.get().getName()).isEqualTo(SOME_NAME); assertThat(res.get().getConditions()).isEmpty(); } @@ -79,6 +80,7 @@ public class QualityGateServiceImplTest { Optional<QualityGate> res = underTest.findById(SOME_ID); assertThat(res).isPresent(); + assertThat(res.get().getId()).isEqualTo(SOME_ID); assertThat(res.get().getName()).isEqualTo(SOME_NAME); assertThat(res.get().getConditions()).containsOnly( new Condition(METRIC_1, CONDITION_1.getOperator(), CONDITION_1.getErrorThreshold(), CONDITION_1.getWarningThreshold(), CONDITION_1.getPeriod()), diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/qualitygate/QualityGateStatusHolderImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/qualitygate/QualityGateStatusHolderImplTest.java new file mode 100644 index 00000000000..f8948646f82 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/qualitygate/QualityGateStatusHolderImplTest.java @@ -0,0 +1,117 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program 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. + * + * This program 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.qualitygate; + +import com.tngtech.java.junit.dataprovider.DataProvider; +import com.tngtech.java.junit.dataprovider.DataProviderRunner; +import com.tngtech.java.junit.dataprovider.UseDataProvider; +import java.util.Collections; +import java.util.Map; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +@RunWith(DataProviderRunner.class) +public class QualityGateStatusHolderImplTest { + private static final Map<Condition, ConditionStatus> SOME_STATUS_PER_CONDITION = Collections.singletonMap( + mock(Condition.class), ConditionStatus.create(ConditionStatus.EvaluationStatus.OK, "val")); + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private QualityGateStatusHolderImpl underTest = new QualityGateStatusHolderImpl(); + + @Test + public void setStatus_throws_NPE_if_globalStatus_is_null() { + expectedException.expect(NullPointerException.class); + expectedException.expectMessage("global status can not be null"); + + underTest.setStatus(null, SOME_STATUS_PER_CONDITION); + } + + @Test + public void setStatus_throws_NPE_if_statusPerCondition_is_null() { + expectedException.expect(NullPointerException.class); + expectedException.expectMessage("status per condition can not be null"); + + underTest.setStatus(QualityGateStatus.OK, null); + } + + @Test + public void setStatus_throws_ISE_if_called_twice() { + underTest.setStatus(QualityGateStatus.OK, SOME_STATUS_PER_CONDITION); + + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("Quality gate status has already been set in the holder"); + + underTest.setStatus(null, null); + } + + @Test + public void getStatus_throws_ISE_if_setStatus_not_called_yet() { + expectQGNotSetYetISE(); + + underTest.getStatus(); + } + + @Test + @UseDataProvider("qualityGateStatusValue") + public void getStatus_returns_status_argument_from_setStatus(QualityGateStatus status) { + underTest.setStatus(status, SOME_STATUS_PER_CONDITION); + + assertThat(underTest.getStatus()).isEqualTo(status); + } + + @Test + public void getStatusPerConditions_throws_ISE_if_setStatus_not_called_yet() { + expectQGNotSetYetISE(); + + underTest.getStatusPerConditions(); + } + + @Test + public void getStatusPerConditions_returns_statusPerCondition_argument_from_setStatus() { + underTest.setStatus(QualityGateStatus.ERROR, SOME_STATUS_PER_CONDITION); + + assertThat(underTest.getStatusPerConditions()).isEqualTo(SOME_STATUS_PER_CONDITION); + // a copy is made to be immutable + assertThat(underTest.getStatusPerConditions()).isNotSameAs(SOME_STATUS_PER_CONDITION); + } + + private void expectQGNotSetYetISE() { + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("Quality gate status has not been set yet"); + } + + @DataProvider + public static Object[][] qualityGateStatusValue() { + Object[][] res = new Object[QualityGateStatus.values().length][1]; + int i = 0; + for (QualityGateStatus status : QualityGateStatus.values()) { + res[i][0] = status; + i++; + } + return res; + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/QualityGateLoadingStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/QualityGateLoadingStepTest.java index 1a5ebf7c815..024c56109ea 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/QualityGateLoadingStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/QualityGateLoadingStepTest.java @@ -101,7 +101,7 @@ public class QualityGateLoadingStepTest { @Test public void execute_sets_QualityGate_if_it_can_be_found_by_service() { - QualityGate qualityGate = new QualityGate("name", Collections.<Condition>emptyList()); + QualityGate qualityGate = new QualityGate(465, "name", Collections.<Condition>emptyList()); treeRootHolder.setRoot(PROJECT_ALONE); when(settingsRepository.getSettings(PROJECT_ALONE)).thenReturn(new Settings().setProperty("sonar.qualitygate", 10)); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/QualityGateMeasuresStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/QualityGateMeasuresStepTest.java index de11b92aea9..20cb102fc26 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/QualityGateMeasuresStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/QualityGateMeasuresStepTest.java @@ -21,10 +21,14 @@ package org.sonar.server.computation.step; import com.google.common.base.Optional; import java.util.Collections; +import java.util.Map; +import java.util.Objects; import javax.annotation.Nullable; +import org.assertj.core.api.AbstractAssert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.mockito.ArgumentCaptor; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -40,10 +44,14 @@ import org.sonar.server.computation.metric.Metric; import org.sonar.server.computation.metric.MetricImpl; import org.sonar.server.computation.metric.MetricRepository; import org.sonar.server.computation.qualitygate.Condition; +import org.sonar.server.computation.qualitygate.ConditionStatus; import org.sonar.server.computation.qualitygate.EvaluationResult; import org.sonar.server.computation.qualitygate.EvaluationResultTextConverter; +import org.sonar.server.computation.qualitygate.MutableQualityGateStatusHolderRule; import org.sonar.server.computation.qualitygate.QualityGate; import org.sonar.server.computation.qualitygate.QualityGateHolderRule; +import org.sonar.server.computation.qualitygate.QualityGateStatus; +import org.sonar.server.computation.qualitygate.QualityGateStatusHolder; import static com.google.common.collect.ImmutableList.of; import static org.mockito.Matchers.any; @@ -62,11 +70,17 @@ public class QualityGateMeasuresStepTest { private static final MetricImpl INT_METRIC_2 = createIntMetric(2); private static final ReportComponent PROJECT_COMPONENT = ReportComponent.builder(Component.Type.PROJECT, 1).build(); + private static final long SOME_QG_ID = 7521551; + private static final String SOME_QG_NAME = "name"; @Rule + public ExpectedException expectedException = ExpectedException.none(); + @Rule public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); @Rule public QualityGateHolderRule qualityGateHolder = new QualityGateHolderRule(); + @Rule + public MutableQualityGateStatusHolderRule qualityGateStatusHolder = new MutableQualityGateStatusHolderRule(); private static final Metric ALERT_STATUS_METRIC = mock(Metric.class); private static final Metric QUALITY_GATE_DETAILS_METRIC = mock(Metric.class); @@ -77,7 +91,8 @@ public class QualityGateMeasuresStepTest { private MeasureRepository measureRepository = mock(MeasureRepository.class); private MetricRepository metricRepository = mock(MetricRepository.class); private EvaluationResultTextConverter resultTextConverter = mock(EvaluationResultTextConverter.class); - private QualityGateMeasuresStep underTest = new QualityGateMeasuresStep(treeRootHolder, qualityGateHolder, measureRepository, metricRepository, resultTextConverter); + private QualityGateMeasuresStep underTest = new QualityGateMeasuresStep(treeRootHolder, qualityGateHolder, qualityGateStatusHolder, measureRepository, metricRepository, + resultTextConverter); @Before public void setUp() { @@ -122,9 +137,21 @@ public class QualityGateMeasuresStepTest { } @Test + public void mutableQualityGateStatusHolder_is_not_populated_if_there_is_no_qualitygate() { + qualityGateHolder.setQualityGate(null); + + underTest.execute(); + + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("Quality gate status has not been set yet"); + + qualityGateStatusHolder.getStatus(); + } + + @Test public void new_measures_are_created_even_if_there_is_no_rawMeasure_for_metric_of_condition() { Condition equals2Condition = createEqualsCondition(INT_METRIC_1, "2", null); - qualityGateHolder.setQualityGate(new QualityGate("name", of(equals2Condition))); + qualityGateHolder.setQualityGate(new QualityGate(SOME_QG_ID, SOME_QG_NAME, of(equals2Condition))); when(measureRepository.getRawMeasure(PROJECT_COMPONENT, INT_METRIC_1)).thenReturn(Optional.<Measure>absent()); underTest.execute(); @@ -139,6 +166,11 @@ public class QualityGateMeasuresStepTest { .hasQualityGateText(""); assertThat(qgDetailsMeasureCaptor.getValue()) .hasValue(new QualityGateDetailsData(OK, Collections.<EvaluatedCondition>emptyList()).toJson()); + + QualityGateStatusHolderAssertions.assertThat(qualityGateStatusHolder) + .hasStatus(QualityGateStatus.OK) + .hasConditionCount(1) + .hasCondition(equals2Condition, ConditionStatus.EvaluationStatus.NO_VALUE, null); } @Test @@ -147,7 +179,7 @@ public class QualityGateMeasuresStepTest { Condition equals2Condition = createEqualsCondition(INT_METRIC_1, "2", null); Measure rawMeasure = Measure.newMeasureBuilder().create(rawValue, null); - qualityGateHolder.setQualityGate(new QualityGate("name", of(equals2Condition))); + qualityGateHolder.setQualityGate(new QualityGate(SOME_QG_ID, SOME_QG_NAME, of(equals2Condition))); when(measureRepository.getRawMeasure(PROJECT_COMPONENT, INT_METRIC_1)).thenReturn(Optional.of(rawMeasure)); underTest.execute(); @@ -168,6 +200,11 @@ public class QualityGateMeasuresStepTest { .hasQualityGateText(dumbResultTextAnswer(equals2Condition, OK, rawValue)); assertThat(qgDetailsMeasureCaptor.getValue()) .hasValue(new QualityGateDetailsData(OK, of(new EvaluatedCondition(equals2Condition, OK, rawValue))).toJson()); + + QualityGateStatusHolderAssertions.assertThat(qualityGateStatusHolder) + .hasStatus(QualityGateStatus.OK) + .hasConditionCount(1) + .hasCondition(equals2Condition, ConditionStatus.EvaluationStatus.OK, String.valueOf(rawValue)); } @Test @@ -177,7 +214,7 @@ public class QualityGateMeasuresStepTest { Condition equals1WarningCondition = createEqualsCondition(INT_METRIC_2, null, "1"); Measure rawMeasure = Measure.newMeasureBuilder().create(rawValue, null); - qualityGateHolder.setQualityGate(new QualityGate("name", of(equals1ErrorCondition, equals1WarningCondition))); + qualityGateHolder.setQualityGate(new QualityGate(SOME_QG_ID, SOME_QG_NAME, of(equals1ErrorCondition, equals1WarningCondition))); when(measureRepository.getRawMeasure(PROJECT_COMPONENT, INT_METRIC_1)).thenReturn(Optional.of(rawMeasure)); when(measureRepository.getRawMeasure(PROJECT_COMPONENT, INT_METRIC_2)).thenReturn(Optional.of(rawMeasure)); @@ -195,20 +232,25 @@ public class QualityGateMeasuresStepTest { verifyNoMoreInteractions(measureRepository); assertThat(equals1ErrorConditionMeasureCaptor.getValue()) - .hasQualityGateLevel(ERROR) - .hasQualityGateText(dumbResultTextAnswer(equals1ErrorCondition, ERROR, rawValue)); + .hasQualityGateLevel(ERROR) + .hasQualityGateText(dumbResultTextAnswer(equals1ErrorCondition, ERROR, rawValue)); assertThat(equals1WarningConditionMeasureCaptor.getValue()) - .hasQualityGateLevel(WARN) - .hasQualityGateText(dumbResultTextAnswer(equals1WarningCondition, WARN, rawValue)); + .hasQualityGateLevel(WARN) + .hasQualityGateText(dumbResultTextAnswer(equals1WarningCondition, WARN, rawValue)); assertThat(alertStatusMeasureCaptor.getValue()) - .hasQualityGateLevel(ERROR) - .hasQualityGateText(dumbResultTextAnswer(equals1ErrorCondition, ERROR, rawValue) + ", " - + dumbResultTextAnswer(equals1WarningCondition, WARN, rawValue)); + .hasQualityGateLevel(ERROR) + .hasQualityGateText(dumbResultTextAnswer(equals1ErrorCondition, ERROR, rawValue) + ", " + + dumbResultTextAnswer(equals1WarningCondition, WARN, rawValue)); assertThat(qgDetailsMeasureCaptor.getValue()) - .hasValue(new QualityGateDetailsData(ERROR, of( - new EvaluatedCondition(equals1ErrorCondition, ERROR, rawValue), - new EvaluatedCondition(equals1WarningCondition, WARN, rawValue) - )).toJson()); + .hasValue(new QualityGateDetailsData(ERROR, of( + new EvaluatedCondition(equals1ErrorCondition, ERROR, rawValue), + new EvaluatedCondition(equals1WarningCondition, WARN, rawValue))).toJson()); + + QualityGateStatusHolderAssertions.assertThat(qualityGateStatusHolder) + .hasStatus(QualityGateStatus.ERROR) + .hasConditionCount(2) + .hasCondition(equals1ErrorCondition, ConditionStatus.EvaluationStatus.ERROR, String.valueOf(rawValue)) + .hasCondition(equals1WarningCondition, ConditionStatus.EvaluationStatus.WARN, String.valueOf(rawValue)); } @Test @@ -218,7 +260,7 @@ public class QualityGateMeasuresStepTest { Condition equals1WarningCondition = createEqualsCondition(INT_METRIC_2, null, "1"); Measure rawMeasure = Measure.newMeasureBuilder().create(rawValue, null); - qualityGateHolder.setQualityGate(new QualityGate("name", of(equals2Condition, equals1WarningCondition))); + qualityGateHolder.setQualityGate(new QualityGate(SOME_QG_ID, SOME_QG_NAME, of(equals2Condition, equals1WarningCondition))); when(measureRepository.getRawMeasure(PROJECT_COMPONENT, INT_METRIC_1)).thenReturn(Optional.of(rawMeasure)); when(measureRepository.getRawMeasure(PROJECT_COMPONENT, INT_METRIC_2)).thenReturn(Optional.of(rawMeasure)); @@ -236,20 +278,25 @@ public class QualityGateMeasuresStepTest { verifyNoMoreInteractions(measureRepository); assertThat(equals2ConditionMeasureCaptor.getValue()) - .hasQualityGateLevel(OK) - .hasQualityGateText(dumbResultTextAnswer(equals2Condition, OK, rawValue)); + .hasQualityGateLevel(OK) + .hasQualityGateText(dumbResultTextAnswer(equals2Condition, OK, rawValue)); assertThat(equals1WarningConditionMeasureCaptor.getValue()) - .hasQualityGateLevel(WARN) - .hasQualityGateText(dumbResultTextAnswer(equals1WarningCondition, WARN, rawValue)); + .hasQualityGateLevel(WARN) + .hasQualityGateText(dumbResultTextAnswer(equals1WarningCondition, WARN, rawValue)); assertThat(alertStatusMeasureCaptor.getValue()) - .hasQualityGateLevel(WARN) - .hasQualityGateText(dumbResultTextAnswer(equals2Condition, OK, rawValue) + ", " - + dumbResultTextAnswer(equals1WarningCondition, WARN, rawValue)); + .hasQualityGateLevel(WARN) + .hasQualityGateText(dumbResultTextAnswer(equals2Condition, OK, rawValue) + ", " + + dumbResultTextAnswer(equals1WarningCondition, WARN, rawValue)); assertThat(qgDetailsMeasureCaptor.getValue()) - .hasValue(new QualityGateDetailsData(WARN, of( - new EvaluatedCondition(equals2Condition, OK, rawValue), - new EvaluatedCondition(equals1WarningCondition, WARN, rawValue) - )).toJson()); + .hasValue(new QualityGateDetailsData(WARN, of( + new EvaluatedCondition(equals2Condition, OK, rawValue), + new EvaluatedCondition(equals1WarningCondition, WARN, rawValue))).toJson()); + + QualityGateStatusHolderAssertions.assertThat(qualityGateStatusHolder) + .hasStatus(QualityGateStatus.WARN) + .hasConditionCount(2) + .hasCondition(equals2Condition, ConditionStatus.EvaluationStatus.OK, String.valueOf(rawValue)) + .hasCondition(equals1WarningCondition, ConditionStatus.EvaluationStatus.WARN, String.valueOf(rawValue)); } private static Condition createEqualsCondition(Metric metric, @Nullable String errorThreshold, @Nullable String warningThreshold) { @@ -260,4 +307,62 @@ public class QualityGateMeasuresStepTest { return new MetricImpl(index, "metricKey" + index, "metricName" + index, Metric.MetricType.INT); } + private static class QualityGateStatusHolderAssertions extends AbstractAssert<QualityGateStatusHolderAssertions, QualityGateStatusHolder> { + + private QualityGateStatusHolderAssertions(QualityGateStatusHolder actual) { + super(actual, QualityGateStatusHolderAssertions.class); + } + + public static QualityGateStatusHolderAssertions assertThat(QualityGateStatusHolder holder) { + return new QualityGateStatusHolderAssertions(holder); + } + + public QualityGateStatusHolderAssertions hasStatus(QualityGateStatus status) { + if (actual.getStatus() != status) { + failWithMessage( + "Expected QualityGateStatusHolder to have global status <%s> but was <%s>", + status, actual.getStatus()); + } + + return this; + } + + public QualityGateStatusHolderAssertions hasConditionCount(int count) { + int conditionCount = actual.getStatusPerConditions().size(); + if (conditionCount != count) { + failWithMessage( + "Expected QualityGateStatusHolder to have <%s> conditions but it has <%s>", + count, conditionCount); + } + + return this; + } + + public QualityGateStatusHolderAssertions hasCondition(Condition condition, ConditionStatus.EvaluationStatus evaluationStatus, @Nullable String expectedValue) { + for (Map.Entry<Condition, ConditionStatus> entry : actual.getStatusPerConditions().entrySet()) { + if (entry.getKey() == condition) { + ConditionStatus.EvaluationStatus actualStatus = entry.getValue().getStatus(); + if (actualStatus != evaluationStatus) { + failWithMessage( + "Expected Status of condition <%s> in QualityGateStatusHolder to be <%s> but it was <%s>", + condition, evaluationStatus, actualStatus); + } + String actualValue = entry.getValue().getValue(); + if (!Objects.equals(expectedValue, actualValue)) { + failWithMessage( + "Expected Value of condition <%s> in QualityGateStatusHolder to be <%s> but it was <%s>", + condition, expectedValue, actualValue); + } + return this; + } + } + + failWithMessage( + "Expected QualityGateStatusHolder to have an entry for <%s> but none was found", + condition); + + return this; + } + } + } |