@@ -77,6 +77,15 @@ public interface AnalysisMetadataHolder { | |||
*/ | |||
boolean isShortLivingBranch(); | |||
/** | |||
* Convenience method equivalent to do the check using {@link #getBranch()} | |||
* | |||
* @throws IllegalStateException if branch has not been set | |||
*/ | |||
default boolean isSLBorPR() { | |||
return isShortLivingBranch() || isPullRequest(); | |||
} | |||
/** | |||
* Convenience method equivalent to do the check using {@link #getBranch()} | |||
* |
@@ -167,7 +167,7 @@ public class PostProjectAnalysisTasksExecutor implements ComputationStepExecutor | |||
} | |||
@CheckForNull | |||
private QualityGateImpl createQualityGate() { | |||
private QualityGate createQualityGate() { | |||
Optional<org.sonar.ce.task.projectanalysis.qualitygate.QualityGate> qualityGateOptional = this.qualityGateHolder.getQualityGate(); | |||
if (qualityGateOptional.isPresent()) { | |||
org.sonar.ce.task.projectanalysis.qualitygate.QualityGate qualityGate = qualityGateOptional.get(); |
@@ -0,0 +1,54 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info 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.ce.task.projectanalysis.api.posttask; | |||
import java.util.Set; | |||
import java.util.function.Function; | |||
import org.sonar.api.ce.posttask.QualityGate; | |||
import org.sonar.api.measures.Metric; | |||
import org.sonar.core.util.stream.MoreCollectors; | |||
import org.sonar.server.qualitygate.Condition; | |||
import org.sonar.server.qualitygate.EvaluatedCondition; | |||
import org.sonar.server.qualitygate.EvaluatedQualityGate; | |||
public class QGToEvaluatedQG implements Function<QualityGate, EvaluatedQualityGate> { | |||
public static final QGToEvaluatedQG INSTANCE = new QGToEvaluatedQG(); | |||
private QGToEvaluatedQG() { | |||
// static only | |||
} | |||
@Override public EvaluatedQualityGate apply(QualityGate qg) { | |||
EvaluatedQualityGate.Builder builder = EvaluatedQualityGate.newBuilder(); | |||
Set<org.sonar.server.qualitygate.Condition> conditions = qg.getConditions().stream() | |||
.map(q -> { | |||
org.sonar.server.qualitygate.Condition condition = new org.sonar.server.qualitygate.Condition(q.getMetricKey(), Condition.Operator.valueOf(q.getOperator().name()), | |||
q.getErrorThreshold()); | |||
builder.addCondition(condition, | |||
EvaluatedCondition.EvaluationStatus.valueOf(q.getStatus().name()), | |||
q.getStatus() == org.sonar.api.ce.posttask.QualityGate.EvaluationStatus.NO_VALUE ? null : q.getValue()); | |||
return condition; | |||
}) | |||
.collect(MoreCollectors.toSet()); | |||
return builder.setQualityGate(new org.sonar.server.qualitygate.QualityGate(qg.getId(), qg.getName(), conditions)) | |||
.setStatus(Metric.Level.valueOf(qg.getStatus().name())) | |||
.build(); | |||
} | |||
} |
@@ -44,7 +44,7 @@ public class IssueTrackingDelegator { | |||
} | |||
public TrackingResult track(Component component) { | |||
if (analysisMetadataHolder.isShortLivingBranch() || analysisMetadataHolder.isPullRequest()) { | |||
if (analysisMetadataHolder.isSLBorPR()) { | |||
return standardResult(shortBranchOrPullRequestTracker.track(component)); | |||
} else if (isFirstAnalysisSecondaryLongLivingBranch()) { | |||
Tracking<DefaultIssue, DefaultIssue> tracking = mergeBranchTracker.track(component); |
@@ -22,13 +22,12 @@ package org.sonar.ce.task.projectanalysis.qualitygate; | |||
import java.util.Collection; | |||
import java.util.Objects; | |||
import java.util.Optional; | |||
import org.sonar.ce.task.projectanalysis.analysis.Organization; | |||
import org.sonar.ce.task.projectanalysis.metric.MetricRepository; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.qualitygate.QualityGateConditionDto; | |||
import org.sonar.db.qualitygate.QualityGateDto; | |||
import org.sonar.ce.task.projectanalysis.analysis.Organization; | |||
import org.sonar.server.qualitygate.ShortLivingBranchQualityGate; | |||
import static org.sonar.core.util.stream.MoreCollectors.toList; | |||
@@ -44,9 +43,6 @@ public class QualityGateServiceImpl implements QualityGateService { | |||
@Override | |||
public Optional<QualityGate> findById(long id) { | |||
if (id == ShortLivingBranchQualityGate.ID) { | |||
return Optional.of(buildShortLivingBranchHardcodedQualityGate()); | |||
} | |||
try (DbSession dbSession = dbClient.openSession(false)) { | |||
QualityGateDto qualityGateDto = dbClient.qualityGateDao().selectById(dbSession, id); | |||
if (qualityGateDto == null) { | |||
@@ -79,14 +75,4 @@ public class QualityGateServiceImpl implements QualityGateService { | |||
return new QualityGate(qualityGateDto.getId(), qualityGateDto.getName(), conditions); | |||
} | |||
private QualityGate buildShortLivingBranchHardcodedQualityGate() { | |||
return new QualityGate( | |||
ShortLivingBranchQualityGate.ID, | |||
ShortLivingBranchQualityGate.NAME, | |||
ShortLivingBranchQualityGate.CONDITIONS.stream() | |||
.map(c -> new Condition(metricRepository.getByKey(c.getMetricKey()), c.getOperator(), c.getErrorThreshold())) | |||
.collect(toList(ShortLivingBranchQualityGate.CONDITIONS.size()))); | |||
} | |||
} |
@@ -49,7 +49,7 @@ public class NewLinesRepository { | |||
} | |||
public boolean newLinesAvailable() { | |||
return isPullRequestOrShortLivedBranch() || periodHolder.hasPeriod(); | |||
return analysisMetadataHolder.isSLBorPR() || periodHolder.hasPeriod(); | |||
} | |||
public Optional<Set<Integer>> getNewLines(Component file) { | |||
@@ -96,17 +96,13 @@ public class NewLinesRepository { | |||
} | |||
private Optional<Set<Integer>> getChangedLinesFromReport(Component file) { | |||
if (isPullRequestOrShortLivedBranch()) { | |||
if (analysisMetadataHolder.isSLBorPR()) { | |||
return reportChangedLinesCache.computeIfAbsent(file, this::readFromReport); | |||
} | |||
return Optional.empty(); | |||
} | |||
private boolean isPullRequestOrShortLivedBranch() { | |||
return analysisMetadataHolder.isPullRequest() || analysisMetadataHolder.isShortLivingBranch(); | |||
} | |||
private Optional<Set<Integer>> readFromReport(Component file) { | |||
return reportReader.readComponentChangedLines(file.getReportAttributes().getRef()) | |||
.map(c -> new HashSet<>(c.getLineList())); |
@@ -101,7 +101,7 @@ public class BuildComponentTreeStep implements ComputationStep { | |||
Component reportTreeRoot = builder.buildProject(reportProject, relativePathFromScmRoot); | |||
if (analysisMetadataHolder.isShortLivingBranch() || analysisMetadataHolder.isPullRequest()) { | |||
if (analysisMetadataHolder.isSLBorPR()) { | |||
Component changedComponentTreeRoot = builder.buildChangedComponentTreeRoot(reportTreeRoot); | |||
treeRootHolder.setRoots(changedComponentTreeRoot, reportTreeRoot); | |||
} else { |
@@ -85,7 +85,7 @@ public class LoadDuplicationsFromReportStep implements ComputationStep { | |||
if (input.getOtherFileRef() != 0) { | |||
checkArgument(input.getOtherFileRef() != file.getReportAttributes().getRef(), "file and otherFile references can not be the same"); | |||
Component otherComponent = treeRootHolder.getReportTreeComponentByRef(input.getOtherFileRef()); | |||
if ((analysisMetadataHolder.isShortLivingBranch() || analysisMetadataHolder.isPullRequest()) && otherComponent.getStatus() == Component.Status.SAME) { | |||
if (analysisMetadataHolder.isSLBorPR() && otherComponent.getStatus() == Component.Status.SAME) { | |||
return new InExtendedProjectDuplicate(otherComponent, convert(input.getRange())); | |||
} else { | |||
return new InProjectDuplicate(otherComponent, convert(input.getRange())); |
@@ -20,14 +20,15 @@ | |||
package org.sonar.ce.task.projectanalysis.step; | |||
import java.util.Optional; | |||
import java.util.stream.Collectors; | |||
import org.sonar.api.config.Configuration; | |||
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder; | |||
import org.sonar.ce.task.projectanalysis.component.ConfigurationRepository; | |||
import org.sonar.ce.task.projectanalysis.qualitygate.Condition; | |||
import org.sonar.ce.task.projectanalysis.qualitygate.MutableQualityGateHolder; | |||
import org.sonar.ce.task.projectanalysis.qualitygate.QualityGate; | |||
import org.sonar.ce.task.projectanalysis.qualitygate.QualityGateService; | |||
import org.sonar.ce.task.step.ComputationStep; | |||
import org.sonar.server.qualitygate.ShortLivingBranchQualityGate; | |||
import static org.apache.commons.lang.StringUtils.isBlank; | |||
@@ -53,30 +54,22 @@ public class LoadQualityGateStep implements ComputationStep { | |||
@Override | |||
public void execute(ComputationStep.Context context) { | |||
Optional<QualityGate> qualityGate = getShortLivingBranchQualityGate(); | |||
Optional<QualityGate> qualityGate = getProjectQualityGate(); | |||
if (!qualityGate.isPresent()) { | |||
// Not on a short living branch, let's retrieve the QG of the project | |||
qualityGate = getProjectQualityGate(); | |||
if (!qualityGate.isPresent()) { | |||
// No QG defined for the project, let's retrieve the QG on the organization | |||
qualityGate = Optional.of(getOrganizationDefaultQualityGate()); | |||
} | |||
// No QG defined for the project, let's retrieve the QG on the organization | |||
qualityGate = Optional.of(getOrganizationDefaultQualityGate()); | |||
} | |||
if (analysisMetadataHolder.isSLBorPR()) { | |||
qualityGate = filterQGForSLB(qualityGate); | |||
} | |||
qualityGateHolder.setQualityGate(qualityGate.orElseThrow(() -> new IllegalStateException("Quality gate not present"))); | |||
} | |||
private Optional<QualityGate> getShortLivingBranchQualityGate() { | |||
if (analysisMetadataHolder.isShortLivingBranch() || analysisMetadataHolder.isPullRequest()) { | |||
Optional<QualityGate> qualityGate = qualityGateService.findById(ShortLivingBranchQualityGate.ID); | |||
if (qualityGate.isPresent()) { | |||
return qualityGate; | |||
} else { | |||
throw new IllegalStateException("Failed to retrieve hardcoded short living branch Quality Gate"); | |||
} | |||
} else { | |||
return Optional.empty(); | |||
} | |||
private static Optional<QualityGate> filterQGForSLB(Optional<QualityGate> qualityGate) { | |||
return qualityGate.map(qg -> new QualityGate(qg.getId(), qg.getName(), | |||
qg.getConditions().stream().filter(Condition::useVariation).collect(Collectors.toList()))); | |||
} | |||
private Optional<QualityGate> getProjectQualityGate() { |
@@ -70,7 +70,7 @@ public class QualityGateEventsStep implements ComputationStep { | |||
@Override | |||
public void execute(ComputationStep.Context context) { | |||
// no notification on short living branch and pull request as there is no real Quality Gate on those | |||
if (analysisMetadataHolder.isShortLivingBranch() || analysisMetadataHolder.isPullRequest()) { | |||
if (analysisMetadataHolder.isSLBorPR()) { | |||
return; | |||
} | |||
new DepthTraversalTypeAwareCrawler( |
@@ -88,7 +88,7 @@ public class ValidateProjectStep implements ComputationStep { | |||
} | |||
private void validateTargetBranch(DbSession session) { | |||
if (!analysisMetadataHolder.isShortLivingBranch() && !analysisMetadataHolder.isPullRequest()) { | |||
if (!analysisMetadataHolder.isSLBorPR()) { | |||
return; | |||
} | |||
String mergeBranchUuid = analysisMetadataHolder.getBranch().getMergeBranchUuid().get(); |
@@ -21,12 +21,8 @@ package org.sonar.ce.task.projectanalysis.webhook; | |||
import java.util.Map; | |||
import java.util.Optional; | |||
import java.util.Set; | |||
import org.sonar.api.ce.posttask.PostProjectAnalysisTask; | |||
import org.sonar.api.measures.Metric; | |||
import org.sonar.core.util.stream.MoreCollectors; | |||
import org.sonar.server.qualitygate.Condition; | |||
import org.sonar.server.qualitygate.EvaluatedCondition; | |||
import org.sonar.ce.task.projectanalysis.api.posttask.QGToEvaluatedQG; | |||
import org.sonar.server.qualitygate.EvaluatedQualityGate; | |||
import org.sonar.server.webhook.Analysis; | |||
import org.sonar.server.webhook.Branch; | |||
@@ -36,7 +32,6 @@ import org.sonar.server.webhook.WebHooks; | |||
import org.sonar.server.webhook.WebhookPayloadFactory; | |||
public class WebhookPostTask implements PostProjectAnalysisTask { | |||
private final WebhookPayloadFactory payloadFactory; | |||
private final WebHooks webHooks; | |||
@@ -60,21 +55,7 @@ public class WebhookPostTask implements PostProjectAnalysisTask { | |||
Analysis analysis = projectAnalysis.getAnalysis().map(a -> new Analysis(a.getAnalysisUuid(), a.getDate().getTime())).orElse(null); | |||
Branch branch = projectAnalysis.getBranch().map(b -> new Branch(b.isMain(), b.getName().orElse(null), Branch.Type.valueOf(b.getType().name()))).orElse(null); | |||
EvaluatedQualityGate qualityGate = Optional.ofNullable(projectAnalysis.getQualityGate()) | |||
.map(qg -> { | |||
EvaluatedQualityGate.Builder builder = EvaluatedQualityGate.newBuilder(); | |||
Set<Condition> conditions = qg.getConditions().stream() | |||
.map(q -> { | |||
Condition condition = new Condition(q.getMetricKey(), Condition.Operator.valueOf(q.getOperator().name()), q.getErrorThreshold()); | |||
builder.addCondition(condition, | |||
EvaluatedCondition.EvaluationStatus.valueOf(q.getStatus().name()), | |||
q.getStatus() == org.sonar.api.ce.posttask.QualityGate.EvaluationStatus.NO_VALUE ? null : q.getValue()); | |||
return condition; | |||
}) | |||
.collect(MoreCollectors.toSet()); | |||
return builder.setQualityGate(new org.sonar.server.qualitygate.QualityGate(qg.getId(), qg.getName(), conditions)) | |||
.setStatus(Metric.Level.valueOf(qg.getStatus().name())) | |||
.build(); | |||
}) | |||
.map(QGToEvaluatedQG.INSTANCE) | |||
.orElse(null); | |||
Long date = projectAnalysis.getAnalysis().map(a -> a.getDate().getTime()).orElse(null); | |||
Map<String, String> properties = projectAnalysis.getScannerContext().getProperties(); |
@@ -93,7 +93,7 @@ public class IssueTrackingDelegatorTest { | |||
Branch branch = mock(Branch.class); | |||
when(branch.getType()).thenReturn(BranchType.SHORT); | |||
when(analysisMetadataHolder.getBranch()).thenReturn(mock(Branch.class)); | |||
when(analysisMetadataHolder.isShortLivingBranch()).thenReturn(true); | |||
when(analysisMetadataHolder.isSLBorPR()).thenReturn(true); | |||
underTest.track(component); | |||
@@ -107,7 +107,7 @@ public class IssueTrackingDelegatorTest { | |||
Branch branch = mock(Branch.class); | |||
when(branch.getType()).thenReturn(BranchType.PULL_REQUEST); | |||
when(analysisMetadataHolder.getBranch()).thenReturn(mock(Branch.class)); | |||
when(analysisMetadataHolder.isShortLivingBranch()).thenReturn(true); | |||
when(analysisMetadataHolder.isSLBorPR()).thenReturn(true); | |||
underTest.track(component); | |||
@@ -22,28 +22,21 @@ package org.sonar.ce.task.projectanalysis.qualitygate; | |||
import com.google.common.collect.ImmutableList; | |||
import java.util.Collections; | |||
import java.util.Optional; | |||
import java.util.Random; | |||
import org.apache.commons.lang.RandomStringUtils; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import org.sonar.api.measures.CoreMetrics; | |||
import org.sonar.ce.task.projectanalysis.metric.Metric; | |||
import org.sonar.ce.task.projectanalysis.metric.MetricImpl; | |||
import org.sonar.ce.task.projectanalysis.metric.MetricRepository; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.qualitygate.QualityGateConditionDao; | |||
import org.sonar.db.qualitygate.QualityGateConditionDto; | |||
import org.sonar.db.qualitygate.QualityGateDao; | |||
import org.sonar.db.qualitygate.QualityGateDto; | |||
import org.sonar.server.qualitygate.ShortLivingBranchQualityGate; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.core.api.Assertions.tuple; | |||
import static org.mockito.ArgumentMatchers.any; | |||
import static org.mockito.ArgumentMatchers.eq; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
import static org.sonar.ce.task.projectanalysis.qualitygate.Condition.Operator.GREATER_THAN; | |||
public class QualityGateServiceImplTest { | |||
private static final long SOME_ID = 123; | |||
@@ -124,32 +117,4 @@ public class QualityGateServiceImplTest { | |||
assertThat(res.get().getConditions()).containsOnly( | |||
new Condition(METRIC_2, CONDITION_2.getOperator(), CONDITION_2.getErrorThreshold())); | |||
} | |||
@Test | |||
public void findById_of_hardcoded_short_living_branch_returns_hardcoded_qg() { | |||
MetricImpl bugsMetric = mockMetricInRepository(CoreMetrics.BUGS_KEY); | |||
MetricImpl vulnerabilitiesMetric = mockMetricInRepository(CoreMetrics.VULNERABILITIES_KEY); | |||
MetricImpl codeSmellsMetric = mockMetricInRepository(CoreMetrics.CODE_SMELLS_KEY); | |||
MetricImpl openedIssueMetric = mockMetricInRepository(CoreMetrics.OPEN_ISSUES_KEY); | |||
MetricImpl reOpenedIssueMetric = mockMetricInRepository(CoreMetrics.REOPENED_ISSUES_KEY); | |||
Optional<QualityGate> res = underTest.findById(ShortLivingBranchQualityGate.ID); | |||
assertThat(res).isPresent(); | |||
QualityGate qualityGate = res.get(); | |||
assertThat(qualityGate.getId()).isEqualTo(ShortLivingBranchQualityGate.ID); | |||
assertThat(qualityGate.getName()).isEqualTo("Hardcoded short living branch quality gate"); | |||
assertThat(qualityGate.getConditions()) | |||
.extracting(Condition::getMetric, Condition::getOperator, Condition::getErrorThreshold) | |||
.containsOnly( | |||
tuple(openedIssueMetric, GREATER_THAN, "0"), | |||
tuple(reOpenedIssueMetric, GREATER_THAN, "0")); | |||
} | |||
private MetricImpl mockMetricInRepository(String metricKey) { | |||
MetricImpl metric = new MetricImpl(new Random().nextInt(999), metricKey, RandomStringUtils.randomAlphanumeric(20), Metric.MetricType.INT); | |||
when(metricRepository.getByKey(metricKey)) | |||
.thenReturn(metric); | |||
return metric; | |||
} | |||
} |
@@ -19,6 +19,7 @@ | |||
*/ | |||
package org.sonar.ce.task.projectanalysis.step; | |||
import java.util.Arrays; | |||
import java.util.Optional; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
@@ -27,11 +28,13 @@ import org.junit.rules.ExpectedException; | |||
import org.sonar.api.config.internal.MapSettings; | |||
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder; | |||
import org.sonar.ce.task.projectanalysis.component.ConfigurationRepository; | |||
import org.sonar.ce.task.projectanalysis.metric.Metric; | |||
import org.sonar.ce.task.projectanalysis.metric.MetricImpl; | |||
import org.sonar.ce.task.projectanalysis.qualitygate.Condition; | |||
import org.sonar.ce.task.projectanalysis.qualitygate.MutableQualityGateHolderRule; | |||
import org.sonar.ce.task.projectanalysis.qualitygate.QualityGate; | |||
import org.sonar.ce.task.projectanalysis.qualitygate.QualityGateService; | |||
import org.sonar.ce.task.step.TestComputationStepContext; | |||
import org.sonar.server.qualitygate.ShortLivingBranchQualityGate; | |||
import static java.util.Collections.emptyList; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
@@ -58,25 +61,39 @@ public class LoadQualityGateStepTest { | |||
} | |||
@Test | |||
public void add_hardcoded_QG_on_short_living_branch() { | |||
when(analysisMetadataHolder.isShortLivingBranch()).thenReturn(true); | |||
QualityGate qualityGate = mock(QualityGate.class); | |||
when(qualityGateService.findById(ShortLivingBranchQualityGate.ID)).thenReturn(Optional.of(qualityGate)); | |||
public void filter_conditions_on_short_living_branch() { | |||
when(settingsRepository.getConfiguration()).thenReturn(new MapSettings().asConfig()); | |||
Metric newMetric = new MetricImpl(1, "new_key", "name", Metric.MetricType.INT); | |||
Metric metric = new MetricImpl(2, "key", "name", Metric.MetricType.INT); | |||
Condition variation = new Condition(newMetric, Condition.Operator.GREATER_THAN.getDbValue(), "1.0"); | |||
Condition condition = new Condition(metric, Condition.Operator.GREATER_THAN.getDbValue(), "1.0"); | |||
when(analysisMetadataHolder.isSLBorPR()).thenReturn(true); | |||
QualityGate defaultGate = new QualityGate(1, "qg", Arrays.asList(variation, condition)); | |||
when(qualityGateService.findDefaultQualityGate(any())).thenReturn(defaultGate); | |||
underTest.execute(new TestComputationStepContext()); | |||
assertThat(mutableQualityGateHolder.getQualityGate().get()).isSameAs(qualityGate); | |||
assertThat(mutableQualityGateHolder.getQualityGate().get().getConditions()).containsExactly(variation); | |||
} | |||
@Test | |||
public void add_hardcoded_QG_on_pull_request() { | |||
when(analysisMetadataHolder.isPullRequest()).thenReturn(true); | |||
QualityGate qualityGate = mock(QualityGate.class); | |||
when(qualityGateService.findById(ShortLivingBranchQualityGate.ID)).thenReturn(Optional.of(qualityGate)); | |||
public void filter_conditions_on_pull_request() { | |||
when(settingsRepository.getConfiguration()).thenReturn(new MapSettings().asConfig()); | |||
Metric newMetric = new MetricImpl(1, "new_key", "name", Metric.MetricType.INT); | |||
Metric metric = new MetricImpl(2, "key", "name", Metric.MetricType.INT); | |||
Condition variation = new Condition(newMetric, Condition.Operator.GREATER_THAN.getDbValue(), "1.0"); | |||
Condition condition = new Condition(metric, Condition.Operator.GREATER_THAN.getDbValue(), "1.0"); | |||
when(analysisMetadataHolder.isSLBorPR()).thenReturn(true); | |||
QualityGate defaultGate = new QualityGate(1, "qg", Arrays.asList(variation, condition)); | |||
when(qualityGateService.findDefaultQualityGate(any())).thenReturn(defaultGate); | |||
underTest.execute(new TestComputationStepContext()); | |||
assertThat(mutableQualityGateHolder.getQualityGate().get()).isSameAs(qualityGate); | |||
assertThat(mutableQualityGateHolder.getQualityGate().get().getConditions()).containsExactly(variation); | |||
} | |||
@Test |
@@ -1,70 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info 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.qualitygate; | |||
import com.google.common.collect.ImmutableList; | |||
import com.google.common.collect.ImmutableSet; | |||
import java.util.List; | |||
import org.sonar.api.measures.CoreMetrics; | |||
import static org.sonar.db.qualitygate.QualityGateConditionDto.OPERATOR_GREATER_THAN; | |||
/** | |||
* Offers constants describing the Hardcoded Quality Gate for short living branches and pull requests. | |||
*/ | |||
public final class ShortLivingBranchQualityGate { | |||
public static final long ID = -1_963_456_987L; | |||
public static final String NAME = "Hardcoded short living branch quality gate"; | |||
public static final List<Condition> CONDITIONS = ImmutableList.of( | |||
new Condition(CoreMetrics.OPEN_ISSUES_KEY, OPERATOR_GREATER_THAN, "0"), | |||
new Condition(CoreMetrics.REOPENED_ISSUES_KEY, OPERATOR_GREATER_THAN, "0")); | |||
public static final QualityGate GATE = new QualityGate(String.valueOf(ID), NAME, ImmutableSet.of( | |||
new org.sonar.server.qualitygate.Condition(CoreMetrics.OPEN_ISSUES_KEY, org.sonar.server.qualitygate.Condition.Operator.GREATER_THAN, "0"), | |||
new org.sonar.server.qualitygate.Condition(CoreMetrics.REOPENED_ISSUES_KEY, org.sonar.server.qualitygate.Condition.Operator.GREATER_THAN, "0"))); | |||
private ShortLivingBranchQualityGate() { | |||
// prevents instantiation | |||
} | |||
public static final class Condition { | |||
private final String metricKey; | |||
private final String operator; | |||
private final String errorThreshold; | |||
public Condition(String metricKey, String operator, String errorThreshold) { | |||
this.metricKey = metricKey; | |||
this.operator = operator; | |||
this.errorThreshold = errorThreshold; | |||
} | |||
public String getMetricKey() { | |||
return metricKey; | |||
} | |||
public String getOperator() { | |||
return operator; | |||
} | |||
public String getErrorThreshold() { | |||
return errorThreshold; | |||
} | |||
} | |||
} |
@@ -1,40 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info 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.qualitygate; | |||
import org.junit.Test; | |||
import org.sonar.api.measures.CoreMetrics; | |||
import org.sonar.server.qualitygate.ShortLivingBranchQualityGate.Condition; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.core.api.Assertions.tuple; | |||
public class ShortLivingBranchQualityGateTest { | |||
@Test | |||
public void defines_short_living_branches_hardcoded_quality_gate_conditions() { | |||
assertThat(ShortLivingBranchQualityGate.CONDITIONS) | |||
.extracting(Condition::getMetricKey, Condition::getOperator, Condition::getErrorThreshold) | |||
.containsExactly( | |||
tuple(CoreMetrics.OPEN_ISSUES_KEY, "GT", "0"), | |||
tuple(CoreMetrics.REOPENED_ISSUES_KEY, "GT", "0")); | |||
} | |||
} |
@@ -25,6 +25,7 @@ import java.util.Map; | |||
import java.util.Optional; | |||
import java.util.OptionalDouble; | |||
import java.util.Set; | |||
import java.util.stream.Stream; | |||
import org.sonar.api.measures.CoreMetrics; | |||
import org.sonar.api.measures.Metric; | |||
import org.sonar.db.DbClient; | |||
@@ -43,7 +44,6 @@ import org.sonar.server.qualitygate.QualityGate; | |||
import org.sonar.server.qualitygate.QualityGateConverter; | |||
import org.sonar.server.qualitygate.QualityGateEvaluator; | |||
import org.sonar.server.qualitygate.QualityGateFinder; | |||
import org.sonar.server.qualitygate.ShortLivingBranchQualityGate; | |||
import static java.lang.String.format; | |||
import static org.sonar.core.util.stream.MoreCollectors.toHashSet; | |||
@@ -63,10 +63,6 @@ public class LiveQualityGateComputerImpl implements LiveQualityGateComputer { | |||
@Override | |||
public QualityGate loadQualityGate(DbSession dbSession, OrganizationDto organization, ComponentDto project, BranchDto branch) { | |||
if (branch.getBranchType() == BranchType.SHORT || branch.getBranchType() == BranchType.PULL_REQUEST) { | |||
return ShortLivingBranchQualityGate.GATE; | |||
} | |||
ComponentDto mainProject = project.getMainBranchProjectUuid() == null ? project : dbClient.componentDao().selectOrFailByKey(dbSession, project.getKey()); | |||
QualityGateDto gateDto = qGateFinder.getQualityGate(dbSession, organization, mainProject) | |||
.orElseThrow(() -> new IllegalStateException(format("Quality Gate not found for project %s", mainProject.getKey()))) | |||
@@ -77,13 +73,17 @@ public class LiveQualityGateComputerImpl implements LiveQualityGateComputer { | |||
Map<Integer, MetricDto> metricsById = dbClient.metricDao().selectByIds(dbSession, metricIds).stream() | |||
.collect(uniqueIndex(MetricDto::getId)); | |||
Set<Condition> conditions = conditionDtos.stream().map(conditionDto -> { | |||
Stream<Condition> conditions = conditionDtos.stream().map(conditionDto -> { | |||
String metricKey = metricsById.get((int) conditionDto.getMetricId()).getKey(); | |||
Condition.Operator operator = Condition.Operator.fromDbValue(conditionDto.getOperator()); | |||
return new Condition(metricKey, operator, conditionDto.getErrorThreshold()); | |||
}).collect(toHashSet(conditionDtos.size())); | |||
}); | |||
if (branch.getBranchType() == BranchType.PULL_REQUEST || branch.getBranchType() == BranchType.SHORT) { | |||
conditions = conditions.filter(Condition::isOnLeakPeriod); | |||
} | |||
return new QualityGate(String.valueOf(gateDto.getId()), gateDto.getName(), conditions); | |||
return new QualityGate(String.valueOf(gateDto.getId()), gateDto.getName(), conditions.collect(toHashSet(conditionDtos.size()))); | |||
} | |||
@Override |
@@ -44,7 +44,6 @@ import org.sonar.server.qualitygate.EvaluatedQualityGate; | |||
import org.sonar.server.qualitygate.QualityGate; | |||
import org.sonar.server.qualitygate.QualityGateEvaluator; | |||
import org.sonar.server.qualitygate.QualityGateFinder; | |||
import org.sonar.server.qualitygate.ShortLivingBranchQualityGate; | |||
import static com.google.common.base.Preconditions.checkState; | |||
import static java.util.Arrays.asList; | |||
@@ -71,24 +70,40 @@ public class LiveQualityGateComputerImplTest { | |||
public void loadQualityGate_returns_hardcoded_gate_for_short_living_branches() { | |||
OrganizationDto organization = db.organizations().insert(); | |||
ComponentDto project = db.components().insertPublicProject(organization); | |||
BranchDto branch = newBranchDto(project).setBranchType(BranchType.SHORT); | |||
db.components().insertProjectBranch(project, branch); | |||
MetricDto metric1 = db.measures().insertMetric(m -> m.setKey("new_metric")); | |||
MetricDto metric2 = db.measures().insertMetric(m -> m.setKey("metric")); | |||
QualityGate result = underTest.loadQualityGate(db.getSession(), organization, project, branch); | |||
QGateWithOrgDto gate = db.qualityGates().insertQualityGate(organization); | |||
db.qualityGates().setDefaultQualityGate(organization, gate); | |||
assertThat(result).isSameAs(ShortLivingBranchQualityGate.GATE); | |||
db.qualityGates().addCondition(gate, metric1); | |||
db.qualityGates().addCondition(gate, metric2); | |||
QualityGate result = underTest.loadQualityGate(db.getSession(), organization, project, branch); | |||
assertThat(result.getConditions()).extracting(Condition::getMetricKey).containsExactly("new_metric"); | |||
} | |||
@Test | |||
public void loadQualityGate_returns_hardcoded_gate_for_pull_requests() { | |||
OrganizationDto organization = db.organizations().insert(); | |||
ComponentDto project = db.components().insertPublicProject(organization); | |||
BranchDto pullRequest = newBranchDto(project).setBranchType(BranchType.PULL_REQUEST); | |||
db.components().insertProjectBranch(project, pullRequest); | |||
QualityGate result = underTest.loadQualityGate(db.getSession(), organization, project, pullRequest); | |||
BranchDto branch = newBranchDto(project).setBranchType(BranchType.SHORT); | |||
db.components().insertProjectBranch(project, branch); | |||
MetricDto metric1 = db.measures().insertMetric(m -> m.setKey("new_metric")); | |||
MetricDto metric2 = db.measures().insertMetric(m -> m.setKey("metric")); | |||
QGateWithOrgDto gate = db.qualityGates().insertQualityGate(organization); | |||
db.qualityGates().setDefaultQualityGate(organization, gate); | |||
db.qualityGates().addCondition(gate, metric1); | |||
db.qualityGates().addCondition(gate, metric2); | |||
assertThat(result).isSameAs(ShortLivingBranchQualityGate.GATE); | |||
QualityGate result = underTest.loadQualityGate(db.getSession(), organization, project, branch); | |||
assertThat(result.getConditions()).extracting(Condition::getMetricKey).containsExactly("new_metric"); | |||
} | |||
@Test | |||
@@ -164,7 +179,8 @@ public class LiveQualityGateComputerImplTest { | |||
LiveMeasureDto numericMeasure = new LiveMeasureDto().setMetricId(numericMetric.getId()).setValue(1.23).setVariation(4.56).setComponentUuid(project.uuid()); | |||
LiveMeasureDto numericNewMeasure = new LiveMeasureDto().setMetricId(numericNewMetric.getId()).setValue(7.8).setVariation(8.9).setComponentUuid(project.uuid()); | |||
LiveMeasureDto stringMeasure = new LiveMeasureDto().setMetricId(stringMetric.getId()).setData("bar").setComponentUuid(project.uuid()); | |||
MeasureMatrix matrix = new MeasureMatrix(singleton(project), asList(statusMetric, detailsMetric, numericMetric, numericNewMetric, stringMetric), asList(numericMeasure, numericNewMeasure, stringMeasure)); | |||
MeasureMatrix matrix = new MeasureMatrix(singleton(project), asList(statusMetric, detailsMetric, numericMetric, numericNewMetric, stringMetric), | |||
asList(numericMeasure, numericNewMeasure, stringMeasure)); | |||
underTest.refreshGateStatus(project, gate, matrix); | |||