/* * SonarQube * Copyright (C) 2009-2017 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.platform.db.migration.version.v62; import com.google.common.collect.ImmutableList; 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.sql.SQLException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import javax.annotation.CheckForNull; import javax.annotation.Nullable; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.sonar.db.CoreDbTester; import static java.lang.String.format; import static org.assertj.core.api.Assertions.assertThat; @RunWith(DataProviderRunner.class) public class UpdateQualityGateConditionsOnCoverageTest { private static final String TABLE_QUALITY_GATES = "quality_gates"; private static final String TABLE_QUALITY_GATE_CONDITIONS = "quality_gate_conditions"; @Rule public CoreDbTester dbTester = CoreDbTester.createForSchema(UpdateQualityGateConditionsOnCoverageTest.class, "schema.sql"); @Rule public ExpectedException expectedException = ExpectedException.none(); private UpdateQualityGateConditionsOnCoverage underTest = new UpdateQualityGateConditionsOnCoverage(dbTester.database()); @Test public void move_overall_coverage_condition_to_coverage() throws SQLException { Map<String, Long> metricIdsByMetricKeys = insertSampleMetrics(); long qualityGateId = insertQualityGate("default"); insertQualityGateCondition(qualityGateId, metricIdsByMetricKeys.get("overall_coverage"), null, "GT", "10", null); underTest.execute(); assertThat(dbTester.countRowsOfTable(TABLE_QUALITY_GATE_CONDITIONS)).isEqualTo(1); verifyConditions(qualityGateId, new QualityGateCondition("coverage", null, "GT", "10", null)); } @Test public void move_overall_coverage_condition_to_coverage_when_overall_coverage_exists_condition_on_overall_coverage_exists() throws SQLException { Map<String, Long> metricIdsByMetricKeys = insertSampleMetrics(); long qualityGateId = insertQualityGate("default"); insertQualityGateCondition(qualityGateId, metricIdsByMetricKeys.get("overall_coverage"), null, "GT", "10", null); insertQualityGateCondition(qualityGateId, metricIdsByMetricKeys.get("coverage"), null, "LT", null, "20"); underTest.execute(); assertThat(dbTester.countRowsOfTable(TABLE_QUALITY_GATE_CONDITIONS)).isEqualTo(1); verifyConditions(qualityGateId, new QualityGateCondition("coverage", null, "GT", "10", null)); } @Test public void remove_it_coverage_condition_when_overall_coverage_condition_exists_and_no_coverage_condition() throws Exception { Map<String, Long> metricIdsByMetricKeys = insertSampleMetrics(); long qualityGateId = insertQualityGate("default"); insertQualityGateCondition(qualityGateId, metricIdsByMetricKeys.get("overall_coverage"), null, "GT", "10", null); insertQualityGateCondition(qualityGateId, metricIdsByMetricKeys.get("it_coverage"), null, "LT", null, "20"); underTest.execute(); assertThat(dbTester.countRowsOfTable(TABLE_QUALITY_GATE_CONDITIONS)).isEqualTo(1); verifyConditions(qualityGateId, new QualityGateCondition("coverage", null, "GT", "10", null)); } @Test public void keep_coverage_condition_when_no_overall_and_it_coverage() throws SQLException { Map<String, Long> metricIdsByMetricKeys = insertSampleMetrics(); long qualityGateId = insertQualityGate("default"); insertQualityGateCondition(qualityGateId, metricIdsByMetricKeys.get("coverage"), null, "GT", "10", null); underTest.execute(); assertThat(dbTester.countRowsOfTable(TABLE_QUALITY_GATE_CONDITIONS)).isEqualTo(1); verifyConditions(qualityGateId, new QualityGateCondition("coverage", null, "GT", "10", null)); } @Test public void remove_it_coverage_condition_when_coverage_condition_exists_and_no_overall_coverage_condition() throws SQLException { Map<String, Long> metricIdsByMetricKeys = insertSampleMetrics(); long qualityGateId = insertQualityGate("default"); insertQualityGateCondition(qualityGateId, metricIdsByMetricKeys.get("coverage"), null, "GT", "10", null); insertQualityGateCondition(qualityGateId, metricIdsByMetricKeys.get("it_coverage"), null, "LT", null, "20"); underTest.execute(); assertThat(dbTester.countRowsOfTable(TABLE_QUALITY_GATE_CONDITIONS)).isEqualTo(1); verifyConditions(qualityGateId, new QualityGateCondition("coverage", null, "GT", "10", null)); } @Test public void move_it_coverage_condition_to_coverage_when_only_it_coverage_condition() throws SQLException { Map<String, Long> metricIdsByMetricKeys = insertSampleMetrics(); long qualityGateId = insertQualityGate("default"); insertQualityGateCondition(qualityGateId, metricIdsByMetricKeys.get("it_coverage"), null, "GT", "10", null); underTest.execute(); assertThat(dbTester.countRowsOfTable(TABLE_QUALITY_GATE_CONDITIONS)).isEqualTo(1); verifyConditions(qualityGateId, new QualityGateCondition("coverage", null, "GT", "10", null)); } @Test public void move_new_coverage_conditions() throws SQLException { Map<String, Long> metricIdsByMetricKeys = insertMetrics("new_coverage", "new_overall_coverage", "new_it_coverage"); long qualityGate1 = insertQualityGate("qualityGate1"); insertQualityGateCondition(qualityGate1, metricIdsByMetricKeys.get("new_coverage"), 1L, "GT", "10", null); insertQualityGateCondition(qualityGate1, metricIdsByMetricKeys.get("new_overall_coverage"), 1L, "GT", "7", "15"); insertQualityGateCondition(qualityGate1, metricIdsByMetricKeys.get("new_it_coverage"), 2L, "LT", "8", null); long qualityGate2 = insertQualityGate("qualityGate2"); insertQualityGateCondition(qualityGate2, metricIdsByMetricKeys.get("new_overall_coverage"), 2L, "GT", "15", null); insertQualityGateCondition(qualityGate2, metricIdsByMetricKeys.get("new_it_coverage"), 2L, "GT", null, "5"); long qualityGate3 = insertQualityGate("qualityGate3"); insertQualityGateCondition(qualityGate3, metricIdsByMetricKeys.get("new_it_coverage"), 3L, "GT", null, "5"); underTest.execute(); assertThat(dbTester.countRowsOfTable(TABLE_QUALITY_GATE_CONDITIONS)).isEqualTo(3); verifyConditions(qualityGate1, new QualityGateCondition("new_coverage", 1L, "GT", "7", "15")); verifyConditions(qualityGate2, new QualityGateCondition("new_coverage", 2L, "GT", "15", null)); verifyConditions(qualityGate3, new QualityGateCondition("new_coverage", 3L, "GT", null, "5")); } @DataProvider public static Object[][] metricKeys() { List<String> metrics = ImmutableList.of( "coverage", "lines_to_cover", "uncovered_lines", "line_coverage", "conditions_to_cover", "uncovered_conditions", "branch_coverage"); Object[][] res = new Object[metrics.size()][3]; int i = 0; for (String metricKey : metrics) { res[i][0] = metricKey; res[i][1] = "overall_" + metricKey; res[i][2] = "it_" + metricKey; i++; } return res; } @Test @UseDataProvider("metricKeys") public void move_conditions_from_many_quality_gates_on_all_metrics(String coverageKey, String overallCoverageKey, String itCoverageKey) throws SQLException { Map<String, Long> metricIdsByMetricKeys = insertMetrics(coverageKey, overallCoverageKey, itCoverageKey, "other"); long qualityGate1 = insertQualityGate("qualityGate1"); insertQualityGateCondition(qualityGate1, metricIdsByMetricKeys.get(coverageKey), null, "GT", "10", null); insertQualityGateCondition(qualityGate1, metricIdsByMetricKeys.get(overallCoverageKey), null, "GT", "7", "15"); insertQualityGateCondition(qualityGate1, metricIdsByMetricKeys.get(itCoverageKey), null, "LT", "8", null); long qualityGate2 = insertQualityGate("qualityGate2"); insertQualityGateCondition(qualityGate2, metricIdsByMetricKeys.get(overallCoverageKey), null, "GT", "15", null); insertQualityGateCondition(qualityGate2, metricIdsByMetricKeys.get(itCoverageKey), null, "GT", null, "5"); long qualityGate3 = insertQualityGate("qualityGate3"); insertQualityGateCondition(qualityGate3, metricIdsByMetricKeys.get(itCoverageKey), null, "GT", null, "5"); insertQualityGateCondition(qualityGate3, metricIdsByMetricKeys.get("other"), null, "GT", "11", null); underTest.execute(); assertThat(dbTester.countRowsOfTable(TABLE_QUALITY_GATE_CONDITIONS)).isEqualTo(4); verifyConditions(qualityGate1, new QualityGateCondition(coverageKey, null, "GT", "7", "15")); verifyConditions(qualityGate2, new QualityGateCondition(coverageKey, null, "GT", "15", null)); verifyConditions(qualityGate3, new QualityGateCondition(coverageKey, null, "GT", null, "5"), new QualityGateCondition("other", null, "GT", "11", null)); } @Test public void does_not_update_conditions_on_none_related_coverage_metrics() throws Exception { insertMetrics(); long metric1 = insertMetric("metric1"); long metric2 = insertMetric("metric2"); long qualityGate1 = insertQualityGate("qualityGate1"); insertQualityGateCondition(qualityGate1, metric1, null, "GT", "10", null); long qualityGate2 = insertQualityGate("qualityGate2"); insertQualityGateCondition(qualityGate2, metric2, null, "LT", null, "20"); underTest.execute(); assertThat(dbTester.countRowsOfTable(TABLE_QUALITY_GATE_CONDITIONS)).isEqualTo(2); verifyConditions(qualityGate1, new QualityGateCondition("metric1", null, "GT", "10", null)); verifyConditions(qualityGate2, new QualityGateCondition("metric2", null, "LT", null, "20")); } @Test public void move_conditions_linked_to_same_metric() throws Exception { insertMetric("coverage"); long metricId = insertMetric("overall_coverage"); long qualityGate = insertQualityGate("qualityGate"); insertQualityGateCondition(qualityGate, metricId, null, "GT", "7", "15"); insertQualityGateCondition(qualityGate, metricId, 1L, "GT", "10", null); underTest.execute(); assertThat(dbTester.countRowsOfTable(TABLE_QUALITY_GATE_CONDITIONS)).isEqualTo(2); verifyConditions(qualityGate, new QualityGateCondition("coverage", null, "GT", "7", "15"), new QualityGateCondition("coverage", 1L, "GT", "10", null)); } @Test public void does_nothing_when_no_quality_gates() throws Exception { insertMetrics("coverage", "new_coverage", "overall_coverage"); underTest.execute(); assertThat(dbTester.countRowsOfTable(TABLE_QUALITY_GATE_CONDITIONS)).isZero(); } @Test public void does_nothing_when_no_metrics() throws Exception { underTest.execute(); assertThat(dbTester.countRowsOfTable(TABLE_QUALITY_GATE_CONDITIONS)).isZero(); } private Map<String, Long> insertSampleMetrics() { return insertMetrics("coverage", "overall_coverage", "it_coverage"); } private Map<String, Long> insertMetrics(String... metrics) { Map<String, Long> metricIdsByMetricKeys = new HashMap<>(); for (String metricKey : metrics) { metricIdsByMetricKeys.put(metricKey, insertMetric(metricKey)); } return metricIdsByMetricKeys; } private long insertMetric(String key) { dbTester.executeInsert("metrics", "NAME", key); return (Long) dbTester.selectFirst(format("select id as \"id\" from metrics where name='%s'", key)).get("id"); } private long insertQualityGate(String qualityGate) { dbTester.executeInsert(TABLE_QUALITY_GATES, "NAME", qualityGate); return (Long) dbTester.selectFirst(format("select id as \"id\" from %s where name='%s'", TABLE_QUALITY_GATES, qualityGate)).get("id"); } private long insertQualityGateCondition(long qualityGateId, long metricId, @Nullable Long period, String operator, @Nullable String error, @Nullable String warning) { Map<String, Object> values = new HashMap<>(ImmutableMap.of("QGATE_ID", qualityGateId, "METRIC_ID", metricId, "OPERATOR", operator)); if (period != null) { values.put("PERIOD", period); } if (error != null) { values.put("VALUE_ERROR", error); } if (warning != null) { values.put("VALUE_WARNING", warning); } dbTester.executeInsert(TABLE_QUALITY_GATE_CONDITIONS, values); String sql = format("select id as \"id\" from %s where qgate_id='%s' and metric_id='%s'", TABLE_QUALITY_GATE_CONDITIONS, qualityGateId, metricId); sql += period == null ? "" : format(" and period='%s'", period); return (Long) dbTester .selectFirst(sql) .get("id"); } private void verifyConditions(long qualityGateId, QualityGateCondition... expectedConditions) { List<Map<String, Object>> results = dbTester.select( format("select m.name as \"metricKey\", qgc.period as \"period\", qgc.operator as \"operator\", qgc.value_error as \"error\", qgc.value_warning as \"warning\" from %s qgc " + "inner join metrics m on m.id=qgc.metric_id " + "where qgc.qgate_id = '%s'", TABLE_QUALITY_GATE_CONDITIONS, qualityGateId)); List<QualityGateCondition> conditions = results.stream().map(QualityGateCondition::new).collect(Collectors.toList()); assertThat(conditions).containsOnly(expectedConditions); } private static class QualityGateCondition { String metricKey; Long period; String operator; String valueError; String valueWarning; public QualityGateCondition(String metricKey, @Nullable Long period, String operator, @Nullable String valueError, @Nullable String valueWarning) { this.metricKey = metricKey; this.period = period; this.operator = operator; this.valueError = valueError; this.valueWarning = valueWarning; } QualityGateCondition(Map<String, Object> map) { this.metricKey = (String) map.get("metricKey"); this.period = (Long) map.get("period"); this.operator = (String) map.get("operator"); this.valueError = (String) map.get("error"); this.valueWarning = (String) map.get("warning"); } public String getMetricKey() { return metricKey; } @CheckForNull public Long getPeriod() { return period; } public String getOperator() { return operator; } @CheckForNull public String getValueError() { return valueError; } @CheckForNull public String getValueWarning() { return valueWarning; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } QualityGateCondition that = (QualityGateCondition) o; return new EqualsBuilder() .append(metricKey, that.getMetricKey()) .append(period, that.getPeriod()) .append(operator, that.getOperator()) .append(valueError, that.getValueError()) .append(valueWarning, that.getValueWarning()) .isEquals(); } @Override public int hashCode() { return new HashCodeBuilder(15, 31) .append(metricKey) .append(period) .append(operator) .append(valueError) .append(valueWarning) .toHashCode(); } @Override public String toString() { return "QualityGateCondition{" + "metricKey='" + metricKey + '\'' + ", period=" + period + ", operator=" + operator + ", valueError='" + valueError + '\'' + ", valueWarning='" + valueWarning + '\'' + '}'; } } }