From c72da32c69c14f71b68c81f46adfe219ecc40c9e Mon Sep 17 00:00:00 2001 From: Daniel Schwarz Date: Tue, 23 May 2017 11:56:52 +0200 Subject: [PATCH] SONAR-9260 allow to filter on NO_DATA in project search --- .../it/projectSearch/SearchProjectsTest.java | 11 ++- .../ws/ProjectMeasuresQueryFactory.java | 11 ++- .../component/ws/SearchProjectsAction.java | 6 +- .../measure/index/ProjectMeasuresIndex.java | 13 ++- .../measure/index/ProjectMeasuresQuery.java | 30 +++++-- .../ws/ProjectMeasuresQueryFactoryTest.java | 31 +++++++ .../ws/ProjectMeasuresQueryValidatorTest.java | 26 +++--- .../ws/SearchProjectsActionTest.java | 34 +++++++- .../index/ProjectMeasuresIndexTest.java | 80 ++++++++++++++----- .../ProjectMeasuresIndexTextSearchTest.java | 10 +-- .../index/ProjectMeasuresQueryTest.java | 36 ++++++++- 11 files changed, 237 insertions(+), 51 deletions(-) diff --git a/it/it-tests/src/test/java/it/projectSearch/SearchProjectsTest.java b/it/it-tests/src/test/java/it/projectSearch/SearchProjectsTest.java index 5070fd3c5c9..4d4b50369a3 100644 --- a/it/it-tests/src/test/java/it/projectSearch/SearchProjectsTest.java +++ b/it/it-tests/src/test/java/it/projectSearch/SearchProjectsTest.java @@ -86,13 +86,22 @@ public class SearchProjectsTest { @Test public void filter_projects_by_measure_values() throws Exception { String projectKey = newProjectKey(); - analyzeProject(projectKey, "shared/xoo-sample"); + analyzeProject(projectKey,"shared/xoo-sample"); verifyFilterMatches(projectKey, "ncloc > 1"); verifyFilterMatches(projectKey, "ncloc > 1 and comment_lines < 10000"); verifyFilterDoesNotMatch("ncloc <= 1"); } + @Test + public void find_projects_with_no_data() throws Exception { + String projectKey = newProjectKey(); + analyzeProject(projectKey,"shared/xoo-sample"); + + verifyFilterMatches(projectKey, "coverage = NO_DATA"); + verifyFilterDoesNotMatch("ncloc = NO_DATA"); + } + @Test public void filter_by_text_query() throws IOException { orchestrator.executeBuild(create(projectDir("shared/xoo-sample"), "sonar.projectKey", "project1", "sonar.projectName", "apachee")); diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/ProjectMeasuresQueryFactory.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/ProjectMeasuresQueryFactory.java index 08d8e55e3dc..87c54c55c84 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/ws/ProjectMeasuresQueryFactory.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/ws/ProjectMeasuresQueryFactory.java @@ -48,6 +48,7 @@ class ProjectMeasuresQueryFactory { public static final String IS_FAVORITE_CRITERION = "isFavorite"; public static final String QUERY_KEY = "query"; + private static final String NO_DATA = "NO_DATA"; private static final Map> CRITERION_PROCESSORS = ImmutableMap.>builder() .put(IS_FAVORITE_CRITERION.toLowerCase(ENGLISH), (criterion, query) -> processIsFavorite(criterion)) @@ -132,7 +133,15 @@ class ProjectMeasuresQueryFactory { private static void processMetricCriterion(Criterion criterion, ProjectMeasuresQuery query) { checkOperator(criterion); checkValue(criterion); - query.addMetricCriterion(new MetricCriterion(criterion.getKey().toLowerCase(ENGLISH), criterion.getOperator(), parseValue(criterion.getValue()))); + query.addMetricCriterion(createMetricCriterion(criterion, criterion.getKey().toLowerCase(ENGLISH), criterion.getOperator())); + } + + private static MetricCriterion createMetricCriterion(Criterion criterion, String metricKey, Operator operator) { + if (NO_DATA.equalsIgnoreCase(criterion.getValue())) { + checkArgument(EQ.equals(operator), "%s can only be used with equals operator", NO_DATA); + return MetricCriterion.createNoData(metricKey); + } + return MetricCriterion.create(metricKey, operator, parseValue(criterion.getValue())); } private static double parseValue(String value) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/SearchProjectsAction.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/SearchProjectsAction.java index e8cb88abc4f..fb51e78a818 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/ws/SearchProjectsAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/ws/SearchProjectsAction.java @@ -109,7 +109,9 @@ public class SearchProjectsAction implements ComponentsWsAction { .setResponseExample(getClass().getResource("search_projects-example.json")) .setChangelog( new Change("6.4", format("The '%s' parameter accepts '%s' to filter by language", FILTER_LANGUAGES, PARAM_FILTER)), - new Change("6.4", "The 'visibility' field is added")) + new Change("6.4", "The 'visibility' field is added"), + new Change("6.5", "The 'filter' parameter now allows 'NO_DATA' as value for numeric metrics") + ) .setHandler(this); action.createFieldsParam(POSSIBLE_FIELDS) @@ -133,6 +135,8 @@ public class SearchProjectsAction implements ComponentsWsAction { " filter=\"alert_status = ERROR and isFavorite and coverage >= 60 and coverage < 80\"" + "
  • to filter projects with a reliability, security and maintainability rating equals or worse than B:
    " + " filter=\"reliability_rating>=2 and security_rating>=2 and sqale_rating>=2\"
  • " + + "
  • to filter projects without duplication data:
    " + + " filter=\"duplicated_lines_density = NO_DATA\"
  • " + "" + "To filter on project name or key, use the 'query' keyword, for instance : filter='query = \"Sonar\"'.
    " + "
    " + diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/index/ProjectMeasuresIndex.java b/server/sonar-server/src/main/java/org/sonar/server/measure/index/ProjectMeasuresIndex.java index 596323a4187..8fb31c3ee65 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/index/ProjectMeasuresIndex.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/index/ProjectMeasuresIndex.java @@ -284,9 +284,7 @@ public class ProjectMeasuresIndex { BoolQueryBuilder metricFilters = boolQuery(); entry.getValue() .stream() - .map(criterion -> nestedQuery(FIELD_MEASURES, boolQuery() - .filter(termQuery(FIELD_MEASURES_KEY, criterion.getMetricKey())) - .filter(toValueQuery(criterion)))) + .map(criterion -> toQuery(criterion)) .forEach(metricFilters::must); filters.put(entry.getKey(), metricFilters); }); @@ -318,6 +316,15 @@ public class ProjectMeasuresIndex { return Optional.of(ProjectsTextSearchQueryFactory.createQuery(queryText.get())); } + private static QueryBuilder toQuery(MetricCriterion criterion) { + if (criterion.isNoData()) { + return boolQuery().mustNot(nestedQuery(FIELD_MEASURES, termQuery(FIELD_MEASURES_KEY, criterion.getMetricKey()))); + } + return nestedQuery(FIELD_MEASURES, boolQuery() + .filter(termQuery(FIELD_MEASURES_KEY, criterion.getMetricKey())) + .filter(toValueQuery(criterion))); + } + private static QueryBuilder toValueQuery(MetricCriterion criterion) { String fieldName = FIELD_MEASURES_VALUE; diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/index/ProjectMeasuresQuery.java b/server/sonar-server/src/main/java/org/sonar/server/measure/index/ProjectMeasuresQuery.java index f0847e8d061..ba07efe7d75 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/index/ProjectMeasuresQuery.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/index/ProjectMeasuresQuery.java @@ -26,6 +26,7 @@ import java.util.Set; import javax.annotation.Nullable; import org.sonar.api.measures.Metric; +import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; import static org.sonar.server.component.ws.FilterParser.Operator; @@ -127,12 +128,13 @@ public class ProjectMeasuresQuery { public static class MetricCriterion { private final String metricKey; private final Operator operator; - private final double value; + @Nullable + private final Double value; - public MetricCriterion(String metricKey, Operator operator, double value) { - this.metricKey = requireNonNull(metricKey); - this.operator = requireNonNull(operator); - this.value = requireNonNull(value); + private MetricCriterion(String metricKey, @Nullable Operator operator, @Nullable Double value) { + this.metricKey = metricKey; + this.operator = operator; + this.value = value; } public String getMetricKey() { @@ -140,12 +142,30 @@ public class ProjectMeasuresQuery { } public Operator getOperator() { + checkDataAvailable(); return operator; } public double getValue() { + checkDataAvailable(); return value; } + + public boolean isNoData() { + return value == null; + } + + public static MetricCriterion createNoData(String metricKey) { + return new MetricCriterion(requireNonNull(metricKey), null, null); + } + + public static MetricCriterion create(String metricKey, Operator operator, double value) { + return new MetricCriterion(requireNonNull(metricKey), requireNonNull(operator), value); + } + + private void checkDataAvailable() { + checkState(!isNoData(), "The criterion for metric %s has no data", metricKey); + } } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/ProjectMeasuresQueryFactoryTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ProjectMeasuresQueryFactoryTest.java index 023d39899f2..a6a25b6691d 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ws/ProjectMeasuresQueryFactoryTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ProjectMeasuresQueryFactoryTest.java @@ -273,6 +273,37 @@ public class ProjectMeasuresQueryFactoryTest { tuple("coverage", Operator.LTE, 80d)); } + @Test + public void filter_no_data() throws Exception { + List criteria = singletonList(Criterion.builder().setKey("duplicated_lines_density").setOperator(EQ).setValue("NO_DATA").build()); + + ProjectMeasuresQuery underTest = newProjectMeasuresQuery(criteria, emptySet()); + + assertThat(underTest.getMetricCriteria()) + .extracting(MetricCriterion::getMetricKey, MetricCriterion::isNoData) + .containsOnly(tuple("duplicated_lines_density", true)); + } + + @Test + public void fail_to_use_no_data_with_operator_lower_than() throws Exception { + List criteria = singletonList(Criterion.builder().setKey("duplicated_lines_density").setOperator(LT).setValue("NO_DATA").build()); + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("NO_DATA can only be used with equals operator"); + + newProjectMeasuresQuery(criteria, emptySet()); + } + + @Test + public void filter_no_data_with_other_case() throws Exception { + List criteria = singletonList(Criterion.builder().setKey("duplicated_lines_density").setOperator(EQ).setValue("nO_DaTa").build()); + + ProjectMeasuresQuery underTest = newProjectMeasuresQuery(criteria, emptySet()); + + assertThat(underTest.getMetricCriteria()) + .extracting(MetricCriterion::getMetricKey, MetricCriterion::isNoData) + .containsOnly(tuple("duplicated_lines_density", true)); + } + @Test public void accept_empty_query() throws Exception { ProjectMeasuresQuery result = newProjectMeasuresQuery(emptyList(), emptySet()); diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/ProjectMeasuresQueryValidatorTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ProjectMeasuresQueryValidatorTest.java index c6e0e6fef9e..9220c27e19a 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ws/ProjectMeasuresQueryValidatorTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ProjectMeasuresQueryValidatorTest.java @@ -62,7 +62,7 @@ public class ProjectMeasuresQueryValidatorTest { @Test public void does_not_fail_when_metric_criteria_contains_an_existing_metric() throws Exception { insertValidMetric("ncloc"); - ProjectMeasuresQuery query = new ProjectMeasuresQuery().addMetricCriterion(new MetricCriterion("ncloc", GT, 10d)); + ProjectMeasuresQuery query = new ProjectMeasuresQuery().addMetricCriterion(MetricCriterion.create("ncloc", GT, 10d)); underTest.validate(dbSession, query); } @@ -71,7 +71,7 @@ public class ProjectMeasuresQueryValidatorTest { public void does_not_fail_when_sort_is_by_name() throws Exception { insertValidMetric("ncloc"); ProjectMeasuresQuery query = new ProjectMeasuresQuery() - .addMetricCriterion(new MetricCriterion("ncloc", GT, 10d)) + .addMetricCriterion(MetricCriterion.create("ncloc", GT, 10d)) .setSort("name"); underTest.validate(dbSession, query); @@ -82,7 +82,7 @@ public class ProjectMeasuresQueryValidatorTest { insertValidMetric("ncloc"); insertValidMetric("debt"); ProjectMeasuresQuery query = new ProjectMeasuresQuery() - .addMetricCriterion(new MetricCriterion("ncloc", GT, 10d)) + .addMetricCriterion(MetricCriterion.create("ncloc", GT, 10d)) .setSort("debt"); underTest.validate(dbSession, query); @@ -96,11 +96,11 @@ public class ProjectMeasuresQueryValidatorTest { insertMetric(createValidMetric("distrib").setValueType(DISTRIB.name())); insertMetric(createValidMetric("string").setValueType(STRING.name())); ProjectMeasuresQuery query = new ProjectMeasuresQuery() - .addMetricCriterion(new MetricCriterion("data", GT, 10d)) - .addMetricCriterion(new MetricCriterion("distrib", EQ, 11d)) - .addMetricCriterion(new MetricCriterion("ncloc", LTE, 20d)) - .addMetricCriterion(new MetricCriterion("debt", LT, 20d)) - .addMetricCriterion(new MetricCriterion("string", EQ, 40d)); + .addMetricCriterion(MetricCriterion.create("data", GT, 10d)) + .addMetricCriterion(MetricCriterion.create("distrib", EQ, 11d)) + .addMetricCriterion(MetricCriterion.create("ncloc", LTE, 20d)) + .addMetricCriterion(MetricCriterion.create("debt", LT, 20d)) + .addMetricCriterion(MetricCriterion.create("string", EQ, 40d)); expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("Following metrics are not numeric : [data, distrib, string]"); @@ -112,7 +112,7 @@ public class ProjectMeasuresQueryValidatorTest { insertMetric(createValidMetric("ncloc").setEnabled(false)); insertMetric(createValidMetric("debt").setEnabled(false)); ProjectMeasuresQuery query = new ProjectMeasuresQuery() - .addMetricCriterion(new MetricCriterion("ncloc", GT, 10d)) + .addMetricCriterion(MetricCriterion.create("ncloc", GT, 10d)) .setSort("debt"); expectedException.expect(IllegalArgumentException.class); @@ -124,7 +124,7 @@ public class ProjectMeasuresQueryValidatorTest { public void fail_when_metric_does_not_exists() throws Exception { insertValidMetric("ncloc"); ProjectMeasuresQuery query = new ProjectMeasuresQuery() - .addMetricCriterion(new MetricCriterion("unknown", GT, 10d)) + .addMetricCriterion(MetricCriterion.create("unknown", GT, 10d)) .setSort("debt"); expectedException.expect(IllegalArgumentException.class); @@ -136,9 +136,9 @@ public class ProjectMeasuresQueryValidatorTest { public void return_all_unknown_metrics() throws Exception { insertValidMetric("ncloc"); ProjectMeasuresQuery query = new ProjectMeasuresQuery() - .addMetricCriterion(new MetricCriterion("debt", GT, 10d)) - .addMetricCriterion(new MetricCriterion("ncloc", LTE, 20d)) - .addMetricCriterion(new MetricCriterion("coverage", GT, 30d)) + .addMetricCriterion(MetricCriterion.create("debt", GT, 10d)) + .addMetricCriterion(MetricCriterion.create("ncloc", LTE, 20d)) + .addMetricCriterion(MetricCriterion.create("coverage", GT, 30d)) .setSort("duplications"); expectedException.expect(IllegalArgumentException.class); diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchProjectsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchProjectsActionTest.java index 02fb375157d..f54ef4b2e94 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchProjectsActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchProjectsActionTest.java @@ -149,7 +149,9 @@ public class SearchProjectsActionTest { assertThat(def.params().stream().map(Param::key).collect(toList())).containsOnly("organization", "filter", "facets", "s", "asc", "ps", "p", "f"); assertThat(def.changelog()).extracting(Change::getVersion, Change::getDescription).containsExactlyInAnyOrder( tuple("6.4", "The 'languages' parameter accepts 'filter' to filter by language"), - tuple("6.4", "The 'visibility' field is added")); + tuple("6.4", "The 'visibility' field is added"), + tuple("6.5", "The 'filter' parameter now allows 'NO_DATA' as value for numeric metrics") + ); Param organization = def.param("organization"); assertThat(organization.isRequired()).isFalse(); @@ -268,7 +270,7 @@ public class SearchProjectsActionTest { userSession.logIn(); OrganizationDto organization1 = db.organizations().insert(); OrganizationDto organization2 = db.organizations().insert(); - MetricDto coverage = db.measureDbTester().insertMetric(c -> c.setKey(COVERAGE).setValueType(INT.name())); + MetricDto coverage = db.measureDbTester().insertMetric(c -> c.setKey(COVERAGE).setValueType(PERCENT.name())); MetricDto ncloc = db.measureDbTester().insertMetric(c -> c.setKey(NCLOC).setValueType(INT.name())); ComponentDto project1 = insertProject(organization1, new Measure(coverage, c -> c.setValue(81d)), new Measure(ncloc, c -> c.setValue(10_000d))); ComponentDto project2 = insertProject(organization1, new Measure(coverage, c -> c.setValue(80d)), new Measure(ncloc, c -> c.setValue(10_000d))); @@ -401,6 +403,34 @@ public class SearchProjectsActionTest { assertThat(result.getComponentsList()).extracting(Component::getKey).containsExactlyInAnyOrder(project1.getKey(), project3.key()); } + @Test + public void filter_projects_by_no_duplication() { + userSession.logIn(); + OrganizationDto organizationDto = db.organizations().insert(); + MetricDto coverage = db.measureDbTester().insertMetric(c -> c.setKey(COVERAGE).setValueType(PERCENT.name())); + MetricDto duplications = db.measureDbTester().insertMetric(c -> c.setKey(DUPLICATED_LINES_DENSITY_KEY).setValueType(PERCENT.name())); + ComponentDto project1 = insertProject(organizationDto, new Measure(coverage, c -> c.setValue(10d))); + ComponentDto project2 = insertProject(organizationDto, new Measure(duplications, c -> c.setValue(0d))); + ComponentDto project3 = insertProject(organizationDto, new Measure(duplications, c -> c.setValue(79d))); + + SearchProjectsWsResponse result = call(request.setFilter("duplicated_lines_density = NO_DATA")); + + assertThat(result.getComponentsList()).extracting(Component::getKey).containsExactlyInAnyOrder(project1.getKey()); + } + + @Test + public void filter_projects_by_no_duplication_should_not_return_projects_with_duplication() { + userSession.logIn(); + OrganizationDto organizationDto = db.organizations().insert(); + MetricDto coverage = db.measureDbTester().insertMetric(c -> c.setKey(COVERAGE).setValueType(PERCENT.name())); + MetricDto duplications = db.measureDbTester().insertMetric(c -> c.setKey(DUPLICATED_LINES_DENSITY_KEY).setValueType(PERCENT.name())); + insertProject(organizationDto, new Measure(duplications, c -> c.setValue(10d)), new Measure(coverage, c -> c.setValue(50d))); + + SearchProjectsWsResponse result = call(request.setFilter("duplicated_lines_density = NO_DATA")); + + assertThat(result.getComponentsList()).extracting(Component::getKey).isEmpty(); + } + @Test public void filter_projects_by_new_duplications() { userSession.logIn(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/measure/index/ProjectMeasuresIndexTest.java b/server/sonar-server/src/test/java/org/sonar/server/measure/index/ProjectMeasuresIndexTest.java index 6802c072051..3affb0a44be 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/measure/index/ProjectMeasuresIndexTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/measure/index/ProjectMeasuresIndexTest.java @@ -215,7 +215,7 @@ public class ProjectMeasuresIndexTest { newDoc(PROJECT3, COVERAGE, 81d, NCLOC, 10_000d)); ProjectMeasuresQuery query = new ProjectMeasuresQuery() - .addMetricCriterion(new MetricCriterion(COVERAGE, Operator.LT, 80d)); + .addMetricCriterion(MetricCriterion.create(COVERAGE, Operator.LT, 80d)); assertResults(query, PROJECT1); } @@ -228,7 +228,7 @@ public class ProjectMeasuresIndexTest { newDoc(PROJECT3, COVERAGE, 81d, NCLOC, 10_000d)); ProjectMeasuresQuery query = new ProjectMeasuresQuery() - .addMetricCriterion(new MetricCriterion(COVERAGE, Operator.LTE, 80d)); + .addMetricCriterion(MetricCriterion.create(COVERAGE, Operator.LTE, 80d)); assertResults(query, PROJECT1, PROJECT2); } @@ -240,10 +240,10 @@ public class ProjectMeasuresIndexTest { newDoc(PROJECT2, COVERAGE, 80d, NCLOC, 30_001d), newDoc(PROJECT3, COVERAGE, 80d, NCLOC, 30_001d)); - ProjectMeasuresQuery query = new ProjectMeasuresQuery().addMetricCriterion(new MetricCriterion(NCLOC, Operator.GT, 30_000d)); + ProjectMeasuresQuery query = new ProjectMeasuresQuery().addMetricCriterion(MetricCriterion.create(NCLOC, Operator.GT, 30_000d)); assertResults(query, PROJECT2, PROJECT3); - query = new ProjectMeasuresQuery().addMetricCriterion(new MetricCriterion(NCLOC, Operator.GT, 100_000d)); + query = new ProjectMeasuresQuery().addMetricCriterion(MetricCriterion.create(NCLOC, Operator.GT, 100_000d)); assertNoResults(query); } @@ -254,10 +254,10 @@ public class ProjectMeasuresIndexTest { newDoc(PROJECT2, COVERAGE, 80d, NCLOC, 30_001d), newDoc(PROJECT3, COVERAGE, 80d, NCLOC, 30_001d)); - ProjectMeasuresQuery query = new ProjectMeasuresQuery().addMetricCriterion(new MetricCriterion(NCLOC, Operator.GTE, 30_001d)); + ProjectMeasuresQuery query = new ProjectMeasuresQuery().addMetricCriterion(MetricCriterion.create(NCLOC, Operator.GTE, 30_001d)); assertResults(query, PROJECT2, PROJECT3); - query = new ProjectMeasuresQuery().addMetricCriterion(new MetricCriterion(NCLOC, Operator.GTE, 100_000d)); + query = new ProjectMeasuresQuery().addMetricCriterion(MetricCriterion.create(NCLOC, Operator.GTE, 100_000d)); assertNoResults(query); } @@ -269,11 +269,53 @@ public class ProjectMeasuresIndexTest { newDoc(PROJECT3, COVERAGE, 81d, NCLOC, 10_000d)); ProjectMeasuresQuery query = new ProjectMeasuresQuery() - .addMetricCriterion(new MetricCriterion(COVERAGE, Operator.EQ, 80d)); + .addMetricCriterion(MetricCriterion.create(COVERAGE, Operator.EQ, 80d)); assertResults(query, PROJECT2); } + @Test + public void filter_on_no_data_with_several_projects() { + index( + newDoc(PROJECT1, NCLOC, 1d), + newDoc(PROJECT2, DUPLICATION, 80d)); + + ProjectMeasuresQuery query = new ProjectMeasuresQuery() + .addMetricCriterion(MetricCriterion.createNoData(DUPLICATION)); + + assertResults(query, PROJECT1); + } + + @Test + public void filter_on_no_data_should_not_return_projects_with_data_and_other_measures() { + ComponentDto project = ComponentTesting.newPrivateProjectDto(ORG); + index(newDoc(project, DUPLICATION, 80d, NCLOC, 1d)); + + ProjectMeasuresQuery query = new ProjectMeasuresQuery().addMetricCriterion(MetricCriterion.createNoData(DUPLICATION)); + + assertNoResults(query); + } + + @Test + public void filter_on_no_data_should_not_return_projects_with_data() { + ComponentDto project = ComponentTesting.newPrivateProjectDto(ORG); + index(newDoc(project, DUPLICATION, 80d)); + + ProjectMeasuresQuery query = new ProjectMeasuresQuery().addMetricCriterion(MetricCriterion.createNoData(DUPLICATION)); + + assertNoResults(query); + } + + @Test + public void filter_on_no_data_should_return_projects_with_no_data() { + ComponentDto project = ComponentTesting.newPrivateProjectDto(ORG); + index(newDoc(project, NCLOC, 1d)); + + ProjectMeasuresQuery query = new ProjectMeasuresQuery().addMetricCriterion(MetricCriterion.createNoData(DUPLICATION)); + + assertResults(query, project); + } + @Test public void filter_on_several_metrics() { index( @@ -282,9 +324,9 @@ public class ProjectMeasuresIndexTest { newDoc(PROJECT3, COVERAGE, 79d, NCLOC, 10_000d)); ProjectMeasuresQuery esQuery = new ProjectMeasuresQuery() - .addMetricCriterion(new MetricCriterion(COVERAGE, Operator.LTE, 80d)) - .addMetricCriterion(new MetricCriterion(NCLOC, Operator.GT, 10_000d)) - .addMetricCriterion(new MetricCriterion(NCLOC, Operator.LT, 11_000d)); + .addMetricCriterion(MetricCriterion.create(COVERAGE, Operator.LTE, 80d)) + .addMetricCriterion(MetricCriterion.create(NCLOC, Operator.GT, 10_000d)) + .addMetricCriterion(MetricCriterion.create(NCLOC, Operator.LT, 11_000d)); assertResults(esQuery, PROJECT2); } @@ -479,8 +521,8 @@ public class ProjectMeasuresIndexTest { newDoc(NCLOC, 501_000d, COVERAGE, 81d, DUPLICATION, 20d)); Facets facets = underTest.search(new ProjectMeasuresQuery() - .addMetricCriterion(new MetricCriterion(NCLOC, Operator.LT, 10_000d)) - .addMetricCriterion(new MetricCriterion(DUPLICATION, Operator.LT, 10d)), + .addMetricCriterion(MetricCriterion.create(NCLOC, Operator.LT, 10_000d)) + .addMetricCriterion(MetricCriterion.create(DUPLICATION, Operator.LT, 10d)), new SearchOptions().addFacets(NCLOC, COVERAGE)).getFacets(); // Sticky facet on ncloc does not take into account ncloc filter @@ -620,8 +662,8 @@ public class ProjectMeasuresIndexTest { newDoc(NCLOC, 501_000d, COVERAGE, 810d, DUPLICATION, 20d)); Facets facets = underTest.search(new ProjectMeasuresQuery() - .addMetricCriterion(new MetricCriterion(COVERAGE, Operator.LT, 30d)) - .addMetricCriterion(new MetricCriterion(DUPLICATION, Operator.LT, 10d)), + .addMetricCriterion(MetricCriterion.create(COVERAGE, Operator.LT, 30d)) + .addMetricCriterion(MetricCriterion.create(DUPLICATION, Operator.LT, 10d)), new SearchOptions().addFacets(COVERAGE, NCLOC)).getFacets(); // Sticky facet on coverage does not take into account coverage filter @@ -764,8 +806,8 @@ public class ProjectMeasuresIndexTest { newDoc(DUPLICATION, 20d, NCLOC, 1000000d, COVERAGE, 40d)); Facets facets = underTest.search(new ProjectMeasuresQuery() - .addMetricCriterion(new MetricCriterion(DUPLICATION, Operator.LT, 10d)) - .addMetricCriterion(new MetricCriterion(COVERAGE, Operator.LT, 30d)), + .addMetricCriterion(MetricCriterion.create(DUPLICATION, Operator.LT, 10d)) + .addMetricCriterion(MetricCriterion.create(COVERAGE, Operator.LT, 30d)), new SearchOptions().addFacets(DUPLICATION, NCLOC)).getFacets(); // Sticky facet on duplication does not take into account duplication filter @@ -922,8 +964,8 @@ public class ProjectMeasuresIndexTest { newDoc(metricKey, 5d, NCLOC, 800000d, COVERAGE, 60d)); Facets facets = underTest.search(new ProjectMeasuresQuery() - .addMetricCriterion(new MetricCriterion(metricKey, Operator.LT, 3d)) - .addMetricCriterion(new MetricCriterion(COVERAGE, Operator.LT, 30d)), + .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 @@ -1017,7 +1059,7 @@ public class ProjectMeasuresIndexTest { Facets facets = underTest.search(new ProjectMeasuresQuery() .setQualityGateStatus(ERROR) - .addMetricCriterion(new MetricCriterion(COVERAGE, Operator.LT, 55d)), + .addMetricCriterion(MetricCriterion.create(COVERAGE, Operator.LT, 55d)), new SearchOptions().addFacets(ALERT_STATUS_KEY, NCLOC)).getFacets(); // Sticky facet on quality gate does not take into account quality gate filter diff --git a/server/sonar-server/src/test/java/org/sonar/server/measure/index/ProjectMeasuresIndexTextSearchTest.java b/server/sonar-server/src/test/java/org/sonar/server/measure/index/ProjectMeasuresIndexTextSearchTest.java index b34c381afed..c7f2f9bbbf7 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/measure/index/ProjectMeasuresIndexTextSearchTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/measure/index/ProjectMeasuresIndexTextSearchTest.java @@ -280,11 +280,11 @@ public class ProjectMeasuresIndexTextSearchTest { newDoc(newPrivateProjectDto(ORG).setUuid("project3").setName("Apache").setKey("project3"), NCLOC, 50_000d), newDoc(newPrivateProjectDto(ORG).setUuid("project4").setName("Apache").setKey("project4"), NCLOC, 60_000d)); - assertResults(new ProjectMeasuresQuery().setQueryText("apache").addMetricCriterion(new MetricCriterion(NCLOC, GT, 20_000d)), "project3", "project4", "project2"); - assertResults(new ProjectMeasuresQuery().setQueryText("apache").addMetricCriterion(new MetricCriterion(NCLOC, LT, 55_000d)), "project3", "project2"); - assertResults(new ProjectMeasuresQuery().setQueryText("PAC").addMetricCriterion(new MetricCriterion(NCLOC, LT, 55_000d)), "project3", "project2"); - assertResults(new ProjectMeasuresQuery().setQueryText("apachee").addMetricCriterion(new MetricCriterion(NCLOC, GT, 30_000d)), "project2"); - assertResults(new ProjectMeasuresQuery().setQueryText("unknown").addMetricCriterion(new MetricCriterion(NCLOC, GT, 20_000d))); + assertResults(new ProjectMeasuresQuery().setQueryText("apache").addMetricCriterion(MetricCriterion.create(NCLOC, GT, 20_000d)), "project3", "project4", "project2"); + assertResults(new ProjectMeasuresQuery().setQueryText("apache").addMetricCriterion(MetricCriterion.create(NCLOC, LT, 55_000d)), "project3", "project2"); + assertResults(new ProjectMeasuresQuery().setQueryText("PAC").addMetricCriterion(MetricCriterion.create(NCLOC, LT, 55_000d)), "project3", "project2"); + assertResults(new ProjectMeasuresQuery().setQueryText("apachee").addMetricCriterion(MetricCriterion.create(NCLOC, GT, 30_000d)), "project2"); + assertResults(new ProjectMeasuresQuery().setQueryText("unknown").addMetricCriterion(MetricCriterion.create(NCLOC, GT, 20_000d))); } private void index(ProjectMeasuresDoc... docs) { diff --git a/server/sonar-server/src/test/java/org/sonar/server/measure/index/ProjectMeasuresQueryTest.java b/server/sonar-server/src/test/java/org/sonar/server/measure/index/ProjectMeasuresQueryTest.java index 27529639601..d72676cf991 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/measure/index/ProjectMeasuresQueryTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/measure/index/ProjectMeasuresQueryTest.java @@ -46,13 +46,31 @@ public class ProjectMeasuresQueryTest { @Test public void add_metric_criterion() throws Exception { - underTest.addMetricCriterion(new MetricCriterion("coverage", EQ, 10d)); + underTest.addMetricCriterion(MetricCriterion.create("coverage", EQ, 10d)); assertThat(underTest.getMetricCriteria()) .extracting(MetricCriterion::getMetricKey, MetricCriterion::getOperator, MetricCriterion::getValue) .containsOnly(tuple("coverage", EQ, 10d)); } + @Test + public void isNoData_returns_true_when_no_data() throws Exception { + underTest.addMetricCriterion(MetricCriterion.createNoData("coverage")); + + assertThat(underTest.getMetricCriteria()) + .extracting(MetricCriterion::getMetricKey, MetricCriterion::isNoData) + .containsOnly(tuple("coverage", true)); + } + + @Test + public void isNoData_returns_false_when_data_exists() throws Exception { + underTest.addMetricCriterion(MetricCriterion.create("coverage", EQ, 10d)); + + assertThat(underTest.getMetricCriteria()) + .extracting(MetricCriterion::getMetricKey, MetricCriterion::getOperator, MetricCriterion::isNoData) + .containsOnly(tuple("coverage", EQ, false)); + } + @Test public void set_quality_gate_status() throws Exception { underTest.setQualityGateStatus(OK); @@ -72,4 +90,20 @@ public class ProjectMeasuresQueryTest { underTest.setSort(null); } + + @Test + public void fail_to_get_value_when_no_data() throws Exception { + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("The criterion for metric coverage has no data"); + + MetricCriterion.createNoData("coverage").getValue(); + } + + @Test + public void fail_to_get_operator_when_no_data() throws Exception { + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("The criterion for metric coverage has no data"); + + MetricCriterion.createNoData("coverage").getOperator(); + } } -- 2.39.5