diff options
author | Dejan Milisavljevic <130993898+dejan-milisavljevic-sonarsource@users.noreply.github.com> | 2024-08-14 12:03:22 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2024-08-26 20:03:06 +0000 |
commit | fc0739fd4eaf7a467de77773be76ddbb5f8040ef (patch) | |
tree | 466d0bc4725038ae206318d8e89ff4fb4bcb8b2d /server | |
parent | 9324868e5ad6264744afd9f5a5ea7555ab2de65b (diff) | |
download | sonarqube-fc0739fd4eaf7a467de77773be76ddbb5f8040ef.tar.gz sonarqube-fc0739fd4eaf7a467de77773be76ddbb5f8040ef.zip |
SONAR-22727 index software qualitiy measure to project measures
Co-authored-by: Léo Geoffroy <leo.geoffroy@sonarsource.com>
Diffstat (limited to 'server')
14 files changed, 626 insertions, 174 deletions
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/measure/ProjectMeasuresIndexerIterator.java b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/ProjectMeasuresIndexerIterator.java index 95c0eb350dc..f7d898745b0 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/measure/ProjectMeasuresIndexerIterator.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/measure/ProjectMeasuresIndexerIterator.java @@ -40,6 +40,7 @@ import javax.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.resources.Qualifiers; +import org.sonar.core.metric.SoftwareQualitiesMetrics; import org.sonar.core.util.CloseableIterator; import org.sonar.db.DatabaseUtils; import org.sonar.db.DbSession; @@ -70,7 +71,18 @@ public class ProjectMeasuresIndexerIterator extends CloseableIterator<ProjectMea CoreMetrics.NEW_COVERAGE_KEY, CoreMetrics.NEW_DUPLICATED_LINES_DENSITY_KEY, CoreMetrics.NEW_LINES_KEY, - CoreMetrics.NEW_RELIABILITY_RATING_KEY); + CoreMetrics.NEW_RELIABILITY_RATING_KEY, + + //Ratings based on software quality + SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, + SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, + SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING_KEY, + SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY, + SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY, + SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY, + SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, + SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY + ); private static final String SQL_PROJECTS = "SELECT p.uuid, p.kee, p.name, p.created_at, s.created_at, p.tags, p.qualifier " + "FROM projects p " + diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/es/IndexDefinitionHash.java b/server/sonar-server-common/src/main/java/org/sonar/server/es/IndexDefinitionHash.java index e36f8a1acb1..42783e5404f 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/es/IndexDefinitionHash.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/es/IndexDefinitionHash.java @@ -45,7 +45,8 @@ class IndexDefinitionHash { index.getSettings().toString(), Map.of(mainType.getIndex(), mainType), index.getRelationTypes().stream().collect(Collectors.toMap(IndexType.IndexRelationType::getName, Function.identity())), - index.getAttributes()); + index.getAttributes(), + index.getCustomHashMetadata()); } private static String of(String str, Map<?, ?>... maps) { diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/es/newindex/BuiltIndex.java b/server/sonar-server-common/src/main/java/org/sonar/server/es/newindex/BuiltIndex.java index 1e2c0b74208..62763890bd2 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/es/newindex/BuiltIndex.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/es/newindex/BuiltIndex.java @@ -44,12 +44,14 @@ public final class BuiltIndex<T extends NewIndex<T>> { private final Set<IndexRelationType> relationTypes; private final Settings settings; private final Map<String, Object> attributes; + private final Map<String, String> customHashMetadata; BuiltIndex(T newIndex) { this.mainType = newIndex.getMainType(); this.settings = newIndex.getSettings().build(); this.relationTypes = newIndex.getRelationsStream().collect(Collectors.toSet()); this.attributes = buildAttributes(newIndex); + this.customHashMetadata = newIndex.getCustomHashMetadata(); } private static Map<String, Object> buildAttributes(NewIndex<?> newIndex) { @@ -121,4 +123,8 @@ public final class BuiltIndex<T extends NewIndex<T>> { public Map<String, Object> getAttributes() { return attributes; } + + public Map<String, String> getCustomHashMetadata() { + return customHashMetadata; + } } diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/es/newindex/NewIndex.java b/server/sonar-server-common/src/main/java/org/sonar/server/es/newindex/NewIndex.java index dc719bd4d64..1a5c6fff264 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/es/newindex/NewIndex.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/es/newindex/NewIndex.java @@ -47,6 +47,7 @@ public abstract class NewIndex<T extends NewIndex<T>> { private final Settings.Builder settings = DefaultIndexSettings.defaults(); private final Map<String, Object> attributes = new TreeMap<>(); private final Map<String, Object> properties = new TreeMap<>(); + private final Map<String, String> customHashMetadata = new TreeMap<>(); public NewIndex(Index index, SettingsConfiguration settingsConfiguration) { this.index = index; @@ -158,4 +159,16 @@ public abstract class NewIndex<T extends NewIndex<T>> { public abstract BuiltIndex<T> build(); + /** + * Set additional information to be used to compute the hash of the index. + * If hash metadata changes, the hash of index will also change. + */ + public T addCustomHashMetadata(String key, String value) { + this.customHashMetadata.put(key, value); + return castThis(); + } + + public Map<String, String> getCustomHashMetadata() { + return customHashMetadata; + } } diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/measure/index/ProjectMeasuresIndexDefinition.java b/server/sonar-server-common/src/main/java/org/sonar/server/measure/index/ProjectMeasuresIndexDefinition.java index 3172c6f3bd0..5766293f352 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/measure/index/ProjectMeasuresIndexDefinition.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/measure/index/ProjectMeasuresIndexDefinition.java @@ -22,6 +22,7 @@ package org.sonar.server.measure.index; import javax.inject.Inject; import org.sonar.api.config.Configuration; import org.sonar.api.config.internal.MapSettings; +import org.sonar.db.measure.ProjectMeasuresIndexerIterator; import org.sonar.server.es.Index; import org.sonar.server.es.IndexDefinition; import org.sonar.server.es.IndexType; @@ -65,6 +66,7 @@ public class ProjectMeasuresIndexDefinition implements IndexDefinition { public static final String FIELD_NCLOC_DISTRIBUTION_LANGUAGE = FIELD_NCLOC_DISTRIBUTION + "." + SUB_FIELD_DISTRIB_LANGUAGE; public static final String FIELD_NCLOC_DISTRIBUTION_NCLOC = FIELD_NCLOC_DISTRIBUTION + "." + SUB_FIELD_DISTRIB_NCLOC; + private static final String METRICS_CUSTOM_METADATA_KEY = "metrics"; private final Configuration config; private final boolean enableSource; @@ -94,7 +96,8 @@ public class ProjectMeasuresIndexDefinition implements IndexDefinition { .setRefreshInterval(MANUAL_REFRESH_INTERVAL) .setDefaultNbOfShards(5) .build()) - .setEnableSource(enableSource); + .setEnableSource(enableSource) + .addCustomHashMetadata(METRICS_CUSTOM_METADATA_KEY, String.join(",", ProjectMeasuresIndexerIterator.METRIC_KEYS)); TypeMapping mapping = index.createTypeMapping(TYPE_PROJECT_MEASURES); mapping.keywordFieldBuilder(FIELD_UUID).disableNorms().build(); diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/measure/index/ProjectMeasuresIndexer.java b/server/sonar-server-common/src/main/java/org/sonar/server/measure/index/ProjectMeasuresIndexer.java index d698e08a619..6f5898c73c6 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/measure/index/ProjectMeasuresIndexer.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/measure/index/ProjectMeasuresIndexer.java @@ -211,7 +211,7 @@ public class ProjectMeasuresIndexer implements EventIndexer, AnalysisIndexer, Ne .setTags(project.getTags()) .setAnalysedAt(analysisDate == null ? null : new Date(analysisDate)) .setCreatedAt(new Date(project.getCreationDate())) - .setMeasuresFromMap(projectMeasures.getMeasures().getNumericMeasures()) + .setMeasuresFromMap(ProjectMeasuresSoftwareQualityRatingsInitializer.initializeSoftwareQualityRatings(projectMeasures.getMeasures().getNumericMeasures())) .setLanguages(new ArrayList<>(projectMeasures.getMeasures().getNclocByLanguages().keySet())) .setNclocLanguageDistributionFromMap(projectMeasures.getMeasures().getNclocByLanguages()); } diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/measure/index/ProjectMeasuresSoftwareQualityRatingsInitializer.java b/server/sonar-server-common/src/main/java/org/sonar/server/measure/index/ProjectMeasuresSoftwareQualityRatingsInitializer.java new file mode 100644 index 00000000000..fecce77fcd4 --- /dev/null +++ b/server/sonar-server-common/src/main/java/org/sonar/server/measure/index/ProjectMeasuresSoftwareQualityRatingsInitializer.java @@ -0,0 +1,65 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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.index; + +import java.util.Map; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.core.metric.SoftwareQualitiesMetrics; +import org.sonar.server.measure.Rating; + +/** + * This class defines "default" measures values for the "Software Quality Rating Metrics" when they do not exist. + * The "default" value is the same as the equivalent "Rule Type Rating Metric", except for E Rating that is converted to D Rating + * If the "Software Quality Rating Metrics" exists, then no changes are made + */ +public class ProjectMeasuresSoftwareQualityRatingsInitializer { + + private static final Map<String, String> RATING_KEY_TO_SOFTWARE_QUALITY_RATING_KEY = Map.of( + CoreMetrics.SQALE_RATING_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, + CoreMetrics.RELIABILITY_RATING_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, + CoreMetrics.SECURITY_RATING_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING_KEY, + CoreMetrics.SECURITY_REVIEW_RATING_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY, + CoreMetrics.NEW_SECURITY_RATING_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY, + CoreMetrics.NEW_SECURITY_REVIEW_RATING_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY, + CoreMetrics.NEW_MAINTAINABILITY_RATING_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, + CoreMetrics.NEW_RELIABILITY_RATING_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY + ); + + private ProjectMeasuresSoftwareQualityRatingsInitializer() { + } + + public static Map<String, Double> initializeSoftwareQualityRatings(Map<String, Double> measures) { + + RATING_KEY_TO_SOFTWARE_QUALITY_RATING_KEY.forEach((k, v) -> initializeSoftwareQualityRatingMeasure(measures, k, v)); + return measures; + } + + private static void initializeSoftwareQualityRatingMeasure(Map<String, Double> measures, String ruleTypeMetric, + String softwareQualityMetric) { + if (measures.containsKey(softwareQualityMetric)) { + return; + } + + Double value = measures.get(ruleTypeMetric); + if (value != null) { + measures.put(softwareQualityMetric, value > Rating.D.getIndex() ? Double.valueOf(Rating.D.getIndex()) : value); + } + } +} diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/es/IndexDefinitionHashTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/es/IndexDefinitionHashTest.java index 9c8047bcf41..4403aec2ae1 100644 --- a/server/sonar-server-common/src/test/java/org/sonar/server/es/IndexDefinitionHashTest.java +++ b/server/sonar-server-common/src/test/java/org/sonar/server/es/IndexDefinitionHashTest.java @@ -414,6 +414,30 @@ public class IndexDefinitionHashTest { .isNotEqualTo(hashOf(new TestNewIndex(mainType, someRefreshInterval))); } + @Test + public void hash_changes_if_customHashMetadata_changes() { + Index index = Index.simple("foo"); + Configuration emptySettings = new MapSettings().asConfig(); + SettingsConfiguration emptyConfiguration = SettingsConfiguration.newBuilder(emptySettings) + .build(); + IndexMainType mainType = IndexMainType.main(index, "bar"); + assertThat(hashOf(new TestNewIndex(mainType, emptyConfiguration))) + .isNotEqualTo(hashOf(new TestNewIndex(mainType, emptyConfiguration) + .addCustomHashMetadata("foo", "bar"))); + + assertThat(hashOf(new TestNewIndex(mainType, emptyConfiguration).addCustomHashMetadata("foo", "bar"))) + .isNotEqualTo(hashOf(new TestNewIndex(mainType, emptyConfiguration).addCustomHashMetadata("foo2", "bar"))); + + assertThat(hashOf(new TestNewIndex(mainType, emptyConfiguration).addCustomHashMetadata("foo", "bar"))) + .isNotEqualTo(hashOf(new TestNewIndex(mainType, emptyConfiguration).addCustomHashMetadata("foo", "bar,bar2"))); + + assertThat(hashOf(new TestNewIndex(mainType, emptyConfiguration).addCustomHashMetadata("foo", "bar"))) + .isEqualTo(hashOf(new TestNewIndex(mainType, emptyConfiguration).addCustomHashMetadata("foo", "bar"))); + + assertThat(hashOf(new TestNewIndex(mainType, emptyConfiguration).addCustomHashMetadata("foo", "bar").addCustomHashMetadata("foo2", "bar2"))) + .isEqualTo(hashOf(new TestNewIndex(mainType, emptyConfiguration).addCustomHashMetadata("foo2", "bar2").addCustomHashMetadata("foo", "bar"))); + } + private static SettingsConfiguration settingsConfigurationOf(MapSettings settings) { return SettingsConfiguration.newBuilder(settings.asConfig()).build(); } diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/es/newindex/NewIndexTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/es/newindex/NewIndexTest.java index eab6b8f9d11..6824d572e02 100644 --- a/server/sonar-server-common/src/test/java/org/sonar/server/es/newindex/NewIndexTest.java +++ b/server/sonar-server-common/src/test/java/org/sonar/server/es/newindex/NewIndexTest.java @@ -341,6 +341,17 @@ public class NewIndexTest { } @Test + @UseDataProvider("indexWithAndWithoutRelations") + public void index_withHashMetadata(Index index) { + NewIndex newIndex = new SimplestNewIndex(IndexType.main(index, "foo"), defaultSettingsConfiguration).addCustomHashMetadata("custom", "hash"); + + assertThat(newIndex.getCustomHashMetadata()).containsExactly(entry("custom", "hash")); + + BuiltIndex build = newIndex.build(); + assertThat(build.getCustomHashMetadata()).containsExactly(entry("custom", "hash")); + } + + @Test public void createTypeMapping_with_IndexRelationType_fails_with_ISE_if_index_does_not_allow_relations() { IndexType.IndexRelationType indexRelationType = IndexType.relation(IndexType.main(Index.withRelations(someIndexName), "bar"), "bar"); diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/measure/index/ProjectMeasuresSoftwareQualityRatingsInitializerTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/measure/index/ProjectMeasuresSoftwareQualityRatingsInitializerTest.java new file mode 100644 index 00000000000..ed9119174ec --- /dev/null +++ b/server/sonar-server-common/src/test/java/org/sonar/server/measure/index/ProjectMeasuresSoftwareQualityRatingsInitializerTest.java @@ -0,0 +1,101 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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.index; + +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.core.metric.SoftwareQualitiesMetrics; +import org.sonar.server.measure.Rating; + +import static org.assertj.core.api.Assertions.assertThat; + +class ProjectMeasuresSoftwareQualityRatingsInitializerTest { + + @Test + void initializeSoftwareQualityRatings_whenNoRating_thenNoSoftwareQualityRatingMetric() { + Map<String, Double> initialMeasures = new HashMap<>(); + initialMeasures.put(CoreMetrics.BRANCH_COVERAGE_KEY, null); + initialMeasures.put(CoreMetrics.ACCEPTED_ISSUES_KEY, 12.0); + initialMeasures.put(CoreMetrics.SQALE_RATING_KEY, null); + + Map<String, Double> measures = new HashMap<>(initialMeasures); + ProjectMeasuresSoftwareQualityRatingsInitializer.initializeSoftwareQualityRatings(measures); + + assertThat(measures).containsExactlyInAnyOrderEntriesOf(initialMeasures); + } + + @Test + void initializeSoftwareQualityRatings_whenRatingAndNoSoftwareQualityRating_thenSoftwareQualityRatingMetricAreCreated() { + Map<String, Double> initialMeasures = new HashMap<>(); + initialMeasures.put(CoreMetrics.SQALE_RATING_KEY, 1.0); + initialMeasures.put(CoreMetrics.RELIABILITY_RATING_KEY, 2.0); + initialMeasures.put(CoreMetrics.SECURITY_RATING_KEY, 3.0); + initialMeasures.put(CoreMetrics.SECURITY_REVIEW_RATING_KEY, 4.0); + initialMeasures.put(CoreMetrics.NEW_SECURITY_RATING_KEY, 4.0); + initialMeasures.put(CoreMetrics.NEW_SECURITY_REVIEW_RATING_KEY, 3.0); + initialMeasures.put(CoreMetrics.NEW_MAINTAINABILITY_RATING_KEY, 2.0); + initialMeasures.put(CoreMetrics.NEW_RELIABILITY_RATING_KEY, 1.0); + + Map<String, Double> measures = new HashMap<>(initialMeasures); + + ProjectMeasuresSoftwareQualityRatingsInitializer.initializeSoftwareQualityRatings(measures); + + assertThat(measures).hasSize(initialMeasures.size() * 2) + .containsAllEntriesOf(initialMeasures) + .containsEntry(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, 1.0) + .containsEntry(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, 2.0) + .containsEntry(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING_KEY, 3.0) + .containsEntry(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY, 4.0) + .containsEntry(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY, 4.0) + .containsEntry(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY, 3.0) + .containsEntry(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, 2.0) + .containsEntry(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, 1.0); + } + + @Test + void initializeSoftwareQualityRatings_whenERating_thenSoftwareQualityRatingCreatedWithD() { + Map<String, Double> initialMeasures = new HashMap<>(); + initialMeasures.put(CoreMetrics.SQALE_RATING_KEY, (double) Rating.E.getIndex()); + initialMeasures.put(CoreMetrics.RELIABILITY_RATING_KEY, (double) Rating.E.getIndex()); + initialMeasures.put(CoreMetrics.SECURITY_RATING_KEY, (double) Rating.E.getIndex()); + initialMeasures.put(CoreMetrics.SECURITY_REVIEW_RATING_KEY, (double) Rating.E.getIndex()); + initialMeasures.put(CoreMetrics.NEW_SECURITY_RATING_KEY, (double) Rating.E.getIndex()); + initialMeasures.put(CoreMetrics.NEW_SECURITY_REVIEW_RATING_KEY, (double) Rating.E.getIndex()); + initialMeasures.put(CoreMetrics.NEW_MAINTAINABILITY_RATING_KEY, (double) Rating.E.getIndex()); + initialMeasures.put(CoreMetrics.NEW_RELIABILITY_RATING_KEY, (double) Rating.E.getIndex()); + + Map<String, Double> measures = new HashMap<>(initialMeasures); + + ProjectMeasuresSoftwareQualityRatingsInitializer.initializeSoftwareQualityRatings(measures); + + assertThat(measures).hasSize(initialMeasures.size() * 2) + .containsAllEntriesOf(initialMeasures) + .containsEntry(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, (double) Rating.D.getIndex()) + .containsEntry(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, (double) Rating.D.getIndex()) + .containsEntry(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING_KEY, (double) Rating.D.getIndex()) + .containsEntry(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY, (double) Rating.D.getIndex()) + .containsEntry(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY, (double) Rating.D.getIndex()) + .containsEntry(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY, (double) Rating.D.getIndex()) + .containsEntry(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, (double) Rating.D.getIndex()) + .containsEntry(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, (double) Rating.D.getIndex()); + } +} diff --git a/server/sonar-webserver-es/build.gradle b/server/sonar-webserver-es/build.gradle index d59d52be520..d51c2b4f16b 100644 --- a/server/sonar-webserver-es/build.gradle +++ b/server/sonar-webserver-es/build.gradle @@ -21,6 +21,7 @@ dependencies { testImplementation 'com.github.spotbugs:spotbugs-annotations' testImplementation 'com.tngtech.java:junit-dataprovider' testImplementation 'org.junit.jupiter:junit-jupiter-api' + testImplementation 'org.junit.jupiter:junit-jupiter-params' testImplementation 'org.mockito:mockito-core' testImplementation 'org.sonarsource.api.plugin:sonar-plugin-api-test-fixtures' testImplementation testFixtures(project(':server:sonar-webserver-auth')) diff --git a/server/sonar-webserver-es/src/main/java/org/sonar/server/measure/index/ProjectMeasuresIndex.java b/server/sonar-webserver-es/src/main/java/org/sonar/server/measure/index/ProjectMeasuresIndex.java index 5e33203b4bd..9d6344f96e2 100644 --- a/server/sonar-webserver-es/src/main/java/org/sonar/server/measure/index/ProjectMeasuresIndex.java +++ b/server/sonar-webserver-es/src/main/java/org/sonar/server/measure/index/ProjectMeasuresIndex.java @@ -21,6 +21,7 @@ package org.sonar.server.measure.index; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -103,6 +104,14 @@ import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_KEY; import static org.sonar.api.measures.CoreMetrics.SECURITY_RATING_KEY; import static org.sonar.api.measures.CoreMetrics.SECURITY_REVIEW_RATING_KEY; import static org.sonar.api.measures.CoreMetrics.SQALE_RATING_KEY; +import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY; +import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY; +import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY; +import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY; +import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY; +import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING_KEY; +import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING_KEY; +import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY; import static org.sonar.server.es.EsUtils.escapeSpecialRegexChars; import static org.sonar.server.es.EsUtils.termsToMap; import static org.sonar.server.es.IndexType.FIELD_INDEX_TYPE; @@ -154,6 +163,8 @@ public class ProjectMeasuresIndex { NEW_DUPLICATED_LINES_DENSITY(new RangeWithNoDataMeasureFacet(NEW_DUPLICATED_LINES_DENSITY_KEY, DUPLICATIONS_THRESHOLDS)), COVERAGE(new RangeWithNoDataMeasureFacet(COVERAGE_KEY, COVERAGE_THRESHOLDS)), NEW_COVERAGE(new RangeWithNoDataMeasureFacet(NEW_COVERAGE_KEY, COVERAGE_THRESHOLDS)), + + //RuleType ratings SQALE_RATING(new RatingMeasureFacet(SQALE_RATING_KEY)), NEW_MAINTAINABILITY_RATING(new RatingMeasureFacet(NEW_MAINTAINABILITY_RATING_KEY)), RELIABILITY_RATING(new RatingMeasureFacet(RELIABILITY_RATING_KEY)), @@ -162,6 +173,17 @@ public class ProjectMeasuresIndex { NEW_SECURITY_RATING(new RatingMeasureFacet(NEW_SECURITY_RATING_KEY)), SECURITY_REVIEW_RATING(new RatingMeasureFacet(SECURITY_REVIEW_RATING_KEY)), NEW_SECURITY_REVIEW_RATING(new RatingMeasureFacet(NEW_SECURITY_REVIEW_RATING_KEY)), + + //Software quality ratings + SOFTWARE_QUALITY_MAINTAINABILITY_RATING(new RatingMeasureFacet(SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, 4)), + NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING(new RatingMeasureFacet(NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, 4)), + SOFTWARE_QUALITY_RELIABILITY_RATING(new RatingMeasureFacet(SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, 4)), + NEW_SOFTWARE_QUALITY_RELIABILITY_RATING(new RatingMeasureFacet(NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, 4)), + SOFTWARE_QUALITY_SECURITY_RATING(new RatingMeasureFacet(SOFTWARE_QUALITY_SECURITY_RATING_KEY, 4)), + NEW_SOFTWARE_QUALITY_SECURITY_RATING(new RatingMeasureFacet(NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY, 4)), + SOFTWARE_QUALITY_SECURITY_REVIEW_RATING(new RatingMeasureFacet(SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY, 4)), + NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING(new RatingMeasureFacet(NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY, 4)), + SECURITY_HOTSPOTS_REVIEWED(new RangeMeasureFacet(SECURITY_HOTSPOTS_REVIEWED_KEY, SECURITY_REVIEW_RATING_THRESHOLDS)), NEW_SECURITY_HOTSPOTS_REVIEWED(new RangeMeasureFacet(NEW_SECURITY_HOTSPOTS_REVIEWED_KEY, SECURITY_REVIEW_RATING_THRESHOLDS)), ALERT_STATUS(new MeasureFacet(ALERT_STATUS_KEY, ProjectMeasuresIndex::buildAlertStatusFacet)), @@ -566,14 +588,20 @@ public class ProjectMeasuresIndex { private static class RatingMeasureFacet extends MeasureFacet { private RatingMeasureFacet(String metricKey) { - super(metricKey, new MetricRatingFacetBuilder(metricKey)); + super(metricKey, new MetricRatingFacetBuilder(metricKey, 5)); + } + + private RatingMeasureFacet(String metricKey, int maxRating) { + super(metricKey, new MetricRatingFacetBuilder(metricKey, maxRating)); } private static class MetricRatingFacetBuilder implements FacetBuilder { private final String metricKey; + private final int maxRating; - private MetricRatingFacetBuilder(String metricKey) { + private MetricRatingFacetBuilder(String metricKey, int maxRating) { this.metricKey = metricKey; + this.maxRating = maxRating; } @Override @@ -581,19 +609,20 @@ public class ProjectMeasuresIndex { return topAggregationHelper.buildTopAggregation( facet.getName(), facet.getTopAggregationDef(), NO_EXTRA_FILTER, - t -> t.subAggregation(createMeasureRatingFacet(metricKey))); + t -> t.subAggregation(createMeasureRatingFacet(metricKey, maxRating))); } - private static AbstractAggregationBuilder<?> createMeasureRatingFacet(String metricKey) { + private static AbstractAggregationBuilder<?> createMeasureRatingFacet(String metricKey, int maxRating) { + List<KeyedFilter> filter = new ArrayList<>(); + for (int i = 1; i <= maxRating; i++) { + filter.add(new KeyedFilter(String.valueOf(i), termQuery(FIELD_MEASURES_MEASURE_VALUE, Double.valueOf(i)))); + } + return AggregationBuilders.nested("nested_" + metricKey, FIELD_MEASURES) .subAggregation( AggregationBuilders.filter("filter_" + metricKey, termsQuery(FIELD_MEASURES_MEASURE_KEY, metricKey)) - .subAggregation(filters(metricKey, - new KeyedFilter("1", termQuery(FIELD_MEASURES_MEASURE_VALUE, 1D)), - new KeyedFilter("2", termQuery(FIELD_MEASURES_MEASURE_VALUE, 2D)), - new KeyedFilter("3", termQuery(FIELD_MEASURES_MEASURE_VALUE, 3D)), - new KeyedFilter("4", termQuery(FIELD_MEASURES_MEASURE_VALUE, 4D)), - new KeyedFilter("5", termQuery(FIELD_MEASURES_MEASURE_VALUE, 5D))))); + .subAggregation(filters(metricKey, filter.toArray(new KeyedFilter[0]) + ))); } } } diff --git a/server/sonar-webserver-es/src/test/java/org/sonar/server/measure/index/ProjectMeasuresIndexTest.java b/server/sonar-webserver-es/src/test/java/org/sonar/server/measure/index/ProjectMeasuresIndexTest.java index 6e2961e4c4d..8b21640cbb5 100644 --- a/server/sonar-webserver-es/src/test/java/org/sonar/server/measure/index/ProjectMeasuresIndexTest.java +++ b/server/sonar-webserver-es/src/test/java/org/sonar/server/measure/index/ProjectMeasuresIndexTest.java @@ -19,22 +19,17 @@ */ package org.sonar.server.measure.index; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; -import com.tngtech.java.junit.dataprovider.DataProvider; -import com.tngtech.java.junit.dataprovider.DataProviderRunner; -import com.tngtech.java.junit.dataprovider.UseDataProvider; -import java.time.Instant; import java.util.Arrays; -import java.util.Date; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.IntStream; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.sonar.api.utils.System2; import org.sonar.db.component.ComponentDto; import org.sonar.db.component.ComponentTesting; @@ -55,7 +50,6 @@ import static com.google.common.collect.Sets.newHashSet; import static java.util.Arrays.asList; import static java.util.Arrays.stream; import static java.util.Collections.singletonList; -import static java.util.stream.Collectors.toList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.entry; @@ -73,8 +67,7 @@ import static org.sonar.server.measure.index.ProjectMeasuresIndexDefinition.TYPE import static org.sonar.server.measure.index.ProjectMeasuresQuery.Operator; import static org.sonarqube.ws.client.project.ProjectsWsParameters.FILTER_QUALIFIER; -@RunWith(DataProviderRunner.class) -public class ProjectMeasuresIndexTest { +class ProjectMeasuresIndexTest { private static final String MAINTAINABILITY_RATING = "sqale_rating"; private static final String NEW_MAINTAINABILITY_RATING_KEY = "new_maintainability_rating"; @@ -84,6 +77,17 @@ public class ProjectMeasuresIndexTest { private static final String NEW_SECURITY_RATING = "new_security_rating"; private static final String SECURITY_REVIEW_RATING = "security_review_rating"; private static final String NEW_SECURITY_REVIEW_RATING = "new_security_review_rating"; + + //Software quality ratings + private static final String SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY = "software_quality_maintainability_rating"; + private static final String NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY = "new_software_quality_maintainability_rating"; + private static final String SOFTWARE_QUALITY_RELIABILITY_RATING_KEY = "software_quality_reliability_rating"; + private static final String NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY = "new_software_quality_reliability_rating"; + private static final String SOFTWARE_QUALITY_SECURITY_RATING_KEY = "software_quality_security_rating"; + private static final String NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY = "new_software_quality_security_rating"; + private static final String SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY = "software_quality_security_review_rating"; + private static final String NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY = "new_software_quality_security_review_rating"; + private static final String SECURITY_HOTSPOTS_REVIEWED = "security_hotspots_reviewed"; private static final String NEW_SECURITY_HOTSPOTS_REVIEWED = "new_security_hotspots_reviewed"; private static final String COVERAGE = "coverage"; @@ -105,28 +109,41 @@ public class ProjectMeasuresIndexTest { private static final GroupDto GROUP1 = newGroupDto(); private static final GroupDto GROUP2 = newGroupDto(); - @Rule - public EsTester es = EsTester.create(); - @Rule - public UserSessionRule userSession = UserSessionRule.standalone(); + @RegisterExtension + private final EsTester es = EsTester.create(); + @RegisterExtension + private final UserSessionRule userSession = UserSessionRule.standalone(); + + private static String[] rating_metric_keys() { + return new String[]{ + MAINTAINABILITY_RATING, NEW_MAINTAINABILITY_RATING_KEY, + RELIABILITY_RATING, NEW_RELIABILITY_RATING, + SECURITY_RATING, NEW_SECURITY_RATING, + SECURITY_REVIEW_RATING, NEW_SECURITY_REVIEW_RATING + }; + } - @DataProvider - public static Object[][] rating_metric_keys() { - return new Object[][] {{MAINTAINABILITY_RATING}, {NEW_MAINTAINABILITY_RATING_KEY}, {RELIABILITY_RATING}, {NEW_RELIABILITY_RATING}, {SECURITY_RATING}, {NEW_SECURITY_RATING}, - {SECURITY_REVIEW_RATING}, {NEW_SECURITY_REVIEW_RATING}}; + private static String[] software_quality_rating_metric_keys() { + return new String[]{ + SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, + SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, + SOFTWARE_QUALITY_SECURITY_RATING_KEY, NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY, + SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY, NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY + }; } - private ProjectMeasuresIndexer projectMeasureIndexer = new ProjectMeasuresIndexer(null, es.client()); - private PermissionIndexerTester authorizationIndexer = new PermissionIndexerTester(es, projectMeasureIndexer); - private ProjectMeasuresIndex underTest = new ProjectMeasuresIndex(es.client(), new WebAuthorizationTypeSupport(userSession), System2.INSTANCE); + private final ProjectMeasuresIndexer projectMeasureIndexer = new ProjectMeasuresIndexer(null, es.client()); + private final PermissionIndexerTester authorizationIndexer = new PermissionIndexerTester(es, projectMeasureIndexer); + private final ProjectMeasuresIndex underTest = new ProjectMeasuresIndex(es.client(), new WebAuthorizationTypeSupport(userSession), + System2.INSTANCE); @Test - public void return_empty_if_no_projects() { + void return_empty_if_no_projects() { assertNoResults(new ProjectMeasuresQuery()); } @Test - public void default_sort_is_by_ascending_case_insensitive_name_then_by_key() { + void default_sort_is_by_ascending_case_insensitive_name_then_by_key() { ComponentDto windows = ComponentTesting.newPrivateProjectDto().setUuid("windows").setName("Windows").setKey("project1"); ComponentDto apachee = ComponentTesting.newPrivateProjectDto().setUuid("apachee").setName("apachee").setKey("project2"); ComponentDto apache1 = ComponentTesting.newPrivateProjectDto().setUuid("apache-1").setName("Apache").setKey("project3"); @@ -137,7 +154,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void sort_by_insensitive_name() { + void sort_by_insensitive_name() { ComponentDto windows = ComponentTesting.newPrivateProjectDto().setUuid("windows").setName("Windows"); ComponentDto apachee = ComponentTesting.newPrivateProjectDto().setUuid("apachee").setName("apachee"); ComponentDto apache = ComponentTesting.newPrivateProjectDto().setUuid("apache").setName("Apache"); @@ -148,7 +165,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void sort_by_ncloc() { + void sort_by_ncloc() { index( newDoc(PROJECT1, NCLOC, 15_000d), newDoc(PROJECT2, NCLOC, 30_000d), @@ -159,7 +176,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void sort_by_a_metric_then_by_name_then_by_key() { + void sort_by_a_metric_then_by_name_then_by_key() { ComponentDto windows = ComponentTesting.newPrivateProjectDto().setUuid("windows").setName("Windows").setKey("project1"); ComponentDto apachee = ComponentTesting.newPrivateProjectDto().setUuid("apachee").setName("apachee").setKey("project2"); ComponentDto apache1 = ComponentTesting.newPrivateProjectDto().setUuid("apache-1").setName("Apache").setKey("project3"); @@ -175,7 +192,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void sort_by_quality_gate_status() { + void sort_by_quality_gate_status() { ComponentDto project4 = ComponentTesting.newPrivateProjectDto().setUuid("Project-4").setName("Project 4").setKey("key-4"); index( newDoc(PROJECT1).setQualityGateStatus(OK.name()), @@ -186,40 +203,8 @@ public class ProjectMeasuresIndexTest { assertResults(new ProjectMeasuresQuery().setSort("alert_status").setAsc(false), PROJECT2, PROJECT1, project4); } - public void sort_by_creation_date() { - Instant now = Instant.ofEpochMilli(1000L); - Date nowMinus10 = Date.from(now.minusSeconds(10)); - Date nowMinus20 = Date.from(now.minusSeconds(20)); - Date nowMinus30 = Date.from(now.minusSeconds(30)); - - ComponentDto project4 = ComponentTesting.newPrivateProjectDto().setUuid("Project-4").setName("Project 4").setKey("key-4"); - index( - newDoc(PROJECT1).setCreatedAt(nowMinus10), - newDoc(PROJECT2).setCreatedAt(nowMinus30), - newDoc(project4).setCreatedAt(nowMinus20)); - - assertResults(new ProjectMeasuresQuery().setSort("creation_date").setAsc(true), PROJECT1, project4, PROJECT2); - assertResults(new ProjectMeasuresQuery().setSort("creation_date").setAsc(false), PROJECT2, PROJECT1, project4); - } - - public void sort_by_analysis_date() { - Instant now = Instant.ofEpochMilli(1000L); - Date nowMinus10 = Date.from(now.minusSeconds(10)); - Date nowMinus20 = Date.from(now.minusSeconds(20)); - Date nowMinus30 = Date.from(now.minusSeconds(30)); - - ComponentDto project4 = ComponentTesting.newPrivateProjectDto().setUuid("Project-4").setName("Project 4").setKey("key-4"); - index( - newDoc(PROJECT1).setAnalysedAt(nowMinus10), - newDoc(PROJECT2).setAnalysedAt(nowMinus30), - newDoc(project4).setAnalysedAt(nowMinus20)); - - assertResults(new ProjectMeasuresQuery().setSort("analysis_date").setAsc(true), PROJECT1, project4, PROJECT2); - assertResults(new ProjectMeasuresQuery().setSort("analysis_date").setAsc(false), PROJECT2, PROJECT1, project4); - } - @Test - public void sort_by_quality_gate_status_then_by_name_then_by_key() { + void sort_by_quality_gate_status_then_by_name_then_by_key() { ComponentDto windows = ComponentTesting.newPrivateProjectDto().setUuid("windows").setName("Windows").setKey("project1"); ComponentDto apachee = ComponentTesting.newPrivateProjectDto().setUuid("apachee").setName("apachee").setKey("project2"); ComponentDto apache1 = ComponentTesting.newPrivateProjectDto().setUuid("apache-1").setName("Apache").setKey("project3"); @@ -235,7 +220,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void paginate_results() { + void paginate_results() { IntStream.rangeClosed(1, 9) .forEach(i -> index(newDoc(newPrivateProjectDto("P" + i)))); @@ -246,7 +231,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void filter_with_lower_than() { + void filter_with_lower_than() { index( newDoc(PROJECT1, COVERAGE, 79d, NCLOC, 10_000d), newDoc(PROJECT2, COVERAGE, 80d, NCLOC, 10_000d), @@ -259,7 +244,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void filter_with_lower_than_or_equals() { + void filter_with_lower_than_or_equals() { index( newDoc(PROJECT1, COVERAGE, 79d, NCLOC, 10_000d), newDoc(PROJECT2, COVERAGE, 80d, NCLOC, 10_000d), @@ -272,7 +257,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void filter_with_greater_than() { + void filter_with_greater_than() { index( newDoc(PROJECT1, COVERAGE, 80d, NCLOC, 30_000d), newDoc(PROJECT2, COVERAGE, 80d, NCLOC, 30_001d), @@ -286,7 +271,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void filter_with_greater_than_or_equals() { + void filter_with_greater_than_or_equals() { index( newDoc(PROJECT1, COVERAGE, 80d, NCLOC, 30_000d), newDoc(PROJECT2, COVERAGE, 80d, NCLOC, 30_001d), @@ -300,7 +285,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void filter_with_equals() { + void filter_with_equals() { index( newDoc(PROJECT1, COVERAGE, 79d, NCLOC, 10_000d), newDoc(PROJECT2, COVERAGE, 80d, NCLOC, 10_000d), @@ -313,7 +298,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void filter_on_no_data_with_several_projects() { + void filter_on_no_data_with_several_projects() { index( newDoc(PROJECT1, NCLOC, 1d), newDoc(PROJECT2, DUPLICATION, 80d)); @@ -325,7 +310,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void filter_on_no_data_should_not_return_projects_with_data_and_other_measures() { + void filter_on_no_data_should_not_return_projects_with_data_and_other_measures() { ComponentDto project = ComponentTesting.newPrivateProjectDto(); index(newDoc(project, DUPLICATION, 80d, NCLOC, 1d)); @@ -335,7 +320,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void filter_on_no_data_should_not_return_projects_with_data() { + void filter_on_no_data_should_not_return_projects_with_data() { ComponentDto project = ComponentTesting.newPrivateProjectDto(); index(newDoc(project, DUPLICATION, 80d)); @@ -345,7 +330,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void filter_on_no_data_should_return_projects_with_no_data() { + void filter_on_no_data_should_return_projects_with_no_data() { ComponentDto project = ComponentTesting.newPrivateProjectDto(); index(newDoc(project, NCLOC, 1d)); @@ -355,7 +340,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void filter_on_several_metrics() { + void filter_on_several_metrics() { index( newDoc(PROJECT1, COVERAGE, 81d, NCLOC, 10_001d), newDoc(PROJECT2, COVERAGE, 80d, NCLOC, 10_001d), @@ -369,7 +354,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void facet_security_hotspots_reviewed() { + void facet_security_hotspots_reviewed() { index( // 2 docs with no measure newDocWithNoMeasure(), @@ -406,7 +391,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void facet_new_security_hotspots_reviewed() { + void facet_new_security_hotspots_reviewed() { index( // 2 docs with no measure newDocWithNoMeasure(), @@ -443,7 +428,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void filter_on_quality_gate_status() { + void filter_on_quality_gate_status() { index( newDoc(PROJECT1).setQualityGateStatus(OK.name()), newDoc(PROJECT2).setQualityGateStatus(OK.name()), @@ -454,7 +439,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void filter_on_languages() { + void filter_on_languages() { ComponentDto project4 = ComponentTesting.newPrivateProjectDto().setUuid("Project-4").setName("Project 4").setKey("key-4"); index( newDoc(PROJECT1).setLanguages(singletonList("java")), @@ -468,7 +453,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void filter_on_query_text() { + void filter_on_query_text() { ComponentDto windows = ComponentTesting.newPrivateProjectDto().setUuid("windows").setName("Windows").setKey("project1"); ComponentDto apachee = ComponentTesting.newPrivateProjectDto().setUuid("apachee").setName("apachee").setKey("project2"); ComponentDto apache1 = ComponentTesting.newPrivateProjectDto().setUuid("apache-1").setName("Apache").setKey("project3"); @@ -481,7 +466,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void filter_on_ids() { + void filter_on_ids() { index( newDoc(PROJECT1), newDoc(PROJECT2), @@ -492,7 +477,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void filter_on_tags() { + void filter_on_tags() { index( newDoc(PROJECT1).setTags(newArrayList("finance", "platform")), newDoc(PROJECT2).setTags(newArrayList("marketing", "platform")), @@ -506,7 +491,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void filter_on_qualifier() { + void filter_on_qualifier() { index(newDoc(PROJECT1), newDoc(PROJECT2), newDoc(PROJECT3), newDoc(APP1), newDoc(APP2), newDoc(APP3)); @@ -524,7 +509,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void return_correct_number_of_total_if_exceeds_index_max_results() { + void return_correct_number_of_total_if_exceeds_index_max_results() { index(IntStream.range(0, 12_000) .mapToObj(operand -> newDoc(ComponentTesting.newPrivateProjectDto())) .toArray(ProjectMeasuresDoc[]::new)); @@ -535,7 +520,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void return_only_projects_and_applications_authorized_for_user() { + void return_only_projects_and_applications_authorized_for_user() { indexForUser(USER1, newDoc(PROJECT1), newDoc(PROJECT2), newDoc(APP1), newDoc(APP2)); indexForUser(USER2, newDoc(PROJECT3), newDoc(APP3)); @@ -545,7 +530,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void return_only_projects_and_applications_authorized_for_user_groups() { + void return_only_projects_and_applications_authorized_for_user_groups() { indexForGroup(GROUP1, newDoc(PROJECT1), newDoc(PROJECT2), newDoc(APP1), newDoc(APP2)); indexForGroup(GROUP2, newDoc(PROJECT3)); @@ -555,7 +540,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void return_only_projects_and_applications_authorized_for_user_and_groups() { + void return_only_projects_and_applications_authorized_for_user_and_groups() { indexForUser(USER1, newDoc(PROJECT1), newDoc(PROJECT2), newDoc(APP1), newDoc(APP2)); indexForGroup(GROUP1, newDoc(PROJECT3)); @@ -565,7 +550,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void anonymous_user_can_only_access_projects_and_applications_authorized_for_anyone() { + void anonymous_user_can_only_access_projects_and_applications_authorized_for_anyone() { index(newDoc(PROJECT1), newDoc(APP1)); indexForUser(USER1, newDoc(PROJECT2), newDoc(APP2)); @@ -574,7 +559,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void return_all_projects_and_applications_when_setIgnoreAuthorization_is_true() { + void return_all_projects_and_applications_when_setIgnoreAuthorization_is_true() { indexForUser(USER1, newDoc(PROJECT1), newDoc(PROJECT2), newDoc(APP1), newDoc(APP2)); indexForUser(USER2, newDoc(PROJECT3), newDoc(APP3)); userSession.logIn(USER1); @@ -584,7 +569,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void does_not_return_facet_when_no_facets_in_options() { + void does_not_return_facet_when_no_facets_in_options() { index( newDoc(PROJECT1, NCLOC, 10d, COVERAGE_KEY, 30d, MAINTAINABILITY_RATING, 3d) .setQualityGateStatus(OK.name())); @@ -595,7 +580,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void facet_ncloc() { + void facet_ncloc() { index( // 3 docs with ncloc<1K newDoc(NCLOC, 0d), @@ -630,7 +615,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void facet_ncloc_is_sticky() { + void facet_ncloc_is_sticky() { index( // 1 docs with ncloc<1K newDoc(NCLOC, 999d, COVERAGE, 0d, DUPLICATION, 0d), @@ -670,7 +655,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void facet_ncloc_contains_only_projects_authorized_for_user() { + void facet_ncloc_contains_only_projects_authorized_for_user() { // User can see these projects indexForUser(USER1, // docs with ncloc<1K @@ -702,7 +687,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void facet_new_lines() { + void facet_new_lines() { index( // 3 docs with ncloc<1K newDoc(NEW_LINES, 0d), @@ -737,7 +722,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void facet_coverage() { + void facet_coverage() { index( // 1 doc with no coverage newDocWithNoMeasure(), @@ -775,7 +760,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void facet_coverage_is_sticky() { + void facet_coverage_is_sticky() { index( // docs with no coverage newDoc(NCLOC, 999d, DUPLICATION, 0d), @@ -819,7 +804,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void facet_coverage_contains_only_projects_authorized_for_user() { + void facet_coverage_contains_only_projects_authorized_for_user() { // User can see these projects indexForUser(USER1, // 1 doc with no coverage @@ -857,7 +842,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void facet_new_coverage() { + void facet_new_coverage() { index( // 1 doc with no coverage newDocWithNoMeasure(), @@ -895,7 +880,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void facet_duplicated_lines_density() { + void facet_duplicated_lines_density() { index( // 1 doc with no duplication newDocWithNoMeasure(), @@ -933,7 +918,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void facet_duplicated_lines_density_is_sticky() { + void facet_duplicated_lines_density_is_sticky() { index( // docs with no duplication newDoc(NCLOC, 50_001d, COVERAGE, 29d), @@ -973,7 +958,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void facet_duplicated_lines_density_contains_only_projects_authorized_for_user() { + void facet_duplicated_lines_density_contains_only_projects_authorized_for_user() { // User can see these projects indexForUser(USER1, // docs with no duplication @@ -1011,7 +996,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void facet_new_duplicated_lines_density() { + void facet_new_duplicated_lines_density() { index( // 2 docs with no measure newDocWithNoMeasure(), @@ -1049,9 +1034,9 @@ public class ProjectMeasuresIndexTest { entry("20.0-*", 5L)); } - @Test - @UseDataProvider("rating_metric_keys") - public void facet_on_rating(String metricKey) { + @ParameterizedTest + @MethodSource("rating_metric_keys") + void facet_on_rating(String metricKey) { index( // 3 docs with rating A newDoc(metricKey, 1d), @@ -1085,9 +1070,38 @@ public class ProjectMeasuresIndexTest { entry("5", 5L)); } - @Test - @UseDataProvider("rating_metric_keys") - public void facet_on_rating_is_sticky(String metricKey) { + @ParameterizedTest + @MethodSource("software_quality_rating_metric_keys") + void facet_on_software_quality_rating(String metricKey) { + index( + // 3 docs with rating A + newDoc(metricKey, 1d), + newDoc(metricKey, 1d), + newDoc(metricKey, 1d), + // 2 docs with rating B + newDoc(metricKey, 2d), + newDoc(metricKey, 2d), + // 4 docs with rating C + newDoc(metricKey, 3d), + newDoc(metricKey, 3d), + newDoc(metricKey, 3d), + newDoc(metricKey, 3d), + // 2 docs with rating D + newDoc(metricKey, 4d), + newDoc(metricKey, 4d)); + + Facets facets = underTest.search(new ProjectMeasuresQuery(), new SearchOptions().addFacets(metricKey)).getFacets(); + + assertThat(facets.get(metricKey)).containsExactly( + entry("1", 3L), + entry("2", 2L), + entry("3", 4L), + entry("4", 2L)); + } + + @ParameterizedTest + @MethodSource("rating_metric_keys") + void facet_on_rating_is_sticky(String metricKey) { index( // docs with rating A newDoc(metricKey, 1d, NCLOC, 100d, COVERAGE, 0d), @@ -1129,9 +1143,48 @@ public class ProjectMeasuresIndexTest { entry("500000.0-*", 0L)); } - @Test - @UseDataProvider("rating_metric_keys") - public void facet_on_rating_contains_only_projects_authorized_for_user(String metricKey) { + @ParameterizedTest + @MethodSource("software_quality_rating_metric_keys") + void facet_on_software_quality_rating_is_sticky(String metricKey) { + index( + // docs with rating A + newDoc(metricKey, 1d, NCLOC, 100d, COVERAGE, 0d), + newDoc(metricKey, 1d, NCLOC, 200d, COVERAGE, 0d), + newDoc(metricKey, 1d, NCLOC, 999d, COVERAGE, 0d), + // docs with rating B + newDoc(metricKey, 2d, NCLOC, 2000d, COVERAGE, 0d), + newDoc(metricKey, 2d, NCLOC, 5000d, COVERAGE, 0d), + // docs with rating C + newDoc(metricKey, 3d, NCLOC, 20000d, COVERAGE, 0d), + newDoc(metricKey, 3d, NCLOC, 30000d, COVERAGE, 0d), + newDoc(metricKey, 3d, NCLOC, 40000d, COVERAGE, 0d), + newDoc(metricKey, 3d, NCLOC, 50000d, COVERAGE, 0d), + // docs with rating D + newDoc(metricKey, 4d, NCLOC, 120000d, COVERAGE, 0d)); + + Facets facets = underTest.search(new ProjectMeasuresQuery() + .addMetricCriterion(MetricCriterion.create(metricKey, Operator.LT, 3d)) + .addMetricCriterion(MetricCriterion.create(COVERAGE, Operator.LT, 30d)), + new SearchOptions().addFacets(metricKey, NCLOC)).getFacets(); + + // Sticky facet on maintainability rating does not take into account maintainability rating filter + assertThat(facets.get(metricKey)).containsExactly( + entry("1", 3L), + entry("2", 2L), + entry("3", 4L), + entry("4", 1L)); + // But facet on ncloc does well take into into filters + assertThat(facets.get(NCLOC)).containsExactly( + entry("*-1000.0", 3L), + entry("1000.0-10000.0", 2L), + entry("10000.0-100000.0", 0L), + entry("100000.0-500000.0", 0L), + entry("500000.0-*", 0L)); + } + + @ParameterizedTest + @MethodSource("rating_metric_keys") + void facet_on_rating_contains_only_projects_authorized_for_user(String metricKey) { // User can see these projects indexForUser(USER1, // 3 docs with rating A @@ -1162,8 +1215,38 @@ public class ProjectMeasuresIndexTest { entry("5", 0L)); } + @ParameterizedTest + @MethodSource("software_quality_rating_metric_keys") + void facet_on_software_quality_rating_contains_only_projects_authorized_for_user(String metricKey) { + // User can see these projects + indexForUser(USER1, + // 3 docs with rating A + newDoc(metricKey, 1d), + newDoc(metricKey, 1d), + newDoc(metricKey, 1d), + // 2 docs with rating B + newDoc(metricKey, 2d), + newDoc(metricKey, 2d)); + + // User cannot see these projects + indexForUser(USER2, + // docs with rating C + newDoc(metricKey, 3d), + // docs with rating D + newDoc(metricKey, 4d)); + + userSession.logIn(USER1); + Facets facets = underTest.search(new ProjectMeasuresQuery(), new SearchOptions().addFacets(metricKey)).getFacets(); + + assertThat(facets.get(metricKey)).containsExactly( + entry("1", 3L), + entry("2", 2L), + entry("3", 0L), + entry("4", 0L)); + } + @Test - public void facet_quality_gate() { + void facet_quality_gate() { index( // 2 docs with QG OK newDoc().setQualityGateStatus(OK.name()), @@ -1182,7 +1265,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void facet_quality_gate_is_sticky() { + void facet_quality_gate_is_sticky() { index( // 2 docs with QG OK newDoc(NCLOC, 10d, COVERAGE, 0d).setQualityGateStatus(OK.name()), @@ -1212,7 +1295,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void facet_quality_gate_contains_only_projects_authorized_for_user() { + void facet_quality_gate_contains_only_projects_authorized_for_user() { // User can see these projects indexForUser(USER1, // 2 docs with QG OK @@ -1236,7 +1319,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void facet_languages() { + void facet_languages() { index( newDoc().setLanguages(singletonList("java")), newDoc().setLanguages(singletonList("java")), @@ -1255,7 +1338,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void facet_languages_is_limited_to_10_languages() { + void facet_languages_is_limited_to_10_languages() { index( newDoc().setLanguages(asList("<null>", "java", "xoo", "css", "cpp")), newDoc().setLanguages(asList("xml", "php", "python", "perl", "ruby")), @@ -1267,7 +1350,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void facet_languages_is_sticky() { + void facet_languages_is_sticky() { index( newDoc(NCLOC, 10d).setLanguages(singletonList("java")), newDoc(NCLOC, 10d).setLanguages(singletonList("java")), @@ -1277,7 +1360,7 @@ public class ProjectMeasuresIndexTest { newDoc(NCLOC, 5000d).setLanguages(asList("<null>", "java", "xoo"))); Facets facets = underTest.search( - new ProjectMeasuresQuery().setLanguages(ImmutableSet.of("java")), + new ProjectMeasuresQuery().setLanguages(Set.of("java")), new SearchOptions().addFacets(LANGUAGES, NCLOC)).getFacets(); // Sticky facet on language does not take into account language filter @@ -1296,13 +1379,14 @@ public class ProjectMeasuresIndexTest { } @Test - public void facet_languages_returns_more_than_10_languages_when_languages_filter_contains_value_not_in_top_10() { + void facet_languages_returns_more_than_10_languages_when_languages_filter_contains_value_not_in_top_10() { index( newDoc().setLanguages(asList("<null>", "java", "xoo", "css", "cpp")), newDoc().setLanguages(asList("xml", "php", "python", "perl", "ruby")), newDoc().setLanguages(asList("js", "scala"))); - Facets facets = underTest.search(new ProjectMeasuresQuery().setLanguages(ImmutableSet.of("xoo", "xml")), new SearchOptions().addFacets(LANGUAGES)).getFacets(); + Facets facets = underTest.search(new ProjectMeasuresQuery().setLanguages(Set.of("xoo", "xml")), + new SearchOptions().addFacets(LANGUAGES)).getFacets(); assertThat(facets.get(LANGUAGES)).containsOnly( entry("<null>", 1L), @@ -1320,7 +1404,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void facet_languages_contains_only_projects_authorized_for_user() { + void facet_languages_contains_only_projects_authorized_for_user() { // User can see these projects indexForUser(USER1, newDoc().setLanguages(singletonList("java")), @@ -1340,7 +1424,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void facet_qualifier() { + void facet_qualifier() { index( // 2 docs with qualifier APP newDoc().setQualifier(APP), @@ -1359,7 +1443,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void facet_qualifier_is_sticky() { + void facet_qualifier_is_sticky() { index( // 2 docs with qualifier APP newDoc(NCLOC, 10d, COVERAGE, 0d).setQualifier(APP), @@ -1389,7 +1473,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void facet_qualifier_contains_only_app_and_projects_authorized_for_user() { + void facet_qualifier_contains_only_app_and_projects_authorized_for_user() { // User can see these projects indexForUser(USER1, // 3 docs with qualifier APP, PROJECT @@ -1414,7 +1498,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void facet_tags() { + void facet_tags() { index( newDoc().setTags(newArrayList("finance", "offshore", "java")), newDoc().setTags(newArrayList("finance", "javascript")), @@ -1434,7 +1518,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void facet_tags_is_sticky() { + void facet_tags_is_sticky() { index( newDoc().setTags(newArrayList("finance")).setQualityGateStatus(OK.name()), newDoc().setTags(newArrayList("finance")).setQualityGateStatus(ERROR.name()), @@ -1454,7 +1538,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void facet_tags_returns_10_elements_by_default() { + void facet_tags_returns_10_elements_by_default() { index( newDoc().setTags(newArrayList("finance1", "finance2", "finance3", "finance4", "finance5", "finance6", "finance7", "finance8", "finance9", "finance10")), newDoc().setTags(newArrayList("finance1", "finance2", "finance3", "finance4", "finance5", "finance6", "finance7", "finance8", "finance9", "finance10")), @@ -1466,13 +1550,14 @@ public class ProjectMeasuresIndexTest { } @Test - public void facet_tags_returns_more_than_10_tags_when_tags_filter_contains_value_not_in_top_10() { + void facet_tags_returns_more_than_10_tags_when_tags_filter_contains_value_not_in_top_10() { index( newDoc().setTags(newArrayList("finance1", "finance2", "finance3", "finance4", "finance5", "finance6", "finance7", "finance8", "finance9", "finance10")), newDoc().setTags(newArrayList("finance1", "finance2", "finance3", "finance4", "finance5", "finance6", "finance7", "finance8", "finance9", "finance10")), newDoc().setTags(newArrayList("solo", "solo2"))); - Map<String, Long> result = underTest.search(new ProjectMeasuresQuery().setTags(ImmutableSet.of("solo", "solo2")), new SearchOptions().addFacets(FIELD_TAGS)).getFacets() + Map<String, Long> result = underTest.search(new ProjectMeasuresQuery().setTags(Set.of("solo", "solo2")), + new SearchOptions().addFacets(FIELD_TAGS)).getFacets() .get(FIELD_TAGS); assertThat(result).hasSize(12).containsOnlyKeys("finance1", "finance2", "finance3", "finance4", "finance5", "finance6", "finance7", "finance8", "finance9", "finance10", "solo", @@ -1480,7 +1565,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void search_tags() { + void search_tags() { index( newDoc().setTags(newArrayList("finance", "offshore", "java")), newDoc().setTags(newArrayList("official", "javascript")), @@ -1495,7 +1580,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void search_tags_return_all_tags() { + void search_tags_return_all_tags() { index( newDoc().setTags(newArrayList("finance", "offshore", "java")), newDoc().setTags(newArrayList("official", "javascript")), @@ -1510,7 +1595,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void search_tags_in_lexical_order() { + void search_tags_in_lexical_order() { index( newDoc().setTags(newArrayList("finance", "offshore", "java")), newDoc().setTags(newArrayList("official", "javascript")), @@ -1525,7 +1610,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void search_tags_follows_paging() { + void search_tags_follows_paging() { index( newDoc().setTags(newArrayList("finance", "offshore", "java")), newDoc().setTags(newArrayList("official", "javascript")), @@ -1548,7 +1633,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void search_tags_returns_nothing_if_page_too_large() { + void search_tags_returns_nothing_if_page_too_large() { index( newDoc().setTags(newArrayList("finance", "offshore", "java")), newDoc().setTags(newArrayList("official", "javascript")), @@ -1563,7 +1648,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void search_tags_only_of_authorized_projects() { + void search_tags_only_of_authorized_projects() { indexForUser(USER1, newDoc(PROJECT1).setTags(singletonList("finance")), newDoc(PROJECT2).setTags(singletonList("marketing"))); @@ -1578,14 +1663,14 @@ public class ProjectMeasuresIndexTest { } @Test - public void search_tags_with_no_tags() { + void search_tags_with_no_tags() { List<String> result = underTest.searchTags("whatever", 1, 10); assertThat(result).isEmpty(); } @Test - public void search_tags_with_page_size_at_0() { + void search_tags_with_page_size_at_0() { index(newDoc().setTags(newArrayList("offshore"))); List<String> result = underTest.searchTags(null, 1, 0); @@ -1594,14 +1679,14 @@ public class ProjectMeasuresIndexTest { } @Test - public void search_statistics() { + void search_statistics() { es.putDocuments(TYPE_PROJECT_MEASURES, newDoc("lines", 10, "coverage", 80) .setLanguages(Arrays.asList("java", "cs", "js")) - .setNclocLanguageDistributionFromMap(ImmutableMap.of("java", 200, "cs", 250, "js", 50)), + .setNclocLanguageDistributionFromMap(Map.of("java", 200, "cs", 250, "js", 50)), newDoc("lines", 20, "coverage", 80) .setLanguages(Arrays.asList("java", "python", "kotlin")) - .setNclocLanguageDistributionFromMap(ImmutableMap.of("java", 300, "python", 100, "kotlin", 404))); + .setNclocLanguageDistributionFromMap(Map.of("java", 300, "python", 100, "kotlin", 404))); ProjectMeasuresStatistics result = underTest.searchSupportStatistics(); @@ -1613,7 +1698,7 @@ public class ProjectMeasuresIndexTest { } @Test - public void search_statistics_for_large_instances() { + void search_statistics_for_large_instances() { int nbProjects = 25000; int javaLocByProjects = 100; int jsLocByProjects = 900; @@ -1621,7 +1706,7 @@ public class ProjectMeasuresIndexTest { ProjectMeasuresDoc[] documents = IntStream.range(0, nbProjects).mapToObj(i -> newDoc("lines", 10, "coverage", 80) .setLanguages(asList("java", "cs", "js")) - .setNclocLanguageDistributionFromMap(ImmutableMap.of("java", javaLocByProjects, "cs", csLocByProjects, "js", jsLocByProjects))).toArray(ProjectMeasuresDoc[]::new); + .setNclocLanguageDistributionFromMap(Map.of("java", javaLocByProjects, "cs", csLocByProjects, "js", jsLocByProjects))).toArray(ProjectMeasuresDoc[]::new); es.putDocuments(TYPE_PROJECT_MEASURES, documents); @@ -1642,23 +1727,23 @@ public class ProjectMeasuresIndexTest { } @Test - public void search_statistics_should_ignore_applications() { + void search_statistics_should_ignore_applications() { es.putDocuments(TYPE_PROJECT_MEASURES, // insert projects newDoc(ComponentTesting.newPrivateProjectDto(), "lines", 10, "coverage", 80) .setLanguages(Arrays.asList("java", "cs", "js")) - .setNclocLanguageDistributionFromMap(ImmutableMap.of("java", 200, "cs", 250, "js", 50)), + .setNclocLanguageDistributionFromMap(Map.of("java", 200, "cs", 250, "js", 50)), newDoc(ComponentTesting.newPrivateProjectDto(), "lines", 20, "coverage", 80) .setLanguages(Arrays.asList("java", "python", "kotlin")) - .setNclocLanguageDistributionFromMap(ImmutableMap.of("java", 300, "python", 100, "kotlin", 404)), + .setNclocLanguageDistributionFromMap(Map.of("java", 300, "python", 100, "kotlin", 404)), // insert applications newDoc(ComponentTesting.newApplication(), "lines", 1000, "coverage", 70) .setLanguages(Arrays.asList("java", "python", "kotlin")) - .setNclocLanguageDistributionFromMap(ImmutableMap.of("java", 300, "python", 100, "kotlin", 404)), + .setNclocLanguageDistributionFromMap(Map.of("java", 300, "python", 100, "kotlin", 404)), newDoc(ComponentTesting.newApplication(), "lines", 20, "coverage", 80) .setLanguages(Arrays.asList("java", "python", "kotlin")) - .setNclocLanguageDistributionFromMap(ImmutableMap.of("java", 300, "python", 100, "kotlin", 404))); + .setNclocLanguageDistributionFromMap(Map.of("java", 300, "python", 100, "kotlin", 404))); ProjectMeasuresStatistics result = underTest.searchSupportStatistics(); @@ -1670,15 +1755,15 @@ public class ProjectMeasuresIndexTest { } @Test - public void search_statistics_should_count_0_if_no_projects() { + void search_statistics_should_count_0_if_no_projects() { es.putDocuments(TYPE_PROJECT_MEASURES, // insert applications newDoc(ComponentTesting.newApplication(), "lines", 1000, "coverage", 70) - .setLanguages(Arrays.asList("java", "python", "kotlin")) - .setNclocLanguageDistributionFromMap(ImmutableMap.of("java", 300, "python", 100, "kotlin", 404)), + .setLanguages(Arrays.asList("java", "Map", "kotlin")) + .setNclocLanguageDistributionFromMap(Map.of("java", 300, "python", 100, "kotlin", 404)), newDoc(ComponentTesting.newApplication(), "lines", 20, "coverage", 80) .setLanguages(Arrays.asList("java", "python", "kotlin")) - .setNclocLanguageDistributionFromMap(ImmutableMap.of("java", 300, "python", 100, "kotlin", 404))); + .setNclocLanguageDistributionFromMap(Map.of("java", 300, "python", 100, "kotlin", 404))); ProjectMeasuresStatistics result = underTest.searchSupportStatistics(); @@ -1687,14 +1772,14 @@ public class ProjectMeasuresIndexTest { } @Test - public void fail_if_page_size_greater_than_100() { + void fail_if_page_size_greater_than_100() { assertThatThrownBy(() -> underTest.searchTags("whatever", 1, 101)) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("Page size must be lower than or equals to 100"); } @Test - public void fail_if_page_greater_than_20() { + void fail_if_page_greater_than_20() { assertThatThrownBy(() -> underTest.searchTags("whatever", 21, 100)) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("Page must be between 0 and 20"); @@ -1702,17 +1787,17 @@ public class ProjectMeasuresIndexTest { private void index(ProjectMeasuresDoc... docs) { es.putDocuments(TYPE_PROJECT_MEASURES, docs); - authorizationIndexer.allow(stream(docs).map(doc -> new IndexPermissions(doc.getId(), PROJECT).allowAnyone()).collect(toList())); + authorizationIndexer.allow(stream(docs).map(doc -> new IndexPermissions(doc.getId(), PROJECT).allowAnyone()).toList()); } private void indexForUser(UserDto user, ProjectMeasuresDoc... docs) { es.putDocuments(TYPE_PROJECT_MEASURES, docs); - authorizationIndexer.allow(stream(docs).map(doc -> new IndexPermissions(doc.getId(), PROJECT).addUserUuid(user.getUuid())).collect(toList())); + authorizationIndexer.allow(stream(docs).map(doc -> new IndexPermissions(doc.getId(), PROJECT).addUserUuid(user.getUuid())).toList()); } private void indexForGroup(GroupDto group, ProjectMeasuresDoc... docs) { es.putDocuments(TYPE_PROJECT_MEASURES, docs); - authorizationIndexer.allow(stream(docs).map(doc -> new IndexPermissions(doc.getId(), PROJECT).addGroupUuid(group.getUuid())).collect(toList())); + authorizationIndexer.allow(stream(docs).map(doc -> new IndexPermissions(doc.getId(), PROJECT).addGroupUuid(group.getUuid())).toList()); } private static ProjectMeasuresDoc newDoc(ComponentDto project) { @@ -1740,7 +1825,7 @@ public class ProjectMeasuresIndexTest { } private static Map<String, Object> newMeasure(String key, Object value) { - return ImmutableMap.of("key", key, "value", value); + return Map.of("key", key, "value", value); } private static ProjectMeasuresDoc newDocWithNoMeasure() { diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/component/ws/SearchProjectsActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/component/ws/SearchProjectsActionIT.java index eda28adb2fe..3269fa61cce 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/component/ws/SearchProjectsActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/component/ws/SearchProjectsActionIT.java @@ -23,6 +23,7 @@ import com.google.common.base.Joiner; import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; +import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; @@ -95,6 +96,14 @@ import static org.sonar.api.server.ws.WebService.Param.PAGE; import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE; import static org.sonar.api.server.ws.WebService.Param.SORT; import static org.sonar.api.utils.DateUtils.formatDateTime; +import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY; +import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY; +import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY; +import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY; +import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY; +import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING_KEY; +import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING_KEY; +import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY; import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_002; import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_003; @@ -127,11 +136,39 @@ public class SearchProjectsActionIT { } @DataProvider + public static Object[][] software_quality_rating_metric_keys() { + return new Object[][]{{SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY}, {SOFTWARE_QUALITY_RELIABILITY_RATING_KEY}, + {SOFTWARE_QUALITY_SECURITY_RATING_KEY}, {SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY}}; + } + + @DataProvider + public static Object[][] all_rating_metric_keys() { + List<Object[]> result = new ArrayList<>(); + result.addAll(Arrays.asList(rating_metric_keys())); + result.addAll(Arrays.asList(software_quality_rating_metric_keys())); + return result.toArray(new Object[result.size()][]); + } + + @DataProvider public static Object[][] new_rating_metric_keys() { return new Object[][]{{NEW_MAINTAINABILITY_RATING_KEY}, {NEW_RELIABILITY_RATING_KEY}, {NEW_SECURITY_RATING_KEY}}; } @DataProvider + public static Object[][] new_software_quality_rating_metric_keys() { + return new Object[][]{{NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY}, {NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY}, + {NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY}, {NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY}}; + } + + @DataProvider + public static Object[][] all_new_rating_metric_keys() { + List<Object[]> result = new ArrayList<>(); + result.addAll(Arrays.asList(new_rating_metric_keys())); + result.addAll(Arrays.asList(new_software_quality_rating_metric_keys())); + return result.toArray(new Object[result.size()][]); + } + + @DataProvider public static Object[][] component_qualifiers_for_valid_editions() { return new Object[][]{ {new String[]{Qualifiers.PROJECT}, Edition.COMMUNITY}, @@ -207,7 +244,15 @@ public class SearchProjectsActionIT { "new_maintainability_rating", "name", "analysisDate", - "creationDate"); + "creationDate", + "new_software_quality_maintainability_rating", + "new_software_quality_reliability_rating", + "new_software_quality_security_rating", + "new_software_quality_security_review_rating", + "software_quality_maintainability_rating", + "software_quality_reliability_rating", + "software_quality_security_rating", + "software_quality_security_review_rating"); Param asc = def.param("asc"); assertThat(asc.defaultValue()).isEqualTo("true"); @@ -223,7 +268,15 @@ public class SearchProjectsActionIT { , "security_rating", "alert_status", "languages", "tags", "qualifier", "new_reliability_rating", "new_security_rating", "new_maintainability_rating", "new_coverage", "new_duplicated_lines_density", "new_lines", - "security_review_rating", "security_hotspots_reviewed", "new_security_hotspots_reviewed", "new_security_review_rating"); + "security_review_rating", "security_hotspots_reviewed", "new_security_hotspots_reviewed", "new_security_review_rating", + "new_software_quality_maintainability_rating", + "new_software_quality_reliability_rating", + "new_software_quality_security_rating", + "new_software_quality_security_review_rating", + "software_quality_maintainability_rating", + "software_quality_reliability_rating", + "software_quality_security_rating", + "software_quality_security_review_rating"); } @Test @@ -366,7 +419,7 @@ public class SearchProjectsActionIT { } @Test - @UseDataProvider("rating_metric_keys") + @UseDataProvider("all_rating_metric_keys") public void filter_projects_by_rating(String metricKey) { userSession.logIn(); MetricDto ratingMetric = db.measures().insertMetric(c -> c.setKey(metricKey).setValueType(INT.name())); @@ -381,7 +434,7 @@ public class SearchProjectsActionIT { } @Test - @UseDataProvider("new_rating_metric_keys") + @UseDataProvider("all_new_rating_metric_keys") public void filter_projects_by_new_rating(String newMetricKey) { userSession.logIn(); MetricDto ratingMetric = db.measures().insertMetric(c -> c.setKey(newMetricKey).setValueType(INT.name())); @@ -945,6 +998,30 @@ public class SearchProjectsActionIT { } @Test + @UseDataProvider("software_quality_rating_metric_keys") + public void return_software_quality_rating_facet(String ratingMetricKey) { + userSession.logIn(); + MetricDto ratingMetric = db.measures().insertMetric(c -> c.setKey(ratingMetricKey).setValueType("RATING")); + insertProject(new Measure(ratingMetric, c -> c.setValue(1d))); + insertProject(new Measure(ratingMetric, c -> c.setValue(1d))); + insertProject(new Measure(ratingMetric, c -> c.setValue(3d))); + index(); + + SearchProjectsWsResponse result = call(request.setFacets(singletonList(ratingMetricKey))); + + Common.Facet facet = result.getFacets().getFacetsList().stream() + .filter(oneFacet -> ratingMetricKey.equals(oneFacet.getProperty())) + .findFirst().orElseThrow(IllegalStateException::new); + assertThat(facet.getValuesList()) + .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount) + .containsExactly( + tuple("1", 2L), + tuple("2", 0L), + tuple("3", 1L), + tuple("4", 0L)); + } + + @Test @UseDataProvider("new_rating_metric_keys") public void return_new_rating_facet(String newRatingMetricKey) { userSession.logIn(); @@ -971,6 +1048,30 @@ public class SearchProjectsActionIT { } @Test + @UseDataProvider("new_software_quality_rating_metric_keys") + public void return_new_software_quality_rating_facet(String newRatingMetricKey) { + userSession.logIn(); + MetricDto newRatingMetric = db.measures().insertMetric(c -> c.setKey(newRatingMetricKey).setValueType("RATING")); + insertProject(new Measure(newRatingMetric, c -> c.setValue(1d))); + insertProject(new Measure(newRatingMetric, c -> c.setValue(1d))); + insertProject(new Measure(newRatingMetric, c -> c.setValue(3d))); + index(); + + SearchProjectsWsResponse result = call(request.setFacets(singletonList(newRatingMetricKey))); + + Common.Facet facet = result.getFacets().getFacetsList().stream() + .filter(oneFacet -> newRatingMetricKey.equals(oneFacet.getProperty())) + .findFirst().orElseThrow(IllegalStateException::new); + assertThat(facet.getValuesList()) + .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount) + .containsExactly( + tuple("1", 2L), + tuple("2", 0L), + tuple("3", 1L), + tuple("4", 0L)); + } + + @Test public void return_coverage_facet() { userSession.logIn(); MetricDto coverage = db.measures().insertMetric(c -> c.setKey(COVERAGE).setValueType("PERCENT")); |