@@ -20,7 +20,6 @@ | |||
package org.sonar.server.qualitygate; | |||
import java.util.List; | |||
import java.util.stream.IntStream; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.sonar.api.measures.Metric; | |||
@@ -30,14 +29,17 @@ import org.sonar.db.metric.MetricDto; | |||
import org.sonar.db.qualitygate.QualityGateConditionDto; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.assertFalse; | |||
import static org.junit.Assert.assertTrue; | |||
import static org.sonar.api.measures.CoreMetrics.BLOCKER_VIOLATIONS; | |||
import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES; | |||
import static org.sonar.api.measures.CoreMetrics.FUNCTION_COMPLEXITY; | |||
import static org.sonar.api.measures.CoreMetrics.LINE_COVERAGE; | |||
import static org.sonar.api.measures.CoreMetrics.NEW_COVERAGE; | |||
import static org.sonar.api.measures.CoreMetrics.NEW_DUPLICATED_LINES_DENSITY; | |||
import static org.sonar.api.measures.CoreMetrics.NEW_MAINTAINABILITY_RATING; | |||
import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_RATING; | |||
import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED; | |||
import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_RATING; | |||
import static org.sonar.api.measures.CoreMetrics.NEW_VIOLATIONS; | |||
import static org.sonar.server.qualitygate.QualityGateCaycChecker.BEST_VALUE_REQUIREMENTS; | |||
import static org.sonar.server.qualitygate.QualityGateCaycChecker.CAYC_METRICS; | |||
import static org.sonar.server.qualitygate.QualityGateCaycStatus.COMPLIANT; | |||
import static org.sonar.server.qualitygate.QualityGateCaycStatus.NON_COMPLIANT; | |||
@@ -50,41 +52,57 @@ public class QualityGateCaycCheckerIT { | |||
QualityGateCaycChecker underTest = new QualityGateCaycChecker(db.getDbClient()); | |||
@Test | |||
public void checkCaycCompliant() { | |||
public void checkCaycCompliant_when_contains_all_and_only_complicant_conditions_should_return_compliant() { | |||
String qualityGateUuid = "abcd"; | |||
CAYC_METRICS.forEach(metric -> insertCondition(insertMetric(metric), qualityGateUuid, metric.getBestValue())); | |||
assertEquals(COMPLIANT, underTest.checkCaycCompliant(db.getSession(), qualityGateUuid)); | |||
} | |||
@Test | |||
public void check_Cayc_non_compliant_with_extra_conditions() { | |||
public void checkCaycCompliant_when_extra_conditions_should_return_over_compliant() { | |||
String qualityGateUuid = "abcd"; | |||
CAYC_METRICS.forEach(metric -> insertCondition(insertMetric(metric), qualityGateUuid, metric.getBestValue())); | |||
// extra conditions outside of CAYC requirements | |||
List.of(LINE_COVERAGE, DUPLICATED_LINES).forEach(metric -> insertCondition(insertMetric(metric), qualityGateUuid, metric.getBestValue())); | |||
List.of(LINE_COVERAGE, DUPLICATED_LINES).forEach(metric -> insertCondition(insertMetric(metric), qualityGateUuid, | |||
metric.getBestValue())); | |||
assertEquals(OVER_COMPLIANT, underTest.checkCaycCompliant(db.getSession(), qualityGateUuid)); | |||
} | |||
@Test | |||
public void check_Cayc_NonCompliant_with_lesser_threshold_value() { | |||
public void checkCaycCompliant_when_conditions_have_lesser_threshold_value_should_return_non_compliant() { | |||
var metrics = CAYC_METRICS.stream().map(this::insertMetric).toList(); | |||
IntStream.range(0, 4).forEach(idx -> { | |||
String qualityGateUuid = "abcd" + idx; | |||
for (int i = 0; i < metrics.size(); i++) { | |||
var metric = metrics.get(i); | |||
insertCondition(metric, qualityGateUuid, idx == i ? metric.getWorstValue() : metric.getBestValue()); | |||
String qualityGateUuid = "abcd"; | |||
for (var metric : metrics) { | |||
if (BEST_VALUE_REQUIREMENTS.keySet().contains(metric.getKey())) { | |||
insertCondition(metric, qualityGateUuid, metric.getBestValue() - 1); | |||
} else { | |||
insertCondition(metric, qualityGateUuid, metric.getBestValue()); | |||
} | |||
assertEquals(NON_COMPLIANT, underTest.checkCaycCompliant(db.getSession(), qualityGateUuid)); | |||
}); | |||
} | |||
assertEquals(NON_COMPLIANT, underTest.checkCaycCompliant(db.getSession(), qualityGateUuid)); | |||
} | |||
@Test | |||
public void isCaycCondition_when_check_compliant_condition_should_return_true() { | |||
CAYC_METRICS.stream().map(this::toMetricDto) | |||
.forEach(metricDto -> assertTrue(underTest.isCaycCondition(metricDto))); | |||
} | |||
@Test | |||
public void isCaycCondition_when_check_non_compliant_condition_should_return_false() { | |||
List.of(BLOCKER_VIOLATIONS, FUNCTION_COMPLEXITY) | |||
.stream().map(this::toMetricDto) | |||
.forEach(metricDto -> assertFalse(underTest.isCaycCondition(metricDto))); | |||
} | |||
@Test | |||
public void check_Cayc_NonCompliant_with_missing_metric() { | |||
public void checkCaycCompliant_when_missing_compliant_condition_should_return_non_compliant() { | |||
String qualityGateUuid = "abcd"; | |||
List.of(NEW_MAINTAINABILITY_RATING, NEW_RELIABILITY_RATING, NEW_SECURITY_HOTSPOTS_REVIEWED, NEW_DUPLICATED_LINES_DENSITY) | |||
List.of(NEW_VIOLATIONS, NEW_SECURITY_HOTSPOTS_REVIEWED) | |||
.forEach(metric -> insertCondition(insertMetric(metric), qualityGateUuid, metric.getBestValue())); | |||
assertEquals(NON_COMPLIANT, underTest.checkCaycCompliant(db.getSession(), qualityGateUuid)); | |||
} | |||
@@ -92,7 +110,7 @@ public class QualityGateCaycCheckerIT { | |||
@Test | |||
public void existency_requirements_check_only_existency() { | |||
String qualityGateUuid = "abcd"; | |||
List.of(NEW_MAINTAINABILITY_RATING, NEW_RELIABILITY_RATING, NEW_SECURITY_HOTSPOTS_REVIEWED, NEW_SECURITY_RATING) | |||
List.of(NEW_VIOLATIONS, NEW_SECURITY_HOTSPOTS_REVIEWED) | |||
.forEach(metric -> insertCondition(insertMetric(metric), qualityGateUuid, metric.getBestValue())); | |||
List.of(NEW_COVERAGE, NEW_DUPLICATED_LINES_DENSITY) | |||
.forEach(metric -> insertCondition(insertMetric(metric), qualityGateUuid, metric.getWorstValue())); | |||
@@ -115,7 +133,16 @@ public class QualityGateCaycCheckerIT { | |||
.setValueType(metric.getType().name()) | |||
.setHidden(false) | |||
.setBestValue(metric.getBestValue()) | |||
.setBestValue(metric.getWorstValue()) | |||
.setWorstValue(metric.getWorstValue()) | |||
.setDirection(metric.getDirection())); | |||
} | |||
private MetricDto toMetricDto(Metric metric) { | |||
return new MetricDto() | |||
.setKey(metric.key()) | |||
.setValueType(metric.getType().name()) | |||
.setBestValue(metric.getBestValue()) | |||
.setWorstValue(metric.getWorstValue()) | |||
.setDirection(metric.getDirection()); | |||
} | |||
} |
@@ -29,6 +29,7 @@ import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.runner.RunWith; | |||
import org.sonar.api.measures.CoreMetrics; | |||
import org.sonar.api.measures.Metric; | |||
import org.sonar.api.server.ws.WebService; | |||
import org.sonar.core.util.UuidFactoryFast; | |||
@@ -64,7 +65,8 @@ public class CreateActionIT { | |||
private final DbClient dbClient = db.getDbClient(); | |||
private final DbSession dbSession = db.getSession(); | |||
private final CreateAction underTest = new CreateAction(dbClient, userSession, new QualityGateUpdater(dbClient, UuidFactoryFast.getInstance()), | |||
private final CreateAction underTest = new CreateAction(dbClient, userSession, new QualityGateUpdater(dbClient, | |||
UuidFactoryFast.getInstance()), | |||
new QualityGateConditionsUpdater(dbClient)); | |||
private final WsActionTester ws = new WsActionTester(underTest); | |||
@@ -89,7 +91,7 @@ public class CreateActionIT { | |||
var conditions = getConditions(dbSession, qualityGateDto); | |||
CAYC_METRICS.stream() | |||
.map(m -> dbClient.metricDao().selectByKey(dbSession, m.getKey())) | |||
.map(metric -> dbClient.metricDao().selectByKey(dbSession, metric.getKey())) | |||
.forEach(metricDto -> assertThat(conditions) | |||
.anyMatch(c -> metricDto.getUuid().equals(c.getMetricUuid()) && c.getErrorThreshold().equals(String.valueOf(getDefaultCaycValue(metricDto))))); | |||
} | |||
@@ -139,7 +141,7 @@ public class CreateActionIT { | |||
@DataProvider | |||
public static Object[][] nullOrEmpty() { | |||
return new Object[][] { | |||
return new Object[][]{ | |||
{null}, | |||
{""}, | |||
{" "} |
@@ -20,51 +20,46 @@ | |||
package org.sonar.server.qualitygate; | |||
import java.io.Serializable; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Optional; | |||
import java.util.Set; | |||
import java.util.function.Function; | |||
import java.util.stream.Collectors; | |||
import java.util.stream.Stream; | |||
import org.sonar.api.measures.Metric; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.metric.MetricDto; | |||
import org.sonar.db.qualitygate.QualityGateConditionDto; | |||
import static java.util.stream.Collectors.toUnmodifiableMap; | |||
import static java.util.stream.Collectors.toMap; | |||
import static org.sonar.api.measures.CoreMetrics.NEW_COVERAGE; | |||
import static org.sonar.api.measures.CoreMetrics.NEW_COVERAGE_KEY; | |||
import static org.sonar.api.measures.CoreMetrics.NEW_DUPLICATED_LINES_DENSITY; | |||
import static org.sonar.api.measures.CoreMetrics.NEW_DUPLICATED_LINES_DENSITY_KEY; | |||
import static org.sonar.api.measures.CoreMetrics.NEW_MAINTAINABILITY_RATING; | |||
import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_RATING; | |||
import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED; | |||
import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_RATING; | |||
import static org.sonar.api.measures.CoreMetrics.NEW_VIOLATIONS; | |||
import static org.sonar.server.qualitygate.QualityGateCaycStatus.COMPLIANT; | |||
import static org.sonar.server.qualitygate.QualityGateCaycStatus.NON_COMPLIANT; | |||
import static org.sonar.server.qualitygate.QualityGateCaycStatus.OVER_COMPLIANT; | |||
public class QualityGateCaycChecker { | |||
public static final List<Metric<? extends Serializable>> CAYC_METRICS = List.of( | |||
NEW_MAINTAINABILITY_RATING, | |||
NEW_RELIABILITY_RATING, | |||
static final Map<String, Double> BEST_VALUE_REQUIREMENTS = Stream.of( | |||
NEW_VIOLATIONS, | |||
NEW_SECURITY_HOTSPOTS_REVIEWED | |||
).collect(toMap(Metric::getKey, Metric::getBestValue)); | |||
static final Set<String> EXISTENCY_REQUIREMENTS = Set.of( | |||
NEW_DUPLICATED_LINES_DENSITY_KEY, | |||
NEW_COVERAGE_KEY | |||
); | |||
public static final Set<Metric<? extends Serializable>> CAYC_METRICS = Set.of( | |||
NEW_VIOLATIONS, | |||
NEW_SECURITY_HOTSPOTS_REVIEWED, | |||
NEW_SECURITY_RATING, | |||
NEW_DUPLICATED_LINES_DENSITY, | |||
NEW_COVERAGE | |||
); | |||
private static final Set<String> EXISTENCY_REQUIREMENTS = Set.of( | |||
NEW_DUPLICATED_LINES_DENSITY_KEY, | |||
NEW_COVERAGE_KEY | |||
); | |||
private static final Map<String, Double> BEST_VALUE_REQUIREMENTS = CAYC_METRICS.stream() | |||
.filter(metric -> !EXISTENCY_REQUIREMENTS.contains(metric.getKey())) | |||
.collect(toUnmodifiableMap(Metric::getKey, Metric::getBestValue)); | |||
private final DbClient dbClient; | |||
public QualityGateCaycChecker(DbClient dbClient) { | |||
@@ -105,6 +100,10 @@ public class QualityGateCaycChecker { | |||
.orElse(NON_COMPLIANT); | |||
} | |||
public boolean isCaycCondition(MetricDto metric) { | |||
return CAYC_METRICS.stream().map(Metric::getKey).anyMatch(metric.getKey()::equals); | |||
} | |||
private static boolean checkMetricCaycCompliant(QualityGateConditionDto condition, MetricDto metric) { | |||
if (EXISTENCY_REQUIREMENTS.contains(metric.getKey())) { | |||
return true; |
@@ -111,9 +111,9 @@ public class CreateAction implements QualityGatesWsAction { | |||
} | |||
private void addCaycConditions(DbSession dbSession, QualityGateDto newQualityGate) { | |||
CAYC_METRICS.forEach(m -> | |||
qualityGateConditionsUpdater.createCondition(dbSession, newQualityGate, m.getKey(), OPERATORS_BY_DIRECTION.get(m.getDirection()).getDbValue(), | |||
String.valueOf(getDefaultCaycValue(m))) | |||
CAYC_METRICS.forEach(metric -> | |||
qualityGateConditionsUpdater.createCondition(dbSession, newQualityGate, metric.getKey(), OPERATORS_BY_DIRECTION.get(metric.getDirection()).getDbValue(), | |||
String.valueOf(getDefaultCaycValue(metric))) | |||
); | |||
} | |||
@@ -105,8 +105,8 @@ public class ShowAction implements QualityGatesWsAction { | |||
return dbClient.metricDao().selectByUuids(dbSession, metricUuids).stream().filter(MetricDto::isEnabled).collect(Collectors.toMap(MetricDto::getUuid, Function.identity())); | |||
} | |||
private ShowWsResponse buildResponse(DbSession dbSession, QualityGateDto qualityGate, QualityGateDto defaultQualityGate, Collection<QualityGateConditionDto> conditions, | |||
Map<String, MetricDto> metricsByUuid, QualityGateCaycStatus caycStatus) { | |||
private ShowWsResponse buildResponse(DbSession dbSession, QualityGateDto qualityGate, QualityGateDto defaultQualityGate, | |||
Collection<QualityGateConditionDto> conditions, Map<String, MetricDto> metricsByUuid, QualityGateCaycStatus caycStatus) { | |||
return ShowWsResponse.newBuilder() | |||
.setName(qualityGate.getName()) | |||
.setIsBuiltIn(qualityGate.isBuiltIn()) | |||
@@ -118,7 +118,7 @@ public class ShowAction implements QualityGatesWsAction { | |||
.build(); | |||
} | |||
private static Function<QualityGateConditionDto, ShowWsResponse.Condition> toWsCondition(Map<String, MetricDto> metricsByUuid) { | |||
private Function<QualityGateConditionDto, ShowWsResponse.Condition> toWsCondition(Map<String, MetricDto> metricsByUuid) { | |||
return condition -> { | |||
String metricUuid = condition.getMetricUuid(); | |||
MetricDto metric = metricsByUuid.get(metricUuid); | |||
@@ -126,6 +126,7 @@ public class ShowAction implements QualityGatesWsAction { | |||
ShowWsResponse.Condition.Builder builder = ShowWsResponse.Condition.newBuilder() | |||
.setId(condition.getUuid()) | |||
.setMetric(metric.getKey()) | |||
.setIsCaycCondition(qualityGateCaycChecker.isCaycCondition(metric)) | |||
.setOp(condition.getOperator()); | |||
ofNullable(condition.getErrorThreshold()).ifPresent(builder::setError); | |||
return builder.build(); |
@@ -127,10 +127,11 @@ message ShowWsResponse { | |||
optional string caycStatus = 6; | |||
message Condition { | |||
optional string id = 1; | |||
optional string metric = 2; | |||
optional string op = 4; | |||
required string id = 1; | |||
required string metric = 2; | |||
required string op = 4; | |||
optional string error = 6; | |||
required bool isCaycCondition = 7; | |||
} | |||
} | |||