aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-server-common/src
diff options
context:
space:
mode:
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>2018-07-03 12:26:32 +0200
committersonartech <sonartech@sonarsource.com>2018-07-09 10:39:33 +0200
commit9e3bc71eb9e0ac3b384c2fbfaa8355596e5ea7f2 (patch)
tree5aa1306fd44789b6d17b3274c353420f492727d5 /server/sonar-server-common/src
parent12349c8c275dc316e09451c4f468376e26be53b3 (diff)
downloadsonarqube-9e3bc71eb9e0ac3b384c2fbfaa8355596e5ea7f2.tar.gz
sonarqube-9e3bc71eb9e0ac3b384c2fbfaa8355596e5ea7f2.zip
sonar-ce-task-projectanalysis depends on only sonar-server-common
Diffstat (limited to 'server/sonar-server-common/src')
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/measure/DebtRatingGrid.java110
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/measure/Rating.java63
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/ShortLivingBranchQualityGate.java82
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/qualityprofile/QPMeasureData.java101
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/qualityprofile/QualityProfile.java90
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/rule/CommonRuleKeys.java46
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/setting/ChildSettings.java83
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/measure/DebtRatingGridTest.java100
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/ShortLivingBranchQualityGateTest.java40
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/qualityprofile/QualityProfileTest.java80
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/rule/CommonRuleKeysTest.java39
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/setting/ChildSettingsTest.java123
12 files changed, 957 insertions, 0 deletions
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/measure/DebtRatingGrid.java b/server/sonar-server-common/src/main/java/org/sonar/server/measure/DebtRatingGrid.java
new file mode 100644
index 00000000000..faac02fc016
--- /dev/null
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/measure/DebtRatingGrid.java
@@ -0,0 +1,110 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.measure;
+
+import com.google.common.annotations.VisibleForTesting;
+import java.util.Arrays;
+import java.util.EnumMap;
+import java.util.Map;
+import org.sonar.api.config.Configuration;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.lang.String.format;
+import static org.sonar.api.CoreProperties.RATING_GRID;
+import static org.sonar.api.CoreProperties.RATING_GRID_DEF_VALUES;
+import static org.sonar.server.measure.Rating.A;
+import static org.sonar.server.measure.Rating.B;
+import static org.sonar.server.measure.Rating.C;
+import static org.sonar.server.measure.Rating.D;
+import static org.sonar.server.measure.Rating.E;
+
+public class DebtRatingGrid {
+
+ private final double[] gridValues;
+ private final EnumMap<Rating, Bounds> ratingBounds;
+
+ public DebtRatingGrid(Configuration config) {
+ try {
+ String[] grades = config.getStringArray(RATING_GRID);
+ gridValues = new double[4];
+ for (int i = 0; i < 4; i++) {
+ gridValues[i] = Double.parseDouble(grades[i]);
+ }
+ this.ratingBounds = buildRatingBounds(gridValues);
+ } catch (Exception e) {
+ throw new IllegalArgumentException("The rating grid is incorrect. Expected something similar to '"
+ + RATING_GRID_DEF_VALUES + "' and got '" + config.get(RATING_GRID).orElse("") + "'", e);
+ }
+ }
+
+ public DebtRatingGrid(double[] gridValues) {
+ this.gridValues = Arrays.copyOf(gridValues, gridValues.length);
+ this.ratingBounds = buildRatingBounds(gridValues);
+ }
+
+ private static EnumMap<Rating, Bounds> buildRatingBounds(double[] gridValues) {
+ checkState(gridValues.length == 4, "Rating grid should contains 4 values");
+ EnumMap<Rating, Bounds> ratingBounds = new EnumMap<>(Rating.class);
+ ratingBounds.put(A, new Bounds(0d, gridValues[0]));
+ ratingBounds.put(B, new Bounds(gridValues[0], gridValues[1]));
+ ratingBounds.put(C, new Bounds(gridValues[1], gridValues[2]));
+ ratingBounds.put(D, new Bounds(gridValues[2], gridValues[3]));
+ ratingBounds.put(E, new Bounds(gridValues[3], Double.MAX_VALUE));
+ return ratingBounds;
+ }
+
+ public Rating getRatingForDensity(double value) {
+ return ratingBounds.entrySet().stream()
+ .filter(e -> e.getValue().match(value))
+ .map(Map.Entry::getKey)
+ .findFirst()
+ .orElseThrow(() -> new IllegalArgumentException(format("Invalid value '%s'", value)));
+ }
+
+ public double getGradeLowerBound(Rating rating) {
+ if (rating.getIndex() > 1) {
+ return gridValues[rating.getIndex() - 2];
+ }
+ return 0;
+ }
+
+ @VisibleForTesting
+ public double[] getGridValues() {
+ return gridValues;
+ }
+
+ private static class Bounds {
+ private final double lowerBound;
+ private final double higherBound;
+ private final boolean isLowerBoundInclusive;
+
+ private Bounds(double lowerBound, double higherBound) {
+ this.lowerBound = lowerBound;
+ this.higherBound = higherBound;
+ this.isLowerBoundInclusive = lowerBound == 0;
+ }
+
+ boolean match(double value) {
+ boolean lowerBoundMatch = isLowerBoundInclusive ? (value >= lowerBound) : (value > lowerBound);
+ return lowerBoundMatch && value <= higherBound;
+ }
+ }
+
+}
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/measure/Rating.java b/server/sonar-server-common/src/main/java/org/sonar/server/measure/Rating.java
new file mode 100644
index 00000000000..873f682f998
--- /dev/null
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/measure/Rating.java
@@ -0,0 +1,63 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.measure;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+
+import static java.lang.String.format;
+import static java.util.Arrays.stream;
+import static org.sonar.api.rule.Severity.BLOCKER;
+import static org.sonar.api.rule.Severity.CRITICAL;
+import static org.sonar.api.rule.Severity.INFO;
+import static org.sonar.api.rule.Severity.MAJOR;
+import static org.sonar.api.rule.Severity.MINOR;
+
+public enum Rating {
+ E(5),
+ D(4),
+ C(3),
+ B(2),
+ A(1);
+
+ private final int index;
+
+ Rating(int index) {
+ this.index = index;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ public static Rating valueOf(int index) {
+ return stream(Rating.values())
+ .filter(r -> r.getIndex() == index)
+ .findFirst()
+ .orElseThrow(() -> new IllegalArgumentException(format("Unknown value '%s'", index)));
+ }
+
+ public static final Map<String, Rating> RATING_BY_SEVERITY = ImmutableMap.of(
+ BLOCKER, E,
+ CRITICAL, D,
+ MAJOR, C,
+ MINOR, B,
+ INFO, A);
+}
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/ShortLivingBranchQualityGate.java b/server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/ShortLivingBranchQualityGate.java
new file mode 100644
index 00000000000..f77b6e65ee8
--- /dev/null
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/ShortLivingBranchQualityGate.java
@@ -0,0 +1,82 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 javax.annotation.CheckForNull;
+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", false),
+ new Condition(CoreMetrics.REOPENED_ISSUES_KEY, OPERATOR_GREATER_THAN, "0", false));
+
+ 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", null, false),
+ new org.sonar.server.qualitygate.Condition(CoreMetrics.REOPENED_ISSUES_KEY, org.sonar.server.qualitygate.Condition.Operator.GREATER_THAN, "0", null, false)));
+
+ private ShortLivingBranchQualityGate() {
+ // prevents instantiation
+ }
+
+ public static final class Condition {
+ private final String metricKey;
+ private final String operator;
+ private final String errorThreshold;
+ private final boolean onLeak;
+
+ public Condition(String metricKey, String operator, String errorThreshold, boolean onLeak) {
+ this.metricKey = metricKey;
+ this.operator = operator;
+ this.errorThreshold = errorThreshold;
+ this.onLeak = onLeak;
+ }
+
+ public String getMetricKey() {
+ return metricKey;
+ }
+
+ public String getOperator() {
+ return operator;
+ }
+
+ public String getErrorThreshold() {
+ return errorThreshold;
+ }
+
+ @CheckForNull
+ public String getWarnThreshold() {
+ return null;
+ }
+
+ public boolean isOnLeak() {
+ return onLeak;
+ }
+ }
+}
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/qualityprofile/QPMeasureData.java b/server/sonar-server-common/src/main/java/org/sonar/server/qualityprofile/QPMeasureData.java
new file mode 100644
index 00000000000..8c4a40a68b3
--- /dev/null
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/qualityprofile/QPMeasureData.java
@@ -0,0 +1,101 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.qualityprofile;
+
+import com.google.common.collect.ImmutableSortedSet;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import java.io.StringWriter;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+import javax.annotation.concurrent.Immutable;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.core.util.UtcDateUtils;
+
+import static java.util.function.Function.identity;
+import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
+
+/**
+ * Represents the array of JSON objects stored in the value of the
+ * {@link org.sonar.api.measures.CoreMetrics#QUALITY_PROFILES} measures.
+ */
+@Immutable
+public class QPMeasureData {
+
+ private final SortedSet<QualityProfile> profiles;
+
+ public QPMeasureData(Iterable<QualityProfile> qualityProfiles) {
+ this.profiles = ImmutableSortedSet.copyOf(QualityProfileComparator.INSTANCE, qualityProfiles);
+ }
+
+ public static QPMeasureData fromJson(String json) {
+ return new QPMeasureData(StreamSupport.stream(new JsonParser().parse(json).getAsJsonArray().spliterator(), false)
+ .map(jsonElement -> {
+ JsonObject jsonProfile = jsonElement.getAsJsonObject();
+ return new QualityProfile(
+ jsonProfile.get("key").getAsString(),
+ jsonProfile.get("name").getAsString(),
+ jsonProfile.get("language").getAsString(),
+ UtcDateUtils.parseDateTime(jsonProfile.get("rulesUpdatedAt").getAsString()));
+ }).collect(Collectors.toList()));
+ }
+
+ public static String toJson(QPMeasureData data) {
+ StringWriter json = new StringWriter();
+ try (JsonWriter writer = JsonWriter.of(json)) {
+ writer.beginArray();
+ for (QualityProfile profile : data.getProfiles()) {
+ writer
+ .beginObject()
+ .prop("key", profile.getQpKey())
+ .prop("language", profile.getLanguageKey())
+ .prop("name", profile.getQpName())
+ .prop("rulesUpdatedAt", UtcDateUtils.formatDateTime(profile.getRulesUpdatedAt()))
+ .endObject();
+ }
+ writer.endArray();
+ }
+ return json.toString();
+ }
+
+ public SortedSet<QualityProfile> getProfiles() {
+ return profiles;
+ }
+
+ public Map<String, QualityProfile> getProfilesByKey() {
+ return profiles.stream().collect(uniqueIndex(QualityProfile::getQpKey, identity()));
+ }
+
+ private enum QualityProfileComparator implements Comparator<QualityProfile> {
+ INSTANCE;
+
+ @Override
+ public int compare(QualityProfile o1, QualityProfile o2) {
+ int c = o1.getLanguageKey().compareTo(o2.getLanguageKey());
+ if (c == 0) {
+ c = o1.getQpName().compareTo(o2.getQpName());
+ }
+ return c;
+ }
+ }
+}
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/qualityprofile/QualityProfile.java b/server/sonar-server-common/src/main/java/org/sonar/server/qualityprofile/QualityProfile.java
new file mode 100644
index 00000000000..024319ad7f5
--- /dev/null
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/qualityprofile/QualityProfile.java
@@ -0,0 +1,90 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.qualityprofile;
+
+import com.google.common.base.MoreObjects;
+import java.util.Date;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Represents the JSON object an array of which is stored in the value of the
+ * {@link org.sonar.api.measures.CoreMetrics#QUALITY_PROFILES} measures.
+ */
+@Immutable
+public class QualityProfile {
+ private final String qpKey;
+ private final String qpName;
+ private final String languageKey;
+ private final Date rulesUpdatedAt;
+
+ public QualityProfile(String qpKey, String qpName, String languageKey, Date rulesUpdatedAt) {
+ this.qpKey = requireNonNull(qpKey);
+ this.qpName = requireNonNull(qpName);
+ this.languageKey = requireNonNull(languageKey);
+ this.rulesUpdatedAt = requireNonNull(rulesUpdatedAt);
+ }
+
+ public String getQpKey() {
+ return qpKey;
+ }
+
+ public String getQpName() {
+ return qpName;
+ }
+
+ public String getLanguageKey() {
+ return languageKey;
+ }
+
+ public Date getRulesUpdatedAt() {
+ return new Date(rulesUpdatedAt.getTime());
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ QualityProfile qProfile = (QualityProfile) o;
+ return qpKey.equals(qProfile.qpKey);
+ }
+
+ @Override
+ public int hashCode() {
+ return qpKey.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("key", qpKey)
+ .add("name", qpName)
+ .add("language", languageKey)
+ .add("rulesUpdatedAt", rulesUpdatedAt.getTime())
+ .toString();
+ }
+}
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/rule/CommonRuleKeys.java b/server/sonar-server-common/src/main/java/org/sonar/server/rule/CommonRuleKeys.java
new file mode 100644
index 00000000000..2c3a7aa2bc0
--- /dev/null
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/rule/CommonRuleKeys.java
@@ -0,0 +1,46 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.rule;
+
+public class CommonRuleKeys {
+
+ public static final String REPOSITORY_PREFIX = "common-";
+
+ public static final String INSUFFICIENT_BRANCH_COVERAGE = "InsufficientBranchCoverage";
+ public static final String INSUFFICIENT_BRANCH_COVERAGE_PROPERTY = "minimumBranchCoverageRatio";
+
+ public static final String INSUFFICIENT_LINE_COVERAGE = "InsufficientLineCoverage";
+ public static final String INSUFFICIENT_LINE_COVERAGE_PROPERTY = "minimumLineCoverageRatio";
+
+ public static final String INSUFFICIENT_COMMENT_DENSITY = "InsufficientCommentDensity";
+ public static final String INSUFFICIENT_COMMENT_DENSITY_PROPERTY = "minimumCommentDensity";
+
+ public static final String DUPLICATED_BLOCKS = "DuplicatedBlocks";
+ public static final String FAILED_UNIT_TESTS = "FailedUnitTests";
+ public static final String SKIPPED_UNIT_TESTS = "SkippedUnitTests";
+
+ private CommonRuleKeys() {
+ // only static methods
+ }
+
+ public static String commonRepositoryForLang(String language) {
+ return REPOSITORY_PREFIX + language;
+ }
+}
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/setting/ChildSettings.java b/server/sonar-server-common/src/main/java/org/sonar/server/setting/ChildSettings.java
new file mode 100644
index 00000000000..4961afd3df1
--- /dev/null
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/setting/ChildSettings.java
@@ -0,0 +1,83 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.setting;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import org.sonar.api.config.Configuration;
+import org.sonar.api.config.Settings;
+import org.sonar.api.config.internal.ConfigurationBridge;
+
+import static java.util.Objects.requireNonNull;
+
+public class ChildSettings extends Settings {
+
+ private final Settings parentSettings;
+ private final Map<String, String> localProperties = new HashMap<>();
+
+ public ChildSettings(Settings parentSettings) {
+ super(parentSettings.getDefinitions(), parentSettings.getEncryption());
+ this.parentSettings = parentSettings;
+ }
+
+ @Override
+ protected Optional<String> get(String key) {
+ String value = localProperties.get(key);
+ if (value != null) {
+ return Optional.of(value);
+ }
+ return parentSettings.getRawString(key);
+ }
+
+ @Override
+ protected void set(String key, String value) {
+ localProperties.put(
+ requireNonNull(key, "key can't be null"),
+ requireNonNull(value, "value can't be null").trim());
+ }
+
+ @Override
+ protected void remove(String key) {
+ localProperties.remove(key);
+ }
+
+ /**
+ * Only returns the currently loaded properties.
+ *
+ * <p>
+ * On the Web Server, global properties are loaded lazily when requested by name. Therefor,
+ * this will return only global properties which have been requested using
+ * {@link #get(String)} at least once prior to this call.
+ */
+ @Override
+ public Map<String, String> getProperties() {
+ // order is important. local properties override parent properties.
+ ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
+ builder.putAll(parentSettings.getProperties());
+ builder.putAll(localProperties);
+ return builder.build();
+ }
+
+ public Configuration asConfiguration() {
+ return new ConfigurationBridge(this);
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/measure/DebtRatingGridTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/measure/DebtRatingGridTest.java
new file mode 100644
index 00000000000..0208ae16f02
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/measure/DebtRatingGridTest.java
@@ -0,0 +1,100 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.measure;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.server.measure.Rating.A;
+import static org.sonar.server.measure.Rating.B;
+import static org.sonar.server.measure.Rating.C;
+import static org.sonar.server.measure.Rating.D;
+import static org.sonar.server.measure.Rating.E;
+
+public class DebtRatingGridTest {
+
+ private DebtRatingGrid ratingGrid;
+
+ @Rule
+ public ExpectedException throwable = ExpectedException.none();
+
+ @Before
+ public void setUp() {
+ double[] gridValues = new double[] {0.1, 0.2, 0.5, 1};
+ ratingGrid = new DebtRatingGrid(gridValues);
+ }
+
+ @Test
+ public void return_rating_matching_density() {
+ assertThat(ratingGrid.getRatingForDensity(0)).isEqualTo(A);
+ assertThat(ratingGrid.getRatingForDensity(0.05)).isEqualTo(A);
+ assertThat(ratingGrid.getRatingForDensity(0.09999999)).isEqualTo(A);
+ assertThat(ratingGrid.getRatingForDensity(0.1)).isEqualTo(A);
+ assertThat(ratingGrid.getRatingForDensity(0.15)).isEqualTo(B);
+ assertThat(ratingGrid.getRatingForDensity(0.2)).isEqualTo(B);
+ assertThat(ratingGrid.getRatingForDensity(0.25)).isEqualTo(C);
+ assertThat(ratingGrid.getRatingForDensity(0.5)).isEqualTo(C);
+ assertThat(ratingGrid.getRatingForDensity(0.65)).isEqualTo(D);
+ assertThat(ratingGrid.getRatingForDensity(1)).isEqualTo(D);
+ assertThat(ratingGrid.getRatingForDensity(1.01)).isEqualTo(E);
+ }
+
+ @Test
+ public void density_matching_exact_grid_values() {
+ assertThat(ratingGrid.getRatingForDensity(0.1)).isEqualTo(A);
+ assertThat(ratingGrid.getRatingForDensity(0.2)).isEqualTo(B);
+ assertThat(ratingGrid.getRatingForDensity(0.5)).isEqualTo(C);
+ assertThat(ratingGrid.getRatingForDensity(1)).isEqualTo(D);
+ }
+
+ @Test
+ public void convert_int_to_rating() {
+ assertThat(Rating.valueOf(1)).isEqualTo(A);
+ assertThat(Rating.valueOf(2)).isEqualTo(B);
+ assertThat(Rating.valueOf(3)).isEqualTo(C);
+ assertThat(Rating.valueOf(4)).isEqualTo(D);
+ assertThat(Rating.valueOf(5)).isEqualTo(E);
+ }
+
+ @Test
+ public void fail_on_invalid_density() {
+ throwable.expect(IllegalArgumentException.class);
+ throwable.expectMessage("Invalid value '-1.0'");
+
+ ratingGrid.getRatingForDensity(-1);
+ }
+
+ @Test
+ public void fail_to_concert_invalid_value() {
+ throwable.expect(IllegalArgumentException.class);
+ Rating.valueOf(10);
+ }
+
+ @Test
+ public void fail_on_invalid_grid() {
+ throwable.expect(IllegalStateException.class);
+ throwable.expectMessage("Rating grid should contains 4 values");
+
+ ratingGrid = new DebtRatingGrid(new double[] {0.1, 0.2, 0.5});
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/ShortLivingBranchQualityGateTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/ShortLivingBranchQualityGateTest.java
new file mode 100644
index 00000000000..3f4ad722b67
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/ShortLivingBranchQualityGateTest.java
@@ -0,0 +1,40 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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, Condition::getWarnThreshold, Condition::isOnLeak)
+ .containsExactly(
+ tuple(CoreMetrics.OPEN_ISSUES_KEY, "GT", "0", null, false),
+ tuple(CoreMetrics.REOPENED_ISSUES_KEY, "GT", "0", null, false));
+ }
+
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/qualityprofile/QualityProfileTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/qualityprofile/QualityProfileTest.java
new file mode 100644
index 00000000000..9642502884b
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/qualityprofile/QualityProfileTest.java
@@ -0,0 +1,80 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.qualityprofile;
+
+import java.util.Date;
+import org.junit.Test;
+import org.sonar.api.utils.DateUtils;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class QualityProfileTest {
+
+ private static final String SOME_QP_KEY = "qpKey";
+ private static final String SOME_QP_NAME = "qpName";
+ private static final String SOME_LANGUAGE_KEY = "languageKey";
+ private static final Date SOME_DATE = DateUtils.parseDateTimeQuietly("2010-05-18T15:50:45+0100");
+ private static final QualityProfile QUALITY_PROFILE = new QualityProfile(SOME_QP_KEY, SOME_QP_NAME, SOME_LANGUAGE_KEY, SOME_DATE);
+
+ @Test(expected = NullPointerException.class)
+ public void constructor_throws_NPE_if_qkKey_arg_is_null() {
+ new QualityProfile(null, SOME_QP_NAME, SOME_LANGUAGE_KEY, SOME_DATE);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void constructor_throws_NPE_if_qpName_arg_is_null() {
+ new QualityProfile(SOME_QP_KEY, null, SOME_LANGUAGE_KEY, SOME_DATE);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void constructor_throws_NPE_if_languageKey_arg_is_null() {
+ new QualityProfile(SOME_QP_KEY, SOME_QP_NAME, null, SOME_DATE);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void constructor_throws_NPE_if_rulesUpdatedAt_arg_is_null() {
+ new QualityProfile(SOME_QP_KEY, SOME_QP_NAME, SOME_LANGUAGE_KEY, null);
+ }
+
+ @Test
+ public void verify_properties() {
+ assertThat(QUALITY_PROFILE.getQpKey()).isEqualTo(SOME_QP_KEY);
+ assertThat(QUALITY_PROFILE.getQpName()).isEqualTo(SOME_QP_NAME);
+ assertThat(QUALITY_PROFILE.getLanguageKey()).isEqualTo(SOME_LANGUAGE_KEY);
+ assertThat(QUALITY_PROFILE.getRulesUpdatedAt()).isEqualTo(SOME_DATE);
+ }
+
+ @Test
+ public void verify_getRulesUpdatedAt_keeps_object_immutable() {
+ assertThat(QUALITY_PROFILE.getRulesUpdatedAt()).isNotSameAs(SOME_DATE);
+ }
+
+ @Test
+ public void verify_equals() {
+ assertThat(QUALITY_PROFILE).isEqualTo(new QualityProfile(SOME_QP_KEY, SOME_QP_NAME, SOME_LANGUAGE_KEY, SOME_DATE));
+ assertThat(QUALITY_PROFILE).isEqualTo(QUALITY_PROFILE);
+ assertThat(QUALITY_PROFILE).isNotEqualTo(null);
+ }
+
+ @Test
+ public void verify_toString() {
+ assertThat(QUALITY_PROFILE.toString()).isEqualTo("QualityProfile{key=qpKey, name=qpName, language=languageKey, rulesUpdatedAt=1274194245000}");
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/rule/CommonRuleKeysTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/rule/CommonRuleKeysTest.java
new file mode 100644
index 00000000000..45ab75f9e49
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/rule/CommonRuleKeysTest.java
@@ -0,0 +1,39 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.rule;
+
+import org.junit.Test;
+import org.sonar.test.TestUtils;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CommonRuleKeysTest {
+
+ @Test
+ public void wonderful_test_for_commonRepositoryForLang() {
+ assertThat(CommonRuleKeys.commonRepositoryForLang("java")).isEqualTo("common-java");
+ }
+
+ @Test
+ public void wonderful_test_to_verify_that_this_class_is_an_helper_class() {
+ assertThat(TestUtils.hasOnlyPrivateConstructors(CommonRuleKeys.class)).isTrue();
+
+ }
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/setting/ChildSettingsTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/setting/ChildSettingsTest.java
new file mode 100644
index 00000000000..6400e8cae09
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/setting/ChildSettingsTest.java
@@ -0,0 +1,123 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.setting;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.Optional;
+import java.util.Random;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.config.PropertyDefinition;
+import org.sonar.api.config.PropertyDefinitions;
+import org.sonar.api.config.internal.MapSettings;
+
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ChildSettingsTest {
+ private static final Random RANDOM = new Random();
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private MapSettings parent = new MapSettings();
+ private ChildSettings underTest = new ChildSettings(parent);
+
+ @Test
+ public void childSettings_should_retrieve_parent_settings() {
+ String multipleValuesKey = randomAlphanumeric(19);
+ PropertyDefinition multipleValues = PropertyDefinition.builder(multipleValuesKey).multiValues(true).build();
+ MapSettings parent = new MapSettings(new PropertyDefinitions(Collections.singletonList(multipleValues)));
+ ChildSettings underTest = new ChildSettings(parent);
+
+ parent.setProperty(randomAlphanumeric(10), randomAlphanumeric(20));
+ parent.setProperty(randomAlphanumeric(11), RANDOM.nextLong());
+ parent.setProperty(randomAlphanumeric(12), RANDOM.nextDouble());
+ parent.setProperty(randomAlphanumeric(13), RANDOM.nextFloat());
+ parent.setProperty(randomAlphanumeric(14), RANDOM.nextBoolean());
+ parent.setProperty(randomAlphanumeric(15), RANDOM.nextInt());
+ parent.setProperty(randomAlphanumeric(16), new Date(RANDOM.nextInt()));
+ parent.setProperty(randomAlphanumeric(17), new Date(RANDOM.nextInt()), true);
+ parent.setProperty(randomAlphanumeric(18), new Date(RANDOM.nextInt()), false);
+ parent.setProperty(multipleValuesKey, new String[] { randomAlphanumeric(10), randomAlphanumeric(20) });
+
+ assertThat(underTest.getProperties()).isEqualTo(parent.getProperties());
+ }
+
+ @Test
+ public void set_will_throw_NPE_if_key_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("key can't be null");
+
+ underTest.set(null, "");
+ }
+
+
+ @Test
+ public void set_will_throw_NPE_if_value_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("value can't be null");
+
+ underTest.set(randomAlphanumeric(10), null);
+ }
+
+ @Test
+ public void childSettings_override_parent() {
+ String key = randomAlphanumeric(10);
+ parent.setProperty(key, randomAlphanumeric(20));
+ underTest.setProperty(key, randomAlphanumeric(10));
+
+ assertThat(underTest.get(key)).isNotEqualTo(parent.getString(key));
+ }
+
+ @Test
+ public void remove_should_not_throw_exception_if_key_is_not_present() {
+ underTest.remove(randomAlphanumeric(90));
+ }
+
+ @Test
+ public void remove_should_remove_value() {
+ String key = randomAlphanumeric(10);
+ String childValue = randomAlphanumeric(10);
+
+ underTest.set(key, childValue);
+ assertThat(underTest.get(key)).isEqualTo(Optional.of(childValue));
+
+ underTest.remove(key);
+ assertThat(underTest.get(key)).isEqualTo(Optional.empty());
+ }
+
+ @Test
+ public void remove_should_retrieve_parent_value() {
+ String key = randomAlphanumeric(10);
+ String childValue = randomAlphanumeric(10);
+ String parentValue = randomAlphanumeric(10);
+
+ parent.setProperty(key, parentValue);
+ underTest.set(key, childValue);
+ assertThat(underTest.get(key)).isEqualTo(Optional.of(childValue));
+
+ underTest.remove(key);
+ assertThat(underTest.get(key)).isEqualTo(Optional.of(parentValue));
+ }
+
+}