diff options
author | Julien Lancelot <julien.lancelot@sonarsource.com> | 2016-11-04 15:05:13 +0100 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@sonarsource.com> | 2016-11-08 11:12:51 +0100 |
commit | c654cda21f67a65da5528a56653ecae721484f04 (patch) | |
tree | f9c21b6b923d975eb197f493ddbacfc196f5a019 /sonar-db | |
parent | b67b21e7324e78bab3876f460cfe426455b2d367 (diff) | |
download | sonarqube-c654cda21f67a65da5528a56653ecae721484f04.tar.gz sonarqube-c654cda21f67a65da5528a56653ecae721484f04.zip |
SONAR-8089 Create new MeasureDao#selectTreeByQuery
Diffstat (limited to 'sonar-db')
13 files changed, 462 insertions, 42 deletions
diff --git a/sonar-db/src/main/java/org/sonar/db/component/ComponentDao.java b/sonar-db/src/main/java/org/sonar/db/component/ComponentDao.java index 62c4652b05d..16e4dab6eac 100644 --- a/sonar-db/src/main/java/org/sonar/db/component/ComponentDao.java +++ b/sonar-db/src/main/java/org/sonar/db/component/ComponentDao.java @@ -175,7 +175,7 @@ public class ComponentDao implements Dao { return emptyList(); } ComponentDto component = componentOpt.get(); - return mapper(dbSession).selectDescendants(query, query.getUuidPath(component)); + return mapper(dbSession).selectDescendants(query, componentOpt.get().uuid(), query.getUuidPath(component)); } public ComponentDto selectOrFailByKey(DbSession session, String key) { diff --git a/sonar-db/src/main/java/org/sonar/db/component/ComponentMapper.java b/sonar-db/src/main/java/org/sonar/db/component/ComponentMapper.java index 7e41d5c97b4..6badff92945 100644 --- a/sonar-db/src/main/java/org/sonar/db/component/ComponentMapper.java +++ b/sonar-db/src/main/java/org/sonar/db/component/ComponentMapper.java @@ -64,7 +64,7 @@ public interface ComponentMapper { List<ComponentDto> selectAncestors(@Param("query") ComponentTreeQuery query, @Param("baseUuidPathLike") String baseUuidPathLike); - List<ComponentDto> selectDescendants(@Param("query") ComponentTreeQuery query, @Param("baseUuidPath") String baseUuidPath); + List<ComponentDto> selectDescendants(@Param("query") ComponentTreeQuery query, @Param("baseUuid") String baseUuid, @Param("baseUuidPath") String baseUuidPath); /** * Return all project (PRJ/TRK) uuids diff --git a/sonar-db/src/main/java/org/sonar/db/component/ComponentTreeQuery.java b/sonar-db/src/main/java/org/sonar/db/component/ComponentTreeQuery.java index c8f47f6cd75..dff3bc31448 100644 --- a/sonar-db/src/main/java/org/sonar/db/component/ComponentTreeQuery.java +++ b/sonar-db/src/main/java/org/sonar/db/component/ComponentTreeQuery.java @@ -68,6 +68,7 @@ public class ComponentTreeQuery { this.sqlSort = builder.sortFields != null ? sortFieldsToSqlSort(builder.sortFields, direction) : null; } + @CheckForNull public Collection<String> getQualifiers() { return qualifiers; } diff --git a/sonar-db/src/main/java/org/sonar/db/measure/MeasureDao.java b/sonar-db/src/main/java/org/sonar/db/measure/MeasureDao.java index 9deba33c76c..0daf18d49b1 100644 --- a/sonar-db/src/main/java/org/sonar/db/measure/MeasureDao.java +++ b/sonar-db/src/main/java/org/sonar/db/measure/MeasureDao.java @@ -28,6 +28,7 @@ import java.util.Optional; import org.apache.ibatis.session.ResultHandler; import org.sonar.db.Dao; import org.sonar.db.DbSession; +import org.sonar.db.component.ComponentDto; import static org.sonar.db.DatabaseUtils.executeLargeInputs; import static org.sonar.db.DatabaseUtils.executeLargeInputsWithoutOutput; @@ -103,6 +104,13 @@ public class MeasureDao implements Dao { mapper(dbSession).selectByQueryOnSingleComponent(query, resultHandler); } + public List<MeasureDto> selectTreeByQuery(DbSession dbSession, ComponentDto baseComponent, MeasureTreeQuery query) { + if (query.returnsEmpty()) { + return Collections.emptyList(); + } + return mapper(dbSession).selectTreeByQuery(query, baseComponent.uuid(), query.getUuidPath(baseComponent)); + } + public List<PastMeasureDto> selectPastMeasures(DbSession dbSession, String componentUuid, String analysisUuid, diff --git a/sonar-db/src/main/java/org/sonar/db/measure/MeasureMapper.java b/sonar-db/src/main/java/org/sonar/db/measure/MeasureMapper.java index 7b385815f20..978b8b0c47c 100644 --- a/sonar-db/src/main/java/org/sonar/db/measure/MeasureMapper.java +++ b/sonar-db/src/main/java/org/sonar/db/measure/MeasureMapper.java @@ -38,6 +38,8 @@ public interface MeasureMapper { void selectByQueryOnSingleComponent(@Param("query") MeasureQuery query, ResultHandler resultHandler); + List<MeasureDto> selectTreeByQuery(@Param("query") MeasureTreeQuery measureQuery, @Param("baseUuid") String baseUuid, @Param("baseUuidPath") String baseUuidPath); + List<PastMeasureDto> selectPastMeasures(@Param("componentUuid") String componentUuid, @Param("analysisUuid") String analysisUuid, @Param("metricIds") List<Integer> metricIds); List<MeasureDto> selectProjectMeasuresOfDeveloper(@Param("developerId") long developerId, @Param("metricIds") Collection<Integer> metricIds); diff --git a/sonar-db/src/main/java/org/sonar/db/measure/MeasureQuery.java b/sonar-db/src/main/java/org/sonar/db/measure/MeasureQuery.java index 382c44f5313..ec557a7b746 100644 --- a/sonar-db/src/main/java/org/sonar/db/measure/MeasureQuery.java +++ b/sonar-db/src/main/java/org/sonar/db/measure/MeasureQuery.java @@ -29,6 +29,7 @@ import static java.util.Collections.singleton; import static java.util.Objects.requireNonNull; public class MeasureQuery { + @CheckForNull private final String analysisUuid; @CheckForNull @@ -136,7 +137,7 @@ public class MeasureQuery { return false; } MeasureQuery that = (MeasureQuery) o; - return analysisUuid.equals(that.analysisUuid) && + return Objects.equals(analysisUuid, that.analysisUuid) && Objects.equals(projectUuids, that.projectUuids) && Objects.equals(componentUuids, that.componentUuids) && Objects.equals(metricIds, that.metricIds) && diff --git a/sonar-db/src/main/java/org/sonar/db/measure/MeasureTreeQuery.java b/sonar-db/src/main/java/org/sonar/db/measure/MeasureTreeQuery.java new file mode 100644 index 00000000000..8ccd5a6a85d --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/measure/MeasureTreeQuery.java @@ -0,0 +1,158 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.db.measure; + +import java.util.Collection; +import java.util.Locale; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.sonar.db.WildcardPosition; +import org.sonar.db.component.ComponentDto; + +import static com.google.common.collect.Lists.newArrayList; +import static java.util.Objects.requireNonNull; +import static org.sonar.db.DatabaseUtils.buildLikeValue; +import static org.sonar.db.WildcardPosition.AFTER; + +public class MeasureTreeQuery { + + public enum Strategy { + CHILDREN, LEAVES + } + + @CheckForNull + private final String nameOrKeyQuery; + // SONAR-7681 a public implementation of List must be used in MyBatis - potential concurrency exceptions otherwise + @CheckForNull + private final Collection<String> qualifiers; + private final Strategy strategy; + + @CheckForNull + private final Collection<Integer> metricIds; + + @CheckForNull + private final Long personId; + + private MeasureTreeQuery(Builder builder) { + this.nameOrKeyQuery = builder.nameOrKeyQuery; + this.qualifiers = builder.qualifiers == null ? null : newArrayList(builder.qualifiers); + this.strategy = requireNonNull(builder.strategy); + this.metricIds = builder.metricIds; + this.personId = builder.personId; + } + + @CheckForNull + public String getNameOrKeyQuery() { + return nameOrKeyQuery; + } + + @CheckForNull + public String getNameOrKeyQueryToSqlForResourceIndex() { + return nameOrKeyQuery == null ? null : buildLikeValue(nameOrKeyQuery, AFTER).toLowerCase(Locale.ENGLISH); + } + + @CheckForNull + public Collection<String> getQualifiers() { + return qualifiers; + } + + public Strategy getStrategy() { + return strategy; + } + + @CheckForNull + public Collection<Integer> getMetricIds() { + return metricIds; + } + + @CheckForNull + public Long getPersonId() { + return personId; + } + + public String getUuidPath(ComponentDto component) { + switch (strategy) { + case CHILDREN: + return component.getUuidPath() + component.uuid() + "."; + case LEAVES: + return buildLikeValue(component.getUuidPath() + component.uuid() + ".", WildcardPosition.AFTER); + default: + throw new IllegalArgumentException("Unknown strategy : " + strategy); + } + } + + public boolean returnsEmpty() { + return (metricIds != null && metricIds.isEmpty()) || (qualifiers != null && qualifiers.isEmpty()); + } + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + + @CheckForNull + private String nameOrKeyQuery; + @CheckForNull + private Collection<String> qualifiers; + private Strategy strategy; + + @CheckForNull + private Collection<Integer> metricIds; + + @CheckForNull + private Long personId; + + private Builder() { + } + + public Builder setNameOrKeyQuery(@Nullable String nameOrKeyQuery) { + this.nameOrKeyQuery = nameOrKeyQuery; + return this; + } + + public Builder setQualifiers(Collection<String> qualifiers) { + this.qualifiers = qualifiers; + return this; + } + + public Builder setStrategy(Strategy strategy) { + this.strategy = requireNonNull(strategy); + return this; + } + + /** + * All the measures are returned if parameter is {@code null}. + */ + public Builder setMetricIds(@Nullable Collection<Integer> metricIds) { + this.metricIds = metricIds; + return this; + } + + public Builder setPersonId(@Nullable Long personId) { + this.personId = personId; + return this; + } + + public MeasureTreeQuery build() { + return new MeasureTreeQuery(this); + } + } +} diff --git a/sonar-db/src/main/resources/org/sonar/db/component/ComponentMapper.xml b/sonar-db/src/main/resources/org/sonar/db/component/ComponentMapper.xml index 8a3aa4973a9..4ca89c19d30 100644 --- a/sonar-db/src/main/resources/org/sonar/db/component/ComponentMapper.xml +++ b/sonar-db/src/main/resources/org/sonar/db/component/ComponentMapper.xml @@ -334,23 +334,23 @@ <select id="selectDescendants" resultType="Component"> select <include refid="componentColumns"/> - <include refid="selectDescendantsQuery"/> - </select> - - <sql id="selectDescendantsQuery"> from projects p - inner join projects base on base.project_uuid = p.project_uuid and base.uuid = #{query.baseUuid} + <include refid="selectDescendantsJoins"/> <where> - <choose> - <when test="query.getStrategy().name() == 'CHILDREN'"> - and p.uuid_path = #{baseUuidPath} - </when> - <otherwise> - and p.uuid_path like #{baseUuidPath} ESCAPE '/' - </otherwise> - </choose> <include refid="selectDescendantsFilters"/> </where> + </select> + + <sql id="selectDescendantsJoins"> + inner join projects base on base.project_uuid = p.project_uuid and base.uuid = #{baseUuid} + <choose> + <when test="query.getStrategy().name() == 'CHILDREN'"> + and p.uuid_path = #{baseUuidPath} + </when> + <otherwise> + and p.uuid_path like #{baseUuidPath} ESCAPE '/' + </otherwise> + </choose> </sql> <sql id="selectDescendantsFilters"> diff --git a/sonar-db/src/main/resources/org/sonar/db/measure/MeasureMapper.xml b/sonar-db/src/main/resources/org/sonar/db/measure/MeasureMapper.xml index 00534d151eb..61b1268b219 100644 --- a/sonar-db/src/main/resources/org/sonar/db/measure/MeasureMapper.xml +++ b/sonar-db/src/main/resources/org/sonar/db/measure/MeasureMapper.xml @@ -85,14 +85,48 @@ </if> <choose> <when test="query.getPersonId() != null"> - and person_id = #{query.personId} + and pm.person_id = #{query.personId} </when> <otherwise> - and person_id is null + and pm.person_id is null </otherwise> </choose> </sql> + <select id="selectTreeByQuery" parameterType="map" resultType="Measure"> + select <include refid="measureColumns"/> from project_measures pm + inner join snapshots analysis on analysis.uuid = pm.analysis_uuid + inner join projects p on p.project_uuid=analysis.component_uuid and p.uuid=pm.component_uuid + <include refid="org.sonar.db.component.ComponentMapper.selectDescendantsJoins"/> + <where> + <include refid="selectTreeByQueryFilters"/> + </where> + -- Add measures of base component + union all + select <include refid="measureColumns"/> from project_measures pm + inner join snapshots analysis on analysis.uuid = pm.analysis_uuid + inner join projects p on p.project_uuid=analysis.component_uuid and p.uuid=pm.component_uuid and pm.component_uuid=#{baseUuid} + <where> + <include refid="selectTreeByQueryFilters"/> + </where> + </select> + + <sql id="selectTreeByQueryFilters"> + <if test="query.getMetricIds() != null"> + and pm.metric_id in + <foreach item="metricId" collection="query.getMetricIds()" open="(" separator="," close=")">#{metricId}</foreach> + </if> + <choose> + <when test="query.getPersonId() != null"> + and pm.person_id = #{query.personId} + </when> + <otherwise> + and pm.person_id is null + </otherwise> + </choose> + <include refid="org.sonar.db.component.ComponentMapper.selectDescendantsFilters"/> + </sql> + <select id="selectPastMeasures" parameterType="map" resultType="org.sonar.db.measure.PastMeasureDto"> select pm.id as id, pm.metric_id as metricId, pm.person_id as personId, pm.value as value from project_measures pm diff --git a/sonar-db/src/test/java/org/sonar/db/component/ComponentDaoTest.java b/sonar-db/src/test/java/org/sonar/db/component/ComponentDaoTest.java index 70f3e206e23..a74b0460405 100644 --- a/sonar-db/src/test/java/org/sonar/db/component/ComponentDaoTest.java +++ b/sonar-db/src/test/java/org/sonar/db/component/ComponentDaoTest.java @@ -948,7 +948,7 @@ public class ComponentDaoTest { } @Test - public void select_descendants_with_leaves_stragegy() { + public void select_descendants_with_leaves_strategy() { ComponentDto project = newProjectDto(PROJECT_UUID); componentDb.insertProjectAndSnapshot(project); componentDb.insertComponent(newModuleDto("module-1-uuid", project)); diff --git a/sonar-db/src/test/java/org/sonar/db/component/ComponentTreeQueryTest.java b/sonar-db/src/test/java/org/sonar/db/component/ComponentTreeQueryTest.java index 44795f230a0..405f6a5449d 100644 --- a/sonar-db/src/test/java/org/sonar/db/component/ComponentTreeQueryTest.java +++ b/sonar-db/src/test/java/org/sonar/db/component/ComponentTreeQueryTest.java @@ -23,42 +23,67 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import static com.google.common.collect.Lists.newArrayList; -import static java.util.Collections.singletonList; +import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.db.component.ComponentTreeQuery.Strategy.CHILDREN; +import static org.sonar.db.component.ComponentTreeQuery.Strategy.LEAVES; public class ComponentTreeQueryTest { - private static final String AN_UUID = "u1"; - + private static final String BASE_UUID = "ABCD"; @Rule public ExpectedException expectedException = ExpectedException.none(); @Test - public void convert_sorts_in_sql_representation() { - ComponentTreeQuery result = ComponentTreeQuery.builder() - .setBaseUuid(AN_UUID) - .setSortFields(newArrayList("name", "path", "qualifier")) + public void create_query() throws Exception { + ComponentTreeQuery query = ComponentTreeQuery.builder() + .setBaseUuid(BASE_UUID) + .setStrategy(CHILDREN) + .setQualifiers(asList("FIL", "DIR")) + .setNameOrKeyQuery("teSt") .build(); - assertThat(result.getSqlSort()).isEqualTo("LOWER(p.name) ASC, p.name ASC, LOWER(p.path) ASC, p.path ASC, LOWER(p.qualifier) ASC, p.qualifier ASC"); + assertThat(query.getBaseUuid()).isEqualTo(BASE_UUID); + assertThat(query.getStrategy()).isEqualTo(CHILDREN); + assertThat(query.getQualifiers()).containsOnly("FIL", "DIR"); + assertThat(query.getNameOrKeyQuery()).isEqualTo("teSt"); } @Test - public void fail_if_no_base_uuid() { - expectedException.expect(NullPointerException.class); + public void create_minimal_query() throws Exception { + ComponentTreeQuery query = ComponentTreeQuery.builder() + .setBaseUuid(BASE_UUID) + .setStrategy(CHILDREN) + .build(); + + assertThat(query.getBaseUuid()).isEqualTo(BASE_UUID); + assertThat(query.getStrategy()).isEqualTo(CHILDREN); + assertThat(query.getQualifiers()).isNull(); + assertThat(query.getNameOrKeyQuery()).isNull(); + } + + @Test + public void test_getUuidPath() throws Exception { + assertThat(ComponentTreeQuery.builder().setBaseUuid(BASE_UUID).setStrategy(CHILDREN) + .build().getUuidPath(ComponentTesting.newProjectDto("PROJECT_UUID"))).isEqualTo(".PROJECT_UUID."); + + assertThat(ComponentTreeQuery.builder().setBaseUuid(BASE_UUID).setStrategy(LEAVES) + .build().getUuidPath(ComponentTesting.newProjectDto("PROJECT_UUID"))).isEqualTo(".PROJECT/_UUID.%"); + } + @Test + public void fail_when_no_base_uuid() throws Exception { + expectedException.expect(NullPointerException.class); ComponentTreeQuery.builder() - .setSortFields(singletonList("name")) + .setStrategy(CHILDREN) .build(); } @Test - public void fail_if_no_sort() { + public void fail_when_no_strategy() throws Exception { expectedException.expect(NullPointerException.class); - ComponentTreeQuery.builder() - .setBaseUuid(AN_UUID) + .setBaseUuid(BASE_UUID) .build(); } } diff --git a/sonar-db/src/test/java/org/sonar/db/measure/MeasureDaoTest.java b/sonar-db/src/test/java/org/sonar/db/measure/MeasureDaoTest.java index af492d3666f..beddaf6c737 100644 --- a/sonar-db/src/test/java/org/sonar/db/measure/MeasureDaoTest.java +++ b/sonar-db/src/test/java/org/sonar/db/measure/MeasureDaoTest.java @@ -27,21 +27,26 @@ import javax.annotation.Nullable; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import org.sonar.api.resources.Qualifiers; import org.sonar.api.resources.Scopes; import org.sonar.api.utils.System2; import org.sonar.core.util.UuidFactoryImpl; import org.sonar.db.DbTester; import org.sonar.db.component.ComponentDto; -import org.sonar.db.component.ComponentTesting; import org.sonar.db.component.SnapshotTesting; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.api.resources.Qualifiers.FILE; +import static org.sonar.api.resources.Qualifiers.PROJECT; +import static org.sonar.api.resources.Qualifiers.UNIT_TEST_FILE; +import static org.sonar.api.resources.Qualifiers.VIEW; import static org.sonar.db.component.ComponentTesting.newDeveloper; import static org.sonar.db.component.ComponentTesting.newFileDto; +import static org.sonar.db.component.ComponentTesting.newModuleDto; +import static org.sonar.db.measure.MeasureTreeQuery.Strategy.CHILDREN; +import static org.sonar.db.measure.MeasureTreeQuery.Strategy.LEAVES; public class MeasureDaoTest { @@ -51,10 +56,11 @@ public class MeasureDaoTest { private static final long A_PERSON_ID = 444L; private static final String LAST_ANALYSIS_UUID = "A1"; private static final String OTHER_ANALYSIS_UUID = "A2"; - public static final String PREVIOUS_ANALYSIS_UUID = "previous analysis UUID"; + private static final String PREVIOUS_ANALYSIS_UUID = "previous analysis UUID"; @Rule public ExpectedException expectedException = ExpectedException.none(); + @Rule public DbTester db = DbTester.create(System2.INSTANCE); @@ -106,7 +112,7 @@ public class MeasureDaoTest { @Test public void selectByQuery() { ComponentDto project1 = db.components().insertProject(); - ComponentDto module = db.components().insertComponent(ComponentTesting.newModuleDto(project1)); + ComponentDto module = db.components().insertComponent(newModuleDto(project1)); db.components().insertComponent(newFileDto(module).setUuid("C1")); db.components().insertComponent(newFileDto(module).setUuid("C2")); insertAnalysis(LAST_ANALYSIS_UUID, project1.uuid(), true); @@ -201,7 +207,7 @@ public class MeasureDaoTest { @Test public void selectByQuery_with_handler() { ComponentDto project1 = db.components().insertProject(); - ComponentDto module = db.components().insertComponent(ComponentTesting.newModuleDto(project1)); + ComponentDto module = db.components().insertComponent(newModuleDto(project1)); db.components().insertComponent(newFileDto(module).setUuid("C1")); db.components().insertComponent(newFileDto(module).setUuid("C2")); insertAnalysis(LAST_ANALYSIS_UUID, project1.uuid(), true); @@ -325,9 +331,9 @@ public class MeasureDaoTest { long developerId = dev.getId(); assertThat(underTest.selectProjectMeasuresOfDeveloper(db.getSession(), developerId, allMetricIds)).isEmpty(); - String projectUuid = insertComponent(Scopes.PROJECT, Qualifiers.PROJECT, true); - String viewUuid = insertComponent(Scopes.PROJECT, Qualifiers.VIEW, true); - String disabledProjectUuid = insertComponent(Scopes.PROJECT, Qualifiers.PROJECT, false); + String projectUuid = insertComponent(Scopes.PROJECT, PROJECT, true); + String viewUuid = insertComponent(Scopes.PROJECT, VIEW, true); + String disabledProjectUuid = insertComponent(Scopes.PROJECT, PROJECT, false); insertMeasure("M1", LAST_ANALYSIS_UUID, projectUuid, NCLOC_METRIC_ID); insertMeasure("M2", LAST_ANALYSIS_UUID, projectUuid, COMPLEXITY_METRIC_ID); insertMeasure("M3", LAST_ANALYSIS_UUID, projectUuid, COVERAGE_METRIC_ID); @@ -355,6 +361,58 @@ public class MeasureDaoTest { .containsOnly("M11", "M54"); } + @Test + public void select_tree_by_query() { + ComponentDto project = db.components().insertProject(); + ComponentDto module1 = db.components().insertComponent(newModuleDto(project)); + ComponentDto module2 = db.components().insertComponent(newModuleDto(project)); + ComponentDto file1 = db.components().insertComponent(newFileDto(module1).setUuid("C1").setName("File One")); + db.components().insertComponent(newFileDto(module2).setUuid("C2").setName("File Two").setQualifier(UNIT_TEST_FILE)); + insertAnalysis(LAST_ANALYSIS_UUID, project.uuid(), true); + db.components().indexAllComponents(); + + // project + insertMeasure("PROJECT_M1", LAST_ANALYSIS_UUID, project.uuid(), NCLOC_METRIC_ID); + // module 1 + insertMeasure("MODULE_M1", LAST_ANALYSIS_UUID, module1.uuid(), NCLOC_METRIC_ID); + // component C1 + insertMeasure("M2", LAST_ANALYSIS_UUID, "C1", NCLOC_METRIC_ID); + insertMeasure("M3", LAST_ANALYSIS_UUID, "C1", COVERAGE_METRIC_ID); + insertMeasureOnPerson("M4", LAST_ANALYSIS_UUID, "C1", NCLOC_METRIC_ID, A_PERSON_ID); + // component C2 + insertMeasure("M6", LAST_ANALYSIS_UUID, "C2", NCLOC_METRIC_ID); + db.commit(); + + // Children measures of project + verifyMeasures(project, MeasureTreeQuery.builder().setStrategy(CHILDREN), "PROJECT_M1", "MODULE_M1"); + + // Children measures of module 1 + verifyMeasures(module1, MeasureTreeQuery.builder().setStrategy(CHILDREN), "M2", "M3", "MODULE_M1"); + + // Children measure on file => only measures from itself + verifyMeasures(file1, MeasureTreeQuery.builder().setStrategy(CHILDREN), "M2", "M3"); + + // Leaves measures of project + verifyMeasures(project, MeasureTreeQuery.builder().setStrategy(LEAVES), "PROJECT_M1", "MODULE_M1", "M2", "M3", "M6"); + + // Leaves measures of module 1 + verifyMeasures(module1, MeasureTreeQuery.builder().setStrategy(LEAVES), "MODULE_M1", "M2", "M3"); + + // Leaves measures of project by metric ids + verifyMeasures(project, MeasureTreeQuery.builder().setMetricIds(asList(NCLOC_METRIC_ID)).setStrategy(LEAVES), "PROJECT_M1", "MODULE_M1", "M2", + "M6"); + + // Leaves measure on file + verifyMeasures(file1, MeasureTreeQuery.builder().setStrategy(LEAVES), "M2", "M3"); + + // Leaves measures of project matching name + verifyMeasures(project, MeasureTreeQuery.builder().setNameOrKeyQuery("OnE").setStrategy(LEAVES), "M2", "M3"); + + // Leaves measures of project matching qualifiers + verifyMeasures(project, MeasureTreeQuery.builder().setQualifiers(asList(FILE)).setStrategy(LEAVES), "M2", "M3"); + verifyMeasures(project, MeasureTreeQuery.builder().setQualifiers(asList(FILE, UNIT_TEST_FILE)).setStrategy(LEAVES), "M2", "M3", "M6"); + } + private Optional<MeasureDto> selectSingle(MeasureQuery.Builder query) { return underTest.selectSingle(db.getSession(), query.build()); } @@ -378,6 +436,16 @@ public class MeasureDaoTest { assertThat(measures).isEmpty(); } + private void verifyMeasures(ComponentDto baseComponent, MeasureTreeQuery.Builder measureQuery, String... expectedIds) { + assertThat(underTest.selectTreeByQuery(db.getSession(), baseComponent, measureQuery.build())) + .extracting(MeasureDto::getData).containsOnly(expectedIds); + } + + private void verifyZeroMeasures(ComponentDto baseComponent, MeasureTreeQuery.Builder measureQuery) { + assertThat(underTest.selectTreeByQuery(db.getSession(), baseComponent, + measureQuery.build())).isEmpty(); + } + private List<MeasureDto> getMeasuresWithHandler(MeasureQuery.Builder query) { List<MeasureDto> measures = new ArrayList<>(); underTest.selectByQuery(db.getSession(), query.build(), resultContext -> measures.add((MeasureDto) resultContext.getResultObject())); diff --git a/sonar-db/src/test/java/org/sonar/db/measure/MeasureTreeQueryTest.java b/sonar-db/src/test/java/org/sonar/db/measure/MeasureTreeQueryTest.java new file mode 100644 index 00000000000..9975ac99043 --- /dev/null +++ b/sonar-db/src/test/java/org/sonar/db/measure/MeasureTreeQueryTest.java @@ -0,0 +1,123 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.db.measure; + +import java.util.Collections; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.db.component.ComponentTesting; + +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.db.measure.MeasureTreeQuery.Strategy.CHILDREN; +import static org.sonar.db.measure.MeasureTreeQuery.Strategy.LEAVES; + +public class MeasureTreeQueryTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void create_query() throws Exception { + MeasureTreeQuery query = MeasureTreeQuery.builder() + .setStrategy(CHILDREN) + .setQualifiers(asList("FIL", "DIR")) + .setNameOrKeyQuery("teSt") + .setMetricIds(asList(10, 11)) + .setPersonId(100L) + .build(); + + assertThat(query.getStrategy()).isEqualTo(CHILDREN); + assertThat(query.getQualifiers()).containsOnly("FIL", "DIR"); + assertThat(query.getNameOrKeyQuery()).isEqualTo("teSt"); + assertThat(query.getMetricIds()).containsOnly(10, 11); + assertThat(query.getPersonId()).isEqualTo(100L); + } + + @Test + public void create_minimal_query() throws Exception { + MeasureTreeQuery query = MeasureTreeQuery.builder() + .setStrategy(CHILDREN) + .build(); + + assertThat(query.getStrategy()).isEqualTo(CHILDREN); + assertThat(query.getQualifiers()).isNull(); + assertThat(query.getNameOrKeyQuery()).isNull(); + assertThat(query.getMetricIds()).isNull(); + assertThat(query.getPersonId()).isNull(); + } + + @Test + public void test_getNameOrKeyQueryToSqlForResourceIndex() throws Exception { + assertThat(MeasureTreeQuery.builder() + .setNameOrKeyQuery("like-\\_%/-value") + .setStrategy(CHILDREN) + .build().getNameOrKeyQueryToSqlForResourceIndex()).isEqualTo("like-\\/_/%//-value%"); + + assertThat(MeasureTreeQuery.builder() + .setStrategy(CHILDREN) + .build().getNameOrKeyQueryToSqlForResourceIndex()).isNull(); + } + + @Test + public void test_getUuidPath() throws Exception { + assertThat(MeasureTreeQuery.builder().setStrategy(CHILDREN) + .build().getUuidPath(ComponentTesting.newProjectDto("PROJECT_UUID"))).isEqualTo(".PROJECT_UUID."); + + assertThat(MeasureTreeQuery.builder().setStrategy(LEAVES) + .build().getUuidPath(ComponentTesting.newProjectDto("PROJECT_UUID"))).isEqualTo(".PROJECT/_UUID.%"); + } + + @Test + public void return_empty_when_metrics_is_empty() throws Exception { + assertThat(MeasureTreeQuery.builder() + .setStrategy(CHILDREN) + .setMetricIds(Collections.emptyList()) + .build().returnsEmpty()).isTrue(); + + assertThat(MeasureTreeQuery.builder() + .setStrategy(CHILDREN) + .setMetricIds(null) + .build().returnsEmpty()).isFalse(); + } + + @Test + public void return_empty_when_qualifiers_is_empty() throws Exception { + assertThat(MeasureTreeQuery.builder() + .setStrategy(CHILDREN) + .setQualifiers(Collections.emptyList()) + .build().returnsEmpty()).isTrue(); + + assertThat(MeasureTreeQuery.builder() + .setStrategy(CHILDREN) + .setQualifiers(asList("FIL", "DIR")) + .build().returnsEmpty()).isFalse(); + } + + @Test + public void fail_when_no_strategy() throws Exception { + expectedException.expect(NullPointerException.class); + MeasureTreeQuery.builder() + .build(); + } + +} |