Browse Source

SONAR-11707 Replace hardcoded QG with project's QG for SLB/PRs

tags/7.7
Duarte Meneses 5 years ago
parent
commit
1634f0473b
19 changed files with 148 additions and 241 deletions
  1. 9
    0
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/analysis/AnalysisMetadataHolder.java
  2. 1
    1
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/api/posttask/PostProjectAnalysisTasksExecutor.java
  3. 54
    0
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/api/posttask/QGToEvaluatedQG.java
  4. 1
    1
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueTrackingDelegator.java
  5. 1
    15
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitygate/QualityGateServiceImpl.java
  6. 2
    6
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/source/NewLinesRepository.java
  7. 1
    1
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStep.java
  8. 1
    1
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadDuplicationsFromReportStep.java
  9. 12
    19
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadQualityGateStep.java
  10. 1
    1
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/QualityGateEventsStep.java
  11. 1
    1
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ValidateProjectStep.java
  12. 2
    21
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/webhook/WebhookPostTask.java
  13. 2
    2
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueTrackingDelegatorTest.java
  14. 0
    35
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitygate/QualityGateServiceImplTest.java
  15. 28
    11
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadQualityGateStepTest.java
  16. 0
    70
      server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/ShortLivingBranchQualityGate.java
  17. 0
    40
      server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/ShortLivingBranchQualityGateTest.java
  18. 8
    8
      server/sonar-server/src/main/java/org/sonar/server/measure/live/LiveQualityGateComputerImpl.java
  19. 24
    8
      server/sonar-server/src/test/java/org/sonar/server/measure/live/LiveQualityGateComputerImplTest.java

+ 9
- 0
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/analysis/AnalysisMetadataHolder.java View File

@@ -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()}
*

+ 1
- 1
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/api/posttask/PostProjectAnalysisTasksExecutor.java View File

@@ -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();

+ 54
- 0
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/api/posttask/QGToEvaluatedQG.java View File

@@ -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();
}
}

+ 1
- 1
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueTrackingDelegator.java View File

@@ -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);

+ 1
- 15
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitygate/QualityGateServiceImpl.java View File

@@ -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())));
}

}

+ 2
- 6
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/source/NewLinesRepository.java View File

@@ -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()));

+ 1
- 1
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStep.java View File

@@ -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 {

+ 1
- 1
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadDuplicationsFromReportStep.java View File

@@ -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()));

+ 12
- 19
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadQualityGateStep.java View File

@@ -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() {

+ 1
- 1
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/QualityGateEventsStep.java View File

@@ -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(

+ 1
- 1
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ValidateProjectStep.java View File

@@ -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();

+ 2
- 21
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/webhook/WebhookPostTask.java View File

@@ -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();

+ 2
- 2
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueTrackingDelegatorTest.java View File

@@ -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);


+ 0
- 35
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitygate/QualityGateServiceImplTest.java View File

@@ -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;
}
}

+ 28
- 11
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadQualityGateStepTest.java View File

@@ -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

+ 0
- 70
server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/ShortLivingBranchQualityGate.java View File

@@ -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;
}
}
}

+ 0
- 40
server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/ShortLivingBranchQualityGateTest.java View File

@@ -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"));
}

}

+ 8
- 8
server/sonar-server/src/main/java/org/sonar/server/measure/live/LiveQualityGateComputerImpl.java View File

@@ -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

+ 24
- 8
server/sonar-server/src/test/java/org/sonar/server/measure/live/LiveQualityGateComputerImplTest.java View File

@@ -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);


Loading…
Cancel
Save