Quellcode durchsuchen

SONAR-10116 Better scalability of loading of project measures

tags/7.0-RC1
Daniel Schwarz vor 6 Jahren
Ursprung
Commit
d987a6a527
100 geänderte Dateien mit 2996 neuen und 2054 gelöschten Zeilen
  1. 1
    1
      server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java
  2. 1
    0
      server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java
  3. 17
    1
      server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl
  4. 2
    0
      server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java
  5. 7
    0
      server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java
  6. 2
    0
      server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java
  7. 0
    13
      server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDto.java
  8. 0
    11
      server/sonar-db-dao/src/main/java/org/sonar/db/component/ResourceDto.java
  9. 100
    0
      server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureDao.java
  10. 141
    0
      server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureDto.java
  11. 58
    0
      server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureMapper.java
  12. 6
    30
      server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureDao.java
  13. 0
    12
      server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureDto.java
  14. 14
    10
      server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureMapper.java
  15. 6
    23
      server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureQuery.java
  16. 0
    17
      server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureTreeQuery.java
  17. 0
    14
      server/sonar-db-dao/src/main/java/org/sonar/db/measure/PastMeasureDto.java
  18. 13
    26
      server/sonar-db-dao/src/main/java/org/sonar/db/measure/ProjectMeasuresIndexerIterator.java
  19. 21
    13
      server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java
  20. 2
    2
      server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeConfiguration.java
  21. 1
    0
      server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java
  22. 2
    0
      server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java
  23. 0
    3
      server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml
  24. 110
    0
      server/sonar-db-dao/src/main/resources/org/sonar/db/measure/LiveMeasureMapper.xml
  25. 22
    83
      server/sonar-db-dao/src/main/resources/org/sonar/db/measure/MeasureMapper.xml
  26. 7
    8
      server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml
  27. 1
    1
      server/sonar-db-dao/src/test/java/org/sonar/db/DaoModuleTest.java
  28. 0
    1
      server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java
  29. 1
    3
      server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDtoTest.java
  30. 146
    0
      server/sonar-db-dao/src/test/java/org/sonar/db/measure/LiveMeasureDaoTest.java
  31. 73
    288
      server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureDaoTest.java
  32. 10
    2
      server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureDbTester.java
  33. 0
    7
      server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureQueryTest.java
  34. 21
    2
      server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureTesting.java
  35. 0
    3
      server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureTreeQueryTest.java
  36. 3
    7
      server/sonar-db-dao/src/test/java/org/sonar/db/measure/PastMeasureDtoTest.java
  37. 92
    133
      server/sonar-db-dao/src/test/java/org/sonar/db/measure/ProjectMeasuresIndexerIteratorTest.java
  38. 1
    1
      server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeCommandsTest.java
  39. 24
    3
      server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java
  40. 1
    1
      server/sonar-db-dao/src/test/resources/org/sonar/db/measure/MeasureDaoTest/insert-result.xml
  41. 0
    59
      server/sonar-db-dao/src/test/resources/org/sonar/db/measure/MeasureDaoTest/past_measures_with_person_id.xml
  42. 0
    123
      server/sonar-db-dao/src/test/resources/org/sonar/db/measure/MeasureDaoTest/with_some_measures_for_developer.xml
  43. 0
    19
      server/sonar-db-dao/src/test/resources/org/sonar/db/purge/PurgeCommandsTest/shouldDeleteWastedMeasuresWhenPurgingAnalysis.xml
  44. 0
    1
      server/sonar-db-dao/src/test/resources/org/sonar/db/purge/PurgeCommandsTest/shouldPurgeAnalysis.xml
  45. 11
    0
      server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/BaseSqlStatement.java
  46. 5
    0
      server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DataChange.java
  47. 2
    0
      server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/SqlStatement.java
  48. 125
    0
      server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/CreateTableLiveMeasures.java
  49. 6
    2
      server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70.java
  50. 84
    0
      server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DeletePersonAndFileMeasures.java
  51. 40
    0
      server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DropIndexOnPersonMeasures.java
  52. 72
    0
      server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasures.java
  53. 59
    0
      server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/CreateTableLiveMeasuresTest.java
  54. 1
    1
      server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70Test.java
  55. 125
    0
      server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DeletePersonAndFileMeasuresTest.java
  56. 48
    0
      server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DropIndexOnPersonMeasuresTest.java
  57. 140
    0
      server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasuresTest.java
  58. 0
    0
      server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/CreateTableLiveMeasuresTest/empty.sql
  59. 98
    0
      server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/DeletePersonAndFileMeasuresTest/initial.sql
  60. 21
    0
      server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/DropIndexOnPersonMeasuresTest/initial.sql
  61. 116
    0
      server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasuresTest/initial.sql
  62. 9
    11
      server/sonar-server/src/main/java/org/sonar/server/branch/ws/ListAction.java
  63. 16
    18
      server/sonar-server/src/main/java/org/sonar/server/component/ws/AppAction.java
  64. 2
    2
      server/sonar-server/src/main/java/org/sonar/server/computation/dbcleaner/ProjectCleaner.java
  65. 3
    4
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/BestValueOptimization.java
  66. 0
    6
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/MapBasedRawMeasureRepository.java
  67. 2
    7
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureRepository.java
  68. 5
    39
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureRepositoryImpl.java
  69. 17
    10
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureToMeasureDto.java
  70. 133
    0
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistLiveMeasuresStep.java
  71. 49
    31
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistMeasuresStep.java
  72. 1
    0
      server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/ReportComputationSteps.java
  73. 3
    8
      server/sonar-server/src/main/java/org/sonar/server/duplication/ws/ShowAction.java
  74. 16
    37
      server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentAction.java
  75. 3
    3
      server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentDtoToWsComponent.java
  76. 14
    19
      server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java
  77. 4
    4
      server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeData.java
  78. 0
    1
      server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeRequest.java
  79. 7
    0
      server/sonar-server/src/main/java/org/sonar/server/measure/ws/MeasureDtoToWsMeasure.java
  80. 4
    14
      server/sonar-server/src/main/java/org/sonar/server/measure/ws/MetricDtoWithBestValue.java
  81. 7
    7
      server/sonar-server/src/main/java/org/sonar/server/measure/ws/SearchAction.java
  82. 8
    15
      server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchMyProjectsAction.java
  83. 5
    5
      server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchMyProjectsData.java
  84. 26
    22
      server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/ProjectStatusAction.java
  85. 1
    1
      server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatter.java
  86. 5
    13
      server/sonar-server/src/main/java/org/sonar/server/ui/ws/ComponentAction.java
  87. 1
    3
      server/sonar-server/src/test/java/org/sonar/server/branch/ws/ListActionTest.java
  88. 8
    12
      server/sonar-server/src/test/java/org/sonar/server/component/ws/AppActionTest.java
  89. 21
    23
      server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchProjectsActionTest.java
  90. 35
    35
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/measure/BestValueOptimizationTest.java
  91. 11
    21
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureRepositoryRule.java
  92. 17
    24
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureToMeasureDtoTest.java
  93. 290
    0
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistLiveMeasuresStepTest.java
  94. 122
    287
      server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistMeasuresStepTest.java
  95. 2
    5
      server/sonar-server/src/test/java/org/sonar/server/duplication/ws/ShowActionTest.java
  96. 103
    96
      server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentActionTest.java
  97. 60
    81
      server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeActionTest.java
  98. 4
    4
      server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeSortTest.java
  99. 123
    262
      server/sonar-server/src/test/java/org/sonar/server/measure/ws/SearchActionTest.java
  100. 0
    0
      server/sonar-server/src/test/java/org/sonar/server/measure/ws/SearchHistoryActionTest.java

+ 1
- 1
server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java Datei anzeigen

@@ -115,7 +115,7 @@ public class ComputeEngineContainerImplTest {
assertThat(picoContainer.getParent().getParent().getParent().getComponentAdapters()).hasSize(
COMPONENTS_IN_LEVEL_1_AT_CONSTRUCTION
+ 26 // level 1
+ 51 // content of DaoModule
+ 52 // content of DaoModule
+ 3 // content of EsSearchModule
+ 67 // content of CorePropertyDefinitions
+ 1 // StopFlagContainer

+ 1
- 0
server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java Datei anzeigen

@@ -69,6 +69,7 @@ public final class SqTables {
"internal_properties",
"issues",
"issue_changes",
"live_measures",
"manual_measures",
"metrics",
"notifications",

+ 17
- 1
server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl Datei anzeigen

@@ -284,6 +284,23 @@ CREATE INDEX "DUPLICATIONS_INDEX_HASH" ON "DUPLICATIONS_INDEX" ("HASH");
CREATE INDEX "DUPLICATION_ANALYSIS_COMPONENT" ON "DUPLICATIONS_INDEX" ("ANALYSIS_UUID", "COMPONENT_UUID");


CREATE TABLE "LIVE_MEASURES" (
"UUID" VARCHAR(40) NOT NULL PRIMARY KEY,
"PROJECT_UUID" VARCHAR(50) NOT NULL,
"COMPONENT_UUID" VARCHAR(50) NOT NULL,
"METRIC_ID" INTEGER NOT NULL,
"VALUE" DOUBLE,
"TEXT_VALUE" VARCHAR(4000),
"VARIATION" DOUBLE,
"MEASURE_DATA" BINARY,
"UPDATE_MARKER" VARCHAR(40),
"CREATED_AT" BIGINT NOT NULL,
"UPDATED_AT" BIGINT NOT NULL
);
CREATE INDEX "LIVE_MEASURES_PROJECT" ON "LIVE_MEASURES" ("PROJECT_UUID");
CREATE UNIQUE INDEX "LIVE_MEASURES_COMPONENT" ON "LIVE_MEASURES" ("COMPONENT_UUID", "METRIC_ID");


CREATE TABLE "PROJECT_MEASURES" (
"ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
"VALUE" DOUBLE,
@@ -304,7 +321,6 @@ CREATE TABLE "PROJECT_MEASURES" (
);
CREATE INDEX "MEASURES_COMPONENT_UUID" ON "PROJECT_MEASURES" ("COMPONENT_UUID");
CREATE INDEX "MEASURES_ANALYSIS_METRIC" ON "PROJECT_MEASURES" ("ANALYSIS_UUID", "METRIC_ID");
CREATE INDEX "MEASURES_PERSON" ON "PROJECT_MEASURES" ("PERSON_ID");


CREATE TABLE "INTERNAL_PROPERTIES" (

+ 2
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java Datei anzeigen

@@ -39,6 +39,7 @@ import org.sonar.db.es.EsQueueDao;
import org.sonar.db.event.EventDao;
import org.sonar.db.issue.IssueChangeDao;
import org.sonar.db.issue.IssueDao;
import org.sonar.db.measure.LiveMeasureDao;
import org.sonar.db.measure.MeasureDao;
import org.sonar.db.measure.custom.CustomMeasureDao;
import org.sonar.db.metric.MetricDao;
@@ -92,6 +93,7 @@ public class DaoModule extends Module {
ComponentDao.class,
ComponentKeyUpdaterDao.class,
ComponentLinkDao.class,
LiveMeasureDao.class,
CustomMeasureDao.class,
DefaultQProfileDao.class,
DuplicationDao.class,

+ 7
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java Datei anzeigen

@@ -37,6 +37,7 @@ import org.sonar.db.es.EsQueueDao;
import org.sonar.db.event.EventDao;
import org.sonar.db.issue.IssueChangeDao;
import org.sonar.db.issue.IssueDao;
import org.sonar.db.measure.LiveMeasureDao;
import org.sonar.db.measure.MeasureDao;
import org.sonar.db.measure.custom.CustomMeasureDao;
import org.sonar.db.metric.MetricDao;
@@ -130,6 +131,7 @@ public class DbClient {
private final AnalysisPropertiesDao analysisPropertiesDao;
private final QProfileEditUsersDao qProfileEditUsersDao;
private final QProfileEditGroupsDao qProfileEditGroupsDao;
private final LiveMeasureDao liveMeasureDao;

public DbClient(Database database, MyBatis myBatis, DBSessions dbSessions, Dao... daos) {
this.database = database;
@@ -191,6 +193,7 @@ public class DbClient {
analysisPropertiesDao = getDao(map, AnalysisPropertiesDao.class);
qProfileEditUsersDao = getDao(map, QProfileEditUsersDao.class);
qProfileEditGroupsDao = getDao(map, QProfileEditGroupsDao.class);
liveMeasureDao = getDao(map, LiveMeasureDao.class);
}

public DbSession openSession(boolean batch) {
@@ -405,6 +408,10 @@ public class DbClient {
return qProfileEditGroupsDao;
}

public LiveMeasureDao liveMeasureDao() {
return liveMeasureDao;
}

protected <K extends Dao> K getDao(Map<Class, Dao> map, Class<K> clazz) {
return (K) map.get(clazz);
}

+ 2
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java Datei anzeigen

@@ -62,6 +62,7 @@ import org.sonar.db.issue.IssueChangeMapper;
import org.sonar.db.issue.IssueDto;
import org.sonar.db.issue.IssueMapper;
import org.sonar.db.issue.ShortBranchIssueDto;
import org.sonar.db.measure.LiveMeasureMapper;
import org.sonar.db.measure.MeasureDto;
import org.sonar.db.measure.MeasureMapper;
import org.sonar.db.measure.custom.CustomMeasureDto;
@@ -207,6 +208,7 @@ public class MyBatis implements Startable {
ComponentKeyUpdaterMapper.class,
ComponentLinkMapper.class,
ComponentMapper.class,
LiveMeasureMapper.class,
CustomMeasureMapper.class,
DefaultQProfileMapper.class,
DuplicationMapper.class,

+ 0
- 13
server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDto.java Datei anzeigen

@@ -138,7 +138,6 @@ public class ComponentDto {
private String moduleUuid;
private String moduleUuidPath;
private String copyComponentUuid;
private String developerUuid;
private String scope;
private String qualifier;
private String path;
@@ -401,16 +400,6 @@ public class ComponentDto {
return this;
}

@CheckForNull
public String getDeveloperUuid() {
return developerUuid;
}

public ComponentDto setDeveloperUuid(@Nullable String developerUuid) {
this.developerUuid = developerUuid;
return this;
}

public Date getCreatedAt() {
return createdAt;
}
@@ -488,7 +477,6 @@ public class ComponentDto {
.append("rootUuid", rootUuid)
.append("mainBranchProjectUuid", mainBranchProjectUuid)
.append("copyComponentUuid", copyComponentUuid)
.append("developerUuid", developerUuid)
.append("path", path)
.append("deprecatedKey", deprecatedKey)
.append("name", name)
@@ -513,7 +501,6 @@ public class ComponentDto {
copy.moduleUuid = moduleUuid;
copy.moduleUuidPath = moduleUuidPath;
copy.copyComponentUuid = copyComponentUuid;
copy.developerUuid = developerUuid;
copy.scope = scope;
copy.qualifier = qualifier;
copy.path = path;

+ 0
- 11
server/sonar-db-dao/src/main/java/org/sonar/db/component/ResourceDto.java Datei anzeigen

@@ -45,7 +45,6 @@ public class ResourceDto {
private String description;
private String language;
private String copyComponentUuid;
private String developerUuid;
private Date createdAt;

public Long getId() {
@@ -202,16 +201,6 @@ public class ResourceDto {
return this;
}

@CheckForNull
public String getDeveloperUuid() {
return developerUuid;
}

public ResourceDto setDeveloperUuid(@Nullable String developerUuid) {
this.developerUuid = developerUuid;
return this;
}

public Date getCreatedAt() {
return createdAt;
}

+ 100
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureDao.java Datei anzeigen

@@ -0,0 +1,100 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.db.measure;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import javax.annotation.Nullable;
import org.apache.ibatis.session.ResultHandler;
import org.sonar.api.utils.System2;
import org.sonar.core.util.Uuids;
import org.sonar.db.Dao;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;

import static java.util.Collections.singletonList;
import static org.sonar.db.DatabaseUtils.executeLargeInputs;

public class LiveMeasureDao implements Dao {

private final System2 system2;

public LiveMeasureDao(System2 system2) {
this.system2 = system2;
}

public List<LiveMeasureDto> selectByComponentUuids(DbSession dbSession, Collection<String> largeComponentUuids, Collection<Integer> metricIds) {
if (largeComponentUuids.isEmpty() || metricIds.isEmpty()) {
return Collections.emptyList();
}

return executeLargeInputs(
largeComponentUuids,
componentUuids -> mapper(dbSession).selectByComponentUuidsAndMetricIds(componentUuids, metricIds));
}

public List<LiveMeasureDto> selectByComponentUuidsAndMetricKeys(DbSession dbSession, Collection<String> largeComponentUuids, Collection<String> metricKeys) {
if (largeComponentUuids.isEmpty() || metricKeys.isEmpty()) {
return Collections.emptyList();
}

return executeLargeInputs(
largeComponentUuids,
componentUuids -> mapper(dbSession).selectByComponentUuidsAndMetricKeys(componentUuids, metricKeys));
}

public Optional<LiveMeasureDto> selectMeasure(DbSession dbSession, String componentUuid, String metricKey) {
List<LiveMeasureDto> measures = selectByComponentUuidsAndMetricKeys(dbSession, singletonList(componentUuid), singletonList(metricKey));
// couple of columns [component_uuid, metric_id] is unique. List can't have more than 1 item.
if (measures.size() == 1) {
return Optional.of(measures.get(0));
}
return Optional.empty();
}

public void selectTreeByQuery(DbSession dbSession, ComponentDto baseComponent, MeasureTreeQuery query, ResultHandler<LiveMeasureDto> resultHandler) {
if (query.returnsEmpty()) {
return;
}
mapper(dbSession).selectTreeByQuery(query, baseComponent.uuid(), query.getUuidPath(baseComponent), resultHandler);
}

public void insert(DbSession dbSession, LiveMeasureDto dto) {
mapper(dbSession).insert(dto, Uuids.create(), null, system2.now());
}

public void insertOrUpdate(DbSession dbSession, LiveMeasureDto dto, @Nullable String marker) {
LiveMeasureMapper mapper = mapper(dbSession);
long now = system2.now();
if (mapper.update(dto, marker, now) == 0) {
mapper.insert(dto, Uuids.create(), marker, now);
}
}

public void deleteByProjectUuidExcludingMarker(DbSession dbSession, String projectUuid, String marker) {
mapper(dbSession).deleteByProjectUuidExcludingMarker(projectUuid, marker);
}

private static LiveMeasureMapper mapper(DbSession dbSession) {
return dbSession.getMapper(LiveMeasureMapper.class);
}
}

+ 141
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureDto.java Datei anzeigen

@@ -0,0 +1,141 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.db.measure;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;

public class LiveMeasureDto {

private static final int MAX_TEXT_VALUE_LENGTH = 4000;

private String componentUuid;
private String projectUuid;
private int metricId;
@Nullable
private Double value;
@Nullable
private String textValue;
@Nullable
private byte[] data;
@Nullable
private Double variation;

public String getComponentUuid() {
return componentUuid;
}

public LiveMeasureDto setComponentUuid(String s) {
this.componentUuid = s;
return this;
}

public String getProjectUuid() {
return projectUuid;
}

public LiveMeasureDto setProjectUuid(String s) {
this.projectUuid = s;
return this;
}

public int getMetricId() {
return metricId;
}

public LiveMeasureDto setMetricId(int i) {
this.metricId = i;
return this;
}

@CheckForNull
public Double getValue() {
return value;
}

public LiveMeasureDto setValue(@Nullable Double value) {
this.value = value;
return this;
}

@CheckForNull
public String getTextValue() {
return textValue;
}

@CheckForNull
public byte[] getData() {
return data;
}

@CheckForNull
public String getDataAsString() {
if (data != null) {
return new String(data, StandardCharsets.UTF_8);
}
return textValue;
}

public LiveMeasureDto setData(@Nullable String data) {
if (data == null) {
this.textValue = null;
this.data = null;
} else if (data.length() > MAX_TEXT_VALUE_LENGTH) {
this.textValue = null;
this.data = data.getBytes(StandardCharsets.UTF_8);
} else {
this.textValue = data;
this.data = null;
}
return this;
}

public LiveMeasureDto setData(@Nullable byte[] data) {
this.textValue = null;
this.data = data;
return this;
}

@CheckForNull
public Double getVariation() {
return variation;
}

public LiveMeasureDto setVariation(@Nullable Double variation) {
this.variation = variation;
return this;
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder("LiveMeasureDto{");
sb.append("componentUuid='").append(componentUuid).append('\'');
sb.append(", projectUuid='").append(projectUuid).append('\'');
sb.append(", metricId=").append(metricId);
sb.append(", value=").append(value);
sb.append(", variation=").append(variation);
sb.append(", textValue='").append(textValue).append('\'');
sb.append(", data=").append(Arrays.toString(data));
sb.append('}');
return sb.toString();
}
}

+ 58
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureMapper.java Datei anzeigen

@@ -0,0 +1,58 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.db.measure;

import java.util.Collection;
import java.util.List;
import javax.annotation.Nullable;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.session.ResultHandler;

public interface LiveMeasureMapper {

List<LiveMeasureDto> selectByComponentUuidsAndMetricIds(
@Param("componentUuids") List<String> componentUuids,
@Param("metricIds") Collection<Integer> metricIds);

List<LiveMeasureDto> selectByComponentUuidsAndMetricKeys(
@Param("componentUuids") List<String> componentUuids,
@Param("metricKeys") Collection<String> metricKeys);

void selectTreeByQuery(
@Param("query") MeasureTreeQuery measureQuery,
@Param("baseUuid") String baseUuid,
@Param("baseUuidPath") String baseUuidPath,
ResultHandler<LiveMeasureDto> resultHandler);

void insert(
@Param("dto") LiveMeasureDto dto,
@Param("uuid") String uuid,
@Nullable @Param("marker") String marker,
@Param("now") long now);

int update(
@Param("dto") LiveMeasureDto dto,
@Nullable @Param("marker") String marker,
@Param("now") long now);

void deleteByProjectUuidExcludingMarker(
@Param("projectUuid") String projectUuid,
@Param("marker") String marker);
}

+ 6
- 30
server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureDao.java Datei anzeigen

@@ -19,24 +19,24 @@
*/
package org.sonar.db.measure;

import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import java.util.Collection;
import java.util.List;
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 java.util.Collections.emptyList;
import static org.sonar.db.DatabaseUtils.executeLargeInputs;

public class MeasureDao implements Dao {

public Optional<MeasureDto> selectSingle(DbSession dbSession, MeasureQuery query) {
List<MeasureDto> measures = selectByQuery(dbSession, query);
return Optional.ofNullable(Iterables.getOnlyElement(measures, null));
public Optional<MeasureDto> selectLastMeasure(DbSession dbSession, String componentUuid, String metricKey) {
return Optional.ofNullable(mapper(dbSession).selectLastMeasure(componentUuid, metricKey));
}

public Optional<MeasureDto> selectMeasure(DbSession dbSession, String analysisUuid, String componentUuid, String metricKey) {
return Optional.ofNullable(mapper(dbSession).selectMeasure(analysisUuid, componentUuid, metricKey));
}

/**
@@ -47,10 +47,6 @@ public class MeasureDao implements Dao {
* - A list of components in {@link MeasureQuery#componentUuids} with one mandatory project in {@link MeasureQuery#projectUuids}
* - One single component in {@link MeasureQuery#componentUuids}
* <p>
* In addition, this method returns measures which are not associated to any developer, unless one is specified in
* {@link MeasureQuery#personId}.
* </p>
* <p>
* Returned measure can optionally be filtered metric (either by specifying {@link MeasureQuery#metricIds}
* or {@link MeasureQuery#metricKeys}).
* </p>
@@ -78,13 +74,6 @@ public class MeasureDao implements Dao {
return mapper(dbSession).selectByQueryOnSingleComponent(query);
}

public void selectTreeByQuery(DbSession dbSession, ComponentDto baseComponent, MeasureTreeQuery query, ResultHandler<MeasureDto> resultHandler) {
if (query.returnsEmpty()) {
return;
}
mapper(dbSession).selectTreeByQuery(query, baseComponent.uuid(), query.getUuidPath(baseComponent), resultHandler);
}

public List<PastMeasureDto> selectPastMeasures(DbSession dbSession, String componentUuid, String analysisUuid, Collection<Integer> metricIds) {
if (metricIds.isEmpty()) {
return emptyList();
@@ -107,19 +96,6 @@ public class MeasureDao implements Dao {
return mapper(dbSession).selectPastMeasuresOnSeveralAnalyses(query);
}

/**
* Used by developer cockpit.
*/
public List<MeasureDto> selectProjectMeasuresOfDeveloper(DbSession dbSession, long developerId, Collection<Integer> metricIds) {
return executeLargeInputs(
metricIds,
ids -> mapper(dbSession).selectProjectMeasuresOfDeveloper(developerId, metricIds));
}

public List<MeasureDto> selectByComponentsAndMetrics(DbSession dbSession, Collection<String> componentUuids, Collection<Integer> metricIds) {
return executeLargeInputs(componentUuids, partitionComponentUuids -> mapper(dbSession).selectByComponentsAndMetrics(partitionComponentUuids, metricIds));
}

public void insert(DbSession session, MeasureDto measureDto) {
mapper(session).insert(measureDto);
}

+ 0
- 12
server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureDto.java Datei anzeigen

@@ -36,7 +36,6 @@ public class MeasureDto {
private String componentUuid;
private String analysisUuid;
private int metricId;
private Long developerId;

@CheckForNull
public Double getValue() {
@@ -128,16 +127,6 @@ public class MeasureDto {
return this;
}

@CheckForNull
public Long getDeveloperId() {
return developerId;
}

public MeasureDto setDeveloperId(@Nullable Long developerId) {
this.developerId = developerId;
return this;
}

@Override
public String toString() {
return MoreObjects.toStringHelper(this)
@@ -150,7 +139,6 @@ public class MeasureDto {
.add("componentUuid", componentUuid)
.add("analysisUuid", analysisUuid)
.add("metricId", metricId)
.add("developerId", developerId)
.toString();
}
}

+ 14
- 10
server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureMapper.java Datei anzeigen

@@ -19,31 +19,35 @@
*/
package org.sonar.db.measure;

import java.util.Collection;
import java.util.List;
import javax.annotation.CheckForNull;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.session.ResultHandler;

public interface MeasureMapper {

@CheckForNull
MeasureDto selectLastMeasure(
@Param("componentUuid") String componentUuid,
@Param("metricKey") String metricKey
);

@CheckForNull
MeasureDto selectMeasure(
@Param("analysisUuid") String analysisUuid,
@Param("componentUuid") String componentUuid,
@Param("metricKey") String metricKey
);

List<MeasureDto> selectByQueryOnProjects(@Param("query") MeasureQuery query);

List<MeasureDto> selectByQueryOnComponents(@Param("query") MeasureQuery query);

List<MeasureDto> selectByQueryOnSingleComponent(@Param("query") MeasureQuery query);

void selectTreeByQuery(@Param("query") MeasureTreeQuery measureQuery, @Param("baseUuid") String baseUuid, @Param("baseUuidPath") String baseUuidPath,
ResultHandler<MeasureDto> resultHandler);


List<PastMeasureDto> selectPastMeasuresOnSingleAnalysis(@Param("componentUuid") String componentUuid, @Param("analysisUuid") String analysisUuid,
@Param("metricIds") List<Integer> metricIds);

List<MeasureDto> selectPastMeasuresOnSeveralAnalyses(@Param("query") PastMeasureQuery query);

List<MeasureDto> selectProjectMeasuresOfDeveloper(@Param("developerId") long developerId, @Param("metricIds") Collection<Integer> metricIds);

List<MeasureDto> selectByComponentsAndMetrics(@Param("componentUuids") List<String> componentUuids, @Param("metricIds") Collection<Integer> metricIds);

void insert(MeasureDto measureDto);
}

+ 6
- 23
server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureQuery.java Datei anzeigen

@@ -44,19 +44,15 @@ public class MeasureQuery {
@CheckForNull
private final Collection<String> metricKeys;

@CheckForNull
private final Long personId;

private MeasureQuery(Builder builder) {
this(builder.analysisUuid, builder.projectUuids, builder.componentUuids, builder.metricIds, builder.metricKeys, builder.personId);
this(builder.analysisUuid, builder.projectUuids, builder.componentUuids, builder.metricIds, builder.metricKeys);
}

private MeasureQuery(@Nullable String analysisUuid,
@Nullable Collection<String> projectUuids,
@Nullable Collection<String> componentUuids,
@Nullable Collection<Integer> metricIds,
@Nullable Collection<String> metricKeys,
@Nullable Long personId) {
@Nullable Collection<String> metricKeys) {
checkArgument(metricIds == null || metricKeys == null, "Metric IDs and keys must not be set both");
checkArgument(projectUuids != null || componentUuids != null, "At least one filter on component UUID is expected");
checkArgument(componentUuids == null || componentUuids.size() == 1 || (projectUuids != null && projectUuids.size() == 1),
@@ -67,7 +63,6 @@ public class MeasureQuery {
this.componentUuids = componentUuids;
this.metricIds = metricIds;
this.metricKeys = metricKeys;
this.personId = personId;
}

public String getAnalysisUuid() {
@@ -104,11 +99,6 @@ public class MeasureQuery {
return metricKeys;
}

@CheckForNull
public Long getPersonId() {
return personId;
}

public boolean returnsEmpty() {
return (projectUuids != null && projectUuids.isEmpty())
|| (componentUuids != null && componentUuids.isEmpty())
@@ -141,13 +131,12 @@ public class MeasureQuery {
Objects.equals(projectUuids, that.projectUuids) &&
Objects.equals(componentUuids, that.componentUuids) &&
Objects.equals(metricIds, that.metricIds) &&
Objects.equals(metricKeys, that.metricKeys) &&
Objects.equals(personId, that.personId);
Objects.equals(metricKeys, that.metricKeys);
}

@Override
public int hashCode() {
return Objects.hash(analysisUuid, componentUuids, metricIds, metricKeys, personId);
return Objects.hash(analysisUuid, componentUuids, metricIds, metricKeys);
}

public static Builder builder() {
@@ -155,11 +144,11 @@ public class MeasureQuery {
}

static MeasureQuery copyWithSubsetOfProjectUuids(MeasureQuery query, Collection<String> projectUuids) {
return new MeasureQuery(query.analysisUuid, projectUuids, query.componentUuids, query.metricIds, query.metricKeys, query.personId);
return new MeasureQuery(query.analysisUuid, projectUuids, query.componentUuids, query.metricIds, query.metricKeys);
}

static MeasureQuery copyWithSubsetOfComponentUuids(MeasureQuery query, Collection<String> componentUuids) {
return new MeasureQuery(query.analysisUuid, query.projectUuids, componentUuids, query.metricIds, query.metricKeys, query.personId);
return new MeasureQuery(query.analysisUuid, query.projectUuids, componentUuids, query.metricIds, query.metricKeys);
}

public static final class Builder {
@@ -168,7 +157,6 @@ public class MeasureQuery {
private Collection<String> componentUuids;
private Collection<Integer> metricIds;
private Collection<String> metricKeys;
private Long personId;

private Builder() {
// see MeasureQuery#builder()
@@ -230,11 +218,6 @@ public class MeasureQuery {
return this;
}

public Builder setPersonId(@Nullable Long l) {
this.personId = l;
return this;
}

public MeasureQuery build() {
return new MeasureQuery(this);
}

+ 0
- 17
server/sonar-db-dao/src/main/java/org/sonar/db/measure/MeasureTreeQuery.java Datei anzeigen

@@ -47,15 +47,11 @@ public class MeasureTreeQuery {
@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
@@ -85,11 +81,6 @@ public class MeasureTreeQuery {
return metricIds;
}

@CheckForNull
public Long getPersonId() {
return personId;
}

public String getUuidPath(ComponentDto component) {
switch (strategy) {
case CHILDREN:
@@ -120,9 +111,6 @@ public class MeasureTreeQuery {
@CheckForNull
private Collection<Integer> metricIds;

@CheckForNull
private Long personId;

private Builder() {
}

@@ -149,11 +137,6 @@ public class MeasureTreeQuery {
return this;
}

public Builder setPersonId(@Nullable Long personId) {
this.personId = personId;
return this;
}

public MeasureTreeQuery build() {
return new MeasureTreeQuery(this);
}

+ 0
- 14
server/sonar-db-dao/src/main/java/org/sonar/db/measure/PastMeasureDto.java Datei anzeigen

@@ -31,9 +31,6 @@ public class PastMeasureDto {
@CheckForNull
private Double value;

@CheckForNull
private Long personId;

public double getValue() {
requireNonNull(value);
return value;
@@ -56,15 +53,4 @@ public class PastMeasureDto {
this.metricId = i;
return this;
}

@CheckForNull
public Long getPersonId() {
return personId;
}

PastMeasureDto setPersonId(@Nullable Long l) {
this.personId = l;
return this;
}

}

+ 13
- 26
server/sonar-db-dao/src/main/java/org/sonar/db/measure/ProjectMeasuresIndexerIterator.java Datei anzeigen

@@ -69,24 +69,23 @@ public class ProjectMeasuresIndexerIterator extends CloseableIterator<ProjectMea
CoreMetrics.NEW_LINES_KEY,
CoreMetrics.NEW_RELIABILITY_RATING_KEY);

private static final String SQL_PROJECTS = "SELECT p.organization_uuid, p.uuid, p.kee, p.name, s.uuid, s.created_at, p.tags " +
private static final String SQL_PROJECTS = "SELECT p.organization_uuid, p.uuid, p.kee, p.name, s.created_at, p.tags " +
"FROM projects p " +
"LEFT OUTER JOIN snapshots s ON s.component_uuid=p.uuid AND s.islast=? " +
"WHERE p.enabled=? AND p.scope=? AND p.qualifier=? and p.main_branch_project_uuid is null ";

private static final String PROJECT_FILTER = " AND p.uuid=?";

private static final String SQL_MEASURES = "SELECT m.name, pm.value, pm.variation_value_1, pm.text_value FROM project_measures pm " +
private static final String SQL_MEASURES = "SELECT m.name, pm.value, pm.variation, pm.text_value FROM live_measures pm " +
"INNER JOIN metrics m ON m.id = pm.metric_id " +
"WHERE pm.component_uuid = ? AND pm.analysis_uuid = ? " +
"WHERE pm.component_uuid = ? " +
"AND m.name IN ({metricNames}) " +
"AND (pm.value IS NOT NULL OR pm.variation_value_1 IS NOT NULL OR pm.text_value IS NOT NULL) " +
"AND pm.person_id IS NULL " +
"AND (pm.value IS NOT NULL OR pm.variation IS NOT NULL OR pm.text_value IS NOT NULL) " +
"AND m.enabled = ? ";
private static final boolean ENABLED = true;
private static final int FIELD_METRIC_NAME = 1;
private static final int FIELD_MEASURE_VALUE = 2;
private static final int FIELD_MEASURE_VARIATION_VALUE_1 = 3;
private static final int FIELD_MEASURE_VARIATION = 3;
private static final int FIELD_MEASURE_TEXT_VALUE = 4;

private final PreparedStatement measuresStatement;
@@ -116,10 +115,9 @@ public class ProjectMeasuresIndexerIterator extends CloseableIterator<ProjectMea
String uuid = rs.getString(2);
String key = rs.getString(3);
String name = rs.getString(4);
String analysisUuid = DatabaseUtils.getString(rs, 5);
Long analysisDate = DatabaseUtils.getLong(rs, 6);
List<String> tags = readDbTags(DatabaseUtils.getString(rs, 7));
Project project = new Project(orgUuid, uuid, key, name, tags, analysisUuid, analysisDate);
Long analysisDate = DatabaseUtils.getLong(rs, 5);
List<String> tags = readDbTags(DatabaseUtils.getString(rs, 6));
Project project = new Project(orgUuid, uuid, key, name, tags, analysisDate);
projects.add(project);
}
return projects;
@@ -165,20 +163,16 @@ public class ProjectMeasuresIndexerIterator extends CloseableIterator<ProjectMea
return null;
}
Project project = projects.next();
Measures measures = selectMeasures(project.getUuid(), project.getAnalysisUuid());
Measures measures = selectMeasures(project.getUuid());
return new ProjectMeasures(project, measures);
}

private Measures selectMeasures(String projectUuid, @Nullable String analysisUuid) {
private Measures selectMeasures(String projectUuid) {
Measures measures = new Measures();
if (analysisUuid == null) {
return measures;
}
ResultSet rs = null;
try {
AtomicInteger index = new AtomicInteger(1);
measuresStatement.setString(index.getAndIncrement(), projectUuid);
measuresStatement.setString(index.getAndIncrement(), analysisUuid);
METRIC_KEYS.forEach(DatabaseUtils.setStrings(measuresStatement, index::getAndIncrement));
measuresStatement.setBoolean(index.getAndIncrement(), ENABLED);
rs = measuresStatement.executeQuery();
@@ -187,7 +181,7 @@ public class ProjectMeasuresIndexerIterator extends CloseableIterator<ProjectMea
}
return measures;
} catch (Exception e) {
throw new IllegalStateException(String.format("Fail to execute request to select measures of project %s, analysis %s", projectUuid, analysisUuid), e);
throw new IllegalStateException(String.format("Fail to execute request to select measures of project %s", projectUuid), e);
} finally {
DatabaseUtils.closeQuietly(rs);
}
@@ -195,7 +189,7 @@ public class ProjectMeasuresIndexerIterator extends CloseableIterator<ProjectMea

private static void readMeasure(ResultSet rs, Measures measures) throws SQLException {
String metricKey = rs.getString(FIELD_METRIC_NAME);
Optional<Double> value = metricKey.startsWith("new_") ? getDouble(rs, FIELD_MEASURE_VARIATION_VALUE_1) : getDouble(rs, FIELD_MEASURE_VALUE);
Optional<Double> value = metricKey.startsWith("new_") ? getDouble(rs, FIELD_MEASURE_VARIATION) : getDouble(rs, FIELD_MEASURE_VALUE);
if (value.isPresent()) {
measures.addNumericMeasure(metricKey, value.get());
return;
@@ -239,17 +233,15 @@ public class ProjectMeasuresIndexerIterator extends CloseableIterator<ProjectMea
private final String uuid;
private final String key;
private final String name;
private final String analysisUuid;
private final Long analysisDate;
private final List<String> tags;

public Project(String organizationUuid, String uuid, String key, String name, List<String> tags, @Nullable String analysisUuid, @Nullable Long analysisDate) {
public Project(String organizationUuid, String uuid, String key, String name, List<String> tags, @Nullable Long analysisDate) {
this.organizationUuid = organizationUuid;
this.uuid = uuid;
this.key = key;
this.name = name;
this.tags = tags;
this.analysisUuid = analysisUuid;
this.analysisDate = analysisDate;
}

@@ -273,11 +265,6 @@ public class ProjectMeasuresIndexerIterator extends CloseableIterator<ProjectMea
return tags;
}

@CheckForNull
public String getAnalysisUuid() {
return analysisUuid;
}

@CheckForNull
public Long getAnalysisDate() {
return analysisDate;

+ 21
- 13
server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java Datei anzeigen

@@ -21,13 +21,11 @@ package org.sonar.db.purge;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.sonar.db.DbSession;

import static com.google.common.collect.FluentIterable.from;
import static java.util.Arrays.asList;

class PurgeCommands {

private static final int MAX_SNAPSHOTS_PER_QUERY = 1000;
@@ -56,14 +54,14 @@ class PurgeCommands {
return purgeMapper.selectAnalysisIdsAndUuids(query);
}

void deleteAnalyses(String rootUuid) {
void deleteAnalyses(String rootComponentUuid) {
profiler.start("deleteAnalyses (events)");
purgeMapper.deleteEventsByComponentUuid(rootUuid);
purgeMapper.deleteEventsByComponentUuid(rootComponentUuid);
session.commit();
profiler.stop();

List<List<String>> analysisUuidsPartitions = Lists.partition(IdUuidPairs.uuids(purgeMapper.selectAnalysisIdsAndUuids(new PurgeSnapshotQuery().setComponentUuid(rootUuid))),
MAX_SNAPSHOTS_PER_QUERY);
List<List<String>> analysisUuidsPartitions = Lists.partition(IdUuidPairs.uuids(
purgeMapper.selectAnalysisIdsAndUuids(new PurgeSnapshotQuery().setComponentUuid(rootComponentUuid))), MAX_SNAPSHOTS_PER_QUERY);

deleteAnalysisDuplications(analysisUuidsPartitions);

@@ -84,9 +82,10 @@ class PurgeCommands {
}

void deleteAnalyses(PurgeSnapshotQuery... queries) {
List<IdUuidPair> snapshotIds = from(asList(queries))
.transformAndConcat(purgeMapper::selectAnalysisIdsAndUuids)
.toList();
List<IdUuidPair> snapshotIds = Arrays.stream(queries)
.flatMap(q -> purgeMapper.selectAnalysisIdsAndUuids(q).stream())
.collect(Collectors.toList());

deleteAnalyses(snapshotIds);
}

@@ -124,9 +123,11 @@ class PurgeCommands {

profiler.start("deleteSnapshotWastedMeasures (project_measures)");
List<Long> metricIdsWithoutHistoricalData = purgeMapper.selectMetricIdsWithoutHistoricalData();
analysisUuidsPartitions
.forEach(analysisUuidsPartition -> purgeMapper.deleteAnalysisWastedMeasures(analysisUuidsPartition, metricIdsWithoutHistoricalData));
session.commit();
if (!metricIdsWithoutHistoricalData.isEmpty()) {
analysisUuidsPartitions
.forEach(analysisUuidsPartition -> purgeMapper.deleteAnalysisWastedMeasures(analysisUuidsPartition, metricIdsWithoutHistoricalData));
session.commit();
}
profiler.stop();

profiler.start("updatePurgeStatusToOne (snapshots)");
@@ -272,4 +273,11 @@ class PurgeCommands {
session.commit();
profiler.stop();
}

void deleteLiveMeasures(String rootUuid) {
profiler.start("deleteLiveMeasures (live_measures)");
purgeMapper.deleteLiveMeasuresByProjectUuid(rootUuid);
session.commit();
profiler.stop();
}
}

+ 2
- 2
server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeConfiguration.java Datei anzeigen

@@ -49,12 +49,12 @@ public class PurgeConfiguration {
this.maxAgeInDaysOfInactiveShortLivingBranches = maxAgeInDaysOfInactiveShortLivingBranches;
}

public static PurgeConfiguration newDefaultPurgeConfiguration(Configuration config, IdUuidPair idUuidPair, Collection<String> disabledComponentUuids) {
public static PurgeConfiguration newDefaultPurgeConfiguration(Configuration config, IdUuidPair rootId, Collection<String> disabledComponentUuids) {
String[] scopes = new String[] {Scopes.FILE};
if (config.getBoolean(PurgeConstants.PROPERTY_CLEAN_DIRECTORY).orElse(false)) {
scopes = new String[] {Scopes.DIRECTORY, Scopes.FILE};
}
return new PurgeConfiguration(idUuidPair, scopes, config.getInt(PurgeConstants.DAYS_BEFORE_DELETING_CLOSED_ISSUES).get(),
return new PurgeConfiguration(rootId, scopes, config.getInt(PurgeConstants.DAYS_BEFORE_DELETING_CLOSED_ISSUES).get(),
config.getInt(PurgeConstants.DAYS_BEFORE_DELETING_INACTIVE_SHORT_LIVING_BRANCHES), System2.INSTANCE, disabledComponentUuids);
}


+ 1
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java Datei anzeigen

@@ -204,6 +204,7 @@ public class PurgeDao implements Dao {
commands.deleteCeQueue(rootUuid);
commands.deleteWebhookDeliveries(rootUuid);
commands.deleteBranch(rootUuid);
commands.deleteLiveMeasures(rootUuid);
}

/**

+ 2
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java Datei anzeigen

@@ -97,4 +97,6 @@ public interface PurgeMapper {
void deleteWebhookDeliveriesByProjectUuid(@Param("projectUuid") String projectUuid);

void deleteBranchByUuid(@Param("uuid") String uuid);

void deleteLiveMeasuresByProjectUuid(@Param("projectUuid") String projectUuid);
}

+ 0
- 3
server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml Datei anzeigen

@@ -24,7 +24,6 @@
p.path as path,
p.enabled as enabled,
p.copy_component_uuid as copyComponentUuid,
p.developer_uuid as developerUuid,
p.private as isPrivate,
p.created_at as createdAt
</sql>
@@ -512,7 +511,6 @@
root_uuid,
path,
copy_component_uuid,
developer_uuid,
enabled,
created_at,
b_changed,
@@ -548,7 +546,6 @@
#{rootUuid,jdbcType=VARCHAR},
#{path,jdbcType=VARCHAR},
#{copyComponentUuid,jdbcType=VARCHAR},
#{developerUuid,jdbcType=VARCHAR},
#{enabled,jdbcType=BOOLEAN},
#{createdAt,jdbcType=TIMESTAMP},
${_false},

+ 110
- 0
server/sonar-db-dao/src/main/resources/org/sonar/db/measure/LiveMeasureMapper.xml Datei anzeigen

@@ -0,0 +1,110 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd">

<mapper namespace="org.sonar.db.measure.LiveMeasureMapper">

<sql id="columns">
lm.component_uuid as componentUuid,
lm.project_uuid as projectUuid,
lm.metric_id as metricId,
lm.value as value,
lm.text_value as textValue,
lm.measure_data as data,
lm.variation as variation
</sql>

<select id="selectByComponentUuidsAndMetricIds" parameterType="map" resultType="org.sonar.db.measure.LiveMeasureDto">
select <include refid="columns"/> from live_measures lm
where
lm.metric_id in <foreach item="metricId" collection="metricIds" open="(" separator="," close=")">#{metricId, jdbcType=INTEGER}</foreach>
and lm.component_uuid in
<foreach item="componentUuid" collection="componentUuids" open="(" separator="," close=")">
#{componentUuid, jdbcType=VARCHAR}
</foreach>
</select>

<select id="selectByComponentUuidsAndMetricKeys" parameterType="map" resultType="org.sonar.db.measure.LiveMeasureDto">
select <include refid="columns"/> from live_measures lm
inner join metrics m on m.id = lm.metric_id
where
m.name in <foreach item="metricKey" collection="metricKeys" open="(" separator="," close=")">#{metricKey, jdbcType=VARCHAR}</foreach>
and lm.component_uuid in
<foreach item="componentUuid" collection="componentUuids" open="(" separator="," close=")">
#{componentUuid, jdbcType=VARCHAR}
</foreach>
</select>

<insert id="insert" parameterType="map" useGeneratedKeys="false">
insert into live_measures (
uuid,
component_uuid,
project_uuid,
metric_id,
value,
text_value,
variation,
measure_data,
update_marker,
created_at,
updated_at
) values (
#{uuid, jdbcType=VARCHAR},
#{dto.componentUuid, jdbcType=VARCHAR},
#{dto.projectUuid, jdbcType=VARCHAR},
#{dto.metricId, jdbcType=INTEGER},
#{dto.value, jdbcType=DOUBLE},
#{dto.textValue, jdbcType=VARCHAR},
#{dto.variation, jdbcType=DOUBLE},
#{dto.data, jdbcType=BINARY},
#{marker, jdbcType=VARCHAR},
#{now, jdbcType=BIGINT},
#{now, jdbcType=BIGINT}
)
</insert>

<update id="update" parameterType="map">
update live_measures set
value = #{dto.value, jdbcType=DOUBLE},
variation = #{dto.variation, jdbcType=DOUBLE},
text_value = #{dto.textValue, jdbcType=VARCHAR},
measure_data = #{dto.data, jdbcType=BINARY},
update_marker = #{marker, jdbcType=VARCHAR},
updated_at = #{now, jdbcType=BIGINT}
where
component_uuid = #{dto.componentUuid, jdbcType=VARCHAR}
and metric_id = #{dto.metricId, jdbcType=INTEGER}
</update>

<delete id="deleteByProjectUuidExcludingMarker" parameterType="map">
delete from live_measures
where
project_uuid = #{projectUuid, jdbcType=VARCHAR} and
(update_marker != #{marker, jdbcType=VARCHAR} or update_marker is null)
</delete>

<select id="selectTreeByQuery" parameterType="map" resultType="org.sonar.db.measure.LiveMeasureDto" fetchSize="${_scrollFetchSize}" resultSetType="FORWARD_ONLY">
select <include refid="columns"/> from live_measures lm
inner join projects p on p.uuid = lm.component_uuid
<!-- TODO do we really need another join on projects ? Using lm.project_uuid should be enough -->
<include refid="org.sonar.db.component.ComponentMapper.selectDescendantsJoins"/>
<where>
<if test="query.getMetricIds() != null">
lm.metric_id in
<foreach item="metricId" collection="query.getMetricIds()" open="(" separator="," close=")">#{metricId,jdbcType=INTEGER}</foreach>
</if>
<include refid="org.sonar.db.component.ComponentMapper.selectDescendantsFilters"/>
</where>

-- Add measures of base component
union all
select <include refid="columns"/> from live_measures lm
inner join projects p on p.uuid = lm.component_uuid and lm.component_uuid = #{baseUuid, jdbcType=VARCHAR}
<where>
<if test="query.getMetricIds() != null">
lm.metric_id in
<foreach item="metricId" collection="query.getMetricIds()" open="(" separator="," close=")">#{metricId,jdbcType=INTEGER}</foreach>
</if>
<include refid="org.sonar.db.component.ComponentMapper.selectDescendantsFilters"/>
</where>
</select>
</mapper>

+ 22
- 83
server/sonar-db-dao/src/main/resources/org/sonar/db/measure/MeasureMapper.xml Datei anzeigen

@@ -5,7 +5,6 @@

<sql id="measureColumns">
pm.metric_id as metricId,
pm.person_id as developerId,
pm.component_uuid as componentUuid,
pm.analysis_uuid as analysisUuid,
pm.value as value,
@@ -16,10 +15,27 @@
pm.variation_value_1 as variation
</sql>

<sql id="extendedMeasureColumns">
<include refid="measureColumns"/>,
metric.name as metricKey
</sql>
<select id="selectLastMeasure" parameterType="map" resultType="Measure">
select <include refid="measureColumns"/>
from project_measures pm
inner join metrics m on m.id = pm.metric_id
inner join snapshots s on s.uuid = pm.analysis_uuid
where
pm.component_uuid = #{componentUuid,jdbcType=VARCHAR} and
m.name = #{metricKey,jdbcType=VARCHAR} and
s.islast= ${_true}
</select>

<select id="selectMeasure" parameterType="map" resultType="Measure">
select <include refid="measureColumns"/>
from project_measures pm
inner join metrics m on m.id = pm.metric_id
inner join snapshots s on s.uuid = pm.analysis_uuid
where
pm.component_uuid = #{componentUuid,jdbcType=VARCHAR} and
m.name = #{metricKey,jdbcType=VARCHAR} and
s.uuid = #{analysisUuid,jdbcType=VARCHAR}
</select>

<select id="selectByQueryOnProjects" parameterType="map" resultType="Measure">
select <include refid="measureColumns"/> from project_measures pm
@@ -79,60 +95,16 @@
#{metricKey,jdbcType=VARCHAR}
</foreach>
</if>
<choose>
<when test="query.getPersonId() != null">
and pm.person_id = #{query.personId,jdbcType=BIGINT}
</when>
<otherwise>
and pm.person_id is null
</otherwise>
</choose>
</sql>

<select id="selectTreeByQuery" parameterType="map" resultType="Measure" fetchSize="${_scrollFetchSize}" resultSetType="FORWARD_ONLY">
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">
and analysis.islast=${_true}
<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,jdbcType=BIGINT}
</when>
<otherwise>
and pm.person_id is null
</otherwise>
</choose>
<include refid="org.sonar.db.component.ComponentMapper.selectDescendantsFilters"/>
</sql>

<select id="selectPastMeasuresOnSingleAnalysis" 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
select pm.id as id, pm.metric_id as metricId, pm.value as value
from project_measures pm
inner join snapshots analysis on analysis.uuid = pm.analysis_uuid
where
pm.component_uuid = #{componentUuid,jdbcType=VARCHAR}
and analysis.uuid = #{analysisUuid,jdbcType=VARCHAR}
and pm.metric_id in <foreach item="metricId" collection="metricIds" open="(" separator="," close=")">#{metricId}</foreach>
and pm.person_id is null
</select>

<select id="selectPastMeasuresOnSeveralAnalyses" parameterType="map" resultType="Measure">
@@ -148,40 +120,9 @@
and analysis.created_at&lt;#{query.to, jdbcType=BIGINT}
</if>
and pm.metric_id in <foreach item="metricId" collection="query.metricIds" open="(" separator="," close=")">#{metricId, jdbcType=VARCHAR}</foreach>
and pm.person_id is null
and analysis.status=#{query.status, jdbcType=VARCHAR}
</select>

<select id="selectProjectMeasuresOfDeveloper" parameterType="map" resultType="Measure">
SELECT
<include refid="measureColumns"/>
from
project_measures pm, snapshots s, projects p
where
pm.person_id=#{developerId,jdbcType=BIGINT}
and pm.metric_id in
<foreach item="metricId" collection="metricIds" open="(" separator="," close=")">
#{metricId}
</foreach>
and s.uuid=pm.analysis_uuid
and s.islast=${_true}
and p.uuid=pm.component_uuid
and p.scope='PRJ'
and p.qualifier='TRK'
</select>

<select id="selectByComponentsAndMetrics" parameterType="map" resultType="Measure">
select <include refid="measureColumns"/>
from project_measures pm
inner join snapshots analysis on analysis.uuid = pm.analysis_uuid and analysis.islast=${_true}
inner join projects p on p.project_uuid=analysis.component_uuid and p.uuid=pm.component_uuid
<where>
and p.uuid in <foreach item="componentUuid" collection="componentUuids" open="(" separator="," close=")">#{componentUuid,jdbcType=VARCHAR}</foreach>
and pm.metric_id in <foreach item="metricId" collection="metricIds" open="(" separator="," close=")">#{metricId,jdbcType=INTEGER}</foreach>
and pm.person_id is null
</where>
</select>

<insert id="insert" parameterType="Measure" useGeneratedKeys="false">
insert into project_measures (
value,
@@ -191,7 +132,6 @@
text_value,
alert_status,
alert_text,
person_id,
variation_value_1,
measure_data)
VALUES (
@@ -202,7 +142,6 @@
#{textValue, jdbcType=VARCHAR},
#{alertStatus, jdbcType=VARCHAR},
#{alertText, jdbcType=VARCHAR},
#{developerId, jdbcType=INTEGER},
#{variation, jdbcType=DOUBLE},
#{dataValue, jdbcType=BINARY}
)

+ 7
- 8
server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml Datei anzeigen

@@ -151,14 +151,10 @@
<foreach collection="analysisUuids" open="(" close=")" item="analysisUuid" separator=",">
#{analysisUuid}
</foreach>
and (person_id is not null
<if test="metricIds.size()>0">
or metric_id in
<foreach collection="metricIds" open="(" item="metricId" separator="," close=")">
#{metricId}
</foreach>
</if>
)
and metric_id in
<foreach collection="metricIds" open="(" item="metricId" separator="," close=")">
#{metricId,jdbcType=INTEGER}
</foreach>
</where>
</delete>

@@ -332,5 +328,8 @@
delete from project_branches where uuid=#{uuid,jdbcType=VARCHAR}
</delete>

<delete id="deleteLiveMeasuresByProjectUuid">
delete from live_measures where project_uuid = #{projectUuid,jdbcType=VARCHAR}
</delete>
</mapper>


+ 1
- 1
server/sonar-db-dao/src/test/java/org/sonar/db/DaoModuleTest.java Datei anzeigen

@@ -30,6 +30,6 @@ public class DaoModuleTest {
public void verify_count_of_added_components() {
ComponentContainer container = new ComponentContainer();
new DaoModule().configure(container);
assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 51);
assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 52);
}
}

+ 0
- 1
server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java Datei anzeigen

@@ -111,7 +111,6 @@ public class ComponentDaoTest {
assertThat(result.scope()).isEqualTo("PRJ");
assertThat(result.language()).isNull();
assertThat(result.getCopyResourceUuid()).isNull();
assertThat(result.getDeveloperUuid()).isNull();
assertThat(result.isPrivate()).isTrue();

assertThat(underTest.selectByUuid(dbSession, "UNKNOWN")).isAbsent();

+ 1
- 3
server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDtoTest.java Datei anzeigen

@@ -42,8 +42,7 @@ public class ComponentDtoTest {
.setDescription("desc")
.setPath("src/org/struts/RequestContext.java")
.setCopyComponentUuid("uuid_5")
.setRootUuid("uuid_3")
.setDeveloperUuid("uuid_6");
.setRootUuid("uuid_3");

assertThat(componentDto.getId()).isEqualTo(1L);
assertThat(componentDto.getDbKey()).isEqualTo("org.struts:struts-core:src/org/struts/RequestContext.java");
@@ -58,7 +57,6 @@ public class ComponentDtoTest {
assertThat(componentDto.description()).isEqualTo("desc");
assertThat(componentDto.getRootUuid()).isEqualTo("uuid_3");
assertThat(componentDto.getCopyResourceUuid()).isEqualTo("uuid_5");
assertThat(componentDto.getDeveloperUuid()).isEqualTo("uuid_6");
assertThat(componentDto.isPrivate()).isFalse();
}


+ 146
- 0
server/sonar-db-dao/src/test/java/org/sonar/db/measure/LiveMeasureDaoTest.java Datei anzeigen

@@ -0,0 +1,146 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.db.measure;

import java.util.List;
import org.assertj.core.groups.Tuple;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.utils.System2;
import org.sonar.db.DbTester;
import org.sonar.db.metric.MetricDto;

import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.db.measure.MeasureTesting.newLiveMeasure;

public class LiveMeasureDaoTest {

private static final int A_METRIC_ID = 42;

@Rule
public DbTester db = DbTester.create(System2.INSTANCE);

private LiveMeasureDao underTest = db.getDbClient().liveMeasureDao();

@Test
public void test_selectByComponentUuids() {
LiveMeasureDto measure1 = newLiveMeasure().setMetricId(A_METRIC_ID);
LiveMeasureDto measure2 = newLiveMeasure().setMetricId(A_METRIC_ID);
underTest.insert(db.getSession(), measure1);
underTest.insert(db.getSession(), measure2);

List<LiveMeasureDto> selected = underTest.selectByComponentUuids(db.getSession(), asList(measure1.getComponentUuid(), measure2.getComponentUuid()), singletonList(A_METRIC_ID));
assertThat(selected)
.extracting(LiveMeasureDto::getComponentUuid, LiveMeasureDto::getProjectUuid, LiveMeasureDto::getMetricId, LiveMeasureDto::getValue, LiveMeasureDto::getDataAsString)
.containsExactlyInAnyOrder(
Tuple.tuple(measure1.getComponentUuid(), measure1.getProjectUuid(), measure1.getMetricId(), measure1.getValue(), measure1.getDataAsString()),
Tuple.tuple(measure2.getComponentUuid(), measure2.getProjectUuid(), measure2.getMetricId(), measure2.getValue(), measure2.getDataAsString()));
}

@Test
public void selectByComponentUuids_returns_empty_list_if_metric_does_not_match() {
LiveMeasureDto measure = newLiveMeasure().setMetricId(10);
underTest.insert(db.getSession(), measure);

List<LiveMeasureDto> selected = underTest.selectByComponentUuids(db.getSession(), singletonList(measure.getComponentUuid()), singletonList(222));

assertThat(selected).isEmpty();
}

@Test
public void selectByComponentUuids_returns_empty_list_if_component_does_not_match() {
LiveMeasureDto measure = newLiveMeasure();
underTest.insert(db.getSession(), measure);

List<LiveMeasureDto> selected = underTest.selectByComponentUuids(db.getSession(), singletonList("_missing_"), singletonList(measure.getMetricId()));

assertThat(selected).isEmpty();
}

@Test
public void test_selectMeasure() {
MetricDto metric = db.measures().insertMetric();
LiveMeasureDto stored = newLiveMeasure().setMetricId(metric.getId());
underTest.insert(db.getSession(), stored);

// metric exists but not component
assertThat(underTest.selectMeasure(db.getSession(), "_missing_", metric.getKey())).isEmpty();

// component exists but not metric
assertThat(underTest.selectMeasure(db.getSession(), stored.getComponentUuid(), "_missing_")).isEmpty();

// component and metric don't match
assertThat(underTest.selectMeasure(db.getSession(), "_missing_", "_missing_")).isEmpty();

// matches
assertThat(underTest.selectMeasure(db.getSession(), stored.getComponentUuid(), metric.getKey()).get())
.isEqualToComparingFieldByField(stored);
}

@Test
public void test_insertOrUpdate() {
// insert
LiveMeasureDto dto = newLiveMeasure();
underTest.insertOrUpdate(db.getSession(), dto, "foo");
verifyPersisted(dto);
verifyTableSize(1);

// update
dto.setValue(dto.getValue() + 1);
dto.setVariation(dto.getVariation() + 10);
dto.setData(dto.getDataAsString() + "_new");
underTest.insertOrUpdate(db.getSession(), dto, "foo");
verifyPersisted(dto);
verifyTableSize(1);
}

@Test
public void deleteByProjectUuidExcludingMarker() {
LiveMeasureDto measure1 = newLiveMeasure().setProjectUuid("P1");
LiveMeasureDto measure2 = newLiveMeasure().setProjectUuid("P1");
LiveMeasureDto measure3DifferentMarker = newLiveMeasure().setProjectUuid("P1");
LiveMeasureDto measure4NoMarker = newLiveMeasure().setProjectUuid("P1");
LiveMeasureDto measure5OtherProject = newLiveMeasure().setProjectUuid("P2");
underTest.insertOrUpdate(db.getSession(), measure1, "foo");
underTest.insertOrUpdate(db.getSession(), measure2, "foo");
underTest.insertOrUpdate(db.getSession(), measure3DifferentMarker, "bar");
underTest.insertOrUpdate(db.getSession(), measure4NoMarker, null);
underTest.insertOrUpdate(db.getSession(), measure5OtherProject, "foo");

underTest.deleteByProjectUuidExcludingMarker(db.getSession(), "P1", "foo");

verifyTableSize(3);
verifyPersisted(measure1);
verifyPersisted(measure2);
verifyPersisted(measure5OtherProject);
}

private void verifyTableSize(int expectedSize) {
assertThat(db.countRowsOfTable(db.getSession(), "live_measures")).isEqualTo(expectedSize);
}

private void verifyPersisted(LiveMeasureDto dto) {
List<LiveMeasureDto> selected = underTest.selectByComponentUuids(db.getSession(), singletonList(dto.getComponentUuid()), singletonList(dto.getMetricId()));
assertThat(selected).hasSize(1);
assertThat(selected.get(0)).isEqualToComparingFieldByField(dto);
}
}

+ 73
- 288
server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureDaoTest.java Datei anzeigen

@@ -19,45 +19,34 @@
*/
package org.sonar.db.measure;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import javax.annotation.Nullable;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.utils.System2;
import org.sonar.core.util.UuidFactoryImpl;
import org.sonar.core.util.Uuids;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.component.SnapshotTesting;
import org.sonar.db.metric.MetricDto;

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.UNIT_TEST_FILE;
import static org.sonar.api.utils.DateUtils.parseDate;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.component.ComponentTesting.newModuleDto;
import static org.sonar.db.component.SnapshotTesting.newAnalysis;
import static org.sonar.db.measure.MeasureTreeQuery.Strategy.CHILDREN;
import static org.sonar.db.measure.MeasureTreeQuery.Strategy.LEAVES;

public class MeasureDaoTest {

private static final int COVERAGE_METRIC_ID = 10;
private static final int COMPLEXITY_METRIC_ID = 11;
private static final int NCLOC_METRIC_ID = 12;
private static final long A_PERSON_ID = 444L;
private static final String LAST_ANALYSIS_UUID = "A1";
private static final String OTHER_ANALYSIS_UUID = "A2";
private static final String PREVIOUS_ANALYSIS_UUID = "previous analysis UUID";

@Rule
public ExpectedException expectedException = ExpectedException.none();
@@ -70,37 +59,48 @@ public class MeasureDaoTest {
private MeasureDao underTest = db.getDbClient().measureDao();

@Test
public void test_inserted_and_selected_columns() {
public void test_selectLastMeasure() {
MetricDto metric = db.measures().insertMetric();
ComponentDto project = db.components().insertPrivateProject();
insertAnalysis(LAST_ANALYSIS_UUID, project.uuid(), true);
db.components().insertComponent(newFileDto(project).setUuid("C4"));

MeasureDto inserted = new MeasureDto()
.setAnalysisUuid(LAST_ANALYSIS_UUID)
.setMetricId(2)
.setDeveloperId(3L)
.setComponentUuid("C4")
.setValue(5.0d)
.setData("data")
.setVariation(1d)
.setAlertStatus("alert")
.setAlertText("alert-text");
underTest.insert(db.getSession(), inserted);
db.commit();
ComponentDto file = db.components().insertComponent(newFileDto(project));
SnapshotDto lastAnalysis = insertAnalysis(project.uuid(), true);
SnapshotDto pastAnalysis = insertAnalysis(project.uuid(), false);

MeasureDto pastMeasure = MeasureTesting.newMeasureDto(metric, file, pastAnalysis);
MeasureDto lastMeasure = MeasureTesting.newMeasureDto(metric, file, lastAnalysis);
underTest.insert(db.getSession(), pastMeasure);
underTest.insert(db.getSession(), lastMeasure);

MeasureDto selected = underTest.selectLastMeasure(db.getSession(), file.uuid(), metric.getKey()).get();
assertThat(selected).isEqualToComparingFieldByField(lastMeasure);

assertThat(underTest.selectLastMeasure(dbSession, "_missing_", metric.getKey())).isEmpty();
assertThat(underTest.selectLastMeasure(dbSession, file.uuid(), "_missing_")).isEmpty();
assertThat(underTest.selectLastMeasure(dbSession, "_missing_", "_missing_")).isEmpty();
}

@Test
public void test_selectMeasure() {
MetricDto metric = db.measures().insertMetric();
ComponentDto project = db.components().insertPrivateProject();
ComponentDto file = db.components().insertComponent(newFileDto(project));
SnapshotDto lastAnalysis = insertAnalysis(project.uuid(), true);
SnapshotDto pastAnalysis = insertAnalysis(project.uuid(), false);

MeasureDto pastMeasure = MeasureTesting.newMeasureDto(metric, file, pastAnalysis);
MeasureDto lastMeasure = MeasureTesting.newMeasureDto(metric, file, lastAnalysis);
underTest.insert(db.getSession(), pastMeasure);
underTest.insert(db.getSession(), lastMeasure);

assertThat(underTest.selectMeasure(db.getSession(), lastAnalysis.getUuid(), file.uuid(), metric.getKey()).get())
.isEqualToComparingFieldByField(lastMeasure);

assertThat(underTest.selectMeasure(db.getSession(), pastAnalysis.getUuid(), file.uuid(), metric.getKey()).get())
.isEqualToComparingFieldByField(pastMeasure);

MeasureDto selected = underTest.selectSingle(db.getSession(), MeasureQuery.builder()
.setComponentUuid(inserted.getComponentUuid())
.setPersonId(inserted.getDeveloperId())
.build()).get();
assertThat(selected.getAnalysisUuid()).isEqualTo(inserted.getAnalysisUuid());
assertThat(selected.getMetricId()).isEqualTo(inserted.getMetricId());
assertThat(selected.getDeveloperId()).isEqualTo(inserted.getDeveloperId());
assertThat(selected.getComponentUuid()).isEqualTo(inserted.getComponentUuid());
assertThat(selected.getValue()).isEqualTo(inserted.getValue());
assertThat(selected.getData()).isEqualTo(inserted.getData());
assertThat(selected.getVariation()).isEqualTo(inserted.getVariation());
assertThat(selected.getAlertStatus()).isEqualTo(inserted.getAlertStatus());
assertThat(selected.getAlertText()).isEqualTo(inserted.getAlertText());
assertThat(underTest.selectMeasure(db.getSession(), "_missing_", file.uuid(), metric.getKey())).isEmpty();
assertThat(underTest.selectMeasure(db.getSession(), pastAnalysis.getUuid(), "_missing_", metric.getKey())).isEmpty();
assertThat(underTest.selectMeasure(db.getSession(), pastAnalysis.getUuid(), file.uuid(), "_missing_")).isEmpty();
}

@Test
@@ -109,28 +109,25 @@ public class MeasureDaoTest {
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);
insertAnalysis(OTHER_ANALYSIS_UUID, project1.uuid(), false);
SnapshotDto lastAnalysis = insertAnalysis(project1.uuid(), true);
SnapshotDto pastAnalysis = insertAnalysis(project1.uuid(), false);

String project2LastAnalysisUuid = "P2_LAST_ANALYSIS";
ComponentDto project2 = db.components().insertPrivateProject();
insertAnalysis(project2LastAnalysisUuid, project2.uuid(), true);
SnapshotDto project2LastAnalysis = insertAnalysis(project2.uuid(), true);

// project 1
insertMeasure("P1_M1", LAST_ANALYSIS_UUID, project1.uuid(), NCLOC_METRIC_ID);
insertMeasure("P1_M2", LAST_ANALYSIS_UUID, project1.uuid(), COVERAGE_METRIC_ID);
insertMeasure("P1_M3", OTHER_ANALYSIS_UUID, project1.uuid(), NCLOC_METRIC_ID);
insertMeasure("P1_M1", lastAnalysis.getUuid(), project1.uuid(), NCLOC_METRIC_ID);
insertMeasure("P1_M2", lastAnalysis.getUuid(), project1.uuid(), COVERAGE_METRIC_ID);
insertMeasure("P1_M3", pastAnalysis.getUuid(), project1.uuid(), NCLOC_METRIC_ID);
// project 2
insertMeasure("P2_M1", project2LastAnalysisUuid, project2.uuid(), NCLOC_METRIC_ID);
insertMeasure("P2_M2", project2LastAnalysisUuid, project2.uuid(), COVERAGE_METRIC_ID);
insertMeasure("P2_M1", project2LastAnalysis.getUuid(), project2.uuid(), NCLOC_METRIC_ID);
insertMeasure("P2_M2", project2LastAnalysis.getUuid(), project2.uuid(), COVERAGE_METRIC_ID);
// component C1
insertMeasure("M1", OTHER_ANALYSIS_UUID, "C1", NCLOC_METRIC_ID);
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);
insertMeasureOnPerson("M5", OTHER_ANALYSIS_UUID, "C1", NCLOC_METRIC_ID, 123L);
insertMeasure("M1", pastAnalysis.getUuid(), "C1", NCLOC_METRIC_ID);
insertMeasure("M2", lastAnalysis.getUuid(), "C1", NCLOC_METRIC_ID);
insertMeasure("M3", lastAnalysis.getUuid(), "C1", COVERAGE_METRIC_ID);
// component C2
insertMeasure("M6", LAST_ANALYSIS_UUID, "C2", NCLOC_METRIC_ID);
insertMeasure("M6", lastAnalysis.getUuid(), "C2", NCLOC_METRIC_ID);
db.commit();

verifyZeroMeasures(MeasureQuery.builder().setComponentUuids(project1.uuid(), emptyList()));
@@ -141,51 +138,37 @@ public class MeasureDaoTest {
// all measures of component C1 of last analysis
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1"), "M2", "M3");
// all measures of component C1 of non last analysis
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(OTHER_ANALYSIS_UUID), "M1");
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(pastAnalysis.getUuid()), "M1");
// all measures of component C1 of last analysis by UUID
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(LAST_ANALYSIS_UUID), "M2", "M3");
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(lastAnalysis.getUuid()), "M2", "M3");

// ncloc measure of component C1 of last analysis
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setMetricId(NCLOC_METRIC_ID), "M2");
// ncloc measure of component C1 of non last analysis
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(OTHER_ANALYSIS_UUID).setMetricId(NCLOC_METRIC_ID), "M1");
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(pastAnalysis.getUuid()).setMetricId(NCLOC_METRIC_ID), "M1");
// ncloc measure of component C1 of last analysis by UUID
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(LAST_ANALYSIS_UUID).setMetricId(NCLOC_METRIC_ID), "M2");
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(lastAnalysis.getUuid()).setMetricId(NCLOC_METRIC_ID), "M2");

// multiple measures of component C1 of last analysis
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setMetricIds(asList(NCLOC_METRIC_ID, COVERAGE_METRIC_ID)), "M2", "M3");
// multiple measures of component C1 of non last analysis
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(OTHER_ANALYSIS_UUID).setMetricIds(asList(NCLOC_METRIC_ID, COVERAGE_METRIC_ID)), "M1");
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(pastAnalysis.getUuid()).setMetricIds(asList(NCLOC_METRIC_ID, COVERAGE_METRIC_ID)), "M1");
// multiple measures of component C1 of last analysis by UUID
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(LAST_ANALYSIS_UUID).setMetricIds(asList(NCLOC_METRIC_ID, COVERAGE_METRIC_ID)), "M2", "M3");
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(lastAnalysis.getUuid()).setMetricIds(asList(NCLOC_METRIC_ID, COVERAGE_METRIC_ID)), "M2", "M3");

// missing measure of component C1 of last analysis
verifyZeroMeasures(MeasureQuery.builder().setComponentUuid("C1").setMetricId(COMPLEXITY_METRIC_ID));
// missing measure of component C1 of non last analysis
verifyZeroMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(OTHER_ANALYSIS_UUID).setMetricId(COMPLEXITY_METRIC_ID));
verifyZeroMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(pastAnalysis.getUuid()).setMetricId(COMPLEXITY_METRIC_ID));
// missing measure of component C1 of last analysis by UUID
verifyZeroMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(LAST_ANALYSIS_UUID).setMetricId(COMPLEXITY_METRIC_ID));
verifyZeroMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(lastAnalysis.getUuid()).setMetricId(COMPLEXITY_METRIC_ID));

// ncloc measures of components C1, C2 and C3 (which does not exist) of last analysis
verifyMeasures(MeasureQuery.builder().setComponentUuids(project1.uuid(), asList("C1", "C2", "C3")), "M2", "M3", "M6");
// ncloc measures of components C1, C2 and C3 (which does not exist) of non last analysis
verifyMeasures(MeasureQuery.builder().setComponentUuids(project1.uuid(), asList("C1", "C2", "C3")).setAnalysisUuid(OTHER_ANALYSIS_UUID), "M1");
verifyMeasures(MeasureQuery.builder().setComponentUuids(project1.uuid(), asList("C1", "C2", "C3")).setAnalysisUuid(pastAnalysis.getUuid()), "M1");
// ncloc measures of components C1, C2 and C3 (which does not exist) of last analysis by UUID
verifyMeasures(MeasureQuery.builder().setComponentUuids(project1.uuid(), asList("C1", "C2", "C3")).setAnalysisUuid(LAST_ANALYSIS_UUID), "M2", "M3", "M6");

// measures of missing developer of component C1 of last analysis
verifyZeroMeasures(MeasureQuery.builder().setComponentUuid("C1").setPersonId(123L));
// measures of missing developer of component C1 of non last analysis
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(OTHER_ANALYSIS_UUID).setPersonId(123L), "M5");
// measures of missing developer of component C1 of last analysis by UUID
verifyZeroMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(LAST_ANALYSIS_UUID).setPersonId(123L));

// developer measures of component C1 of last analysis
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setPersonId(A_PERSON_ID), "M4");
// developer measures of component C1 of non last analysis
verifyZeroMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(OTHER_ANALYSIS_UUID).setPersonId(A_PERSON_ID));
// developer measures of component C1 of last analysis by UUID
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(LAST_ANALYSIS_UUID).setPersonId(A_PERSON_ID), "M4");
verifyMeasures(MeasureQuery.builder().setComponentUuids(project1.uuid(), asList("C1", "C2", "C3")).setAnalysisUuid(lastAnalysis.getUuid()), "M2", "M3", "M6");

// projects measures of last analysis
verifyMeasures(MeasureQuery.builder().setProjectUuids(singletonList(project1.uuid())).setMetricId(NCLOC_METRIC_ID), "P1_M1");
@@ -194,113 +177,8 @@ public class MeasureDaoTest {
verifyMeasures(MeasureQuery.builder().setProjectUuids(asList(project1.uuid(), project2.uuid(), "UNKNOWN")).setMetricId(NCLOC_METRIC_ID), "P1_M1", "P2_M1");

// projects measures of none last analysis
verifyMeasures(MeasureQuery.builder().setProjectUuids(singletonList(project1.uuid())).setMetricId(NCLOC_METRIC_ID).setAnalysisUuid(OTHER_ANALYSIS_UUID), "P1_M3");
verifyMeasures(MeasureQuery.builder().setProjectUuids(asList(project1.uuid(), project2.uuid())).setMetricId(NCLOC_METRIC_ID).setAnalysisUuid(OTHER_ANALYSIS_UUID), "P1_M3");
}

@Test
public void selectSingle() {
ComponentDto project = db.components().insertPrivateProject();
db.components().insertComponent(newFileDto(project).setUuid("C1"));
insertAnalysis(LAST_ANALYSIS_UUID, project.uuid(), true);
insertMeasure("M1", LAST_ANALYSIS_UUID, "C1", NCLOC_METRIC_ID);
insertMeasure("M2", LAST_ANALYSIS_UUID, "C1", COMPLEXITY_METRIC_ID);
db.commit();

assertThat(selectSingle(MeasureQuery.builder().setComponentUuids(project.uuid(), emptyList()))).isNotPresent();
assertThat(selectSingle(MeasureQuery.builder().setComponentUuid("MISSING_COMPONENT"))).isNotPresent();

// select a single measure
assertThat(selectSingle(MeasureQuery.builder().setComponentUuid("C1").setMetricId(NCLOC_METRIC_ID))).isPresent();

// select multiple measures -> fail
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("expected one element");
selectSingle(MeasureQuery.builder().setComponentUuid("C1"));
}

@Test
public void select_tree_by_query() {
ComponentDto project = db.components().insertPrivateProject();
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);

// 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");
}

@Test
public void select_tree_by_query_use_only_latest_analysis() {
ComponentDto project = db.components().insertPrivateProject();
ComponentDto file1 = db.components().insertComponent(newFileDto(project).setUuid("C1").setName("File One"));
db.components().insertComponent(newFileDto(project).setUuid("C2").setName("File Two").setQualifier(UNIT_TEST_FILE));
insertAnalysis(LAST_ANALYSIS_UUID, project.uuid(), true);
insertAnalysis(OTHER_ANALYSIS_UUID, project.uuid(), false);

// project
insertMeasure("PROJECT_M1", LAST_ANALYSIS_UUID, project.uuid(), NCLOC_METRIC_ID);
insertMeasure("PROJECT_M2", OTHER_ANALYSIS_UUID, project.uuid(), NCLOC_METRIC_ID);
// component C1
insertMeasure("M2", LAST_ANALYSIS_UUID, "C1", NCLOC_METRIC_ID);
insertMeasure("M3", LAST_ANALYSIS_UUID, "C1", COVERAGE_METRIC_ID);
insertMeasure("M4", OTHER_ANALYSIS_UUID, "C1", COVERAGE_METRIC_ID);
// component C2
insertMeasure("M5", LAST_ANALYSIS_UUID, "C2", NCLOC_METRIC_ID);
insertMeasure("M6", OTHER_ANALYSIS_UUID, "C2", NCLOC_METRIC_ID);
db.commit();

// Children measures of project
verifyMeasures(project, MeasureTreeQuery.builder().setStrategy(CHILDREN), "PROJECT_M1", "M2", "M3", "M5");

// 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", "M2", "M3", "M5");

// Leaves measure on file
verifyMeasures(file1, MeasureTreeQuery.builder().setStrategy(LEAVES), "M2", "M3");
verifyMeasures(MeasureQuery.builder().setProjectUuids(singletonList(project1.uuid())).setMetricId(NCLOC_METRIC_ID).setAnalysisUuid(pastAnalysis.getUuid()), "P1_M3");
verifyMeasures(MeasureQuery.builder().setProjectUuids(asList(project1.uuid(), project2.uuid())).setMetricId(NCLOC_METRIC_ID).setAnalysisUuid(pastAnalysis.getUuid()), "P1_M3");
}

@Test
@@ -309,14 +187,14 @@ public class MeasureDaoTest {
long lastAnalysisDate = parseDate("2017-01-25").getTime();
long previousAnalysisDate = lastAnalysisDate - 10_000_000_000L;
long oldAnalysisDate = lastAnalysisDate - 100_000_000_000L;
dbClient.snapshotDao().insert(dbSession, newAnalysis(project).setUuid(LAST_ANALYSIS_UUID).setCreatedAt(lastAnalysisDate));
dbClient.snapshotDao().insert(dbSession, newAnalysis(project).setUuid(OTHER_ANALYSIS_UUID).setCreatedAt(previousAnalysisDate).setLast(false));
dbClient.snapshotDao().insert(dbSession, newAnalysis(project).setUuid("OLD_ANALYSIS_UUID").setCreatedAt(oldAnalysisDate).setLast(false));
SnapshotDto lastAnalysis = dbClient.snapshotDao().insert(dbSession, newAnalysis(project).setCreatedAt(lastAnalysisDate));
SnapshotDto pastAnalysis = dbClient.snapshotDao().insert(dbSession, newAnalysis(project).setCreatedAt(previousAnalysisDate).setLast(false));
dbClient.snapshotDao().insert(dbSession, newAnalysis(project).setCreatedAt(oldAnalysisDate).setLast(false));
db.commit();

// project
insertMeasure("PROJECT_M1", LAST_ANALYSIS_UUID, project.uuid(), NCLOC_METRIC_ID);
insertMeasure("PROJECT_M2", OTHER_ANALYSIS_UUID, project.uuid(), NCLOC_METRIC_ID);
insertMeasure("PROJECT_M1", lastAnalysis.getUuid(), project.uuid(), NCLOC_METRIC_ID);
insertMeasure("PROJECT_M2", pastAnalysis.getUuid(), project.uuid(), NCLOC_METRIC_ID);
insertMeasure("PROJECT_M3", "OLD_ANALYSIS_UUID", project.uuid(), NCLOC_METRIC_ID);
db.commit();

@@ -327,58 +205,6 @@ public class MeasureDaoTest {
assertThat(result).hasSize(2).extracting(MeasureDto::getData).containsOnly("PROJECT_M1", "PROJECT_M2");
}

@Test
public void selectByComponentsAndMetrics() {
ComponentDto project1 = db.components().insertPrivateProject(db.getDefaultOrganization(), "P1");
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);
insertAnalysis(OTHER_ANALYSIS_UUID, project1.uuid(), false);

String project2LastAnalysisUuid = "P2_LAST_ANALYSIS";
ComponentDto project2 = db.components().insertPrivateProject(db.getDefaultOrganization(), "P2");
insertAnalysis(project2LastAnalysisUuid, project2.uuid(), true);

// project 1
insertMeasure("P1_M1", LAST_ANALYSIS_UUID, project1.uuid(), NCLOC_METRIC_ID);
insertMeasure("P1_M2", LAST_ANALYSIS_UUID, project1.uuid(), COVERAGE_METRIC_ID);
insertMeasure("P1_M3", OTHER_ANALYSIS_UUID, project1.uuid(), NCLOC_METRIC_ID);
// project 2
insertMeasure("P2_M1", project2LastAnalysisUuid, project2.uuid(), NCLOC_METRIC_ID);
insertMeasure("P2_M2", project2LastAnalysisUuid, project2.uuid(), COVERAGE_METRIC_ID);
// component C1
insertMeasure("M1", OTHER_ANALYSIS_UUID, "C1", NCLOC_METRIC_ID);
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);
insertMeasureOnPerson("M5", OTHER_ANALYSIS_UUID, "C1", NCLOC_METRIC_ID, 123L);
// component C2
insertMeasure("M6", LAST_ANALYSIS_UUID, "C2", NCLOC_METRIC_ID);
db.commit();

assertThat(underTest.selectByComponentsAndMetrics(db.getSession(), Collections.emptyList(), Collections.emptyList())).isEmpty();

// Measures of component C1
assertThat(underTest.selectByComponentsAndMetrics(db.getSession(), singletonList("C1"), singletonList(NCLOC_METRIC_ID))).extracting(MeasureDto::getData).containsOnly("M2");
assertThat(underTest.selectByComponentsAndMetrics(db.getSession(), singletonList("C1"), asList(NCLOC_METRIC_ID, COVERAGE_METRIC_ID))).extracting(MeasureDto::getData)
.containsOnly("M2", "M3");

// ncloc measures of components C1, C2
assertThat(underTest.selectByComponentsAndMetrics(db.getSession(), asList("C1", "C2"), asList(NCLOC_METRIC_ID, COVERAGE_METRIC_ID))).extracting(MeasureDto::getData)
.containsOnly("M2", "M3", "M6");

// projects measures of last analysis
assertThat(underTest.selectByComponentsAndMetrics(db.getSession(), singletonList("P1"), singletonList(NCLOC_METRIC_ID))).extracting(MeasureDto::getData)
.containsOnly("P1_M1");
assertThat(underTest.selectByComponentsAndMetrics(db.getSession(), asList("P1", "P2"), asList(NCLOC_METRIC_ID, COVERAGE_METRIC_ID))).extracting(MeasureDto::getData)
.containsOnly("P1_M1", "P1_M2", "P2_M1", "P2_M2");
}

private Optional<MeasureDto> selectSingle(MeasureQuery.Builder query) {
return underTest.selectSingle(db.getSession(), query.build());
}

private void verifyMeasures(MeasureQuery.Builder query, String... expectedIds) {
List<MeasureDto> measures = underTest.selectByQuery(db.getSession(), query.build());
assertThat(measures).extracting(MeasureDto::getData).containsOnly(expectedIds);
@@ -388,63 +214,22 @@ public class MeasureDaoTest {
assertThat(underTest.selectByQuery(db.getSession(), query.build())).isEmpty();
}

private void verifyMeasures(ComponentDto baseComponent, MeasureTreeQuery.Builder measureQuery, String... expectedIds) {
List<MeasureDto> measures = new ArrayList<>();
underTest.selectTreeByQuery(db.getSession(), baseComponent, measureQuery.build(), result -> measures.add(result.getResultObject()));
assertThat(measures).extracting(MeasureDto::getData).containsOnly(expectedIds);
}

private void insertMeasure(String id, String analysisUuid, String componentUuid, int metricId) {
insertMeasure(id, analysisUuid, componentUuid, null, metricId);
}

private void insertMeasure(String id, String analysisUuid, String componentUuid, @Nullable Long developerId, int metricId) {
MeasureDto measure = MeasureTesting.newMeasure()
.setAnalysisUuid(analysisUuid)
.setComponentUuid(componentUuid)
.setMetricId(metricId)
// as ids can't be forced when inserting measures, the field "data"
// is used to store a virtual id. It is used then in assertions.
.setData(id)
.setDeveloperId(developerId);
db.getDbClient().measureDao().insert(db.getSession(), measure);
}

private String insertComponent(String scope, String qualifier, boolean enabled) {
String uuid = UuidFactoryImpl.INSTANCE.create();
ComponentDto componentDto = new ComponentDto()
.setOrganizationUuid("org1")
.setUuid(uuid)
.setScope(scope)
.setQualifier(qualifier)
.setProjectUuid("don't care")
.setRootUuid("don't care")
.setUuidPath("don't care")
.setDbKey("kee_" + uuid)
.setEnabled(enabled);
db.getDbClient().componentDao().insert(db.getSession(), componentDto);
return uuid;
}

private void insertMeasureOnPerson(String id, String analysisUuid, String componentUuid, int metricId, long personId) {
MeasureDto measure = MeasureTesting.newMeasure()
.setAnalysisUuid(analysisUuid)
.setComponentUuid(componentUuid)
.setMetricId(metricId)
.setDeveloperId(personId)
// as ids can't be forced when inserting measures, the field "data"
// is used to store a virtual id. It is used then in assertions.
.setData(id);
db.getDbClient().measureDao().insert(db.getSession(), measure);
}

private SnapshotDto insertAnalysis(String uuid, String projectUuid, boolean isLast) {
private SnapshotDto insertAnalysis(String projectUuid, boolean isLast) {
return db.getDbClient().snapshotDao().insert(db.getSession(), SnapshotTesting.newSnapshot()
.setUuid(uuid)
.setUuid(Uuids.createFast())
.setComponentUuid(projectUuid)
.setLast(isLast));
}

// TODO test selectPastMeasures

}

+ 10
- 2
server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureDbTester.java Datei anzeigen

@@ -28,16 +28,15 @@ import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.metric.MetricDto;

import static org.sonar.db.measure.MeasureTesting.newLiveMeasure;
import static org.sonar.db.measure.MeasureTesting.newMeasureDto;
import static org.sonar.db.metric.MetricTesting.newMetricDto;

public class MeasureDbTester {
private final DbTester db;
private final DbClient dbClient;
private final DbSession dbSession;

public MeasureDbTester(DbTester db) {
this.db = db;
this.dbClient = db.getDbClient();
this.dbSession = db.getSession();
}
@@ -51,6 +50,15 @@ public class MeasureDbTester {
return measureDto;
}

@SafeVarargs
public final LiveMeasureDto insertLiveMeasure(ComponentDto component, MetricDto metric, Consumer<LiveMeasureDto>... consumers){
LiveMeasureDto dto = newLiveMeasure(component, metric);
Arrays.stream(consumers).forEach(c -> c.accept(dto));
dbClient.liveMeasureDao().insert(dbSession, dto);
dbSession.commit();
return dto;
}

@SafeVarargs
public final MetricDto insertMetric(Consumer<MetricDto>... consumers){
MetricDto metricDto = newMetricDto();

+ 0
- 7
server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureQueryTest.java Datei anzeigen

@@ -81,13 +81,6 @@ public class MeasureQueryTest {
assertThat(query.getMetricIds()).isNull();
}

@Test
public void create_query_from_person_id() {
MeasureQuery query = MeasureQuery.builder().setProjectUuids(asList("PROJECT_1", "PROJECT_2")).setPersonId(100L).build();

assertThat(query.getPersonId()).isEqualTo(100L);
}

@Test
public void return_empty_when_metrics_are_empty() {
assertThat(MeasureQuery.builder()

+ 21
- 2
server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureTesting.java Datei anzeigen

@@ -50,10 +50,29 @@ public class MeasureTesting {
.setMetricId(cursor++)
.setComponentUuid(String.valueOf(cursor++))
.setAnalysisUuid(String.valueOf(cursor++))
.setDeveloperId(null)
.setData(String.valueOf(cursor++))
.setAlertStatus(String.valueOf(cursor++))
.setAlertText(String.valueOf(cursor++))
.setValue((double)cursor++);
.setValue((double) cursor++);
}

public static LiveMeasureDto newLiveMeasure() {
return new LiveMeasureDto()
.setMetricId(cursor++)
.setComponentUuid(String.valueOf(cursor++))
.setProjectUuid(String.valueOf(cursor++))
.setData(String.valueOf(cursor++))
.setValue((double) cursor++)
.setVariation((double) cursor++);
}

public static LiveMeasureDto newLiveMeasure(ComponentDto component, MetricDto metric) {
return new LiveMeasureDto()
.setMetricId(metric.getId())
.setComponentUuid(component.uuid())
.setProjectUuid(component.projectUuid())
.setData(String.valueOf(cursor++))
.setValue((double) cursor++)
.setVariation((double) cursor++);
}
}

+ 0
- 3
server/sonar-db-dao/src/test/java/org/sonar/db/measure/MeasureTreeQueryTest.java Datei anzeigen

@@ -43,14 +43,12 @@ public class MeasureTreeQueryTest {
.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
@@ -63,7 +61,6 @@ public class MeasureTreeQueryTest {
assertThat(query.getQualifiers()).isNull();
assertThat(query.getNameOrKeyQuery()).isNull();
assertThat(query.getMetricIds()).isNull();
assertThat(query.getPersonId()).isNull();
}

@Test

+ 3
- 7
server/sonar-db-dao/src/test/java/org/sonar/db/measure/PastMeasureDtoTest.java Datei anzeigen

@@ -29,26 +29,22 @@ public class PastMeasureDtoTest {
public void test_getter_and_setter() throws Exception {
PastMeasureDto dto = new PastMeasureDto()
.setValue(1d)
.setMetricId(2)
.setPersonId(5L);
.setMetricId(2);

assertThat(dto.hasValue()).isTrue();
assertThat(dto.getValue()).isEqualTo(1d);
assertThat(dto.getMetricId()).isEqualTo(2);
assertThat(dto.getPersonId()).isEqualTo(5L);
}

@Test
public void test_has_value() throws Exception {
PastMeasureDto measureWithValue = new PastMeasureDto()
.setValue(1d)
.setMetricId(2)
.setPersonId(5L);
.setMetricId(2);
assertThat(measureWithValue.hasValue()).isTrue();

PastMeasureDto measureWithoutValue = new PastMeasureDto()
.setMetricId(2)
.setPersonId(5L);
.setMetricId(2);
assertThat(measureWithoutValue.hasValue()).isFalse();
}


+ 92
- 133
server/sonar-db-dao/src/test/java/org/sonar/db/measure/ProjectMeasuresIndexerIteratorTest.java Datei anzeigen

@@ -25,7 +25,6 @@ import javax.annotation.Nullable;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.measures.Metric;
import org.sonar.api.utils.System2;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
@@ -35,7 +34,6 @@ import org.sonar.db.component.ComponentTesting;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.measure.ProjectMeasuresIndexerIterator.ProjectMeasures;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.metric.MetricTesting;
import org.sonar.db.organization.OrganizationDto;

import static org.assertj.core.api.Assertions.assertThat;
@@ -48,7 +46,6 @@ import static org.sonar.api.measures.Metric.ValueType.DISTRIB;
import static org.sonar.api.measures.Metric.ValueType.INT;
import static org.sonar.api.measures.Metric.ValueType.LEVEL;
import static org.sonar.api.measures.Metric.ValueType.STRING;
import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
import static org.sonar.db.component.ComponentTesting.newView;
import static org.sonar.db.component.SnapshotTesting.newAnalysis;

@@ -64,12 +61,13 @@ public class ProjectMeasuresIndexerIteratorTest {

@Test
public void return_project_measure() {
MetricDto metric1 = insertIntMetric("ncloc");
MetricDto metric2 = insertIntMetric("coverage");
ComponentDto project = ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization()).setDbKey("Project-Key").setName("Project Name").setTagsString("platform,java");
SnapshotDto analysis = dbTester.components().insertProjectAndSnapshot(project);
insertMeasure(project, analysis, metric1, 10d);
insertMeasure(project, analysis, metric2, 20d);
OrganizationDto organization = dbTester.organizations().insert();
ComponentDto project = dbTester.components().insertPrivateProject(organization, p -> p.setDbKey("Project-Key").setName("Project Name").setTagsString("platform,java"));
SnapshotDto analysis = dbTester.components().insertSnapshot(project);
MetricDto metric1 = dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setKey("ncloc"));
MetricDto metric2 = dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setKey("coverage"));
dbTester.measures().insertLiveMeasure(project, metric1, m -> m.setValue(10d));
dbTester.measures().insertLiveMeasure(project, metric2, m -> m.setValue(20d));

Map<String, ProjectMeasures> docsById = createResultSetAndReturnDocsById();

@@ -81,15 +79,15 @@ public class ProjectMeasuresIndexerIteratorTest {
assertThat(doc.getProject().getName()).isEqualTo("Project Name");
assertThat(doc.getProject().getTags()).containsExactly("platform", "java");
assertThat(doc.getProject().getAnalysisDate()).isNotNull().isEqualTo(analysis.getCreatedAt());
assertThat(doc.getMeasures().getNumericMeasures()).containsOnly(entry("ncloc", 10d), entry("coverage", 20d));
assertThat(doc.getMeasures().getNumericMeasures()).containsOnly(entry(metric1.getKey(), 10d), entry(metric2.getKey(), 20d));
}

@Test
public void return_project_measure_having_leak() throws Exception {
MetricDto metric = insertIntMetric("new_lines");
ComponentDto project = ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization());
SnapshotDto analysis = dbTester.components().insertProjectAndSnapshot(project);
insertMeasureOnLeak(project, analysis, metric, 10d);
OrganizationDto organization = dbTester.organizations().insert();
ComponentDto project = dbTester.components().insertPrivateProject(organization, p -> p.setDbKey("Project-Key").setName("Project Name").setTagsString("platform,java"));
MetricDto metric = dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setKey("new_lines"));
dbTester.measures().insertLiveMeasure(project, metric, m -> m.setVariation(10d));

Map<String, ProjectMeasures> docsById = createResultSetAndReturnDocsById();

@@ -98,34 +96,40 @@ public class ProjectMeasuresIndexerIteratorTest {

@Test
public void return_quality_gate_status_measure() throws Exception {
MetricDto metric = insertMetric("alert_status", LEVEL);
insertProjectAndMeasure("project1", metric, WARN.name());
insertProjectAndMeasure("project2", metric, OK.name());
insertProjectAndMeasure("project3", metric, ERROR.name());
OrganizationDto organization = dbTester.organizations().insert();
ComponentDto project1 = dbTester.components().insertPrivateProject(organization);
ComponentDto project2 = dbTester.components().insertPrivateProject(organization);
ComponentDto project3 = dbTester.components().insertPrivateProject(organization);
MetricDto metric = dbTester.measures().insertMetric(m -> m.setValueType(LEVEL.name()).setKey("alert_status"));
dbTester.measures().insertLiveMeasure(project1, metric, m -> m.setValue(null).setData(WARN.name()));
dbTester.measures().insertLiveMeasure(project2, metric, m -> m.setValue(null).setData(OK.name()));
dbTester.measures().insertLiveMeasure(project3, metric, m -> m.setValue(null).setData(ERROR.name()));

Map<String, ProjectMeasures> docsById = createResultSetAndReturnDocsById();

assertThat(docsById.get("project1").getMeasures().getQualityGateStatus()).isEqualTo("WARN");
assertThat(docsById.get("project2").getMeasures().getQualityGateStatus()).isEqualTo("OK");
assertThat(docsById.get("project3").getMeasures().getQualityGateStatus()).isEqualTo("ERROR");
assertThat(docsById.get(project1.uuid()).getMeasures().getQualityGateStatus()).isEqualTo("WARN");
assertThat(docsById.get(project2.uuid()).getMeasures().getQualityGateStatus()).isEqualTo("OK");
assertThat(docsById.get(project3.uuid()).getMeasures().getQualityGateStatus()).isEqualTo("ERROR");
}

@Test
public void does_not_fail_when_quality_gate_has_no_value() throws Exception {
MetricDto metric = insertMetric("alert_status", LEVEL);
insertProjectAndMeasure("project", metric, null);
OrganizationDto organization = dbTester.organizations().insert();
ComponentDto project = dbTester.components().insertPrivateProject(organization);
MetricDto metric = dbTester.measures().insertMetric(m -> m.setValueType(LEVEL.name()).setKey("alert_status"));
dbTester.measures().insertLiveMeasure(project, metric, m -> m.setValue(null).setVariation(null).setData((String) null));

Map<String, ProjectMeasures> docsById = createResultSetAndReturnDocsById();

assertThat(docsById.get("project").getMeasures().getNumericMeasures()).isEmpty();
assertThat(docsById.get(project.uuid()).getMeasures().getNumericMeasures()).isEmpty();
}

@Test
public void return_language_distribution_measure() throws Exception {
MetricDto metric = insertMetric("ncloc_language_distribution", DATA);
ComponentDto project = ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization());
SnapshotDto analysis = dbTester.components().insertProjectAndSnapshot(project);
insertMeasure(project, analysis, metric, "<null>=2;java=6;xoo=18");
OrganizationDto organization = dbTester.organizations().insert();
ComponentDto project = dbTester.components().insertPrivateProject(organization);
MetricDto metric = dbTester.measures().insertMetric(m -> m.setValueType(DATA.name()).setKey("ncloc_language_distribution"));
dbTester.measures().insertLiveMeasure(project, metric, m -> m.setValue(null).setData("<null>=2;java=6;xoo=18"));

Map<String, ProjectMeasures> docsById = createResultSetAndReturnDocsById();

@@ -135,14 +139,14 @@ public class ProjectMeasuresIndexerIteratorTest {

@Test
public void does_not_return_none_numeric_metrics() throws Exception {
MetricDto dataMetric = insertMetric("data", DATA);
MetricDto distribMetric = insertMetric("distrib", DISTRIB);
MetricDto stringMetric = insertMetric("string", STRING);
ComponentDto project = ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization());
SnapshotDto analysis = dbTester.components().insertProjectAndSnapshot(project);
insertMeasure(project, analysis, dataMetric, "dat");
insertMeasure(project, analysis, distribMetric, "dis");
insertMeasure(project, analysis, stringMetric, "str");
OrganizationDto organization = dbTester.organizations().insert();
ComponentDto project = dbTester.components().insertPrivateProject(organization);
MetricDto dataMetric = dbTester.measures().insertMetric(m -> m.setValueType(DATA.name()).setKey("data"));
MetricDto distribMetric = dbTester.measures().insertMetric(m -> m.setValueType(DISTRIB.name()).setKey("distrib"));
MetricDto stringMetric = dbTester.measures().insertMetric(m -> m.setValueType(STRING.name()).setKey("string"));
dbTester.measures().insertLiveMeasure(project, dataMetric, m -> m.setData("dat"));
dbTester.measures().insertLiveMeasure(project, distribMetric, m -> m.setData("dis"));
dbTester.measures().insertLiveMeasure(project, stringMetric, m -> m.setData("str"));

Map<String, ProjectMeasures> docsById = createResultSetAndReturnDocsById();

@@ -151,10 +155,10 @@ public class ProjectMeasuresIndexerIteratorTest {

@Test
public void does_not_return_disabled_metrics() throws Exception {
MetricDto disabledMetric = insertMetric("disabled", false, false, INT);
ComponentDto project = ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization());
SnapshotDto analysis = dbTester.components().insertProjectAndSnapshot(project);
insertMeasure(project, analysis, disabledMetric, 10d);
OrganizationDto organization = dbTester.organizations().insert();
ComponentDto project = dbTester.components().insertPrivateProject(organization);
MetricDto disabledMetric = dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setEnabled(false).setHidden(false).setKey("disabled"));
dbTester.measures().insertLiveMeasure(project, disabledMetric, m -> m.setValue(10d));

Map<String, ProjectMeasures> docsById = createResultSetAndReturnDocsById();

@@ -163,29 +167,28 @@ public class ProjectMeasuresIndexerIteratorTest {

@Test
public void ignore_measure_that_does_not_have_value() throws Exception {
MetricDto metric1 = insertIntMetric("coverage");
MetricDto metric2 = insertIntMetric("ncloc");
MetricDto leakMetric = insertIntMetric("new_lines");
ComponentDto project = ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization());
SnapshotDto analysis = dbTester.components().insertProjectAndSnapshot(project);
OrganizationDto organization = dbTester.organizations().insert();
MetricDto metric1 = dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setKey("coverage"));
MetricDto metric2 = dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setKey("ncloc"));
MetricDto leakMetric = dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setKey("new_lines"));
ComponentDto project = dbTester.components().insertPrivateProject(organization);

MeasureDto withValue = insertMeasure(project, analysis, metric1, 10d);
MeasureDto withLeakValue = insertMeasure(project, analysis, leakMetric, null, 20d);
MeasureDto withoutValue = insertMeasure(project, analysis, metric2, null, null);
dbTester.measures().insertLiveMeasure(project, metric1, m -> m.setValue(10d));
dbTester.measures().insertLiveMeasure(project, leakMetric, m -> m.setValue(null).setVariation(20d));
dbTester.measures().insertLiveMeasure(project, metric2, m -> m.setValue(null).setVariation(null));

Map<String, Double> numericMeasures = createResultSetAndReturnDocsById().get(project.uuid()).getMeasures().getNumericMeasures();
assertThat(numericMeasures).containsOnly(entry("coverage", 10d), entry("new_lines", 20d));
assertThat(numericMeasures).containsOnly(entry(metric1.getKey(), 10d), entry(leakMetric.getKey(), 20d));
}

@Test
public void ignore_numeric_measure_that_has_text_value_but_not_numeric_value() throws Exception {
MetricDto metric1 = insertIntMetric("coverage");
MetricDto metric2 = insertIntMetric("ncloc");
ComponentDto project = ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization());
SnapshotDto analysis = dbTester.components().insertProjectAndSnapshot(project);

MeasureDto withNumericValue = insertMeasure(project, analysis, metric1, 10d);
MeasureDto withTextValue = insertMeasure(project, analysis, metric2, "foo");
OrganizationDto organization = dbTester.organizations().insert();
MetricDto metric1 = dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setKey("coverage"));
MetricDto metric2 = dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setKey("ncloc"));
ComponentDto project = dbTester.components().insertPrivateProject(organization);
dbTester.measures().insertLiveMeasure(project, metric1, m -> m.setValue(10d).setData((String) null));
dbTester.measures().insertLiveMeasure(project, metric2, m -> m.setValue(null).setData("foo"));

Map<String, Double> numericMeasures = createResultSetAndReturnDocsById().get(project.uuid()).getMeasures().getNumericMeasures();
assertThat(numericMeasures).containsOnly(entry("coverage", 10d));
@@ -193,9 +196,13 @@ public class ProjectMeasuresIndexerIteratorTest {

@Test
public void return_many_project_measures() {
dbTester.components().insertProjectAndSnapshot(ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization()));
dbTester.components().insertProjectAndSnapshot(ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization()));
dbTester.components().insertProjectAndSnapshot(ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization()));
OrganizationDto organization = dbTester.organizations().insert();
ComponentDto project1 = dbTester.components().insertPrivateProject(organization);
ComponentDto project2 = dbTester.components().insertPrivateProject(organization);
ComponentDto project3 = dbTester.components().insertPrivateProject(organization);
dbTester.components().insertSnapshot(project1);
dbTester.components().insertSnapshot(project2);
dbTester.components().insertSnapshot(project3);

assertThat(createResultSetAndReturnDocsById()).hasSize(3);
}
@@ -215,42 +222,47 @@ public class ProjectMeasuresIndexerIteratorTest {

@Test
public void does_not_return_non_active_projects() throws Exception {
OrganizationDto organization = dbTester.organizations().insert();
// Disabled project
dbTester.components().insertProjectAndSnapshot(ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization()).setEnabled(false));
dbTester.components().insertProjectAndSnapshot(ComponentTesting.newPrivateProjectDto(organization).setEnabled(false));
// Disabled project with analysis
ComponentDto project = dbTester.components().insertComponent(ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization()).setEnabled(false));
ComponentDto project = dbTester.components().insertComponent(ComponentTesting.newPrivateProjectDto(organization).setEnabled(false));
dbClient.snapshotDao().insert(dbSession, newAnalysis(project));

// A view
dbTester.components().insertProjectAndSnapshot(newView(dbTester.getDefaultOrganization()));
dbTester.components().insertProjectAndSnapshot(newView(organization));

dbSession.commit();

assertResultSetIsEmpty();
assertThat(createResultSetAndReturnDocsById()).isEmpty();
}

@Test
public void return_only_docs_from_given_project() throws Exception {
OrganizationDto organizationDto = dbTester.organizations().insert();
ComponentDto project = ComponentTesting.newPrivateProjectDto(organizationDto);
SnapshotDto analysis = dbTester.components().insertProjectAndSnapshot(project);
dbTester.components().insertProjectAndSnapshot(ComponentTesting.newPrivateProjectDto(organizationDto));
dbTester.components().insertProjectAndSnapshot(ComponentTesting.newPrivateProjectDto(organizationDto));
OrganizationDto organization = dbTester.organizations().insert();
ComponentDto project1 = dbTester.components().insertPrivateProject(organization);
ComponentDto project2 = dbTester.components().insertPrivateProject(organization);
ComponentDto project3 = dbTester.components().insertPrivateProject(organization);
SnapshotDto analysis1 = dbTester.components().insertSnapshot(project1);
SnapshotDto analysis2 = dbTester.components().insertSnapshot(project2);
SnapshotDto analysis3 = dbTester.components().insertSnapshot(project3);

Map<String, ProjectMeasures> docsById = createResultSetAndReturnDocsById(project.uuid());
Map<String, ProjectMeasures> docsById = createResultSetAndReturnDocsById(project1.uuid());

assertThat(docsById).hasSize(1);
ProjectMeasures doc = docsById.get(project.uuid());
ProjectMeasures doc = docsById.get(project1.uuid());
assertThat(doc).isNotNull();
assertThat(doc.getProject().getUuid()).isEqualTo(project.uuid());
assertThat(doc.getProject().getKey()).isNotNull().isEqualTo(project.getDbKey());
assertThat(doc.getProject().getName()).isNotNull().isEqualTo(project.name());
assertThat(doc.getProject().getAnalysisDate()).isNotNull().isEqualTo(analysis.getCreatedAt());
assertThat(doc.getProject().getUuid()).isEqualTo(project1.uuid());
assertThat(doc.getProject().getKey()).isNotNull().isEqualTo(project1.getDbKey());
assertThat(doc.getProject().getName()).isNotNull().isEqualTo(project1.name());
assertThat(doc.getProject().getAnalysisDate()).isNotNull().isEqualTo(analysis1.getCreatedAt());
}

@Test
public void return_nothing_on_unknown_project() throws Exception {
dbTester.components().insertProjectAndSnapshot(ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization()));
OrganizationDto organization = dbTester.organizations().insert();
ComponentDto project = dbTester.components().insertPrivateProject(organization);
dbTester.components().insertSnapshot(project);

Map<String, ProjectMeasures> docsById = createResultSetAndReturnDocsById("UNKNOWN");

@@ -259,14 +271,13 @@ public class ProjectMeasuresIndexerIteratorTest {

@Test
public void non_main_branches_are_not_indexed() {
MetricDto metric = insertIntMetric("ncloc");
ComponentDto project = ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization());
SnapshotDto projectAnalysis = dbTester.components().insertProjectAndSnapshot(project);
insertMeasure(project, projectAnalysis, metric, 10d);
OrganizationDto organization = dbTester.organizations().insert();
ComponentDto project = dbTester.components().insertPrivateProject(organization);
MetricDto metric = dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setKey("ncloc"));
dbTester.measures().insertLiveMeasure(project, metric, m -> m.setValue(10d));

ComponentDto branch = dbTester.components().insertProjectBranch(project, b -> b.setKey("feature/foo"));
SnapshotDto branchAnalysis = dbTester.components().insertSnapshot(branch);
insertMeasure(branch, branchAnalysis, metric, 20d);
dbTester.measures().insertLiveMeasure(branch, metric, m -> m.setValue(20d));

Map<String, ProjectMeasures> docsById = createResultSetAndReturnDocsById();

@@ -284,56 +295,4 @@ public class ProjectMeasuresIndexerIteratorTest {
it.close();
return docsById;
}

private void assertResultSetIsEmpty() {
assertThat(createResultSetAndReturnDocsById()).isEmpty();
}

private MetricDto insertIntMetric(String metricKey) {
return insertMetric(metricKey, true, false, INT);
}

private MetricDto insertMetric(String metricKey, Metric.ValueType type) {
return insertMetric(metricKey, true, false, type);
}

private MetricDto insertMetric(String metricKey, boolean enabled, boolean hidden, Metric.ValueType type) {
MetricDto metric = dbClient.metricDao().insert(dbSession,
MetricTesting.newMetricDto()
.setKey(metricKey)
.setEnabled(enabled)
.setHidden(hidden)
.setValueType(type.name()));
dbSession.commit();
return metric;
}

private MeasureDto insertProjectAndMeasure(String projectUuid, MetricDto metric, String value) {
ComponentDto project = newPrivateProjectDto(dbTester.getDefaultOrganization(), projectUuid);
SnapshotDto analysis1 = dbTester.components().insertProjectAndSnapshot(project);
return insertMeasure(project, analysis1, metric, value);
}

private MeasureDto insertMeasure(ComponentDto project, SnapshotDto analysis, MetricDto metric, double value) {
return insertMeasure(project, analysis, metric, value, null);
}

private MeasureDto insertMeasureOnLeak(ComponentDto project, SnapshotDto analysis, MetricDto metric, double value) {
return insertMeasure(project, analysis, metric, null, value);
}

private MeasureDto insertMeasure(ComponentDto project, SnapshotDto analysis, MetricDto metric, String value) {
return insertMeasure(MeasureTesting.newMeasureDto(metric, project, analysis).setData(value));
}

private MeasureDto insertMeasure(ComponentDto project, SnapshotDto analysis, MetricDto metric, @Nullable Double value, @Nullable Double leakValue) {
return insertMeasure(MeasureTesting.newMeasureDto(metric, project, analysis).setValue(value).setVariation(leakValue));
}

private MeasureDto insertMeasure(MeasureDto measure) {
dbClient.measureDao().insert(dbSession, measure);
dbSession.commit();
return measure;
}

}

+ 1
- 1
server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeCommandsTest.java Datei anzeigen

@@ -164,7 +164,7 @@ public class PurgeCommandsTest {
@Test
public void deletePermissions_deletes_permissions_of_view() {
OrganizationDto organization = dbTester.organizations().insert();
ComponentDto project = dbTester.components().insertView(organization);
ComponentDto project = dbTester.components().insertPublicPortfolio(organization);
addPermissions(organization, project);

PurgeCommands purgeCommands = new PurgeCommands(dbTester.getSession(), profiler);

+ 24
- 3
server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java Datei anzeigen

@@ -50,6 +50,7 @@ import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
import org.sonar.db.measure.MeasureDto;
import org.sonar.db.measure.custom.CustomMeasureDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.property.PropertyDto;
import org.sonar.db.rule.RuleDefinitionDto;

@@ -86,7 +87,7 @@ public class PurgeDaoTest {
private PurgeDao underTest = dbTester.getDbClient().purgeDao();

@Test
public void shouldDeleteAbortedBuilds() {
public void purge_failed_ce_tasks() {
dbTester.prepareDbUnit(getClass(), "shouldDeleteAbortedBuilds.xml");

underTest.purge(dbSession, newConfigurationWith30Days(), PurgeListener.EMPTY, new PurgeProfiler());
@@ -96,7 +97,7 @@ public class PurgeDaoTest {
}

@Test
public void should_purge_project() {
public void purge_history_of_project() {
dbTester.prepareDbUnit(getClass(), "shouldPurgeProject.xml");
underTest.purge(dbSession, newConfigurationWith30Days(), PurgeListener.EMPTY, new PurgeProfiler());
dbSession.commit();
@@ -104,7 +105,7 @@ public class PurgeDaoTest {
}

@Test
public void should_purge_inactive_short_living_branches() {
public void purge_inactive_short_living_branches() {
when(system2.now()).thenReturn(new Date().getTime());
RuleDefinitionDto rule = dbTester.rules().insert();
ComponentDto project = dbTester.components().insertMainBranch();
@@ -394,6 +395,26 @@ public class PurgeDaoTest {
verifyNoEffect(componentDbTester.insertView(), componentDbTester.insertPrivateProject(), componentDbTester.insertPublicProject());
}

@Test
public void delete_live_measures_when_deleting_project() {
MetricDto metric = dbTester.measures().insertMetric();

ComponentDto project1 = dbTester.components().insertPublicProject();
ComponentDto module1 = dbTester.components().insertComponent(ComponentTesting.newModuleDto(project1));
dbTester.measures().insertLiveMeasure(project1, metric);
dbTester.measures().insertLiveMeasure(module1, metric);

ComponentDto project2 = dbTester.components().insertPublicProject();
ComponentDto module2 = dbTester.components().insertComponent(ComponentTesting.newModuleDto(project2));
dbTester.measures().insertLiveMeasure(project2, metric);
dbTester.measures().insertLiveMeasure(module2, metric);

underTest.deleteProject(dbSession, project1.uuid());

assertThat(dbClient.liveMeasureDao().selectByComponentUuids(dbSession, asList(project1.uuid(), module1.uuid()), asList(metric.getId()))).isEmpty();
assertThat(dbClient.liveMeasureDao().selectByComponentUuids(dbSession, asList(project2.uuid(), module2.uuid()), asList(metric.getId()))).hasSize(2);
}

private void verifyNoEffect(ComponentDto firstRoot, ComponentDto... otherRoots) {
DbSession dbSession = mock(DbSession.class);


+ 1
- 1
server/sonar-db-dao/src/test/resources/org/sonar/db/measure/MeasureDaoTest/insert-result.xml Datei anzeigen

@@ -4,7 +4,7 @@
analysis_uuid="u2"
metric_id="3"
component_uuid="FILE1"
person_id="23"
person_id="[null]"
value="2.0"
text_value="measure-value"
measure_data="[null]"

+ 0
- 59
server/sonar-db-dao/src/test/resources/org/sonar/db/measure/MeasureDaoTest/past_measures_with_person_id.xml Datei anzeigen

@@ -1,59 +0,0 @@
<dataset>

<metrics delete_historical_data="[null]"
id="1"
name="sqale_index"
VAL_TYPE="INT"
DESCRIPTION="[null]"
enabled="[true]"/>

<!-- project -->
<projects organization_uuid="org1"
uuid="ABCD"
uuid_path="NOT_USED"
project_uuid="ABCD"
module_uuid="[null]"
module_uuid_path=".ABCD."
enabled="[true]"
long_name="[null]"
id="1"
scope="PRJ"
qualifier="TRK"
kee="project"
name="project"
root_uuid="ABCD"/>

<!-- snapshots -->
<snapshots id="1000"
uuid="u1000"
component_uuid="ABCD"
created_at="1225544280000"
build_date="1225544280000"
version="[null]"
status="P"
islast="[false]"
/>

<!-- project measures -->
<project_measures id="1"
VALUE="60"
METRIC_ID="1"
analysis_uuid="u1000"
person_id="[null]"
component_uuid="ABCD"/>

<project_measures id="2"
VALUE="20"
METRIC_ID="1"
analysis_uuid="u1000"
person_id="20"
component_uuid="ABCD"/>

<project_measures id="3"
VALUE="40"
METRIC_ID="1"
analysis_uuid="u1000"
person_id="21"
component_uuid="ABCD"/>

</dataset>

+ 0
- 123
server/sonar-db-dao/src/test/resources/org/sonar/db/measure/MeasureDaoTest/with_some_measures_for_developer.xml Datei anzeigen

@@ -1,123 +0,0 @@
<dataset>

<metrics id="10"
name="authors_by_line"/>
<metrics id="11"
name="coverage_line_hits_data"/>
<metrics id="12"
name="ncloc"/>

<projects organization_uuid="org1"
uuid="uuid_1"
uuid_path="NOT_USED"
root_uuid="uuid_1"
id="1"
kee="org.struts:struts-core:src/org/struts/RequestContext.java"
enabled="[true]"/>
<projects organization_uuid="org1"
uuid="333"
uuid_path="NOT_USED"
root_uuid="333"
id="333"
kee="dev:John-Doe"
enabled="[true]"/>

<snapshots id="5"
uuid="u5"
component_uuid="uuid_1"
islast="[true]"
/>

<project_measures id="20"
analysis_uuid="u5"
metric_id="10"
value="[null]"
text_value="0123456789012345678901234567890123456789"
measure_data="[null]"
variation_value_1="[null]"
variation_value_2="[null]"
variation_value_3="[null]"
variation_value_4="[null]"
variation_value_5="[null]"
alert_status="[null]"
alert_text="[null]"
person_id="[null]"
component_uuid="1"/>
<project_measures id="21"
analysis_uuid="u5"
metric_id="11"
value="[null]"
text_value="36=1;37=1;38=1;39=1;43=1;48=1;53=1"
measure_data="[null]"
variation_value_1="[null]"
variation_value_2="[null]"
variation_value_3="[null]"
variation_value_4="[null]"
variation_value_5="[null]"
alert_status="[null]"
alert_text="[null]"
person_id="[null]"
component_uuid="1"/>
<project_measures id="22"
analysis_uuid="u5"
metric_id="12"
value="10"
text_value="[null]"
measure_data="[null]"
variation_value_1="1"
variation_value_2="2"
variation_value_3="3"
variation_value_4="4"
variation_value_5="-5"
alert_status="OK"
alert_text="Green"
person_id="[null]"
component_uuid="1"/>
<!--measures for developer 333-->
<project_measures id="30"
analysis_uuid="u5"
metric_id="10"
value="[null]"
text_value="0123456789012345678901234567890123456789"
measure_data="[null]"
variation_value_1="[null]"
variation_value_2="[null]"
variation_value_3="[null]"
variation_value_4="[null]"
variation_value_5="[null]"
alert_status="[null]"
alert_text="[null]"
person_id="333"
component_uuid="1"/>
<project_measures id="31"
analysis_uuid="u5"
metric_id="11"
value="[null]"
text_value="36=1;37=1;38=1;39=1;43=1;48=1;53=1"
measure_data="[null]"
variation_value_1="[null]"
variation_value_2="[null]"
variation_value_3="[null]"
variation_value_4="[null]"
variation_value_5="[null]"
alert_status="[null]"
alert_text="[null]"
person_id="333"
component_uuid="1"/>
<project_measures id="32"
analysis_uuid="u5"
metric_id="12"
value="10"
text_value="[null]"
measure_data="[null]"
variation_value_1="1"
variation_value_2="2"
variation_value_3="3"
variation_value_4="4"
variation_value_5="-5"
alert_status="OK"
alert_text="Green"
person_id="333"
component_uuid="1"/>

</dataset>

+ 0
- 19
server/sonar-db-dao/src/test/resources/org/sonar/db/purge/PurgeCommandsTest/shouldDeleteWastedMeasuresWhenPurgingAnalysis.xml Datei anzeigen

@@ -90,23 +90,4 @@
description="[null]"
measure_data="[null]"/>

<!-- delete measure on developers -->
<project_measures id="7"
component_uuid="1"
analysis_uuid="u1"
metric_id="2"
variation_value_1="[null]"
variation_value_2="[null]"
variation_value_3="[null]"
person_id="123456"
variation_value_4="[null]"
variation_value_5="[null]"
alert_text="[null]"
value="10.0"
text_value="[null]"
tendency="[null]"
measure_date="[null]"
alert_status="[null]"
description="[null]"
measure_data="[null]"/>
</dataset>

+ 0
- 1
server/sonar-db-dao/src/test/resources/org/sonar/db/purge/PurgeCommandsTest/shouldPurgeAnalysis.xml Datei anzeigen

@@ -111,7 +111,6 @@
<project_measures ID="2"
component_uuid="2"
analysis_uuid="u2"
characteristic_id="[null]"
variation_value_1="[null]"
variation_value_2="[null]"
variation_value_3="[null]"

+ 11
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/BaseSqlStatement.java Datei anzeigen

@@ -19,6 +19,7 @@
*/
package org.sonar.server.platform.db.migration.step;

import java.io.ByteArrayInputStream;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
@@ -76,6 +77,16 @@ class BaseSqlStatement<CHILD extends SqlStatement> implements SqlStatement<CHILD
return (CHILD) this;
}

@Override
public CHILD setBytes(int columnIndex, @Nullable byte[] value) throws SQLException {
if (value == null) {
pstmt.setNull(columnIndex, Types.BINARY);
} else {
pstmt.setBinaryStream(columnIndex, new ByteArrayInputStream(value));
}
return (CHILD) this;
}

@Override
public CHILD setDouble(int columnIndex, @Nullable Double value) throws SQLException {
if (value == null) {

+ 5
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DataChange.java Datei anzeigen

@@ -22,6 +22,7 @@ package org.sonar.server.platform.db.migration.step;
import java.sql.Connection;
import java.sql.SQLException;
import org.sonar.db.Database;
import org.sonar.db.dialect.Dialect;

public abstract class DataChange implements MigrationStep {

@@ -31,6 +32,10 @@ public abstract class DataChange implements MigrationStep {
this.db = db;
}

protected final Dialect getDialect() {
return db.getDialect();
}

@Override
public final void execute() throws SQLException {
try (Connection readConnection = createReadUncommittedConnection();

+ 2
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/SqlStatement.java Datei anzeigen

@@ -26,6 +26,8 @@ import javax.annotation.Nullable;
public interface SqlStatement<CHILD extends SqlStatement> extends AutoCloseable {
CHILD setBoolean(int columnIndex, @Nullable Boolean value) throws SQLException;

CHILD setBytes(int columnIndex, @Nullable byte[] value) throws SQLException;

CHILD setDate(int columnIndex, @Nullable Date value) throws SQLException;

CHILD setDouble(int columnIndex, @Nullable Double value) throws SQLException;

+ 125
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/CreateTableLiveMeasures.java Datei anzeigen

@@ -0,0 +1,125 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.platform.db.migration.version.v70;

import java.sql.SQLException;
import org.sonar.db.Database;
import org.sonar.server.platform.db.migration.def.VarcharColumnDef;
import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder;
import org.sonar.server.platform.db.migration.sql.CreateTableBuilder;
import org.sonar.server.platform.db.migration.step.DdlChange;

import static org.sonar.server.platform.db.migration.def.BigIntegerColumnDef.newBigIntegerColumnDefBuilder;
import static org.sonar.server.platform.db.migration.def.BlobColumnDef.newBlobColumnDefBuilder;
import static org.sonar.server.platform.db.migration.def.DecimalColumnDef.newDecimalColumnDefBuilder;
import static org.sonar.server.platform.db.migration.def.IntegerColumnDef.newIntegerColumnDefBuilder;
import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.UUID_SIZE;
import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;

public class CreateTableLiveMeasures extends DdlChange {

private static final String TABLE_NAME = "live_measures";

public CreateTableLiveMeasures(Database db) {
super(db);
}

@Override
public void execute(Context context) throws SQLException {
context.execute(new CreateTableBuilder(getDialect(), TABLE_NAME)
.addPkColumn(newVarcharColumnDefBuilder()
.setColumnName("uuid")
.setIsNullable(false)
.setLimit(VarcharColumnDef.UUID_SIZE)
.build())
.addColumn(newVarcharColumnDefBuilder()
.setColumnName("project_uuid")
.setIsNullable(false)
.setLimit(VarcharColumnDef.UUID_VARCHAR_SIZE)
.build())
.addColumn(newVarcharColumnDefBuilder()
.setColumnName("component_uuid")
.setIsNullable(false)
.setLimit(VarcharColumnDef.UUID_VARCHAR_SIZE)
.build())
.addColumn(newIntegerColumnDefBuilder()
.setColumnName("metric_id")
.setIsNullable(false)
.build())
.addColumn(newDecimalColumnDefBuilder()
.setColumnName("value")
.setPrecision(38)
.setScale(20)
.build())
.addColumn(newVarcharColumnDefBuilder()
.setColumnName("text_value")
.setIsNullable(true)
.setLimit(4_000)
.build())
.addColumn(newDecimalColumnDefBuilder()
.setColumnName("variation")
.setPrecision(38)
.setScale(20)
.build())
.addColumn(newBlobColumnDefBuilder()
.setColumnName("measure_data")
.build())
.addColumn(newVarcharColumnDefBuilder()
.setColumnName("update_marker")
.setIsNullable(true)
.setLimit(UUID_SIZE)
.build())
.addColumn(newBigIntegerColumnDefBuilder()
.setColumnName("created_at")
.setIsNullable(false)
.build())
.addColumn(newBigIntegerColumnDefBuilder()
.setColumnName("updated_at")
.setIsNullable(false)
.build())
.build());

context.execute(new CreateIndexBuilder(getDialect())
.addColumn(newVarcharColumnDefBuilder()
.setColumnName("project_uuid")
.setIsNullable(false)
.setLimit(VarcharColumnDef.UUID_VARCHAR_SIZE)
.build())
.setUnique(false)
.setTable(TABLE_NAME)
.setName("live_measures_project")
.build());

context.execute(new CreateIndexBuilder(getDialect())
.addColumn(newVarcharColumnDefBuilder()
.setColumnName("component_uuid")
.setIsNullable(false)
.setLimit(VarcharColumnDef.UUID_VARCHAR_SIZE)
.build())
.addColumn(newIntegerColumnDefBuilder()
.setColumnName("metric_id")
.setIsNullable(false)
.build())
.setUnique(true)
.setTable(TABLE_NAME)
.setName("live_measures_component")
.build());
}
}

+ 6
- 2
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70.java Datei anzeigen

@@ -32,7 +32,11 @@ public class DbVersion70 implements DbVersion {
.add(1902, "Make QUALITY_GATES.IS_BUILT_IN not null", MakeQualityGatesIsBuiltInNotNullable.class)
.add(1903, "Remove quality gates loaded templates", RemoveQualityGateLoadedTemplates.class)
.add(1904, "Rename quality gate \"SonarQube way\" to \"Sonar way\"", RenameOldSonarQubeWayQualityGate.class)
.add(1905, "Drop LOADED_TEMPLATES table", DropLoadedTemplatesTable.class);
.add(1905, "Drop LOADED_TEMPLATES table", DropLoadedTemplatesTable.class)
.add(1906, "Create table live_measures", CreateTableLiveMeasures.class)
.add(1907, "Populate table live_measures", PopulateLiveMeasures.class)
.add(1908, "Delete person and file measures", DeletePersonAndFileMeasures.class)
.add(1909, "Drop index on project_measures.person_id", DropIndexOnPersonMeasures.class)
;
}

}

+ 84
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DeletePersonAndFileMeasures.java Datei anzeigen

@@ -0,0 +1,84 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.platform.db.migration.version.v70;

import java.sql.SQLException;
import org.sonar.db.Database;
import org.sonar.db.dialect.H2;
import org.sonar.db.dialect.MsSql;
import org.sonar.db.dialect.MySql;
import org.sonar.db.dialect.Oracle;
import org.sonar.db.dialect.PostgreSql;
import org.sonar.server.platform.db.migration.step.DataChange;
import org.sonar.server.platform.db.migration.step.MassUpdate;

public class DeletePersonAndFileMeasures extends DataChange {
public DeletePersonAndFileMeasures(Database db) {
super(db);
}

@Override
protected void execute(Context context) throws SQLException {
MassUpdate massUpdate = context.prepareMassUpdate();
massUpdate.select("select uuid from snapshots");
massUpdate.rowPluralName("snapshots");
massUpdate.update(getDeleteSql());

massUpdate.execute((row, update) -> {
update.setString(1, row.getString(1));
return true;
});
}

private String getDeleteSql() {
switch (getDialect().getId()) {
case MySql.ID:
case MsSql.ID:
return "delete pm from project_measures pm " +
"inner join projects c on c.uuid = pm.component_uuid " +
"where pm.analysis_uuid = ? " +
"and (c.qualifier in ('UTS', 'FIL') or pm.person_id is not null)";
case H2.ID:
return "delete from project_measures " +
"where id in ( " +
" select pm.id from project_measures pm " +
" inner join projects c on c.uuid = pm.component_uuid " +
" where pm.analysis_uuid = ? " +
" and (c.qualifier in ('UTS', 'FIL') or pm.person_id is not null) " +
")";
case PostgreSql.ID:
return "delete from project_measures pm " +
"using projects c " +
"where pm.analysis_uuid = ? " +
"and c.uuid = pm.component_uuid " +
"and (c.qualifier in ('UTS', 'FIL') or pm.person_id is not null) ";
case Oracle.ID:
return "delete from project_measures pm where exists (" +
" select 1 from project_measures pm2 " +
" inner join projects c on c.uuid = pm2.component_uuid " +
" where pm2.analysis_uuid = ? " +
" and (c.qualifier in ('UTS', 'FIL') or pm.person_id is not null) " +
" and pm.id = pm2.id" +
")";
default:
throw new IllegalStateException("Unsupported DB dialect: " + getDialect());
}
}
}

+ 40
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DropIndexOnPersonMeasures.java Datei anzeigen

@@ -0,0 +1,40 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.platform.db.migration.version.v70;

import java.sql.SQLException;
import org.sonar.db.Database;
import org.sonar.server.platform.db.migration.sql.DropIndexBuilder;
import org.sonar.server.platform.db.migration.step.DdlChange;

public class DropIndexOnPersonMeasures extends DdlChange {

public DropIndexOnPersonMeasures(Database db) {
super(db);
}

@Override
public void execute(Context context) throws SQLException {
context.execute(new DropIndexBuilder(getDialect())
.setTable("project_measures")
.setName("measures_person")
.build());
}
}

+ 72
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasures.java Datei anzeigen

@@ -0,0 +1,72 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.platform.db.migration.version.v70;

import java.sql.SQLException;
import org.sonar.api.utils.System2;
import org.sonar.core.util.Uuids;
import org.sonar.db.Database;
import org.sonar.server.platform.db.migration.step.DataChange;
import org.sonar.server.platform.db.migration.step.MassUpdate;

public class PopulateLiveMeasures extends DataChange {

private final System2 system2;

public PopulateLiveMeasures(Database db, System2 system2) {
super(db);
this.system2 = system2;
}

@Override
protected void execute(Context context) throws SQLException {
long now = system2.now();
// reentrancy of migration
context.prepareUpsert("TRUNCATE TABLE live_measures").execute();

MassUpdate massUpdate = context.prepareMassUpdate();
massUpdate.select("SELECT p.uuid, p.project_uuid, pm.metric_id, pm.value, pm.text_value, pm.variation_value_1, pm.measure_data " +
"FROM project_measures pm " +
"INNER JOIN projects p on p.uuid = pm.component_uuid " +
"INNER JOIN snapshots s on s.uuid = pm.analysis_uuid " +
"WHERE s.islast = ?")
.setBoolean(1, true);

massUpdate.update("INSERT INTO live_measures "
+ "(uuid, component_uuid, project_uuid, metric_id, value, text_value, variation, measure_data, created_at, updated_at) "
+ "values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");

massUpdate.rowPluralName("live measures");
massUpdate.execute((row, update) -> {
update.setString(1, Uuids.create());
update.setString(2, row.getString(1));
update.setString(3, row.getString(2));
update.setInt(4, row.getInt(3));
update.setDouble(5, row.getNullableDouble(4));
update.setString(6, row.getString(5));
update.setDouble(7, row.getNullableDouble(6));
update.setBytes(8, row.getNullableBytes(7));
update.setLong(9, now);
update.setLong(10, now);
return true;
});
}

}

+ 59
- 0
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/CreateTableLiveMeasuresTest.java Datei anzeigen

@@ -0,0 +1,59 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.platform.db.migration.version.v70;

import java.sql.SQLException;
import java.sql.Types;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.db.CoreDbTester;

import static org.assertj.core.api.Assertions.assertThat;

public class CreateTableLiveMeasuresTest {
private static final String TABLE = "live_measures";

@Rule
public final CoreDbTester db = CoreDbTester.createForSchema(CreateTableLiveMeasuresTest.class, "empty.sql");

private CreateTableLiveMeasures underTest = new CreateTableLiveMeasures(db.database());

@Test
public void creates_table_on_empty_db() throws SQLException {
underTest.execute();

assertThat(db.countRowsOfTable(TABLE)).isEqualTo(0);

db.assertColumnDefinition(TABLE, "uuid", Types.VARCHAR, 40, false);
db.assertColumnDefinition(TABLE, "project_uuid", Types.VARCHAR, 50, false);
db.assertColumnDefinition(TABLE, "component_uuid", Types.VARCHAR, 50, false);
db.assertColumnDefinition(TABLE, "metric_id", Types.INTEGER, null, false);
db.assertColumnDefinition(TABLE, "value", Types.DOUBLE, null, true);
db.assertColumnDefinition(TABLE, "text_value", Types.VARCHAR, 4_000, true);
db.assertColumnDefinition(TABLE, "variation", Types.DOUBLE, null, true);
db.assertColumnDefinition(TABLE, "measure_data", Types.BLOB, null, true);
db.assertColumnDefinition(TABLE, "update_marker", Types.VARCHAR, 40, true);
db.assertColumnDefinition(TABLE, "created_at", Types.BIGINT, null, false);
db.assertColumnDefinition(TABLE, "updated_at", Types.BIGINT, null, false);

db.assertIndex(TABLE, "live_measures_project", "project_uuid");
db.assertUniqueIndex(TABLE, "live_measures_component", "component_uuid", "metric_id");
}
}

+ 1
- 1
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70Test.java Datei anzeigen

@@ -35,7 +35,7 @@ public class DbVersion70Test {

@Test
public void verify_migration_count() {
verifyMigrationCount(underTest, 6);
verifyMigrationCount(underTest, 10);
}

}

+ 125
- 0
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DeletePersonAndFileMeasuresTest.java Datei anzeigen

@@ -0,0 +1,125 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.platform.db.migration.version.v70;

import java.sql.SQLException;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.lang.math.RandomUtils;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.db.CoreDbTester;
import org.sonar.server.platform.db.migration.step.DataChange;

import static org.assertj.core.api.Assertions.assertThat;

public class DeletePersonAndFileMeasuresTest {
private static final AtomicInteger GENERATOR = new AtomicInteger();
@Rule
public CoreDbTester db = CoreDbTester.createForSchema(DeletePersonAndFileMeasuresTest.class, "initial.sql");

private DataChange underTest = new DeletePersonAndFileMeasures(db.database());

@Test
public void delete_file_and_person_measures() throws SQLException {
insertComponent("P1", "PRJ", "TRK");
insertComponent("D1", "DIR", "DIR");
insertComponent("F1", "FIL", "FIL");
insertComponent("F2", "FIL", "UTS");
insertSnapshot("S1", "P1", false);
insertSnapshot("S2", "P1", true);
// past measures
long m1 = insertMeasure("P1", "S1");
long m2 = insertMeasure("D1", "S1");
long m3 = insertMeasure("F1", "S1");
long m4 = insertMeasure("F2", "S1");
long m5 = insertPersonMeasure("P1", "S1");
long m6 = insertPersonMeasure("F1", "S1");
// last measures
long m7 = insertMeasure("P1", "S2");
long m8 = insertMeasure("D1", "S2");
long m9 = insertMeasure("F1", "S2");
long m10 = insertMeasure("F2", "S2");
long m11 = insertPersonMeasure("P1", "S2");
long m12 = insertPersonMeasure("F1", "S2");

underTest.execute();

assertThat(db.countRowsOfTable("PROJECTS")).isEqualTo(4);
assertThat(db.countRowsOfTable("SNAPSHOTS")).isEqualTo(2);
assertThatMeasuresAreExactly(m1, m2, m7, m8);

// migration is re-entrant
underTest.execute();
assertThat(db.countRowsOfTable("PROJECTS")).isEqualTo(4);
assertThat(db.countRowsOfTable("SNAPSHOTS")).isEqualTo(2);
assertThatMeasuresAreExactly(m1, m2, m7, m8);
}

private void assertThatMeasuresAreExactly(long... expectedMeasureIds) {
long[] ids = db.select("select id as \"id\" from project_measures")
.stream()
.mapToLong(m -> (Long) m.get("id"))
.toArray();
assertThat(ids).containsOnly(expectedMeasureIds);
}

private void insertComponent(String uuid, String scope, String qualifier) {
db.executeInsert("PROJECTS",
"ORGANIZATION_UUID", "O1",
"KEE", "" + GENERATOR.incrementAndGet(),
"UUID", uuid,
"PROJECT_UUID", "" + GENERATOR.incrementAndGet(),
"MAIN_BRANCH_PROJECT_UUID", "" + GENERATOR.incrementAndGet(),
"UUID_PATH", ".",
"ROOT_UUID", "" + GENERATOR.incrementAndGet(),
"PRIVATE", "true",
"QUALIFIER", qualifier,
"SCOPE", scope);
}

private void insertSnapshot(String uuid, String projectUuid, boolean last) {
db.executeInsert("SNAPSHOTS",
"UUID", uuid,
"COMPONENT_UUID", projectUuid,
"STATUS", "P",
"ISLAST", last);
}

private long insertMeasure(String componentUuid, String analysisUuid) {
long id = GENERATOR.incrementAndGet();
db.executeInsert("PROJECT_MEASURES",
"ID", id,
"METRIC_ID", "42",
"COMPONENT_UUID", componentUuid,
"ANALYSIS_UUID", analysisUuid);
return id;
}

private long insertPersonMeasure(String componentUuid, String analysisUuid) {
long id = GENERATOR.incrementAndGet();
db.executeInsert("PROJECT_MEASURES",
"ID", id,
"METRIC_ID", "42",
"COMPONENT_UUID", componentUuid,
"ANALYSIS_UUID", analysisUuid,
"PERSON_ID", RandomUtils.nextInt(100));
return id;
}
}

+ 48
- 0
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DropIndexOnPersonMeasuresTest.java Datei anzeigen

@@ -0,0 +1,48 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.platform.db.migration.version.v70;

import java.sql.SQLException;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.db.CoreDbTester;
import org.sonar.server.platform.db.migration.step.DdlChange;

public class DropIndexOnPersonMeasuresTest {

private static final String TABLE = "project_measures";
private static final String INDEX = "measures_person";

@Rule
public CoreDbTester db = CoreDbTester.createForSchema(DropIndexOnPersonMeasuresTest.class, "initial.sql");

private DdlChange underTest = new DropIndexOnPersonMeasures(db.database());

@Test
public void drop_index() throws SQLException {
db.assertIndex(TABLE, INDEX, "person_id");

underTest.execute();

db.assertIndexDoesNotExist(TABLE, INDEX);
}
}



+ 140
- 0
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasuresTest.java Datei anzeigen

@@ -0,0 +1,140 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.platform.db.migration.version.v70;

import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import org.assertj.core.groups.Tuple;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.utils.System2;
import org.sonar.api.utils.internal.TestSystem2;
import org.sonar.db.CoreDbTester;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.tuple;

public class PopulateLiveMeasuresTest {

private System2 system2 = new TestSystem2().setNow(1_500_000_000_000L);

@Rule
public CoreDbTester db = CoreDbTester.createForSchema(PopulateLiveMeasuresTest.class, "initial.sql");

private PopulateLiveMeasures underTest = new PopulateLiveMeasures(db.database(), system2);

@Test
public void do_nothing_when_no_data() throws SQLException {
assertThat(db.countRowsOfTable("PROJECT_MEASURES")).isEqualTo(0);
underTest.execute();
assertThat(db.countRowsOfTable("LIVE_MEASURES")).isEqualTo(0);
}

@Test
public void execute_must_update_database() throws SQLException {
generateProjectMeasures();

underTest.execute();

assertThat(getLiveMeasures()).extracting(
field("COMPONENT_UUID"),
field("PROJECT_UUID"),
field("METRIC_ID"),
field("VALUE"),
field("TEXT_VALUE"),
field("VARIATION"),
field("MEASURE_DATA")
).containsExactlyInAnyOrder(generateLiveMeasures());
}

private Function<Map<String, Object>, Object> field(String name) {
return m -> m.get(name);
}

@Test
public void migration_is_reentrant() throws SQLException {
generateProjectMeasures();

underTest.execute();
underTest.execute();

assertThat(getLiveMeasures()).extracting(
field("COMPONENT_UUID"),
field("PROJECT_UUID"),
field("METRIC_ID"),
field("VALUE"),
field("TEXT_VALUE"),
field("VARIATION"),
field("MEASURE_DATA")
).containsExactlyInAnyOrder(generateLiveMeasures());
}

private void generateProjectMeasures() {
Map<String, Object> project = new HashMap<>();
project.put("UUID", "PRJ1");
project.put("ORGANIZATION_UUID", "ORG1");
project.put("UUID_PATH", "X");
project.put("ROOT_UUID", "X");
project.put("PROJECT_UUID", "PRJ1");
project.put("PRIVATE", "FALSE");
db.executeInsert("PROJECTS", project);

Map<String, Object> analysis1 = new HashMap<>();
analysis1.put("UUID", "A1");
analysis1.put("ISLAST", "FALSE");
analysis1.put("COMPONENT_UUID", "PRJ1");
db.executeInsert("SNAPSHOTS", analysis1);

Map<String, Object> analysis2 = new HashMap<>();
analysis2.put("UUID", "A2");
analysis2.put("ISLAST", "TRUE");
analysis2.put("COMPONENT_UUID", "PRJ1");
db.executeInsert("SNAPSHOTS", analysis2);

Map<String, Object> measure1 = new HashMap<>();
measure1.put("COMPONENT_UUID", "PRJ1");
measure1.put("ANALYSIS_UUID", "A1");
measure1.put("METRIC_ID", "123");
db.executeInsert("PROJECT_MEASURES", measure1);

Map<String, Object> measure2 = new HashMap<>();
measure2.put("COMPONENT_UUID", "PRJ1");
measure2.put("ANALYSIS_UUID", "A2");
measure2.put("METRIC_ID", "123");
measure2.put("VALUE", "234");
measure2.put("TEXT_VALUE", "TEXT_VALUEx");
measure2.put("VARIATION_VALUE_1", "345");
measure2.put("MEASURE_DATA", "FFFF");
db.executeInsert("PROJECT_MEASURES", measure2);
}

private List<Map<String, Object>> getLiveMeasures() {
return db.select("SELECT * FROM LIVE_MEASURES");
}

private Tuple[] generateLiveMeasures() {
return new Tuple[] {
tuple("PRJ1", "PRJ1", 123L, 234.0, "TEXT_VALUEx", 345.0, new byte[] {-1, -1})
};
}
}

+ 0
- 0
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/CreateTableLiveMeasuresTest/empty.sql Datei anzeigen


+ 98
- 0
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/DeletePersonAndFileMeasuresTest/initial.sql Datei anzeigen

@@ -0,0 +1,98 @@
CREATE TABLE "PROJECTS" (
"ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
"ORGANIZATION_UUID" VARCHAR(40) NOT NULL,
"KEE" VARCHAR(400),
"UUID" VARCHAR(50) NOT NULL,
"UUID_PATH" VARCHAR(1500) NOT NULL,
"ROOT_UUID" VARCHAR(50) NOT NULL,
"PROJECT_UUID" VARCHAR(50) NOT NULL,
"MODULE_UUID" VARCHAR(50),
"MODULE_UUID_PATH" VARCHAR(1500),
"MAIN_BRANCH_PROJECT_UUID" VARCHAR(50),
"NAME" VARCHAR(2000),
"DESCRIPTION" VARCHAR(2000),
"PRIVATE" BOOLEAN NOT NULL,
"TAGS" VARCHAR(500),
"ENABLED" BOOLEAN NOT NULL DEFAULT TRUE,
"SCOPE" VARCHAR(3),
"QUALIFIER" VARCHAR(10),
"DEPRECATED_KEE" VARCHAR(400),
"PATH" VARCHAR(2000),
"LANGUAGE" VARCHAR(20),
"COPY_COMPONENT_UUID" VARCHAR(50),
"LONG_NAME" VARCHAR(2000),
"DEVELOPER_UUID" VARCHAR(50),
"CREATED_AT" TIMESTAMP,
"AUTHORIZATION_UPDATED_AT" BIGINT,
"B_CHANGED" BOOLEAN,
"B_COPY_COMPONENT_UUID" VARCHAR(50),
"B_DESCRIPTION" VARCHAR(2000),
"B_ENABLED" BOOLEAN,
"B_UUID_PATH" VARCHAR(1500),
"B_LANGUAGE" VARCHAR(20),
"B_LONG_NAME" VARCHAR(500),
"B_MODULE_UUID" VARCHAR(50),
"B_MODULE_UUID_PATH" VARCHAR(1500),
"B_NAME" VARCHAR(500),
"B_PATH" VARCHAR(2000),
"B_QUALIFIER" VARCHAR(10)
);
CREATE INDEX "PROJECTS_ORGANIZATION" ON "PROJECTS" ("ORGANIZATION_UUID");
CREATE UNIQUE INDEX "PROJECTS_KEE" ON "PROJECTS" ("KEE");
CREATE INDEX "PROJECTS_ROOT_UUID" ON "PROJECTS" ("ROOT_UUID");
CREATE UNIQUE INDEX "PROJECTS_UUID" ON "PROJECTS" ("UUID");
CREATE INDEX "PROJECTS_PROJECT_UUID" ON "PROJECTS" ("PROJECT_UUID");
CREATE INDEX "PROJECTS_MODULE_UUID" ON "PROJECTS" ("MODULE_UUID");
CREATE INDEX "PROJECTS_QUALIFIER" ON "PROJECTS" ("QUALIFIER");


CREATE TABLE "SNAPSHOTS" (
"ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
"UUID" VARCHAR(50) NOT NULL,
"CREATED_AT" BIGINT,
"BUILD_DATE" BIGINT,
"COMPONENT_UUID" VARCHAR(50) NOT NULL,
"STATUS" VARCHAR(4) NOT NULL DEFAULT 'U',
"PURGE_STATUS" INTEGER,
"ISLAST" BOOLEAN NOT NULL DEFAULT FALSE,
"VERSION" VARCHAR(500),
"PERIOD1_MODE" VARCHAR(100),
"PERIOD1_PARAM" VARCHAR(100),
"PERIOD1_DATE" BIGINT,
"PERIOD2_MODE" VARCHAR(100),
"PERIOD2_PARAM" VARCHAR(100),
"PERIOD2_DATE" BIGINT,
"PERIOD3_MODE" VARCHAR(100),
"PERIOD3_PARAM" VARCHAR(100),
"PERIOD3_DATE" BIGINT,
"PERIOD4_MODE" VARCHAR(100),
"PERIOD4_PARAM" VARCHAR(100),
"PERIOD4_DATE" BIGINT,
"PERIOD5_MODE" VARCHAR(100),
"PERIOD5_PARAM" VARCHAR(100),
"PERIOD5_DATE" BIGINT
);
CREATE INDEX "SNAPSHOT_COMPONENT" ON "SNAPSHOTS" ("COMPONENT_UUID");
CREATE UNIQUE INDEX "ANALYSES_UUID" ON "SNAPSHOTS" ("UUID");


CREATE TABLE "PROJECT_MEASURES" (
"ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
"VALUE" DOUBLE,
"METRIC_ID" INTEGER NOT NULL,
"COMPONENT_UUID" VARCHAR(50) NOT NULL,
"ANALYSIS_UUID" VARCHAR(50) NOT NULL,
"TEXT_VALUE" VARCHAR(4000),
"ALERT_STATUS" VARCHAR(5),
"ALERT_TEXT" VARCHAR(4000),
"DESCRIPTION" VARCHAR(4000),
"PERSON_ID" INTEGER,
"VARIATION_VALUE_1" DOUBLE,
"VARIATION_VALUE_2" DOUBLE,
"VARIATION_VALUE_3" DOUBLE,
"VARIATION_VALUE_4" DOUBLE,
"VARIATION_VALUE_5" DOUBLE,
"MEASURE_DATA" BINARY
);
CREATE INDEX "MEASURES_COMPONENT_UUID" ON "PROJECT_MEASURES" ("COMPONENT_UUID");
CREATE INDEX "MEASURES_ANALYSIS_METRIC" ON "PROJECT_MEASURES" ("ANALYSIS_UUID", "METRIC_ID");

+ 21
- 0
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/DropIndexOnPersonMeasuresTest/initial.sql Datei anzeigen

@@ -0,0 +1,21 @@
CREATE TABLE "PROJECT_MEASURES" (
"ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
"VALUE" DOUBLE,
"METRIC_ID" INTEGER NOT NULL,
"COMPONENT_UUID" VARCHAR(50) NOT NULL,
"ANALYSIS_UUID" VARCHAR(50) NOT NULL,
"TEXT_VALUE" VARCHAR(4000),
"ALERT_STATUS" VARCHAR(5),
"ALERT_TEXT" VARCHAR(4000),
"DESCRIPTION" VARCHAR(4000),
"PERSON_ID" INTEGER,
"VARIATION_VALUE_1" DOUBLE,
"VARIATION_VALUE_2" DOUBLE,
"VARIATION_VALUE_3" DOUBLE,
"VARIATION_VALUE_4" DOUBLE,
"VARIATION_VALUE_5" DOUBLE,
"MEASURE_DATA" BINARY
);
CREATE INDEX "MEASURES_COMPONENT_UUID" ON "PROJECT_MEASURES" ("COMPONENT_UUID");
CREATE INDEX "MEASURES_ANALYSIS_METRIC" ON "PROJECT_MEASURES" ("ANALYSIS_UUID", "METRIC_ID");
CREATE INDEX "MEASURES_PERSON" ON "PROJECT_MEASURES" ("PERSON_ID");

+ 116
- 0
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasuresTest/initial.sql Datei anzeigen

@@ -0,0 +1,116 @@
CREATE TABLE "PROJECTS" (
"ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
"ORGANIZATION_UUID" VARCHAR(40) NOT NULL,
"KEE" VARCHAR(400),
"UUID" VARCHAR(50) NOT NULL,
"UUID_PATH" VARCHAR(1500) NOT NULL,
"ROOT_UUID" VARCHAR(50) NOT NULL,
"PROJECT_UUID" VARCHAR(50) NOT NULL,
"MODULE_UUID" VARCHAR(50),
"MODULE_UUID_PATH" VARCHAR(1500),
"MAIN_BRANCH_PROJECT_UUID" VARCHAR(50),
"NAME" VARCHAR(2000),
"DESCRIPTION" VARCHAR(2000),
"PRIVATE" BOOLEAN NOT NULL,
"TAGS" VARCHAR(500),
"ENABLED" BOOLEAN NOT NULL DEFAULT TRUE,
"SCOPE" VARCHAR(3),
"QUALIFIER" VARCHAR(10),
"DEPRECATED_KEE" VARCHAR(400),
"PATH" VARCHAR(2000),
"LANGUAGE" VARCHAR(20),
"COPY_COMPONENT_UUID" VARCHAR(50),
"LONG_NAME" VARCHAR(2000),
"DEVELOPER_UUID" VARCHAR(50),
"CREATED_AT" TIMESTAMP,
"AUTHORIZATION_UPDATED_AT" BIGINT,
"B_CHANGED" BOOLEAN,
"B_COPY_COMPONENT_UUID" VARCHAR(50),
"B_DESCRIPTION" VARCHAR(2000),
"B_ENABLED" BOOLEAN,
"B_UUID_PATH" VARCHAR(1500),
"B_LANGUAGE" VARCHAR(20),
"B_LONG_NAME" VARCHAR(500),
"B_MODULE_UUID" VARCHAR(50),
"B_MODULE_UUID_PATH" VARCHAR(1500),
"B_NAME" VARCHAR(500),
"B_PATH" VARCHAR(2000),
"B_QUALIFIER" VARCHAR(10)
);
CREATE INDEX "PROJECTS_ORGANIZATION" ON "PROJECTS" ("ORGANIZATION_UUID");
CREATE UNIQUE INDEX "PROJECTS_KEE" ON "PROJECTS" ("KEE");
CREATE INDEX "PROJECTS_ROOT_UUID" ON "PROJECTS" ("ROOT_UUID");
CREATE UNIQUE INDEX "PROJECTS_UUID" ON "PROJECTS" ("UUID");
CREATE INDEX "PROJECTS_PROJECT_UUID" ON "PROJECTS" ("PROJECT_UUID");
CREATE INDEX "PROJECTS_MODULE_UUID" ON "PROJECTS" ("MODULE_UUID");
CREATE INDEX "PROJECTS_QUALIFIER" ON "PROJECTS" ("QUALIFIER");

CREATE TABLE "SNAPSHOTS" (
"ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
"UUID" VARCHAR(50) NOT NULL,
"CREATED_AT" BIGINT,
"BUILD_DATE" BIGINT,
"COMPONENT_UUID" VARCHAR(50) NOT NULL,
"STATUS" VARCHAR(4) NOT NULL DEFAULT 'U',
"PURGE_STATUS" INTEGER,
"ISLAST" BOOLEAN NOT NULL DEFAULT FALSE,
"VERSION" VARCHAR(500),
"PERIOD1_MODE" VARCHAR(100),
"PERIOD1_PARAM" VARCHAR(100),
"PERIOD1_DATE" BIGINT,
"PERIOD2_MODE" VARCHAR(100),
"PERIOD2_PARAM" VARCHAR(100),
"PERIOD2_DATE" BIGINT,
"PERIOD3_MODE" VARCHAR(100),
"PERIOD3_PARAM" VARCHAR(100),
"PERIOD3_DATE" BIGINT,
"PERIOD4_MODE" VARCHAR(100),
"PERIOD4_PARAM" VARCHAR(100),
"PERIOD4_DATE" BIGINT,
"PERIOD5_MODE" VARCHAR(100),
"PERIOD5_PARAM" VARCHAR(100),
"PERIOD5_DATE" BIGINT
);
CREATE INDEX "SNAPSHOT_COMPONENT" ON "SNAPSHOTS" ("COMPONENT_UUID");
CREATE UNIQUE INDEX "ANALYSES_UUID" ON "SNAPSHOTS" ("UUID");


CREATE TABLE "LIVE_MEASURES" (
"UUID" VARCHAR(40) NOT NULL PRIMARY KEY,
"PROJECT_UUID" VARCHAR(50) NOT NULL,
"COMPONENT_UUID" VARCHAR(50) NOT NULL,
"METRIC_ID" INTEGER NOT NULL,
"VALUE" DOUBLE,
"TEXT_VALUE" VARCHAR(4000),
"VARIATION" DOUBLE,
"MEASURE_DATA" BINARY,
"UPDATE_MARKER" VARCHAR(40),
"CREATED_AT" BIGINT NOT NULL,
"UPDATED_AT" BIGINT NOT NULL
);
CREATE INDEX "LIVE_MEASURES_PROJECT" ON "LIVE_MEASURES" ("PROJECT_UUID");
CREATE UNIQUE INDEX "LIVE_MEASURES_COMPONENT" ON "LIVE_MEASURES" ("COMPONENT_UUID", "METRIC_ID");


CREATE TABLE "PROJECT_MEASURES" (
"ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
"VALUE" DOUBLE,
"METRIC_ID" INTEGER NOT NULL,
"COMPONENT_UUID" VARCHAR(50) NOT NULL,
"ANALYSIS_UUID" VARCHAR(50) NOT NULL,
"TEXT_VALUE" VARCHAR(4000),
"ALERT_STATUS" VARCHAR(5),
"ALERT_TEXT" VARCHAR(4000),
"DESCRIPTION" VARCHAR(4000),
"PERSON_ID" INTEGER,
"VARIATION_VALUE_1" DOUBLE,
"VARIATION_VALUE_2" DOUBLE,
"VARIATION_VALUE_3" DOUBLE,
"VARIATION_VALUE_4" DOUBLE,
"VARIATION_VALUE_5" DOUBLE,
"MEASURE_DATA" BINARY
);
CREATE INDEX "MEASURES_COMPONENT_UUID" ON "PROJECT_MEASURES" ("COMPONENT_UUID");
CREATE INDEX "MEASURES_ANALYSIS_METRIC" ON "PROJECT_MEASURES" ("ANALYSIS_UUID", "METRIC_ID");
CREATE INDEX "MEASURES_PERSON" ON "PROJECT_MEASURES" ("PERSON_ID");


+ 9
- 11
server/sonar-server/src/main/java/org/sonar/server/branch/ws/ListAction.java Datei anzeigen

@@ -31,14 +31,14 @@ import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.web.UserRole;
import org.sonar.core.util.Protobuf;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.BranchDto;
import org.sonar.db.component.BranchType;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.measure.MeasureDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.measure.LiveMeasureDto;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.issue.index.BranchStatistics;
import org.sonar.server.issue.index.IssueIndex;
@@ -97,13 +97,12 @@ public class ListAction implements BranchWsAction {
checkArgument(project.isEnabled() && PROJECT.equals(project.qualifier()), "Invalid project key");

Collection<BranchDto> branches = dbClient.branchDao().selectByComponent(dbSession, project);
MetricDto qualityGateMetric = dbClient.metricDao().selectOrFailByKey(dbSession, ALERT_STATUS_KEY);
Map<String, BranchDto> mergeBranchesByUuid = dbClient.branchDao()
.selectByUuids(dbSession, branches.stream().map(BranchDto::getMergeBranchUuid).filter(Objects::nonNull).collect(toList()))
.stream().collect(uniqueIndex(BranchDto::getUuid));
Map<String, MeasureDto> qualityGateMeasuresByComponentUuids = dbClient.measureDao()
.selectByComponentsAndMetrics(dbSession, branches.stream().map(BranchDto::getUuid).collect(toList()), singletonList(qualityGateMetric.getId()))
.stream().collect(uniqueIndex(MeasureDto::getComponentUuid));
Map<String, LiveMeasureDto> qualityGateMeasuresByComponentUuids = dbClient.liveMeasureDao()
.selectByComponentUuidsAndMetricKeys(dbSession, branches.stream().map(BranchDto::getUuid).collect(toList()), singletonList(ALERT_STATUS_KEY))
.stream().collect(uniqueIndex(LiveMeasureDto::getComponentUuid));
Map<String, BranchStatistics> branchStatisticsByBranchUuid = issueIndex.searchBranchStatistics(project.uuid(), branches.stream()
.filter(b -> b.getBranchType().equals(SHORT))
.map(BranchDto::getUuid).collect(toList()))
@@ -113,15 +112,14 @@ public class ListAction implements BranchWsAction {
.stream().collect(uniqueIndex(SnapshotDto::getComponentUuid, s -> formatDateTime(s.getCreatedAt())));

ProjectBranches.ListWsResponse.Builder protobufResponse = ProjectBranches.ListWsResponse.newBuilder();
branches.stream()
.forEach(b -> addBranch(protobufResponse, b, mergeBranchesByUuid, qualityGateMeasuresByComponentUuids.get(b.getUuid()), branchStatisticsByBranchUuid.get(b.getUuid()),
branches.forEach(b -> addBranch(protobufResponse, b, mergeBranchesByUuid, qualityGateMeasuresByComponentUuids.get(b.getUuid()), branchStatisticsByBranchUuid.get(b.getUuid()),
analysisDateByBranchUuid.get(b.getUuid())));
WsUtils.writeProtobuf(protobufResponse.build(), request, response);
}
}

private static void addBranch(ProjectBranches.ListWsResponse.Builder response, BranchDto branch, Map<String, BranchDto> mergeBranchesByUuid,
@Nullable MeasureDto qualityGateMeasure, BranchStatistics branchStatistics, @Nullable String analysisDate) {
@Nullable LiveMeasureDto qualityGateMeasure, BranchStatistics branchStatistics, @Nullable String analysisDate) {
ProjectBranches.Branch.Builder builder = toBranchBuilder(branch, Optional.ofNullable(mergeBranchesByUuid.get(branch.getMergeBranchUuid())));
setBranchStatus(builder, branch, qualityGateMeasure, branchStatistics);
if (analysisDate != null) {
@@ -147,11 +145,11 @@ public class ListAction implements BranchWsAction {
return builder;
}

private static void setBranchStatus(ProjectBranches.Branch.Builder builder, BranchDto branch, @Nullable MeasureDto qualityGateMeasure,
private static void setBranchStatus(ProjectBranches.Branch.Builder builder, BranchDto branch, @Nullable LiveMeasureDto qualityGateMeasure,
@Nullable BranchStatistics branchStatistics) {
ProjectBranches.Branch.Status.Builder statusBuilder = ProjectBranches.Branch.Status.newBuilder();
if (branch.getBranchType() == LONG && qualityGateMeasure != null) {
statusBuilder.setQualityGateStatus(qualityGateMeasure.getData());
Protobuf.setNullable(qualityGateMeasure.getDataAsString(), statusBuilder::setQualityGateStatus);
}
if (branch.getBranchType() == BranchType.SHORT) {
statusBuilder.setBugs(branchStatistics == null ? 0L : branchStatistics.getBugs());

+ 16
- 18
server/sonar-server/src/main/java/org/sonar/server/component/ws/AppAction.java Datei anzeigen

@@ -19,12 +19,10 @@
*/
package org.sonar.server.component.ws;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.apache.commons.lang.BooleanUtils;
@@ -37,8 +35,7 @@ import org.sonar.api.web.UserRole;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.measure.MeasureDto;
import org.sonar.db.measure.MeasureQuery;
import org.sonar.db.measure.LiveMeasureDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.property.PropertyDto;
import org.sonar.db.property.PropertyQuery;
@@ -46,6 +43,8 @@ import org.sonar.server.component.ComponentFinder;
import org.sonar.server.user.UserSession;

import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Arrays.asList;
import static java.util.Collections.unmodifiableList;
import static org.sonar.api.measures.CoreMetrics.COVERAGE;
import static org.sonar.api.measures.CoreMetrics.COVERAGE_KEY;
import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES_DENSITY;
@@ -66,12 +65,12 @@ public class AppAction implements ComponentsWsAction {

private static final String PARAM_COMPONENT_ID = "componentId";
private static final String PARAM_COMPONENT = "component";
private static final List<String> METRIC_KEYS = ImmutableList.of(
private static final List<String> METRIC_KEYS = unmodifiableList(asList(
LINES_KEY,
VIOLATIONS_KEY,
COVERAGE_KEY,
DUPLICATED_LINES_DENSITY_KEY,
TESTS_KEY);
TESTS_KEY));

private final DbClient dbClient;

@@ -121,7 +120,7 @@ public class AppAction implements ComponentsWsAction {

JsonWriter json = response.newJsonWriter();
json.beginObject();
Map<String, MeasureDto> measuresByMetricKey = measuresByMetricKey(component, session);
Map<String, LiveMeasureDto> measuresByMetricKey = loadMeasuresGroupedByMetricKey(component, session);
appendComponent(json, component, userSession, session);
appendPermissions(json, userSession);
appendMeasures(json, measuresByMetricKey);
@@ -177,7 +176,7 @@ public class AppAction implements ComponentsWsAction {
json.prop("canMarkAsFavorite", userSession.isLoggedIn());
}

private static void appendMeasures(JsonWriter json, Map<String, MeasureDto> measuresByMetricKey) {
private static void appendMeasures(JsonWriter json, Map<String, LiveMeasureDto> measuresByMetricKey) {
json.name("measures").beginObject();
json.prop("lines", formatMeasure(measuresByMetricKey, LINES));
json.prop("coverage", formatMeasure(measuresByMetricKey, COVERAGE));
@@ -187,12 +186,11 @@ public class AppAction implements ComponentsWsAction {
json.endObject();
}

private Map<String, MeasureDto> measuresByMetricKey(ComponentDto component, DbSession session) {
MeasureQuery query = MeasureQuery.builder().setComponentUuid(component.uuid()).setMetricKeys(METRIC_KEYS).build();
List<MeasureDto> measures = dbClient.measureDao().selectByQuery(session, query);
Set<Integer> metricIds = measures.stream().map(MeasureDto::getMetricId).collect(Collectors.toSet());
List<MetricDto> metrics = dbClient.metricDao().selectByIds(session, metricIds);
private Map<String, LiveMeasureDto> loadMeasuresGroupedByMetricKey(ComponentDto component, DbSession dbSession) {
List<MetricDto> metrics = dbClient.metricDao().selectByKeys(dbSession, METRIC_KEYS);
Map<Integer, MetricDto> metricsById = Maps.uniqueIndex(metrics, MetricDto::getId);
List<LiveMeasureDto> measures = dbClient.liveMeasureDao()
.selectByComponentUuids(dbSession, Collections.singletonList(component.uuid()), metricsById.keySet());
return Maps.uniqueIndex(measures, m -> metricsById.get(m.getMetricId()).getKey());
}

@@ -205,12 +203,12 @@ public class AppAction implements ComponentsWsAction {
}

@CheckForNull
private static String formatMeasure(Map<String, MeasureDto> measuresByMetricKey, Metric metric) {
MeasureDto measure = measuresByMetricKey.get(metric.getKey());
private static String formatMeasure(Map<String, LiveMeasureDto> measuresByMetricKey, Metric metric) {
LiveMeasureDto measure = measuresByMetricKey.get(metric.getKey());
return formatMeasure(measure, metric);
}

private static String formatMeasure(@Nullable MeasureDto measure, Metric metric) {
private static String formatMeasure(@Nullable LiveMeasureDto measure, Metric metric) {
if (measure == null) {
return null;
}
@@ -222,7 +220,7 @@ public class AppAction implements ComponentsWsAction {
}

@CheckForNull
private static Double getDoubleValue(MeasureDto measure, Metric metric) {
private static Double getDoubleValue(LiveMeasureDto measure, Metric metric) {
Double value = measure.getValue();
if (BooleanUtils.isTrue(metric.isOptimizedBestValue()) && value == null) {
value = metric.getBestValue();

+ 2
- 2
server/sonar-server/src/main/java/org/sonar/server/computation/dbcleaner/ProjectCleaner.java Datei anzeigen

@@ -54,11 +54,11 @@ public class ProjectCleaner {
this.purgeListener = purgeListener;
}

public ProjectCleaner purge(DbSession session, IdUuidPair idUuidPair, Configuration projectConfig, Collection<String> disabledComponentUuids) {
public ProjectCleaner purge(DbSession session, IdUuidPair rootId, Configuration projectConfig, Collection<String> disabledComponentUuids) {
long start = System.currentTimeMillis();
profiler.reset();

PurgeConfiguration configuration = newDefaultPurgeConfiguration(projectConfig, idUuidPair, disabledComponentUuids);
PurgeConfiguration configuration = newDefaultPurgeConfiguration(projectConfig, rootId, disabledComponentUuids);

periodCleaner.clean(session, configuration.rootProjectIdUuid().getUuid(), projectConfig);
purgeDao.purge(session, configuration, purgeListener, profiler);

+ 3
- 4
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/BestValueOptimization.java Datei anzeigen

@@ -19,8 +19,7 @@
*/
package org.sonar.server.computation.task.projectanalysis.measure;

import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import java.util.function.Predicate;
import javax.annotation.Nonnull;
import org.sonar.server.computation.task.projectanalysis.component.Component;
import org.sonar.server.computation.task.projectanalysis.metric.Metric;
@@ -40,7 +39,7 @@ public class BestValueOptimization implements Predicate<Measure> {
if (isBestValueOptimized(metric) && isBestValueOptimized(component)) {
return new BestValueOptimization(metric);
}
return Predicates.alwaysFalse();
return x -> false;
}

private static boolean isBestValueOptimized(Metric metric) {
@@ -52,7 +51,7 @@ public class BestValueOptimization implements Predicate<Measure> {
}

@Override
public boolean apply(@Nonnull Measure measure) {
public boolean test(@Nonnull Measure measure) {
return isBestValueOptimized(measure);
}


+ 0
- 6
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/MapBasedRawMeasureRepository.java Datei anzeigen

@@ -24,7 +24,6 @@ import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.SetMultimap;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -60,11 +59,6 @@ public final class MapBasedRawMeasureRepository<T> implements MeasureRepository
throw new UnsupportedOperationException("This implementation of MeasureRepository supports only raw measures");
}

@Override
public int loadAsRawMeasures(Collection<Component> components, Collection<Metric> metrics) {
throw new UnsupportedOperationException("This implementation of MeasureRepository supports only raw measures");
}

@Override
public Optional<Measure> getRawMeasure(final Component component, final Metric metric) {
// fail fast

+ 2
- 7
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureRepository.java Datei anzeigen

@@ -19,16 +19,13 @@
*/
package org.sonar.server.computation.task.projectanalysis.measure;

import java.util.Collection;
import com.google.common.base.Optional;
import com.google.common.collect.SetMultimap;
import java.util.Set;

import org.sonar.server.computation.task.projectanalysis.component.Component;
import org.sonar.server.computation.task.projectanalysis.metric.Metric;
import org.sonar.server.computation.task.projectanalysis.metric.MetricImpl;

import com.google.common.base.Optional;
import com.google.common.collect.SetMultimap;

public interface MeasureRepository {

/**
@@ -43,8 +40,6 @@ public interface MeasureRepository {
*/
Optional<Measure> getBaseMeasure(Component component, Metric metric);

int loadAsRawMeasures(Collection<Component> components, Collection<Metric> metrics);

/**
* Retrieves the measure created during the current analysis for the specified {@link Component} for the specified
* {@link Metric} if it exists (ie. one created by the Compute Engine or the Batch) and which is <strong>not</strong>

+ 5
- 39
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureRepositoryImpl.java Datei anzeigen

@@ -19,21 +19,14 @@
*/
package org.sonar.server.computation.task.projectanalysis.measure;

import static java.util.Objects.requireNonNull;
import static org.sonar.server.computation.task.projectanalysis.component.ComponentFunctions.toReportRef;

import java.util.Collection;
import com.google.common.base.Optional;
import com.google.common.collect.SetMultimap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.sonar.core.util.CloseableIterator;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.measure.MeasureDto;
import org.sonar.db.measure.MeasureQuery;
import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonar.server.computation.task.projectanalysis.batch.BatchReportReader;
import org.sonar.server.computation.task.projectanalysis.component.Component;
@@ -42,8 +35,8 @@ import org.sonar.server.computation.task.projectanalysis.metric.Metric;
import org.sonar.server.computation.task.projectanalysis.metric.MetricRepository;
import org.sonar.server.computation.task.projectanalysis.metric.ReportMetricValidator;

import com.google.common.base.Optional;
import com.google.common.collect.SetMultimap;
import static java.util.Objects.requireNonNull;
import static org.sonar.server.computation.task.projectanalysis.component.ComponentFunctions.toReportRef;

public class MeasureRepositoryImpl implements MeasureRepository {
private final MapBasedRawMeasureRepository<Integer> delegate = new MapBasedRawMeasureRepository<>(toReportRef());
@@ -72,8 +65,7 @@ public class MeasureRepositoryImpl implements MeasureRepository {
requireNonNull(metric);

try (DbSession dbSession = dbClient.openSession(false)) {
MeasureQuery query = MeasureQuery.builder().setComponentUuid(component.getUuid()).setMetricKey(metric.getKey()).build();
java.util.Optional<MeasureDto> measureDto = dbClient.measureDao().selectSingle(dbSession, query);
java.util.Optional<MeasureDto> measureDto = dbClient.measureDao().selectLastMeasure(dbSession, component.getUuid(), metric.getKey());
if (measureDto.isPresent()) {
return measureTransformer.toMeasure(measureDto.get(), metric);
}
@@ -81,32 +73,6 @@ public class MeasureRepositoryImpl implements MeasureRepository {
}
}

@Override
public int loadAsRawMeasures(Collection<Component> components, Collection<Metric> metrics) {
requireNonNull(components);
requireNonNull(metrics);

Map<String, Component> componentsByUuid = components.stream()
.collect(Collectors.toMap(Component::getUuid, c -> c));
Map<Integer, Metric> metricsById = metrics.stream()
.collect(Collectors.toMap(Metric::getId, m -> m));

List<MeasureDto> measuresDto;
try (DbSession dbSession = dbClient.openSession(false)) {
measuresDto = dbClient.measureDao().selectByComponentsAndMetrics(dbSession, componentsByUuid.keySet(), metricsById.keySet());
}
for (MeasureDto dto : measuresDto) {

Metric metric = metricsById.get(dto.getMetricId());
Component component = componentsByUuid.get(dto.getComponentUuid());
Measure measure = measureTransformer.toMeasure(dto, metric).get();

delegate.add(component, metric, measure);
}
return measuresDto.size();
}

@Override
public Optional<Measure> getRawMeasure(Component component, Metric metric) {
Optional<Measure> local = delegate.getRawMeasure(component, metric);

+ 17
- 10
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureToMeasureDto.java Datei anzeigen

@@ -20,25 +20,23 @@
package org.sonar.server.computation.task.projectanalysis.measure;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import org.sonar.db.measure.LiveMeasureDto;
import org.sonar.db.measure.MeasureDto;
import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolder;
import org.sonar.server.computation.task.projectanalysis.component.Component;
import org.sonar.server.computation.task.projectanalysis.component.DbIdsRepository;
import org.sonar.server.computation.task.projectanalysis.component.Developer;
import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolder;
import org.sonar.server.computation.task.projectanalysis.metric.Metric;

public class MeasureToMeasureDto {

private final DbIdsRepository dbIdsRepository;
private final AnalysisMetadataHolder analysisMetadataHolder;
private final TreeRootHolder treeRootHolder;

public MeasureToMeasureDto(DbIdsRepository dbIdsRepository, AnalysisMetadataHolder analysisMetadataHolder) {
this.dbIdsRepository = dbIdsRepository;
public MeasureToMeasureDto(AnalysisMetadataHolder analysisMetadataHolder, TreeRootHolder treeRootHolder) {
this.analysisMetadataHolder = analysisMetadataHolder;
this.treeRootHolder = treeRootHolder;
}

@Nonnull
public MeasureDto toMeasureDto(Measure measure, Metric metric, Component component) {
MeasureDto out = new MeasureDto();
out.setMetricId(metric.getId());
@@ -50,9 +48,18 @@ public class MeasureToMeasureDto {
if (measure.hasQualityGateStatus()) {
setAlert(out, measure.getQualityGateStatus());
}
Developer developer = measure.getDeveloper();
if (developer != null) {
out.setDeveloperId(dbIdsRepository.getDeveloperId(developer));
out.setValue(valueAsDouble(measure));
out.setData(data(measure));
return out;
}

public LiveMeasureDto toLiveMeasureDto(Measure measure, Metric metric, Component component) {
LiveMeasureDto out = new LiveMeasureDto();
out.setMetricId(metric.getId());
out.setComponentUuid(component.getUuid());
out.setProjectUuid(treeRootHolder.getRoot().getUuid());
if (measure.hasVariation()) {
out.setVariation(measure.getVariation());
}
out.setValue(valueAsDouble(measure));
out.setData(data(measure));

+ 133
- 0
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistLiveMeasuresStep.java Datei anzeigen

@@ -0,0 +1,133 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.computation.task.projectanalysis.step;

import com.google.common.collect.Multimap;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import javax.annotation.Nonnull;
import org.sonar.core.util.Uuids;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.measure.LiveMeasureDao;
import org.sonar.server.computation.task.projectanalysis.component.Component;
import org.sonar.server.computation.task.projectanalysis.component.CrawlerDepthLimit;
import org.sonar.server.computation.task.projectanalysis.component.DepthTraversalTypeAwareCrawler;
import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolder;
import org.sonar.server.computation.task.projectanalysis.component.TypeAwareVisitorAdapter;
import org.sonar.server.computation.task.projectanalysis.measure.BestValueOptimization;
import org.sonar.server.computation.task.projectanalysis.measure.Measure;
import org.sonar.server.computation.task.projectanalysis.measure.MeasureRepository;
import org.sonar.server.computation.task.projectanalysis.measure.MeasureToMeasureDto;
import org.sonar.server.computation.task.projectanalysis.metric.Metric;
import org.sonar.server.computation.task.projectanalysis.metric.MetricRepository;
import org.sonar.server.computation.task.step.ComputationStep;

import static java.util.Arrays.asList;
import static java.util.Collections.unmodifiableSet;
import static org.sonar.api.measures.CoreMetrics.CLASS_COMPLEXITY_DISTRIBUTION_KEY;
import static org.sonar.api.measures.CoreMetrics.FILE_COMPLEXITY_DISTRIBUTION_KEY;
import static org.sonar.api.measures.CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION_KEY;
import static org.sonar.server.computation.task.projectanalysis.component.ComponentVisitor.Order.PRE_ORDER;

public class PersistLiveMeasuresStep implements ComputationStep {

/**
* List of metrics that should not be persisted on file measure.
*/
private static final Set<String> NOT_TO_PERSIST_ON_FILE_METRIC_KEYS = unmodifiableSet(new HashSet<>(asList(
FILE_COMPLEXITY_DISTRIBUTION_KEY,
FUNCTION_COMPLEXITY_DISTRIBUTION_KEY,
CLASS_COMPLEXITY_DISTRIBUTION_KEY)));

private final DbClient dbClient;
private final MetricRepository metricRepository;
private final MeasureToMeasureDto measureToMeasureDto;
private final TreeRootHolder treeRootHolder;
private final MeasureRepository measureRepository;

public PersistLiveMeasuresStep(DbClient dbClient, MetricRepository metricRepository, MeasureToMeasureDto measureToMeasureDto,
TreeRootHolder treeRootHolder, MeasureRepository measureRepository) {
this.dbClient = dbClient;
this.metricRepository = metricRepository;
this.measureToMeasureDto = measureToMeasureDto;
this.treeRootHolder = treeRootHolder;
this.measureRepository = measureRepository;
}

@Override
public String getDescription() {
return "Persist live measures";
}

@Override
public void execute() {
try (DbSession dbSession = dbClient.openSession(false)) {
String marker = Uuids.create();
Component root = treeRootHolder.getRoot();
new DepthTraversalTypeAwareCrawler(new MeasureVisitor(dbSession, marker)).visit(root);
dbClient.liveMeasureDao().deleteByProjectUuidExcludingMarker(dbSession, root.getUuid(), marker);
dbSession.commit();
}
}

private class MeasureVisitor extends TypeAwareVisitorAdapter {
private final DbSession dbSession;
private final String marker;

private MeasureVisitor(DbSession dbSession, String marker) {
super(CrawlerDepthLimit.LEAVES, PRE_ORDER);
this.dbSession = dbSession;
this.marker = marker;
}

@Override
public void visitAny(Component component) {
LiveMeasureDao dao = dbClient.liveMeasureDao();
Multimap<String, Measure> measures = measureRepository.getRawMeasures(component);
for (Map.Entry<String, Collection<Measure>> measuresByMetricKey : measures.asMap().entrySet()) {
String metricKey = measuresByMetricKey.getKey();
if (NOT_TO_PERSIST_ON_FILE_METRIC_KEYS.contains(metricKey) && component.getType() == Component.Type.FILE) {
continue;
}
Metric metric = metricRepository.getByKey(metricKey);
Predicate<Measure> notBestValueOptimized = BestValueOptimization.from(metric, component).negate();
measuresByMetricKey.getValue().stream()
.filter(NonEmptyMeasure.INSTANCE)
.filter(notBestValueOptimized)
.map(measure -> measureToMeasureDto.toLiveMeasureDto(measure, metric, component))
.forEach(dto -> dao.insertOrUpdate(dbSession, dto, marker));
}
}
}

private enum NonEmptyMeasure implements Predicate<Measure> {
INSTANCE;

@Override
public boolean test(@Nonnull Measure input) {
return input.getValueType() != Measure.ValueType.NO_VALUE || input.hasVariation() || input.getData() != null;
}
}

}

+ 49
- 31
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistMeasuresStep.java Datei anzeigen

@@ -19,24 +19,23 @@
*/
package org.sonar.server.computation.task.projectanalysis.step;

import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Multimap;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import javax.annotation.Nonnull;
import org.sonar.core.config.PurgeConstants;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.measure.MeasureDao;
import org.sonar.db.measure.MeasureDto;
import org.sonar.server.computation.task.projectanalysis.component.Component;
import org.sonar.server.computation.task.projectanalysis.component.ConfigurationRepository;
import org.sonar.server.computation.task.projectanalysis.component.CrawlerDepthLimit;
import org.sonar.server.computation.task.projectanalysis.component.DepthTraversalTypeAwareCrawler;
import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolder;
import org.sonar.server.computation.task.projectanalysis.component.TypeAwareVisitorAdapter;
import org.sonar.server.computation.task.projectanalysis.measure.BestValueOptimization;
import org.sonar.server.computation.task.projectanalysis.measure.Measure;
import org.sonar.server.computation.task.projectanalysis.measure.MeasureRepository;
import org.sonar.server.computation.task.projectanalysis.measure.MeasureToMeasureDto;
@@ -44,35 +43,32 @@ import org.sonar.server.computation.task.projectanalysis.metric.Metric;
import org.sonar.server.computation.task.projectanalysis.metric.MetricRepository;
import org.sonar.server.computation.task.step.ComputationStep;

import static com.google.common.collect.FluentIterable.from;
import static org.sonar.api.measures.CoreMetrics.CLASS_COMPLEXITY_DISTRIBUTION_KEY;
import static org.sonar.api.measures.CoreMetrics.FILE_COMPLEXITY_DISTRIBUTION_KEY;
import static org.sonar.api.measures.CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION_KEY;
import static org.sonar.server.computation.task.projectanalysis.component.ComponentVisitor.Order.PRE_ORDER;

public class PersistMeasuresStep implements ComputationStep {

/**
* List of metrics that should not be persisted on file measure (Waiting for SONAR-6688 to be implemented)
*/
private static final List<String> NOT_TO_PERSIST_ON_FILE_METRIC_KEYS = ImmutableList.of(
FILE_COMPLEXITY_DISTRIBUTION_KEY,
FUNCTION_COMPLEXITY_DISTRIBUTION_KEY,
CLASS_COMPLEXITY_DISTRIBUTION_KEY);

private final DbClient dbClient;
private final MetricRepository metricRepository;
private final MeasureToMeasureDto measureToMeasureDto;
private final TreeRootHolder treeRootHolder;
private final MeasureRepository measureRepository;
private final boolean persistDirectories;

public PersistMeasuresStep(DbClient dbClient, MetricRepository metricRepository, MeasureToMeasureDto measureToMeasureDto,
TreeRootHolder treeRootHolder, MeasureRepository measureRepository) {
TreeRootHolder treeRootHolder, MeasureRepository measureRepository, ConfigurationRepository settings) {
this(dbClient, metricRepository, measureToMeasureDto, treeRootHolder,measureRepository,
!settings.getConfiguration().getBoolean(PurgeConstants.PROPERTY_CLEAN_DIRECTORY).orElseThrow(() -> new IllegalStateException("Missing default value")));
}

@VisibleForTesting
PersistMeasuresStep(DbClient dbClient, MetricRepository metricRepository, MeasureToMeasureDto measureToMeasureDto, TreeRootHolder treeRootHolder,
MeasureRepository measureRepository, boolean persistDirectories) {
this.dbClient = dbClient;
this.metricRepository = metricRepository;
this.measureToMeasureDto = measureToMeasureDto;
this.treeRootHolder = treeRootHolder;
this.measureRepository = measureRepository;
this.persistDirectories = persistDirectories;
}

@Override
@@ -97,25 +93,47 @@ public class PersistMeasuresStep implements ComputationStep {
}

@Override
public void visitAny(Component component) {
Multimap<String, Measure> measures = measureRepository.getRawMeasures(component);
persistMeasures(component, measures);
public void visitProject(Component project) {
persistMeasures(project);
}

@Override
public void visitModule(Component module) {
persistMeasures(module);
}

@Override
public void visitDirectory(Component directory) {
if (persistDirectories) {
persistMeasures(directory);
}
}

@Override
public void visitView(Component view) {
persistMeasures(view);
}

@Override
public void visitSubView(Component subView) {
persistMeasures(subView);
}

private void persistMeasures(Component component, Multimap<String, Measure> batchReportMeasures) {
for (Map.Entry<String, Collection<Measure>> measures : batchReportMeasures.asMap().entrySet()) {
String metricKey = measures.getKey();
if (NOT_TO_PERSIST_ON_FILE_METRIC_KEYS.contains(metricKey) && component.getType() == Component.Type.FILE) {
continue;
}
@Override
public void visitProjectView(Component projectView) {
persistMeasures(projectView);
}

private void persistMeasures(Component component) {
Multimap<String, Measure> measures = measureRepository.getRawMeasures(component);
for (Map.Entry<String, Collection<Measure>> measuresByMetricKey : measures.asMap().entrySet()) {
String metricKey = measuresByMetricKey.getKey();
Metric metric = metricRepository.getByKey(metricKey);
Predicate<Measure> notBestValueOptimized = Predicates.not(BestValueOptimization.from(metric, component));
MeasureDao measureDao = dbClient.measureDao();
for (Measure measure : from(measures.getValue()).filter(NonEmptyMeasure.INSTANCE).filter(notBestValueOptimized)) {
measuresByMetricKey.getValue().stream().filter(NonEmptyMeasure.INSTANCE).forEach(measure -> {
MeasureDto measureDto = measureToMeasureDto.toMeasureDto(measure, metric, component);
measureDao.insert(session, measureDto);
}
});
}
}

@@ -125,7 +143,7 @@ public class PersistMeasuresStep implements ComputationStep {
INSTANCE;

@Override
public boolean apply(@Nonnull Measure input) {
public boolean test(@Nonnull Measure input) {
return input.getValueType() != Measure.ValueType.NO_VALUE || input.hasVariation() || input.getData() != null;
}
}

+ 1
- 0
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/ReportComputationSteps.java Datei anzeigen

@@ -88,6 +88,7 @@ public class ReportComputationSteps extends AbstractComputationSteps {
PersistAnalysisStep.class,
PersistAnalysisPropertiesStep.class,
PersistMeasuresStep.class,
PersistLiveMeasuresStep.class,
PersistIssuesStep.class,
PersistProjectLinksStep.class,
PersistEventsStep.class,

+ 3
- 8
server/sonar-server/src/main/java/org/sonar/server/duplication/ws/ShowAction.java Datei anzeigen

@@ -30,8 +30,7 @@ import org.sonar.api.web.UserRole;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.measure.MeasureDto;
import org.sonar.db.measure.MeasureQuery;
import org.sonar.db.measure.LiveMeasureDto;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.user.UserSession;

@@ -110,12 +109,8 @@ public class ShowAction implements DuplicationsWsAction {

@CheckForNull
private String findDataFromComponent(DbSession dbSession, ComponentDto component) {
MeasureQuery query = MeasureQuery.builder()
.setComponentUuid(component.uuid())
.setMetricKey(CoreMetrics.DUPLICATIONS_DATA_KEY)
.build();
return dbClient.measureDao().selectSingle(dbSession, query)
.map(MeasureDto::getData)
return dbClient.liveMeasureDao().selectMeasure(dbSession, component.uuid(), CoreMetrics.DUPLICATIONS_DATA_KEY)
.map(LiveMeasureDto::getDataAsString)
.orElse(null);
}
}

+ 16
- 37
server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentAction.java Datei anzeigen

@@ -43,25 +43,22 @@ import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.measure.MeasureDto;
import org.sonar.db.measure.MeasureQuery;
import org.sonar.db.measure.LiveMeasureDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.metric.MetricDtoFunctions;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.measure.ws.MetricDtoWithBestValue.MetricDtoToMetricDtoWithBestValueFunction;
import org.sonar.server.user.UserSession;
import org.sonarqube.ws.Measures;
import org.sonarqube.ws.Measures.ComponentWsResponse;

import static com.google.common.base.Preconditions.checkArgument;
import static java.lang.String.format;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01;
import static org.sonar.server.component.ComponentFinder.ParamNames.COMPONENT_ID_AND_COMPONENT;
import static org.sonar.server.component.ComponentFinder.ParamNames.DEVELOPER_ID_AND_KEY;
import static org.sonar.server.measure.ws.ComponentDtoToWsComponent.componentDtoToWsComponent;
import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createAdditionalFieldsParameter;
import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createDeveloperParameters;
@@ -133,6 +130,10 @@ public class ComponentAction implements MeasuresWsAction {

@Override
public void handle(Request request, Response response) throws Exception {
if (request.param(PARAM_DEVELOPER_ID) != null || request.param(PARAM_DEVELOPER_KEY) != null) {
throw new NotFoundException("The Developer Cockpit feature has been dropped. The specified developer cannot be found.");
}

ComponentWsResponse componentWsResponse = doHandle(toComponentWsRequest(request));
writeProtobuf(componentWsResponse, request, response);
}
@@ -140,13 +141,12 @@ public class ComponentAction implements MeasuresWsAction {
private ComponentWsResponse doHandle(ComponentRequest request) {
try (DbSession dbSession = dbClient.openSession(false)) {
ComponentDto component = loadComponent(dbSession, request);
Long developerId = searchDeveloperId(dbSession, request);
Optional<ComponentDto> refComponent = getReferenceComponent(dbSession, component);
checkPermissions(component);
SnapshotDto analysis = dbClient.snapshotDao().selectLastAnalysisByRootComponentUuid(dbSession, component.projectUuid()).orElse(null);
List<MetricDto> metrics = searchMetrics(dbSession, request);
List<Measures.Period> periods = snapshotToWsPeriods(analysis);
List<MeasureDto> measures = searchMeasures(dbSession, component, analysis, metrics, developerId);
List<LiveMeasureDto> measures = searchMeasures(dbSession, component, metrics);

return buildResponse(request, component, refComponent, measures, metrics, periods);
}
@@ -162,15 +162,6 @@ public class ComponentAction implements MeasuresWsAction {
: componentFinder.getByKeyAndBranch(dbSession, componentKey, branch);
}

@CheckForNull
private Long searchDeveloperId(DbSession dbSession, ComponentRequest request) {
if (request.getDeveloperId() == null && request.getDeveloperKey() == null) {
return null;
}

return componentFinder.getByUuidOrKey(dbSession, request.getDeveloperId(), request.getDeveloperKey(), DEVELOPER_ID_AND_KEY).getId();
}

private Optional<ComponentDto> getReferenceComponent(DbSession dbSession, ComponentDto component) {
if (component.getCopyResourceUuid() == null) {
return Optional.absent();
@@ -179,12 +170,12 @@ public class ComponentAction implements MeasuresWsAction {
return dbClient.componentDao().selectByUuid(dbSession, component.getCopyResourceUuid());
}

private static ComponentWsResponse buildResponse(ComponentRequest request, ComponentDto component, Optional<ComponentDto> refComponent, List<MeasureDto> measures,
private static ComponentWsResponse buildResponse(ComponentRequest request, ComponentDto component, Optional<ComponentDto> refComponent, List<LiveMeasureDto> measures,
List<MetricDto> metrics, List<Measures.Period> periods) {
ComponentWsResponse.Builder response = ComponentWsResponse.newBuilder();
Map<Integer, MetricDto> metricsById = Maps.uniqueIndex(metrics, MetricDto::getId);
Map<MetricDto, MeasureDto> measuresByMetric = new HashMap<>();
for (MeasureDto measure : measures) {
Map<MetricDto, LiveMeasureDto> measuresByMetric = new HashMap<>();
for (LiveMeasureDto measure : measures) {
MetricDto metric = metricsById.get(measure.getMetricId());
measuresByMetric.put(metric, measure);
}
@@ -223,20 +214,10 @@ public class ComponentAction implements MeasuresWsAction {
return metrics;
}

private List<MeasureDto> searchMeasures(DbSession dbSession, ComponentDto component, @Nullable SnapshotDto analysis, List<MetricDto> metrics, @Nullable Long developerId) {
if (analysis == null) {
return emptyList();
}

private List<LiveMeasureDto> searchMeasures(DbSession dbSession, ComponentDto component, List<MetricDto> metrics) {
List<Integer> metricIds = Lists.transform(metrics, MetricDto::getId);
MeasureQuery query = MeasureQuery.builder()
.setPersonId(developerId)
.setMetricIds(metricIds)
.setComponentUuid(component.uuid())
.build();
List<MeasureDto> measures = dbClient.measureDao().selectByQuery(dbSession, query);
List<LiveMeasureDto> measures = dbClient.liveMeasureDao().selectByComponentUuids(dbSession, singletonList(component.uuid()), metricIds);
addBestValuesToMeasures(measures, component, metrics);

return measures;
}

@@ -247,16 +228,16 @@ public class ComponentAction implements MeasuresWsAction {
* <li>metric is optimized for best value</li>
* </ul>
*/
private static void addBestValuesToMeasures(List<MeasureDto> measures, ComponentDto component, List<MetricDto> metrics) {
private static void addBestValuesToMeasures(List<LiveMeasureDto> measures, ComponentDto component, List<MetricDto> metrics) {
if (!QUALIFIERS_ELIGIBLE_FOR_BEST_VALUE.contains(component.qualifier())) {
return;
}

List<MetricDtoWithBestValue> metricWithBestValueList = metrics.stream()
.filter(MetricDtoFunctions.isOptimizedForBestValue())
.map(new MetricDtoToMetricDtoWithBestValueFunction())
.map(MetricDtoWithBestValue::new)
.collect(MoreCollectors.toList(metrics.size()));
Map<Integer, MeasureDto> measuresByMetricId = Maps.uniqueIndex(measures, MeasureDto::getMetricId);
Map<Integer, LiveMeasureDto> measuresByMetricId = Maps.uniqueIndex(measures, LiveMeasureDto::getMetricId);

for (MetricDtoWithBestValue metricWithBestValue : metricWithBestValueList) {
if (measuresByMetricId.get(metricWithBestValue.getMetric().getId()) == null) {
@@ -271,9 +252,7 @@ public class ComponentAction implements MeasuresWsAction {
.setComponent(request.param(PARAM_COMPONENT))
.setBranch(request.param(PARAM_BRANCH))
.setAdditionalFields(request.paramAsStrings(PARAM_ADDITIONAL_FIELDS))
.setMetricKeys(request.mandatoryParamAsStrings(PARAM_METRIC_KEYS))
.setDeveloperId(request.param(PARAM_DEVELOPER_ID))
.setDeveloperKey(request.param(PARAM_DEVELOPER_KEY));
.setMetricKeys(request.mandatoryParamAsStrings(PARAM_METRIC_KEYS));
checkRequest(!componentRequest.getMetricKeys().isEmpty(), "At least one metric key must be provided");
return componentRequest;
}

+ 3
- 3
server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentDtoToWsComponent.java Datei anzeigen

@@ -22,7 +22,7 @@ package org.sonar.server.measure.ws;
import java.util.Map;
import org.sonar.core.util.Protobuf;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.measure.MeasureDto;
import org.sonar.db.measure.LiveMeasureDto;
import org.sonar.db.metric.MetricDto;
import org.sonarqube.ws.Measures;
import org.sonarqube.ws.Measures.Component;
@@ -32,7 +32,7 @@ class ComponentDtoToWsComponent {
// static methods only
}

static Component.Builder componentDtoToWsComponent(ComponentDto component, Map<MetricDto, MeasureDto> measuresByMetric,
static Component.Builder componentDtoToWsComponent(ComponentDto component, Map<MetricDto, LiveMeasureDto> measuresByMetric,
Map<String, ComponentDto> referenceComponentsByUuid) {
Component.Builder wsComponent = componentDtoToWsComponent(component);

@@ -43,7 +43,7 @@ class ComponentDtoToWsComponent {
}

Measures.Measure.Builder measureBuilder = Measures.Measure.newBuilder();
for (Map.Entry<MetricDto, MeasureDto> entry : measuresByMetric.entrySet()) {
for (Map.Entry<MetricDto, LiveMeasureDto> entry : measuresByMetric.entrySet()) {
MeasureDtoToWsMeasure.updateMeasureBuilder(measureBuilder, entry.getKey(), entry.getValue());
wsComponent.addMeasures(measureBuilder);
measureBuilder.clear();

+ 14
- 19
server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java Datei anzeigen

@@ -61,7 +61,7 @@ import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTreeQuery;
import org.sonar.db.component.ComponentTreeQuery.Strategy;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.measure.MeasureDto;
import org.sonar.db.measure.LiveMeasureDto;
import org.sonar.db.measure.MeasureTreeQuery;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.metric.MetricDtoFunctions;
@@ -94,8 +94,8 @@ import static org.sonar.server.measure.ws.MetricDtoToWsMetric.metricDtoToWsMetri
import static org.sonar.server.measure.ws.SnapshotDtoToWsPeriods.snapshotToWsPeriods;
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
import static org.sonar.server.ws.WsParameterBuilder.QualifierParameterContext.newQualifierParameterContext;
import static org.sonar.server.ws.WsParameterBuilder.createQualifiersParameter;
import static org.sonar.server.ws.WsParameterBuilder.QualifierParameterContext.newQualifierParameterContext;
import static org.sonar.server.ws.WsUtils.checkRequest;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.ACTION_COMPONENT_TREE;
@@ -261,6 +261,10 @@ public class ComponentTreeAction implements MeasuresWsAction {
}

private ComponentTreeWsResponse doHandle(ComponentTreeRequest request) {
if (request.getDeveloperId() != null || request.getDeveloperKey() != null) {
return emptyResponse(null, request);
}

ComponentTreeData data = load(request);
if (data.getComponents() == null) {
return emptyResponse(data.getBaseComponent(), request);
@@ -320,13 +324,15 @@ public class ComponentTreeAction implements MeasuresWsAction {
return additionalFields != null && additionalFields.contains(ADDITIONAL_PERIODS);
}

private static ComponentTreeWsResponse emptyResponse(ComponentDto baseComponent, ComponentTreeRequest request) {
private static ComponentTreeWsResponse emptyResponse(@Nullable ComponentDto baseComponent, ComponentTreeRequest request) {
ComponentTreeWsResponse.Builder response = ComponentTreeWsResponse.newBuilder();
response.getPagingBuilder()
.setPageIndex(request.getPage())
.setPageSize(request.getPageSize())
.setTotal(0);
response.setBaseComponent(componentDtoToWsComponent(baseComponent));
if (baseComponent != null) {
response.setBaseComponent(componentDtoToWsComponent(baseComponent));
}
return response.build();
}

@@ -395,14 +401,13 @@ public class ComponentTreeAction implements MeasuresWsAction {
.setBaseComponent(baseComponent)
.build();
}
Long developerId = searchDeveloperId(dbSession, wsRequest);

ComponentTreeQuery componentTreeQuery = toComponentTreeQuery(wsRequest, baseComponent);
List<ComponentDto> components = searchComponents(dbSession, componentTreeQuery);
List<MetricDto> metrics = searchMetrics(dbSession, wsRequest);
Table<String, MetricDto, ComponentTreeData.Measure> measuresByComponentUuidAndMetric = searchMeasuresByComponentUuidAndMetric(dbSession, baseComponent, componentTreeQuery,
components,
metrics, developerId);
metrics);

components = filterComponents(components, measuresByComponentUuidAndMetric, metrics, wsRequest);
components = sortComponents(components, wsRequest, metrics, measuresByComponentUuidAndMetric);
@@ -432,15 +437,6 @@ public class ComponentTreeAction implements MeasuresWsAction {
: componentFinder.getByKeyAndBranch(dbSession, componentKey, branch);
}

@CheckForNull
private Long searchDeveloperId(DbSession dbSession, ComponentTreeRequest wsRequest) {
if (wsRequest.getDeveloperId() == null && wsRequest.getDeveloperKey() == null) {
return null;
}

return componentFinder.getByUuidOrKey(dbSession, wsRequest.getDeveloperId(), wsRequest.getDeveloperKey(), DEVELOPER_ID_AND_KEY).getId();
}

private Map<String, ComponentDto> searchReferenceComponentsById(DbSession dbSession, List<ComponentDto> components) {
List<String> referenceComponentUUids = components.stream()
.map(ComponentDto::getCopyResourceUuid)
@@ -483,20 +479,19 @@ public class ComponentTreeAction implements MeasuresWsAction {
}

private Table<String, MetricDto, ComponentTreeData.Measure> searchMeasuresByComponentUuidAndMetric(DbSession dbSession, ComponentDto baseComponent,
ComponentTreeQuery componentTreeQuery, List<ComponentDto> components, List<MetricDto> metrics, @Nullable Long developerId) {
ComponentTreeQuery componentTreeQuery, List<ComponentDto> components, List<MetricDto> metrics) {

Map<Integer, MetricDto> metricsById = Maps.uniqueIndex(metrics, MetricDto::getId);
MeasureTreeQuery measureQuery = MeasureTreeQuery.builder()
.setStrategy(MeasureTreeQuery.Strategy.valueOf(componentTreeQuery.getStrategy().name()))
.setNameOrKeyQuery(componentTreeQuery.getNameOrKeyQuery())
.setQualifiers(componentTreeQuery.getQualifiers())
.setPersonId(developerId)
.setMetricIds(new ArrayList<>(metricsById.keySet()))
.build();

Table<String, MetricDto, ComponentTreeData.Measure> measuresByComponentUuidAndMetric = HashBasedTable.create(components.size(), metrics.size());
dbClient.measureDao().selectTreeByQuery(dbSession, baseComponent, measureQuery, result -> {
MeasureDto measureDto = result.getResultObject();
dbClient.liveMeasureDao().selectTreeByQuery(dbSession, baseComponent, measureQuery, result -> {
LiveMeasureDto measureDto = result.getResultObject();
measuresByComponentUuidAndMetric.put(
measureDto.getComponentUuid(),
metricsById.get(measureDto.getMetricId()),

+ 4
- 4
server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeData.java Datei anzeigen

@@ -25,7 +25,7 @@ import java.util.Map;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.measure.MeasureDto;
import org.sonar.db.measure.LiveMeasureDto;
import org.sonar.db.metric.MetricDto;
import org.sonarqube.ws.Measures;

@@ -149,9 +149,9 @@ class ComponentTreeData {
private String data;
private double variation;

private Measure(MeasureDto measureDto) {
private Measure(LiveMeasureDto measureDto) {
this.value = toPrimitive(measureDto.getValue());
this.data = measureDto.getData();
this.data = measureDto.getDataAsString();
this.variation = toPrimitive(measureDto.getVariation());
}

@@ -176,7 +176,7 @@ class ComponentTreeData {
return !isNaN(variation);
}

static Measure createFromMeasureDto(MeasureDto measureDto) {
static Measure createFromMeasureDto(LiveMeasureDto measureDto) {
return new Measure(measureDto);
}


+ 0
- 1
server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeRequest.java Datei anzeigen

@@ -26,7 +26,6 @@ import javax.annotation.Nullable;
class ComponentTreeRequest {

private String baseComponentId;
private String baseComponentKey;
private String component;
private String branch;
private String strategy;

+ 7
- 0
server/sonar-server/src/main/java/org/sonar/server/measure/ws/MeasureDtoToWsMeasure.java Datei anzeigen

@@ -20,6 +20,7 @@
package org.sonar.server.measure.ws;

import javax.annotation.Nullable;
import org.sonar.db.measure.LiveMeasureDto;
import org.sonar.db.measure.MeasureDto;
import org.sonar.db.metric.MetricDto;
import org.sonarqube.ws.Measures;
@@ -40,6 +41,12 @@ class MeasureDtoToWsMeasure {
updateMeasureBuilder(measureBuilder, metricDto, value == null ? Double.NaN : value, measureDto.getData(), variation == null ? Double.NaN : variation);
}

static void updateMeasureBuilder(Measure.Builder measureBuilder, MetricDto metricDto, LiveMeasureDto measureDto) {
Double value = measureDto.getValue();
Double variation = measureDto.getVariation();
updateMeasureBuilder(measureBuilder, metricDto, value == null ? Double.NaN : value, measureDto.getDataAsString(), variation == null ? Double.NaN : variation);
}

static void updateMeasureBuilder(Measure.Builder measureBuilder, MetricDto metric, double doubleValue, @Nullable String stringValue, double variation) {
measureBuilder.setMetric(metric.getKey());
// a measure value can be null, new_violations metric for example

+ 4
- 14
server/sonar-server/src/main/java/org/sonar/server/measure/ws/MetricDtoWithBestValue.java Datei anzeigen

@@ -22,12 +22,10 @@ package org.sonar.server.measure.ws;
import com.google.common.collect.ImmutableSortedSet;
import java.util.Locale;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import javax.annotation.Nonnull;
import org.sonar.api.resources.Qualifiers;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.measure.MeasureDto;
import org.sonar.db.measure.LiveMeasureDto;
import org.sonar.db.metric.MetricDto;

class MetricDtoWithBestValue {
@@ -35,11 +33,11 @@ class MetricDtoWithBestValue {
private static final Set<String> QUALIFIERS_ELIGIBLE_FOR_BEST_VALUE = ImmutableSortedSet.of(Qualifiers.FILE, Qualifiers.UNIT_TEST_FILE);

private final MetricDto metric;
private final MeasureDto bestValue;
private final LiveMeasureDto bestValue;

MetricDtoWithBestValue(MetricDto metric) {
this.metric = metric;
MeasureDto measure = new MeasureDto().setMetricId(metric.getId());
LiveMeasureDto measure = new LiveMeasureDto().setMetricId(metric.getId());
boolean isNewTypeMetric = metric.getKey().toLowerCase(Locale.ENGLISH).startsWith(LOWER_CASE_NEW_METRIC_PREFIX);
if (isNewTypeMetric) {
measure.setVariation(metric.getBestValue());
@@ -54,19 +52,11 @@ class MetricDtoWithBestValue {
return metric;
}

MeasureDto getBestValue() {
LiveMeasureDto getBestValue() {
return bestValue;
}

static Predicate<ComponentDto> isEligibleForBestValue() {
return component -> QUALIFIERS_ELIGIBLE_FOR_BEST_VALUE.contains(component.qualifier());
}

static class MetricDtoToMetricDtoWithBestValueFunction implements Function<MetricDto, MetricDtoWithBestValue> {

@Override
public MetricDtoWithBestValue apply(@Nonnull MetricDto input) {
return new MetricDtoWithBestValue(input);
}
}
}

+ 7
- 7
server/sonar-server/src/main/java/org/sonar/server/measure/ws/SearchAction.java Datei anzeigen

@@ -34,7 +34,7 @@ import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.measure.MeasureDto;
import org.sonar.db.measure.LiveMeasureDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.server.user.UserSession;
import org.sonarqube.ws.Measures.Measure;
@@ -105,7 +105,7 @@ public class SearchAction implements MeasuresWsAction {
private SearchRequest request;
private List<ComponentDto> projects;
private List<MetricDto> metrics;
private List<MeasureDto> measures;
private List<LiveMeasureDto> measures;

ResponseBuilder(Request httpRequest, DbSession dbSession) {
this.dbSession = dbSession;
@@ -161,10 +161,10 @@ public class SearchAction implements MeasuresWsAction {
.collect(toList());
}

private List<MeasureDto> searchMeasures() {
return dbClient.measureDao().selectByComponentsAndMetrics(dbSession,
projects.stream().map(ComponentDto::uuid).collect(toList()),
metrics.stream().map(MetricDto::getId).collect(toList()));
private List<LiveMeasureDto> searchMeasures() {
return dbClient.liveMeasureDao().selectByComponentUuids(dbSession,
projects.stream().map(ComponentDto::uuid).collect(MoreCollectors.toArrayList(projects.size())),
metrics.stream().map(MetricDto::getId).collect(MoreCollectors.toArrayList(metrics.size())));
}

private SearchWsResponse buildResponse() {
@@ -179,7 +179,7 @@ public class SearchAction implements MeasuresWsAction {
Map<String, String> componentNamesByKey = projects.stream().collect(toMap(ComponentDto::getDbKey, ComponentDto::name));
Map<Integer, MetricDto> metricsById = metrics.stream().collect(toMap(MetricDto::getId, identity()));

Function<MeasureDto, MetricDto> dbMeasureToDbMetric = dbMeasure -> metricsById.get(dbMeasure.getMetricId());
Function<LiveMeasureDto, MetricDto> dbMeasureToDbMetric = dbMeasure -> metricsById.get(dbMeasure.getMetricId());
Function<Measure, String> byMetricKey = Measure::getMetric;
Function<Measure, String> byComponentName = wsMeasure -> componentNamesByKey.get(wsMeasure.getComponent());


+ 8
- 15
server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchMyProjectsAction.java Datei anzeigen

@@ -19,12 +19,13 @@
*/
package org.sonar.server.project.ws;

import java.util.List;
import java.util.function.Function;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import java.util.List;
import java.util.function.Function;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.server.ws.Change;
@@ -39,19 +40,15 @@ import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentLinkDto;
import org.sonar.db.component.ComponentQuery;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.measure.MeasureDto;
import org.sonar.db.measure.MeasureQuery;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.measure.LiveMeasureDto;
import org.sonar.server.user.UserSession;
import org.sonarqube.ws.Projects.SearchMyProjectsWsResponse;
import org.sonarqube.ws.Projects.SearchMyProjectsWsResponse.Link;
import org.sonarqube.ws.Projects.SearchMyProjectsWsResponse.Project;

import javax.annotation.CheckForNull;
import javax.annotation.Nullable;

import static com.google.common.base.Strings.emptyToNull;
import static com.google.common.base.Strings.isNullOrEmpty;
import static java.util.Collections.singletonList;
import static java.util.Objects.requireNonNull;
import static org.sonar.api.utils.Paging.offset;
import static org.sonar.core.util.Protobuf.setNullable;
@@ -177,12 +174,8 @@ public class SearchMyProjectsAction implements ProjectsWsAction {
List<String> projectUuids = Lists.transform(projects, ComponentDto::projectUuid);
List<ComponentLinkDto> projectLinks = dbClient.componentLinkDao().selectByComponentUuids(dbSession, projectUuids);
List<SnapshotDto> snapshots = dbClient.snapshotDao().selectLastAnalysesByRootComponentUuids(dbSession, projectUuids);
MetricDto gateStatusMetric = dbClient.metricDao().selectOrFailByKey(dbSession, CoreMetrics.ALERT_STATUS_KEY);
MeasureQuery measureQuery = MeasureQuery.builder()
.setProjectUuids(projectUuids)
.setMetricId(gateStatusMetric.getId())
.build();
List<MeasureDto> qualityGates = dbClient.measureDao().selectByQuery(dbSession, measureQuery);
List<LiveMeasureDto> qualityGates = dbClient.liveMeasureDao()
.selectByComponentUuidsAndMetricKeys(dbSession, projectUuids, singletonList(CoreMetrics.ALERT_STATUS_KEY));

data.setProjects(projects)
.setProjectLinks(projectLinks)

+ 5
- 5
server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchMyProjectsData.java Datei anzeigen

@@ -29,7 +29,7 @@ import java.util.stream.Collectors;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentLinkDto;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.measure.MeasureDto;
import org.sonar.db.measure.LiveMeasureDto;

import static com.google.common.collect.ImmutableList.copyOf;
import static java.util.Objects.requireNonNull;
@@ -86,16 +86,16 @@ class SearchMyProjectsData {
snapshot -> formatDateTime(snapshot.getCreatedAt()))));
}

private static Map<String, String> buildQualityGateStatuses(List<MeasureDto> measures) {
private static Map<String, String> buildQualityGateStatuses(List<LiveMeasureDto> measures) {
return ImmutableMap.copyOf(measures.stream()
.collect(Collectors.toMap(MeasureDto::getComponentUuid, MeasureDto::getData)));
.collect(Collectors.toMap(LiveMeasureDto::getComponentUuid, LiveMeasureDto::getDataAsString)));
}

static class Builder {
private List<ComponentDto> projects;
private List<ComponentLinkDto> projectLinks;
private List<SnapshotDto> snapshots;
private List<MeasureDto> qualityGates;
private List<LiveMeasureDto> qualityGates;
private Integer totalNbOfProjects;

private Builder() {
@@ -117,7 +117,7 @@ class SearchMyProjectsData {
return this;
}

public Builder setQualityGates(List<MeasureDto> qGateStatuses) {
public Builder setQualityGates(List<LiveMeasureDto> qGateStatuses) {
this.qualityGates = qGateStatuses;
return this;
}

+ 26
- 22
server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/ProjectStatusAction.java Datei anzeigen

@@ -19,11 +19,11 @@
*/
package org.sonar.server.qualitygate.ws;

import com.google.common.base.Optional;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.server.ws.Change;
import org.sonar.api.server.ws.Request;
@@ -35,8 +35,8 @@ import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.measure.LiveMeasureDto;
import org.sonar.db.measure.MeasureDto;
import org.sonar.db.measure.MeasureQuery;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.component.ComponentFinder.ParamNames;
import org.sonar.server.exceptions.BadRequestException;
@@ -45,7 +45,6 @@ import org.sonar.server.ws.KeyExamples;
import org.sonarqube.ws.Qualitygates.ProjectStatusResponse;

import static com.google.common.base.Strings.isNullOrEmpty;
import static java.util.Collections.singletonList;
import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
import static org.sonar.server.ws.WsUtils.checkFoundWithOptional;
import static org.sonar.server.ws.WsUtils.checkRequest;
@@ -88,8 +87,7 @@ public class ProjectStatusAction implements QualityGatesWsAction {
.setSince("5.3")
.setHandler(this)
.setChangelog(
new Change("6.4", "The field 'ignoredConditions' is added to the response")
);
new Change("6.4", "The field 'ignoredConditions' is added to the response"));

action.createParam(PARAM_ANALYSIS_ID)
.setDescription("Analysis id")
@@ -120,11 +118,11 @@ public class ProjectStatusAction implements QualityGatesWsAction {
writeProtobuf(projectStatusResponse, request, response);
}

private ProjectStatusResponse doHandle(String analysisId, String projectId, String projectKey) {
private ProjectStatusResponse doHandle(@Nullable String analysisId, @Nullable String projectId, @Nullable String projectKey) {
try (DbSession dbSession = dbClient.openSession(false)) {
ProjectAndSnapshot projectAndSnapshot = getProjectAndSnapshot(dbSession, analysisId, projectId, projectKey);
checkPermission(projectAndSnapshot.project);
Optional<String> measureData = getQualityGateDetailsMeasureData(dbSession, projectAndSnapshot.project);
Optional<String> measureData = loadQualityGateDetails(dbSession, projectAndSnapshot, analysisId != null);

return ProjectStatusResponse.newBuilder()
.setProjectStatus(new QualityGateDetailsFormatter(measureData, projectAndSnapshot.snapshotDto).format())
@@ -132,10 +130,11 @@ public class ProjectStatusAction implements QualityGatesWsAction {
}
}

private ProjectAndSnapshot getProjectAndSnapshot(DbSession dbSession, String analysisId, String projectId, String projectKey) {
private ProjectAndSnapshot getProjectAndSnapshot(DbSession dbSession, @Nullable String analysisId, @Nullable String projectId, @Nullable String projectKey) {
if (!isNullOrEmpty(analysisId)) {
return getSnapshotThenProject(dbSession, analysisId);
} else if (!isNullOrEmpty(projectId) ^ !isNullOrEmpty(projectKey)) {
}
if (!isNullOrEmpty(projectId) ^ !isNullOrEmpty(projectKey)) {
return getProjectThenSnapshot(dbSession, projectId, projectKey);
}

@@ -144,7 +143,7 @@ public class ProjectStatusAction implements QualityGatesWsAction {

private ProjectAndSnapshot getProjectThenSnapshot(DbSession dbSession, String projectId, String projectKey) {
ComponentDto projectDto = componentFinder.getByUuidOrKey(dbSession, projectId, projectKey, ParamNames.PROJECT_ID_AND_KEY);
java.util.Optional<SnapshotDto> snapshot = dbClient.snapshotDao().selectLastAnalysisByRootComponentUuid(dbSession, projectDto.projectUuid());
Optional<SnapshotDto> snapshot = dbClient.snapshotDao().selectLastAnalysisByRootComponentUuid(dbSession, projectDto.projectUuid());
return new ProjectAndSnapshot(projectDto, snapshot.orElse(null));
}

@@ -155,20 +154,24 @@ public class ProjectStatusAction implements QualityGatesWsAction {
}

private SnapshotDto getSnapshot(DbSession dbSession, String analysisUuid) {
java.util.Optional<SnapshotDto> snapshotDto = dbClient.snapshotDao().selectByUuid(dbSession, analysisUuid);
Optional<SnapshotDto> snapshotDto = dbClient.snapshotDao().selectByUuid(dbSession, analysisUuid);
return checkFoundWithOptional(snapshotDto, "Analysis with id '%s' is not found", analysisUuid);
}

private Optional<String> getQualityGateDetailsMeasureData(DbSession dbSession, ComponentDto project) {
MeasureQuery measureQuery = MeasureQuery.builder()
.setProjectUuids(singletonList(project.projectUuid()))
.setMetricKey(CoreMetrics.QUALITY_GATE_DETAILS_KEY)
.build();
List<MeasureDto> measures = dbClient.measureDao().selectByQuery(dbSession, measureQuery);
private Optional<String> loadQualityGateDetails(DbSession dbSession, ProjectAndSnapshot projectAndSnapshot, boolean onAnalysis) {
if (onAnalysis) {
if (!projectAndSnapshot.snapshotDto.isPresent()) {
return Optional.empty();
}
// get the gate status as it was computed during the specified analysis
String analysisUuid = projectAndSnapshot.snapshotDto.get().getUuid();
return dbClient.measureDao().selectMeasure(dbSession, analysisUuid, projectAndSnapshot.project.projectUuid(), CoreMetrics.QUALITY_GATE_DETAILS_KEY)
.map(MeasureDto::getData);
}

return measures.isEmpty()
? Optional.absent()
: Optional.fromNullable(measures.get(0).getData());
// do not restrict to a specified analysis, use the live measure
Optional<LiveMeasureDto> measure = dbClient.liveMeasureDao().selectMeasure(dbSession, projectAndSnapshot.project.projectUuid(), CoreMetrics.QUALITY_GATE_DETAILS_KEY);
return measure.map(LiveMeasureDto::getDataAsString);
}

private void checkPermission(ComponentDto project) {
@@ -178,13 +181,14 @@ public class ProjectStatusAction implements QualityGatesWsAction {
}
}

@Immutable
private static class ProjectAndSnapshot {
private final ComponentDto project;
private final Optional<SnapshotDto> snapshotDto;

private ProjectAndSnapshot(ComponentDto project, @Nullable SnapshotDto snapshotDto) {
this.project = project;
this.snapshotDto = Optional.fromNullable(snapshotDto);
this.snapshotDto = Optional.ofNullable(snapshotDto);
}
}
}

+ 1
- 1
server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QualityGateDetailsFormatter.java Datei anzeigen

@@ -19,11 +19,11 @@
*/
package org.sonar.server.qualitygate.ws;

import com.google.common.base.Optional;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.StreamSupport;
import javax.annotation.Nullable;

+ 5
- 13
server/sonar-server/src/main/java/org/sonar/server/ui/ws/ComponentAction.java Datei anzeigen

@@ -26,9 +26,7 @@ import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.resources.ResourceType;
@@ -45,8 +43,7 @@ import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.measure.MeasureDto;
import org.sonar.db.measure.MeasureQuery;
import org.sonar.db.measure.LiveMeasureDto;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.permission.OrganizationPermission;
import org.sonar.db.property.PropertyDto;
@@ -166,10 +163,6 @@ public class ComponentAction implements NavigationWsAction {
.endObject();
}

private static Function<MeasureDto, Stream<QualityProfile>> toQualityProfiles() {
return dbMeasure -> QPMeasureData.fromJson(dbMeasure.getData()).getProfiles().stream();
}

private static void writePage(JsonWriter json, Page page) {
json.beginObject()
.prop("key", page.getKey())
@@ -205,12 +198,11 @@ public class ComponentAction implements NavigationWsAction {
return componentFavourites.size() == 1;
}

private void writeProfiles(JsonWriter json, DbSession session, ComponentDto component) {
private void writeProfiles(JsonWriter json, DbSession dbSession, ComponentDto component) {
json.name("qualityProfiles").beginArray();
dbClient.measureDao().selectSingle(session, MeasureQuery.builder().setComponentUuid(component.projectUuid()).setMetricKey(QUALITY_PROFILES_KEY).build())
.ifPresent(dbMeasure -> Stream.of(dbMeasure)
.flatMap(toQualityProfiles())
.forEach(writeToJson(json)));
dbClient.liveMeasureDao().selectMeasure(dbSession, component.projectUuid(), QUALITY_PROFILES_KEY)
.map(LiveMeasureDto::getDataAsString)
.ifPresent(data -> QPMeasureData.fromJson(data).getProfiles().forEach(writeToJson(json)));
json.endArray();
}


+ 1
- 3
server/sonar-server/src/test/java/org/sonar/server/branch/ws/ListActionTest.java Datei anzeigen

@@ -34,7 +34,6 @@ import org.sonar.db.component.BranchType;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
import org.sonar.db.component.ResourceTypesRule;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.component.SnapshotTesting;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.organization.OrganizationDto;
@@ -250,8 +249,7 @@ public class ListActionTest {
ComponentDto project = db.components().insertMainBranch();
userSession.logIn().addProjectPermission(UserRole.USER, project);
ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.LONG));
SnapshotDto branchAnalysis = db.components().insertSnapshot(branch);
db.measures().insertMeasure(branch, branchAnalysis, qualityGateStatus, m -> m.setData("OK"));
db.measures().insertLiveMeasure(branch, qualityGateStatus, m -> m.setData("OK"));

ListWsResponse response = ws.newRequest()
.setParam("project", project.getKey())

+ 8
- 12
server/sonar-server/src/test/java/org/sonar/server/component/ws/AppActionTest.java Datei anzeigen

@@ -25,7 +25,6 @@ import org.junit.rules.ExpectedException;
import org.sonar.api.server.ws.WebService;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.server.component.TestComponentFinder;
import org.sonar.server.exceptions.ForbiddenException;
@@ -135,19 +134,18 @@ public class AppActionTest {
ComponentDto project = db.components().insertPrivateProject();
ComponentDto directory = db.components().insertComponent(newDirectory(project, "src"));
ComponentDto file = db.components().insertComponent(newFileDto(project, directory));
SnapshotDto analysis = db.components().insertSnapshot(project);
MetricDto lines = db.measures().insertMetric(m -> m.setKey(LINES_KEY));
db.measures().insertMeasure(file, analysis, lines, m -> m.setValue(200d));
db.measures().insertLiveMeasure(file, lines, m -> m.setValue(200d));
MetricDto duplicatedLines = db.measures().insertMetric(m -> m.setKey(DUPLICATED_LINES_DENSITY_KEY));
db.measures().insertMeasure(file, analysis, duplicatedLines, m -> m.setValue(7.4));
db.measures().insertLiveMeasure(file, duplicatedLines, m -> m.setValue(7.4));
MetricDto tests = db.measures().insertMetric(m -> m.setKey(TESTS_KEY));
db.measures().insertMeasure(file, analysis, tests, m -> m.setValue(3d));
db.measures().insertLiveMeasure(file, tests, m -> m.setValue(3d));
MetricDto technicalDebt = db.measures().insertMetric(m -> m.setKey(TECHNICAL_DEBT_KEY));
db.measures().insertMeasure(file, analysis, technicalDebt, m -> m.setValue(182d));
db.measures().insertLiveMeasure(file, technicalDebt, m -> m.setValue(182d));
MetricDto issues = db.measures().insertMetric(m -> m.setKey(VIOLATIONS_KEY));
db.measures().insertMeasure(file, analysis, issues, m -> m.setValue(231d));
db.measures().insertLiveMeasure(file, issues, m -> m.setValue(231d));
MetricDto coverage = db.measures().insertMetric(m -> m.setKey(COVERAGE_KEY));
db.measures().insertMeasure(file, analysis, coverage, m -> m.setValue(95.4d));
db.measures().insertLiveMeasure(file, coverage, m -> m.setValue(95.4d));
userSession.logIn("john").addProjectPermission(USER, project);

String result = ws.newRequest()
@@ -170,9 +168,8 @@ public class AppActionTest {
public void get_by_uuid() {
ComponentDto project = db.components().insertPrivateProject();
ComponentDto file = db.components().insertComponent(newFileDto(project, project));
SnapshotDto analysis = db.components().insertSnapshot(project);
MetricDto coverage = db.measures().insertMetric(m -> m.setKey(COVERAGE_KEY));
db.measures().insertMeasure(file, analysis, coverage, m -> m.setValue(95.4d));
db.measures().insertLiveMeasure(file, coverage, m -> m.setValue(95.4d));
userSession.logIn("john").addProjectPermission(USER, project);

String result = ws.newRequest()
@@ -258,9 +255,8 @@ public class AppActionTest {
ComponentDto module = db.components().insertComponent(newModuleDto(branch));
ComponentDto directory = db.components().insertComponent(newDirectory(module, "src"));
ComponentDto file = db.components().insertComponent(newFileDto(module, directory));
SnapshotDto analysis = db.components().insertSnapshot(branch);
MetricDto coverage = db.measures().insertMetric(m -> m.setKey(COVERAGE_KEY));
db.measures().insertMeasure(file, analysis, coverage, m -> m.setValue(95.4d));
db.measures().insertLiveMeasure(file, coverage, m -> m.setValue(95.4d));

String result = ws.newRequest()
.setParam("component", file.getKey())

+ 21
- 23
server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchProjectsActionTest.java Datei anzeigen

@@ -41,8 +41,7 @@ import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.measure.MeasureDto;
import org.sonar.db.measure.LiveMeasureDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.property.PropertyDto;
@@ -316,9 +315,9 @@ public class SearchProjectsActionTest {
userSession.logIn();
OrganizationDto organizationDto = db.organizations().insert();
MetricDto qualityGateStatus = db.measures().insertMetric(c -> c.setKey(QUALITY_GATE_STATUS).setValueType(LEVEL.name()));
ComponentDto project1 = insertProject(organizationDto, new Measure(qualityGateStatus, c -> c.setData("OK")));
ComponentDto project2 = insertProject(organizationDto, new Measure(qualityGateStatus, c -> c.setData("OK")));
ComponentDto project3 = insertProject(organizationDto, new Measure(qualityGateStatus, c -> c.setData("ERROR")));
ComponentDto project1 = insertProject(organizationDto, new Measure(qualityGateStatus, c -> c.setValue(null).setData("OK")));
ComponentDto project2 = insertProject(organizationDto, new Measure(qualityGateStatus, c -> c.setValue(null).setData("OK")));
ComponentDto project3 = insertProject(organizationDto, new Measure(qualityGateStatus, c -> c.setValue(null).setData("ERROR")));

SearchProjectsWsResponse result = call(request.setFilter("alert_status = OK"));

@@ -332,10 +331,10 @@ public class SearchProjectsActionTest {
userSession.logIn();
OrganizationDto organizationDto = db.organizations().insert();
MetricDto languagesDistribution = db.measures().insertMetric(c -> c.setKey(NCLOC_LANGUAGE_DISTRIBUTION_KEY).setValueType(DATA.name()));
ComponentDto project1 = insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setData("<null>=2;java=6;xoo=18")));
ComponentDto project2 = insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setData("java=3;xoo=9")));
ComponentDto project3 = insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setData("xoo=1")));
ComponentDto project4 = insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setData("<null>=1;java=5;xoo=13")));
ComponentDto project1 = insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setValue(null).setData("<null>=2;java=6;xoo=18")));
ComponentDto project2 = insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setValue(null).setData("java=3;xoo=9")));
ComponentDto project3 = insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setValue(null).setData("xoo=1")));
ComponentDto project4 = insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setValue(null).setData("<null>=1;java=5;xoo=13")));

SearchProjectsWsResponse result = call(request.setFilter("languages IN (java, js, <null>)"));

@@ -628,10 +627,10 @@ public class SearchProjectsActionTest {
userSession.logIn();
OrganizationDto organizationDto = db.organizations().insert();
MetricDto languagesDistribution = db.measures().insertMetric(c -> c.setKey(NCLOC_LANGUAGE_DISTRIBUTION_KEY).setValueType(DATA.name()));
insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setData("<null>=2;java=6;xoo=18")));
insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setData("java=5;xoo=19")));
insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setData("xoo=1")));
insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setData("<null>=1;java=3;xoo=8")));
insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setValue(null).setData("<null>=2;java=6;xoo=18")));
insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setValue(null).setData("java=5;xoo=19")));
insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setValue(null).setData("xoo=1")));
insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setValue(null).setData("<null>=1;java=3;xoo=8")));

SearchProjectsWsResponse result = call(request.setFacets(singletonList(FILTER_LANGUAGES)));

@@ -651,8 +650,8 @@ public class SearchProjectsActionTest {
userSession.logIn();
OrganizationDto organizationDto = db.organizations().insert();
MetricDto languagesDistribution = db.measures().insertMetric(c -> c.setKey(NCLOC_LANGUAGE_DISTRIBUTION_KEY).setValueType(DATA.name()));
insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setData("<null>=2;java=6")));
insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setData("java=5")));
insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setValue(null).setData("<null>=2;java=6")));
insertProject(organizationDto, new Measure(languagesDistribution, c -> c.setValue(null).setData("java=5")));

SearchProjectsWsResponse result = call(request.setFilter("languages = xoo").setFacets(singletonList(FILTER_LANGUAGES)));

@@ -964,10 +963,10 @@ public class SearchProjectsActionTest {
userSession.logIn();
OrganizationDto organization = db.organizations().insert();
MetricDto qualityGateStatus = db.measures().insertMetric(c -> c.setKey(QUALITY_GATE_STATUS).setValueType(LEVEL.name()));
ComponentDto project1 = insertProject(organization, c -> c.setName("Sonar Java"), new Measure(qualityGateStatus, c -> c.setData("ERROR")));
ComponentDto project2 = insertProject(organization, c -> c.setName("Sonar Groovy"), new Measure(qualityGateStatus, c -> c.setData("WARN")));
ComponentDto project3 = insertProject(organization, c -> c.setName("Sonar Markdown"), new Measure(qualityGateStatus, c -> c.setData("OK")));
ComponentDto project4 = insertProject(organization, c -> c.setName("Sonar Qube"), new Measure(qualityGateStatus, c -> c.setData("OK")));
ComponentDto project1 = insertProject(organization, c -> c.setName("Sonar Java"), new Measure(qualityGateStatus, c -> c.setValue(null).setData("ERROR")));
ComponentDto project2 = insertProject(organization, c -> c.setName("Sonar Groovy"), new Measure(qualityGateStatus, c -> c.setValue(null).setData("WARN")));
ComponentDto project3 = insertProject(organization, c -> c.setName("Sonar Markdown"), new Measure(qualityGateStatus, c -> c.setValue(null).setData("OK")));
ComponentDto project4 = insertProject(organization, c -> c.setName("Sonar Qube"), new Measure(qualityGateStatus, c -> c.setValue(null).setData("OK")));

assertThat(call(request.setSort(QUALITY_GATE_STATUS).setAsc(true)).getComponentsList()).extracting(Component::getKey)
.containsExactly(project3.getDbKey(), project4.getDbKey(), project2.getDbKey(), project1.getDbKey());
@@ -1138,8 +1137,7 @@ public class SearchProjectsActionTest {

private ComponentDto insertProject(OrganizationDto organizationDto, Consumer<ComponentDto> projectConsumer, Measure... measures) {
ComponentDto project = db.components().insertPublicProject(organizationDto, projectConsumer);
SnapshotDto analysis = db.components().insertSnapshot(project);
Arrays.stream(measures).forEach(m -> db.measures().insertMeasure(project, analysis, m.metric, m.consumer));
Arrays.stream(measures).forEach(m -> db.measures().insertLiveMeasure(project, m.metric, m.consumer));
authorizationIndexerTester.allowOnlyAnyone(project);
projectMeasuresIndexer.indexOnAnalysis(project.uuid());
return project;
@@ -1147,9 +1145,9 @@ public class SearchProjectsActionTest {

private static class Measure {
private final MetricDto metric;
private final Consumer<MeasureDto> consumer;
private final Consumer<LiveMeasureDto> consumer;

public Measure(MetricDto metric, Consumer<MeasureDto> consumer) {
public Measure(MetricDto metric, Consumer<LiveMeasureDto> consumer) {
this.metric = metric;
this.consumer = consumer;
}

+ 35
- 35
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/measure/BestValueOptimizationTest.java Datei anzeigen

@@ -19,7 +19,7 @@
*/
package org.sonar.server.computation.task.projectanalysis.measure;

import com.google.common.base.Predicate;
import java.util.function.Predicate;
import org.junit.Test;
import org.sonar.server.computation.task.projectanalysis.component.Component;
import org.sonar.server.computation.task.projectanalysis.component.ReportComponent;
@@ -56,18 +56,18 @@ public class BestValueOptimizationTest {
public void apply_returns_true_for_value_true_for_Boolean_Metric_and_best_value_1() {
Predicate<Measure> underTest = BestValueOptimization.from(METRIC_BOOLEAN_TRUE, FILE_COMPONENT);

assertThat(underTest.apply(newMeasureBuilder().create(true))).isTrue();
assertThat(underTest.apply(newMeasureBuilder().setVariation(SOME_EMPTY_VARIATIONS).create(true))).isTrue();
assertThat(underTest.apply(newMeasureBuilder().create(false))).isFalse();
assertThat(underTest.apply(newMeasureBuilder().setVariation(SOME_EMPTY_VARIATIONS).create(false))).isFalse();
assertThat(underTest.test(newMeasureBuilder().create(true))).isTrue();
assertThat(underTest.test(newMeasureBuilder().setVariation(SOME_EMPTY_VARIATIONS).create(true))).isTrue();
assertThat(underTest.test(newMeasureBuilder().create(false))).isFalse();
assertThat(underTest.test(newMeasureBuilder().setVariation(SOME_EMPTY_VARIATIONS).create(false))).isFalse();
}

@Test
public void apply_returns_false_if_component_is_not_a_FILE_for_Boolean_Metric_and_best_value_1() {
Predicate<Measure> underTest = BestValueOptimization.from(METRIC_BOOLEAN_TRUE, SOME_NON_FILE_COMPONENT);

assertThat(underTest.apply(newMeasureBuilder().create(true))).isFalse();
assertThat(underTest.apply(newMeasureBuilder().create(false))).isFalse();
assertThat(underTest.test(newMeasureBuilder().create(true))).isFalse();
assertThat(underTest.test(newMeasureBuilder().create(false))).isFalse();
}

@Test
@@ -75,8 +75,8 @@ public class BestValueOptimizationTest {
Predicate<Measure> underTest = BestValueOptimization.from(METRIC_BOOLEAN_TRUE, FILE_COMPONENT);

for (Measure.NewMeasureBuilder builder : builders_of_non_bestValueOptimized_measures()) {
assertThat(underTest.apply(builder.create(true))).isFalse();
assertThat(underTest.apply(builder.create(false))).isFalse();
assertThat(underTest.test(builder.create(true))).isFalse();
assertThat(underTest.test(builder.create(false))).isFalse();
}
}

@@ -84,25 +84,25 @@ public class BestValueOptimizationTest {
public void apply_returns_false_if_measure_has_data_for_Boolean_Metric_and_best_value_1() {
Predicate<Measure> underTest = BestValueOptimization.from(METRIC_BOOLEAN_TRUE, FILE_COMPONENT);

assertThat(underTest.apply(newMeasureBuilder().create(true, SOME_DATA))).isFalse();
assertThat(underTest.apply(newMeasureBuilder().create(false, SOME_DATA))).isFalse();
assertThat(underTest.test(newMeasureBuilder().create(true, SOME_DATA))).isFalse();
assertThat(underTest.test(newMeasureBuilder().create(false, SOME_DATA))).isFalse();
}

@Test
public void apply_returns_true_for_value_false_for_Boolean_Metric_and_best_value_not_1() {
Predicate<Measure> underTest = BestValueOptimization.from(METRIC_BOOLEAN_FALSE, FILE_COMPONENT);

assertThat(underTest.apply(newMeasureBuilder().create(true))).isFalse();
assertThat(underTest.apply(newMeasureBuilder().create(false))).isTrue();
assertThat(underTest.apply(newMeasureBuilder().setVariation(SOME_EMPTY_VARIATIONS).create(false))).isTrue();
assertThat(underTest.test(newMeasureBuilder().create(true))).isFalse();
assertThat(underTest.test(newMeasureBuilder().create(false))).isTrue();
assertThat(underTest.test(newMeasureBuilder().setVariation(SOME_EMPTY_VARIATIONS).create(false))).isTrue();
}

@Test
public void apply_returns_false_if_component_is_not_a_FILE_for_Boolean_Metric_and_best_value_not_1() {
Predicate<Measure> underTest = BestValueOptimization.from(METRIC_BOOLEAN_FALSE, SOME_NON_FILE_COMPONENT);

assertThat(underTest.apply(newMeasureBuilder().create(true))).isFalse();
assertThat(underTest.apply(newMeasureBuilder().create(false))).isFalse();
assertThat(underTest.test(newMeasureBuilder().create(true))).isFalse();
assertThat(underTest.test(newMeasureBuilder().create(false))).isFalse();
}

@Test
@@ -110,8 +110,8 @@ public class BestValueOptimizationTest {
Predicate<Measure> underTest = BestValueOptimization.from(METRIC_BOOLEAN_FALSE, FILE_COMPONENT);

for (Measure.NewMeasureBuilder builder : builders_of_non_bestValueOptimized_measures()) {
assertThat(underTest.apply(builder.create(true))).isFalse();
assertThat(underTest.apply(builder.create(false))).isFalse();
assertThat(underTest.test(builder.create(true))).isFalse();
assertThat(underTest.test(builder.create(false))).isFalse();
}
}

@@ -119,59 +119,59 @@ public class BestValueOptimizationTest {
public void apply_returns_false_if_measure_has_data_for_Boolean_Metric_and_best_value_not_1() {
Predicate<Measure> underTest = BestValueOptimization.from(METRIC_BOOLEAN_FALSE, FILE_COMPONENT);

assertThat(underTest.apply(newMeasureBuilder().create(true, SOME_DATA))).isFalse();
assertThat(underTest.apply(newMeasureBuilder().create(false, SOME_DATA))).isFalse();
assertThat(underTest.test(newMeasureBuilder().create(true, SOME_DATA))).isFalse();
assertThat(underTest.test(newMeasureBuilder().create(false, SOME_DATA))).isFalse();
}

@Test
public void verify_value_comparison_for_int_metric() {
Predicate<Measure> underTest = BestValueOptimization.from(createMetric(Metric.MetricType.INT, 10), FILE_COMPONENT);

assertThat(underTest.apply(newMeasureBuilder().create(10))).isTrue();
assertThat(underTest.apply(newMeasureBuilder().setVariation(SOME_EMPTY_VARIATIONS).create(10))).isTrue();
assertThat(underTest.apply(newMeasureBuilder().create(11))).isFalse();
assertThat(underTest.test(newMeasureBuilder().create(10))).isTrue();
assertThat(underTest.test(newMeasureBuilder().setVariation(SOME_EMPTY_VARIATIONS).create(10))).isTrue();
assertThat(underTest.test(newMeasureBuilder().create(11))).isFalse();
}

@Test
public void verify_value_comparison_for_long_metric() {
Predicate<Measure> underTest = BestValueOptimization.from(createMetric(Metric.MetricType.WORK_DUR, 9511L), FILE_COMPONENT);

assertThat(underTest.apply(newMeasureBuilder().create(9511L))).isTrue();
assertThat(underTest.apply(newMeasureBuilder().setVariation(SOME_EMPTY_VARIATIONS).create(9511L))).isTrue();
assertThat(underTest.apply(newMeasureBuilder().create(963L))).isFalse();
assertThat(underTest.test(newMeasureBuilder().create(9511L))).isTrue();
assertThat(underTest.test(newMeasureBuilder().setVariation(SOME_EMPTY_VARIATIONS).create(9511L))).isTrue();
assertThat(underTest.test(newMeasureBuilder().create(963L))).isFalse();
}

@Test
public void verify_value_comparison_for_rating_metric() {
Predicate<Measure> underTest = BestValueOptimization.from(createMetric(Metric.MetricType.RATING, A.getIndex()), FILE_COMPONENT);

assertThat(underTest.apply(newMeasureBuilder().create(A.getIndex()))).isTrue();
assertThat(underTest.apply(newMeasureBuilder().setVariation((double) A.getIndex()).createNoValue())).isTrue();
assertThat(underTest.apply(newMeasureBuilder().create(B.getIndex()))).isFalse();
assertThat(underTest.apply(newMeasureBuilder().setVariation((double) B.getIndex()).createNoValue())).isFalse();
assertThat(underTest.test(newMeasureBuilder().create(A.getIndex()))).isTrue();
assertThat(underTest.test(newMeasureBuilder().setVariation((double) A.getIndex()).createNoValue())).isTrue();
assertThat(underTest.test(newMeasureBuilder().create(B.getIndex()))).isFalse();
assertThat(underTest.test(newMeasureBuilder().setVariation((double) B.getIndex()).createNoValue())).isFalse();
}

@Test
public void verify_value_comparison_for_double_metric() {
Predicate<Measure> underTest = BestValueOptimization.from(createMetric(Metric.MetricType.FLOAT, 36.5d), FILE_COMPONENT);

assertThat(underTest.apply(newMeasureBuilder().create(36.5d, 1))).isTrue();
assertThat(underTest.apply(newMeasureBuilder().setVariation(SOME_EMPTY_VARIATIONS).create(36.5d, 1))).isTrue();
assertThat(underTest.apply(newMeasureBuilder().create(36.6d, 1))).isFalse();
assertThat(underTest.test(newMeasureBuilder().create(36.5d, 1))).isTrue();
assertThat(underTest.test(newMeasureBuilder().setVariation(SOME_EMPTY_VARIATIONS).create(36.5d, 1))).isTrue();
assertThat(underTest.test(newMeasureBuilder().create(36.6d, 1))).isFalse();
}

@Test
public void apply_returns_false_for_String_measure() {
Predicate<Measure> underTest = BestValueOptimization.from(createMetric(Metric.MetricType.FLOAT, 36.5d), FILE_COMPONENT);

assertThat(underTest.apply(newMeasureBuilder().create("aaa"))).isFalse();
assertThat(underTest.test(newMeasureBuilder().create("aaa"))).isFalse();
}

@Test
public void apply_returns_false_for_LEVEL_measure() {
Predicate<Measure> underTest = BestValueOptimization.from(createMetric(Metric.MetricType.STRING, 36.5d), FILE_COMPONENT);

assertThat(underTest.apply(newMeasureBuilder().create(Measure.Level.OK))).isFalse();
assertThat(underTest.test(newMeasureBuilder().create(Measure.Level.OK))).isFalse();
}

private static MetricImpl createMetric(Metric.MetricType metricType, double bestValue) {

+ 11
- 21
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureRepositoryRule.java Datei anzeigen

@@ -19,23 +19,19 @@
*/
package org.sonar.server.computation.task.projectanalysis.measure;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.FluentIterable.from;
import static com.google.common.collect.Maps.filterKeys;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;

import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.SetMultimap;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import org.junit.rules.ExternalResource;
import org.sonar.server.computation.task.projectanalysis.component.Component;
import org.sonar.server.computation.task.projectanalysis.component.ComponentProvider;
@@ -48,11 +44,12 @@ import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolde
import org.sonar.server.computation.task.projectanalysis.metric.Metric;
import org.sonar.server.computation.task.projectanalysis.metric.MetricRepositoryRule;

import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.SetMultimap;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.FluentIterable.from;
import static com.google.common.collect.Maps.filterKeys;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;

/**
* An implementation of MeasureRepository as a JUnit rule which provides add methods for raw measures and extra add
@@ -192,13 +189,6 @@ public class MeasureRepositoryRule extends ExternalResource implements MeasureRe
return Optional.fromNullable(baseMeasures.get(new InternalKey(component, metric)));
}

@Override
public int loadAsRawMeasures(Collection<Component> components, Collection<Metric> metrics) {
this.loadedAsRawComponents = components;
this.loadedAsRawMetrics = metrics;
return 0;
}
public Collection<Component> getComponentsLoadedAsRaw() {
return loadedAsRawComponents;
}

+ 17
- 24
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/measure/MeasureToMeasureDtoTest.java Datei anzeigen

@@ -26,12 +26,11 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.sonar.db.measure.LiveMeasureDto;
import org.sonar.db.measure.MeasureDto;
import org.sonar.server.computation.task.projectanalysis.analysis.MutableAnalysisMetadataHolderRule;
import org.sonar.server.computation.task.projectanalysis.component.Component;
import org.sonar.server.computation.task.projectanalysis.component.Developer;
import org.sonar.server.computation.task.projectanalysis.component.DumbDeveloper;
import org.sonar.server.computation.task.projectanalysis.component.MutableDbIdsRepositoryRule;
import org.sonar.server.computation.task.projectanalysis.component.MutableTreeRootHolderRule;
import org.sonar.server.computation.task.projectanalysis.component.ReportComponent;
import org.sonar.server.computation.task.projectanalysis.metric.Metric;
import org.sonar.server.computation.task.projectanalysis.metric.MetricImpl;
@@ -41,7 +40,6 @@ import static org.assertj.core.api.Assertions.assertThat;
@RunWith(DataProviderRunner.class)
public class MeasureToMeasureDtoTest {
private static final MetricImpl SOME_METRIC = new MetricImpl(42, "metric_key", "metric_name", Metric.MetricType.STRING);
private static final int SOME_COMPONENT_ID = 951;
private static final String SOME_DATA = "some_data";
private static final String SOME_STRING = "some_string";
private static final double SOME_VARIATIONS = 1d;
@@ -55,16 +53,15 @@ public class MeasureToMeasureDtoTest {
private static final Component SOME_COMPONENT = ReportComponent.builder(Component.Type.PROJECT, 1).setUuid("uuid_1").build();

@Rule
public MutableDbIdsRepositoryRule dbIdsRepository = MutableDbIdsRepositoryRule.create(SOME_COMPONENT);
public MutableAnalysisMetadataHolderRule analysisMetadataHolder = new MutableAnalysisMetadataHolderRule();

@Rule
public MutableAnalysisMetadataHolderRule analysisMetadataHolder = new MutableAnalysisMetadataHolderRule();
public MutableTreeRootHolderRule treeRootHolder = new MutableTreeRootHolderRule();

MeasureToMeasureDto underTest = new MeasureToMeasureDto(dbIdsRepository, analysisMetadataHolder);
private MeasureToMeasureDto underTest = new MeasureToMeasureDto(analysisMetadataHolder, treeRootHolder);

@Before
public void setUp() throws Exception {
dbIdsRepository.setComponentId(SOME_COMPONENT, SOME_COMPONENT_ID);
analysisMetadataHolder.setUuid(ANALYSIS_UUID);
}

@@ -130,22 +127,6 @@ public class MeasureToMeasureDtoTest {
MeasureDto measureDto = underTest.toMeasureDto(measure, metric, SOME_COMPONENT);

assertThat(measureDto.getComponentUuid()).isEqualTo(SOME_COMPONENT.getUuid());
// assertThat(measureDto.getSnapshotId()).isEqualTo(SOME_SNAPSHOT_ID);
}

@Test
@UseDataProvider("all_types_Measures")
public void toMeasureDto_does_no_set_developerId_if_not_set_in_Measure(Measure measure, Metric metric) {
assertThat(underTest.toMeasureDto(measure, metric, SOME_COMPONENT).getDeveloperId()).isNull();
}

@Test
public void toMeasureDto_sets_developerId_if_set_in_Measure() {
Developer developer = new DumbDeveloper("DEV1");
dbIdsRepository.setDeveloperId(developer, 42);
Measure measure = Measure.newMeasureBuilder().forDeveloper(developer).createNoValue();

assertThat(underTest.toMeasureDto(measure, SOME_BOOLEAN_METRIC, SOME_COMPONENT).getDeveloperId()).isEqualTo(42);
}

@Test
@@ -200,4 +181,16 @@ public class MeasureToMeasureDtoTest {
assertThat(trueMeasureDto.getValue()).isNull();
assertThat(trueMeasureDto.getData()).isEqualTo(Measure.Level.OK.name());
}

@Test
public void toLiveMeasureDto() {
treeRootHolder.setRoot(SOME_COMPONENT);

LiveMeasureDto liveMeasureDto = underTest.toLiveMeasureDto(
Measure.newMeasureBuilder().create(Measure.Level.OK),
SOME_LEVEL_METRIC,
SOME_COMPONENT);

assertThat(liveMeasureDto.getTextValue()).isEqualTo(Measure.Level.OK.name());
}
}

+ 290
- 0
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistLiveMeasuresStepTest.java Datei anzeigen

@@ -0,0 +1,290 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.computation.task.projectanalysis.step;

import java.util.Optional;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.measures.Metric;
import org.sonar.api.utils.System2;
import org.sonar.db.DbClient;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.measure.LiveMeasureDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.server.computation.task.projectanalysis.analysis.MutableAnalysisMetadataHolderRule;
import org.sonar.server.computation.task.projectanalysis.analysis.Project;
import org.sonar.server.computation.task.projectanalysis.component.Component;
import org.sonar.server.computation.task.projectanalysis.component.ReportComponent;
import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolderRule;
import org.sonar.server.computation.task.projectanalysis.component.ViewsComponent;
import org.sonar.server.computation.task.projectanalysis.measure.MeasureRepositoryRule;
import org.sonar.server.computation.task.projectanalysis.measure.MeasureToMeasureDto;
import org.sonar.server.computation.task.projectanalysis.metric.MetricRepositoryRule;
import org.sonar.server.computation.task.step.ComputationStep;

import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.db.measure.MeasureTesting.newLiveMeasure;
import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.DIRECTORY;
import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.FILE;
import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.MODULE;
import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.PROJECT;
import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.PROJECT_VIEW;
import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.SUBVIEW;
import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.VIEW;
import static org.sonar.server.computation.task.projectanalysis.measure.Measure.newMeasureBuilder;

public class PersistLiveMeasuresStepTest extends BaseStepTest {

private static final Metric STRING_METRIC = new Metric.Builder("string-metric", "String metric", Metric.ValueType.STRING).create();
private static final Metric INT_METRIC = new Metric.Builder("int-metric", "int metric", Metric.ValueType.INT).create();
private static final Metric METRIC_WITH_BEST_VALUE = new Metric.Builder("best-value-metric", "best value metric", Metric.ValueType.INT)
.setBestValue(0.0)
.setOptimizedBestValue(true)
.create();

private static final int REF_1 = 1;
private static final int REF_2 = 2;
private static final int REF_3 = 3;
private static final int REF_4 = 4;

@Rule
public DbTester db = DbTester.create(System2.INSTANCE);
@Rule
public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
@Rule
public MetricRepositoryRule metricRepository = new MetricRepositoryRule();
@Rule
public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
@Rule
public MutableAnalysisMetadataHolderRule analysisMetadataHolder = new MutableAnalysisMetadataHolderRule();

private DbClient dbClient = db.getDbClient();

@Before
public void setUp() {
MetricDto stringMetricDto = db.measures().insertMetric(m -> m.setKey(STRING_METRIC.getKey()).setValueType(Metric.ValueType.STRING.name()));
MetricDto intMetricDto = db.measures().insertMetric(m -> m.setKey(INT_METRIC.getKey()).setValueType(Metric.ValueType.INT.name()));
MetricDto bestValueMMetricDto = db.measures().insertMetric(m -> m.setKey(METRIC_WITH_BEST_VALUE.getKey()).setValueType(Metric.ValueType.INT.name()).setOptimizedBestValue(true).setBestValue(0.0));
metricRepository.add(stringMetricDto.getId(), STRING_METRIC);
metricRepository.add(intMetricDto.getId(), INT_METRIC);
metricRepository.add(bestValueMMetricDto.getId(), METRIC_WITH_BEST_VALUE);
}

@Test
public void persist_live_measures_of_project_analysis() {
prepareProject();

// the computed measures
measureRepository.addRawMeasure(REF_1, STRING_METRIC.getKey(), newMeasureBuilder().create("project-value"));
measureRepository.addRawMeasure(REF_2, STRING_METRIC.getKey(), newMeasureBuilder().create("module-value"));
measureRepository.addRawMeasure(REF_3, STRING_METRIC.getKey(), newMeasureBuilder().create("dir-value"));
measureRepository.addRawMeasure(REF_4, STRING_METRIC.getKey(), newMeasureBuilder().create("file-value"));

step().execute();

// all measures are persisted, from project to file
assertThat(db.countRowsOfTable("live_measures")).isEqualTo(4);
assertThat(selectMeasure("project-uuid", STRING_METRIC).get().getDataAsString()).isEqualTo("project-value");
assertThat(selectMeasure("module-uuid", STRING_METRIC).get().getDataAsString()).isEqualTo("module-value");
assertThat(selectMeasure("dir-uuid", STRING_METRIC).get().getDataAsString()).isEqualTo("dir-value");
assertThat(selectMeasure("file-uuid", STRING_METRIC).get().getDataAsString()).isEqualTo("file-value");
}

@Test
public void measures_without_value_are_not_persisted() {
prepareProject();
measureRepository.addRawMeasure(REF_1, STRING_METRIC.getKey(), newMeasureBuilder().createNoValue());
measureRepository.addRawMeasure(REF_1, INT_METRIC.getKey(), newMeasureBuilder().createNoValue());

step().execute();

assertThatMeasureIsNotPersisted("project-uuid", STRING_METRIC);
assertThatMeasureIsNotPersisted("project-uuid", INT_METRIC);
}

@Test
public void measures_on_leak_period_are_persisted() {
prepareProject();
measureRepository.addRawMeasure(REF_1, INT_METRIC.getKey(), newMeasureBuilder().setVariation(42.0).createNoValue());

step().execute();

LiveMeasureDto persistedMeasure = selectMeasure("project-uuid", INT_METRIC).get();
assertThat(persistedMeasure.getValue()).isNull();
assertThat(persistedMeasure.getVariation()).isEqualTo(42.0);
}

@Test
public void delete_measures_from_db_if_no_more_computed() {
prepareProject();
// measure to be updated
LiveMeasureDto measureOnFileInProject = insertMeasure("file-uuid", "project-uuid", INT_METRIC);
// measure to be deleted because on a file that has been deleted
LiveMeasureDto measureOnDeletedFileInProject = insertMeasure("deleted-file-in-project", "project-uuid", INT_METRIC);
// measure to be deleted because not computed anymore
LiveMeasureDto otherMeasureOnFileInProject = insertMeasure("file-uuid", "project-uuid", STRING_METRIC);
// measure in another project, not touched
LiveMeasureDto measureInOtherProject = insertMeasure("other-file-uuid", "other-project-uuid", INT_METRIC);
db.commit();

measureRepository.addRawMeasure(REF_4, INT_METRIC.getKey(), newMeasureBuilder().create(42));

step().execute();

assertThatMeasureHasValue(measureOnFileInProject, 42);
assertThatMeasureDoesNotExist(measureOnDeletedFileInProject);
assertThatMeasureDoesNotExist(otherMeasureOnFileInProject);
assertThatMeasureHasValue(measureInOtherProject, (int)measureInOtherProject.getValue().doubleValue());
}

@Test
public void do_not_persist_file_measures_with_best_value() {
prepareProject();
// measure to be deleted because new value matches the metric best value
LiveMeasureDto oldMeasure = insertMeasure("file-uuid", "project-uuid", INT_METRIC);
db.commit();

// project measure with metric best value -> persist with value 0
measureRepository.addRawMeasure(REF_1, METRIC_WITH_BEST_VALUE.getKey(), newMeasureBuilder().create(0));
// file measure with metric best value -> do not persist
measureRepository.addRawMeasure(REF_4, METRIC_WITH_BEST_VALUE.getKey(), newMeasureBuilder().create(0));

step().execute();

assertThatMeasureDoesNotExist(oldMeasure);
assertThatMeasureHasValue("project-uuid", METRIC_WITH_BEST_VALUE, 0);
}

@Test
public void persist_live_measures_of_portfolio_analysis() {
preparePortfolio();

// the computed measures
measureRepository.addRawMeasure(REF_1, STRING_METRIC.getKey(), newMeasureBuilder().create("view-value"));
measureRepository.addRawMeasure(REF_2, STRING_METRIC.getKey(), newMeasureBuilder().create("subview-value"));
measureRepository.addRawMeasure(REF_3, STRING_METRIC.getKey(), newMeasureBuilder().create("project-value"));

step().execute();

assertThat(db.countRowsOfTable("live_measures")).isEqualTo(3);
assertThat(selectMeasure("view-uuid", STRING_METRIC).get().getDataAsString()).isEqualTo("view-value");
assertThat(selectMeasure("subview-uuid", STRING_METRIC).get().getDataAsString()).isEqualTo("subview-value");
assertThat(selectMeasure("project-uuid", STRING_METRIC).get().getDataAsString()).isEqualTo("project-value");
}

private LiveMeasureDto insertMeasure(String componentUuid, String projectUuid, Metric metric) {
LiveMeasureDto measure = newLiveMeasure()
.setComponentUuid(componentUuid)
.setProjectUuid(projectUuid)
.setMetricId(metricRepository.getByKey(metric.getKey()).getId());
dbClient.liveMeasureDao().insertOrUpdate(db.getSession(), measure, null);
return measure;
}

private void assertThatMeasureHasValue(LiveMeasureDto template, int expectedValue) {
Optional<LiveMeasureDto> persisted = dbClient.liveMeasureDao().selectMeasure(db.getSession(),
template.getComponentUuid(), metricRepository.getById(template.getMetricId()).getKey());
assertThat(persisted).isPresent();
assertThat(persisted.get().getValue()).isEqualTo((double) expectedValue);
}

private void assertThatMeasureHasValue(String componentUuid, Metric metric, int expectedValue) {
Optional<LiveMeasureDto> persisted = dbClient.liveMeasureDao().selectMeasure(db.getSession(),
componentUuid, metric.getKey());
assertThat(persisted).isPresent();
assertThat(persisted.get().getValue()).isEqualTo((double) expectedValue);
}

private void assertThatMeasureDoesNotExist(LiveMeasureDto template) {
assertThat(dbClient.liveMeasureDao().selectMeasure(db.getSession(),
template.getComponentUuid(), metricRepository.getById(template.getMetricId()).getKey()))
.isEmpty();
}

private void prepareProject() {
// tree of components as defined by scanner report
Component project = ReportComponent.builder(PROJECT, REF_1).setUuid("project-uuid")
.addChildren(
ReportComponent.builder(MODULE, REF_2).setUuid("module-uuid")
.addChildren(
ReportComponent.builder(DIRECTORY, REF_3).setUuid("dir-uuid")
.addChildren(
ReportComponent.builder(FILE, REF_4).setUuid("file-uuid")
.build())
.build())
.build())
.build();
treeRootHolder.setRoot(project);
analysisMetadataHolder.setProject(Project.copyOf(project));

// components as persisted in db
ComponentDto projectDto = insertComponent("project-key", "project-uuid");
ComponentDto moduleDto = insertComponent("module-key", "module-uuid");
ComponentDto dirDto = insertComponent("dir-key", "dir-uuid");
ComponentDto fileDto = insertComponent("file-key", "file-uuid");
}

private void preparePortfolio() {
// tree of components
Component portfolio = ViewsComponent.builder(VIEW, REF_1).setUuid("view-uuid")
.addChildren(
ViewsComponent.builder(SUBVIEW, REF_2).setUuid("subview-uuid")
.addChildren(
ViewsComponent.builder(PROJECT_VIEW, REF_3).setUuid("project-uuid")
.build())
.build())
.build();
treeRootHolder.setRoot(portfolio);

// components as persisted in db
ComponentDto portfolioDto = insertComponent("view-key", "view-uuid");
ComponentDto subViewDto = insertComponent("subview-key", "subview-uuid");
ComponentDto projectDto = insertComponent("project-key", "project-uuid");
analysisMetadataHolder.setProject(Project.copyOf(portfolioDto));
}

private void assertThatMeasureIsNotPersisted(String componentUuid, Metric metric) {
assertThat(selectMeasure(componentUuid, metric)).isEmpty();
}

private Optional<LiveMeasureDto> selectMeasure(String componentUuid, Metric metric) {
return dbClient.liveMeasureDao().selectMeasure(db.getSession(), componentUuid, metric.getKey());
}

private ComponentDto insertComponent(String key, String uuid) {
ComponentDto componentDto = new ComponentDto()
.setOrganizationUuid("org1")
.setDbKey(key)
.setUuid(uuid)
.setUuidPath(uuid + ".")
.setRootUuid(uuid)
.setProjectUuid(uuid);
dbClient.componentDao().insert(db.getSession(), componentDto);
return componentDto;
}

@Override
protected ComputationStep step() {
return new PersistLiveMeasuresStep(dbClient, metricRepository, new MeasureToMeasureDto(analysisMetadataHolder, treeRootHolder), treeRootHolder, measureRepository);
}

}

+ 122
- 287
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistMeasuresStepTest.java Datei anzeigen

@@ -19,8 +19,7 @@
*/
package org.sonar.server.computation.task.projectanalysis.step;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -29,12 +28,10 @@ import org.sonar.api.utils.System2;
import org.sonar.db.DbClient;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.rule.RuleDto;
import org.sonar.db.measure.MeasureDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.server.computation.task.projectanalysis.analysis.MutableAnalysisMetadataHolderRule;
import org.sonar.server.computation.task.projectanalysis.component.Component;
import org.sonar.server.computation.task.projectanalysis.component.Developer;
import org.sonar.server.computation.task.projectanalysis.component.DumbDeveloper;
import org.sonar.server.computation.task.projectanalysis.component.MutableDbIdsRepositoryRule;
import org.sonar.server.computation.task.projectanalysis.component.ReportComponent;
import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolderRule;
import org.sonar.server.computation.task.projectanalysis.component.ViewsComponent;
@@ -44,12 +41,6 @@ import org.sonar.server.computation.task.projectanalysis.metric.MetricRepository
import org.sonar.server.computation.task.step.ComputationStep;

import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.api.measures.CoreMetrics.CLASS_COMPLEXITY_DISTRIBUTION;
import static org.sonar.api.measures.CoreMetrics.CLASS_COMPLEXITY_DISTRIBUTION_KEY;
import static org.sonar.api.measures.CoreMetrics.FILE_COMPLEXITY_DISTRIBUTION;
import static org.sonar.api.measures.CoreMetrics.FILE_COMPLEXITY_DISTRIBUTION_KEY;
import static org.sonar.api.measures.CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION;
import static org.sonar.api.measures.CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION_KEY;
import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.DIRECTORY;
import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.FILE;
import static org.sonar.server.computation.task.projectanalysis.component.Component.Type.MODULE;
@@ -61,325 +52,180 @@ import static org.sonar.server.computation.task.projectanalysis.measure.Measure.

public class PersistMeasuresStepTest extends BaseStepTest {

private static final String STRING_METRIC_KEY = "string-metric-key";
private static final String DOUBLE_METRIC_KEY = "double-metric-key";
private static final String INT_METRIC_KEY = "int-metric-key";
private static final String LONG_METRIC_KEY = "long-metric-key";
private static final String OPTIMIZED_METRIC_KEY = "optimized-metric-key";

private static final Metric STRING_METRIC = new Metric.Builder(STRING_METRIC_KEY, "String metric", Metric.ValueType.STRING).create();
private static final Metric DOUBLE_METRIC = new Metric.Builder(DOUBLE_METRIC_KEY, "Double metric", Metric.ValueType.FLOAT).create();
private static final Metric INT_METRIC = new Metric.Builder(INT_METRIC_KEY, "int metric", Metric.ValueType.INT).create();
private static final Metric LONG_METRIC = new Metric.Builder(LONG_METRIC_KEY, "long metric", Metric.ValueType.WORK_DUR).create();

private static final int ROOT_REF = 1;
private static final int INTERMEDIATE_1_REF = 2;
private static final int INTERMEDIATE_2_REF = 3;
private static final int LEAF_REF = 4;
private static final Metric STRING_METRIC = new Metric.Builder("string-metric", "String metric", Metric.ValueType.STRING).create();
private static final Metric INT_METRIC = new Metric.Builder("int-metric", "int metric", Metric.ValueType.INT).create();

private static final String ANALYSIS_UUID = "a1";

private static final int REF_1 = 1;
private static final int REF_2 = 2;
private static final int REF_3 = 3;
private static final int REF_4 = 4;

@Rule
public DbTester dbTester = DbTester.create(System2.INSTANCE);
public DbTester db = DbTester.create(System2.INSTANCE);
@Rule
public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
@Rule
public MetricRepositoryRule metricRepository = new MetricRepositoryRule();
@Rule
public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
@Rule
public MutableDbIdsRepositoryRule dbIdsRepository = MutableDbIdsRepositoryRule.create(treeRootHolder);

@Rule
public MutableAnalysisMetadataHolderRule analysisMetadataHolder = new MutableAnalysisMetadataHolderRule();

DbClient dbClient = dbTester.getDbClient();
RuleDto rule;
ComponentDto rootDto;
ComponentDto intermediate1Dto;
ComponentDto intermediate2Dto;
ComponentDto leafDto;

PersistMeasuresStep underTest;
private DbClient dbClient = db.getDbClient();

@Before
public void setUp() {
underTest = new PersistMeasuresStep(dbClient, metricRepository, new MeasureToMeasureDto(dbIdsRepository, analysisMetadataHolder), treeRootHolder, measureRepository);
analysisMetadataHolder.setUuid(ANALYSIS_UUID);
}

private void setupReportComponents() {
Component project = ReportComponent.builder(PROJECT, ROOT_REF).setUuid("root-uuid")
.addChildren(
ReportComponent.builder(MODULE, INTERMEDIATE_1_REF).setUuid("intermediate1-uuid")
.addChildren(
ReportComponent.builder(DIRECTORY, INTERMEDIATE_2_REF).setUuid("intermediate2-uuid")
.addChildren(
ReportComponent.builder(FILE, LEAF_REF).setUuid("leaf-uuid")
.build())
.build())
.build())
.build();
treeRootHolder.setRoot(project);

setupDbIds();
}

private void setupViewsComponents() {
Component view = ViewsComponent.builder(VIEW, ROOT_REF).setUuid("root-uuid")
.addChildren(
ViewsComponent.builder(SUBVIEW, INTERMEDIATE_1_REF).setUuid("intermediate1-uuid")
.addChildren(
ViewsComponent.builder(SUBVIEW, INTERMEDIATE_2_REF).setUuid("intermediate2-uuid")
.addChildren(
ViewsComponent.builder(PROJECT_VIEW, LEAF_REF).setUuid("leaf-uuid")
.build())
.build())
.build())
.build();
treeRootHolder.setRoot(view);

setupDbIds();
}

private void setupDbIds() {
rootDto = addComponent("root-key", "root-uuid");
intermediate1Dto = addComponent("intermediate1-key", "intermediate1-uuid");
intermediate2Dto = addComponent("intermediate2-key", "intermediate2-uuid");
leafDto = addComponent("leaf-key", "leaf-uuid");

setDbIds(ROOT_REF, rootDto.getId());
setDbIds(INTERMEDIATE_1_REF, intermediate1Dto.getId());
setDbIds(INTERMEDIATE_2_REF, intermediate2Dto.getId());
setDbIds(LEAF_REF, leafDto.getId());
}

private void setDbIds(int componentRef, Long dbId) {
dbIdsRepository.setComponentId(componentRef, dbId);
MetricDto stringMetricDto = db.measures().insertMetric(m -> m.setKey(STRING_METRIC.getKey()).setValueType(Metric.ValueType.STRING.name()));
MetricDto intMetricDto = db.measures().insertMetric(m -> m.setKey(INT_METRIC.getKey()).setValueType(Metric.ValueType.INT.name()));
metricRepository.add(stringMetricDto.getId(), STRING_METRIC);
metricRepository.add(intMetricDto.getId(), INT_METRIC);
}

@Test
public void insert_measures_from_report() {
setupReportComponents();
public void persist_measures_of_project_analysis() {
prepareProject();

insertMeasures();
}
// the computed measures
measureRepository.addRawMeasure(REF_1, STRING_METRIC.getKey(), newMeasureBuilder().create("project-value"));
measureRepository.addRawMeasure(REF_2, STRING_METRIC.getKey(), newMeasureBuilder().create("module-value"));
measureRepository.addRawMeasure(REF_3, STRING_METRIC.getKey(), newMeasureBuilder().create("dir-value"));
measureRepository.addRawMeasure(REF_4, STRING_METRIC.getKey(), newMeasureBuilder().create("file-value"));

@Test
public void insert_measures_from_views() {
setupViewsComponents();

insertMeasures();
}

private void insertMeasures() {
int stringMetricId = 1;
int doubleMetricId = 2;
int intMetricId = 3;
int longMetricId = 4;
metricRepository.add(stringMetricId, STRING_METRIC);
metricRepository.add(doubleMetricId, DOUBLE_METRIC);
metricRepository.add(intMetricId, INT_METRIC);
metricRepository.add(longMetricId, LONG_METRIC);

measureRepository.addRawMeasure(ROOT_REF, STRING_METRIC_KEY, newMeasureBuilder().create("measure-data"));
measureRepository.addRawMeasure(INTERMEDIATE_1_REF, INT_METRIC_KEY, newMeasureBuilder().create(12));
measureRepository.addRawMeasure(INTERMEDIATE_2_REF, LONG_METRIC_KEY, newMeasureBuilder().create(9635L));
measureRepository.addRawMeasure(LEAF_REF, DOUBLE_METRIC_KEY, newMeasureBuilder().create(123.123d, 1));

underTest.execute();

assertThat(dbTester.countRowsOfTable("project_measures")).isEqualTo(4);

List<Map<String, Object>> dtos = selectSnapshots();

Map<String, Object> dto = dtos.get(0);
assertThat(dto.get("analysisUuid")).isEqualTo(ANALYSIS_UUID);
assertThat(dto.get("componentUuid")).isEqualTo(rootDto.uuid());
assertThat(dto.get("metricId")).isEqualTo((long) stringMetricId);
assertThat(dto.get("value")).isNull();
assertThat(dto.get("textValue")).isEqualTo("measure-data");
assertThat(dto.get("severity")).isNull();

dto = dtos.get(1);
assertThat(dto.get("analysisUuid")).isEqualTo(ANALYSIS_UUID);
assertThat(dto.get("componentUuid")).isEqualTo(intermediate1Dto.uuid());
assertThat(dto.get("metricId")).isEqualTo((long) intMetricId);
assertValue(dto, 12d);
assertThat(dto.get("textValue")).isNull();
assertThat(dto.get("severity")).isNull();

dto = dtos.get(2);
assertThat(dto.get("analysisUuid")).isEqualTo(ANALYSIS_UUID);
assertThat(dto.get("componentUuid")).isEqualTo(intermediate2Dto.uuid());
assertThat(dto.get("metricId")).isEqualTo((long) longMetricId);
assertValue(dto, 9635d);
assertThat(dto.get("textValue")).isNull();
assertThat(dto.get("severity")).isNull();

dto = dtos.get(3);
assertThat(dto.get("analysisUuid")).isEqualTo(ANALYSIS_UUID);
assertThat(dto.get("componentUuid")).isEqualTo(leafDto.uuid());
assertThat(dto.get("metricId")).isEqualTo((long) doubleMetricId);
assertValue(dto, 123.1d);
assertThat(dto.get("textValue")).isNull();
assertThat(dto.get("severity")).isNull();
}
execute(true);

/**
* Horrible trick to support oracle retuning number as BigDecimal and DbTester#select converting BigDecimal with no
* scale to Long instead of Double when all other DBs will return a Double anyway.
*/
private static void assertValue(Map<String, Object> dto, double expected) {
Object actual = dto.get("value");
if (expected % 1 == 0d && actual instanceof Long) {
assertThat(actual).isEqualTo((long) expected);
} else {
assertThat(actual).isEqualTo(expected);
}
// project, module and dir measures are persisted, but not file measures
assertThat(db.countRowsOfTable("project_measures")).isEqualTo(3);
assertThat(selectMeasure("project-uuid", STRING_METRIC).get().getData()).isEqualTo("project-value");
assertThat(selectMeasure("module-uuid", STRING_METRIC).get().getData()).isEqualTo("module-value");
assertThat(selectMeasure("dir-uuid", STRING_METRIC).get().getData()).isEqualTo("dir-value");
assertThatMeasuresAreNotPersisted("file-uuid");
}

@Test
public void insert_measure_with_variations_from_report() {
setupReportComponents();

insertMeasureWithVariations();
}

@Test
public void insert_measure_with_variations_from_views() {
setupViewsComponents();

insertMeasureWithVariations();
}

private void insertMeasureWithVariations() {
metricRepository.add(1, DOUBLE_METRIC);
public void persist_measures_of_project_analysis_excluding_directories() {
prepareProject();

measureRepository.addRawMeasure(ROOT_REF, DOUBLE_METRIC_KEY,
newMeasureBuilder()
.setVariation(1.1d)
.create(10d, 1));
// the computed measures
measureRepository.addRawMeasure(REF_1, STRING_METRIC.getKey(), newMeasureBuilder().create("project-value"));
measureRepository.addRawMeasure(REF_2, STRING_METRIC.getKey(), newMeasureBuilder().create("module-value"));
measureRepository.addRawMeasure(REF_3, STRING_METRIC.getKey(), newMeasureBuilder().create("dir-value"));
measureRepository.addRawMeasure(REF_4, STRING_METRIC.getKey(), newMeasureBuilder().create("file-value"));

underTest.execute();
execute(false);

assertThat(dbTester.countRowsOfTable("project_measures")).isEqualTo(1);
List<Map<String, Object>> dtos = selectSnapshots();
Map<String, Object> dto = dtos.get(0);
assertThat(dto.get("variation_value")).isEqualTo(1.1d);
// project, module and dir measures are persisted, but not file measures
assertThat(db.countRowsOfTable("project_measures")).isEqualTo(2);
assertThat(selectMeasure("project-uuid", STRING_METRIC).get().getData()).isEqualTo("project-value");
assertThat(selectMeasure("module-uuid", STRING_METRIC).get().getData()).isEqualTo("module-value");
assertThatMeasuresAreNotPersisted("dir-uuid");
assertThatMeasuresAreNotPersisted("file-uuid");
}

@Test
public void bestValue_measure_of_bestValueOptimized_metrics_are_not_persisted() {
setupReportComponents();
metricRepository.add(1, new Metric.Builder(OPTIMIZED_METRIC_KEY, "Optimized metric", Metric.ValueType.BOOL).setOptimizedBestValue(true).setBestValue(1d).create());
public void measures_without_value_are_not_persisted() {
prepareProject();
measureRepository.addRawMeasure(REF_1, STRING_METRIC.getKey(), newMeasureBuilder().createNoValue());
measureRepository.addRawMeasure(REF_1, INT_METRIC.getKey(), newMeasureBuilder().createNoValue());

measureRepository.addRawMeasure(LEAF_REF, OPTIMIZED_METRIC_KEY, newMeasureBuilder().create(true));
execute(false);

underTest.execute();

assertThat(selectSnapshots()).isEmpty();
assertThatMeasureIsNotPersisted("project-uuid", STRING_METRIC);
assertThatMeasureIsNotPersisted("project-uuid", INT_METRIC);
}

@Test
public void empty_values_are_not_persisted() {
setupReportComponents();

metricRepository.add(1, STRING_METRIC);
metricRepository.add(2, DOUBLE_METRIC);

measureRepository.addRawMeasure(LEAF_REF, STRING_METRIC_KEY, newMeasureBuilder().createNoValue());
measureRepository.addRawMeasure(LEAF_REF, DOUBLE_METRIC_KEY, newMeasureBuilder().createNoValue());
public void measures_on_leak_period_are_persisted() {
prepareProject();
measureRepository.addRawMeasure(REF_1, INT_METRIC.getKey(), newMeasureBuilder().setVariation(42.0).createNoValue());

underTest.execute();
execute(false);

assertThat(selectSnapshots()).isEmpty();
MeasureDto persistedMeasure = selectMeasure("project-uuid", INT_METRIC).get();
assertThat(persistedMeasure.getValue()).isNull();
assertThat(persistedMeasure.getVariation()).isEqualTo(42.0);
}

@Test
public void do_not_insert_file_complexity_distribution_metric_on_files() {
setupReportComponents();
public void persist_all_measures_of_portfolio_analysis() {
preparePortfolio();

metricRepository.add(1, FILE_COMPLEXITY_DISTRIBUTION);
// the computed measures
measureRepository.addRawMeasure(REF_1, STRING_METRIC.getKey(), newMeasureBuilder().create("view-value"));
measureRepository.addRawMeasure(REF_2, STRING_METRIC.getKey(), newMeasureBuilder().create("subview-value"));
measureRepository.addRawMeasure(REF_3, STRING_METRIC.getKey(), newMeasureBuilder().create("project-value"));

measureRepository.addRawMeasure(ROOT_REF, FILE_COMPLEXITY_DISTRIBUTION_KEY, newMeasureBuilder().create("0=1;2=10"));
measureRepository.addRawMeasure(LEAF_REF, FILE_COMPLEXITY_DISTRIBUTION_KEY, newMeasureBuilder().create("0=1;2=10"));
execute(true);

underTest.execute();

assertThat(dbTester.countRowsOfTable("project_measures")).isEqualTo(1);

List<Map<String, Object>> dtos = selectSnapshots();

Map<String, Object> dto = dtos.get(0);
assertThat(dto.get("analysisUuid")).isEqualTo(ANALYSIS_UUID);
assertThat(dto.get("componentUuid")).isEqualTo(rootDto.uuid());
assertThat(dto.get("textValue")).isEqualTo("0=1;2=10");
assertThat(db.countRowsOfTable("project_measures")).isEqualTo(3);
assertThat(selectMeasure("view-uuid", STRING_METRIC).get().getData()).isEqualTo("view-value");
assertThat(selectMeasure("subview-uuid", STRING_METRIC).get().getData()).isEqualTo("subview-value");
assertThat(selectMeasure("project-uuid", STRING_METRIC).get().getData()).isEqualTo("project-value");
}

@Test
public void do_not_insert_function_complexity_distribution_metric_on_files() {
setupReportComponents();
metricRepository.add(1, FUNCTION_COMPLEXITY_DISTRIBUTION);
measureRepository.addRawMeasure(ROOT_REF, FUNCTION_COMPLEXITY_DISTRIBUTION_KEY, newMeasureBuilder().create("0=1;2=10"));
measureRepository.addRawMeasure(LEAF_REF, FUNCTION_COMPLEXITY_DISTRIBUTION_KEY, newMeasureBuilder().create("0=1;2=10"));
underTest.execute();
assertThat(dbTester.countRowsOfTable("project_measures")).isEqualTo(1);
List<Map<String, Object>> dtos = selectSnapshots();
private void prepareProject() {
// tree of components as defined by scanner report
Component project = ReportComponent.builder(PROJECT, REF_1).setUuid("project-uuid")
.addChildren(
ReportComponent.builder(MODULE, REF_2).setUuid("module-uuid")
.addChildren(
ReportComponent.builder(DIRECTORY, REF_3).setUuid("dir-uuid")
.addChildren(
ReportComponent.builder(FILE, REF_4).setUuid("file-uuid")
.build())
.build())
.build())
.build();
treeRootHolder.setRoot(project);

Map<String, Object> dto = dtos.get(0);
assertThat(dto.get("analysisUuid")).isEqualTo(ANALYSIS_UUID);
assertThat(dto.get("componentUuid")).isEqualTo(rootDto.uuid());
assertThat(dto.get("textValue")).isEqualTo("0=1;2=10");
// components as persisted in db
ComponentDto projectDto = insertComponent("project-key", "project-uuid");
ComponentDto moduleDto = insertComponent("module-key", "module-uuid");
ComponentDto dirDto = insertComponent("dir-key", "dir-uuid");
ComponentDto fileDto = insertComponent("file-key", "file-uuid");
db.components().insertSnapshot(projectDto, s -> s.setUuid(ANALYSIS_UUID));
}

@Test
public void do_not_insert_class_complexity_distribution_metric_on_files() {
setupReportComponents();

metricRepository.add(1, CLASS_COMPLEXITY_DISTRIBUTION);

measureRepository.addRawMeasure(ROOT_REF, CLASS_COMPLEXITY_DISTRIBUTION_KEY, newMeasureBuilder().create("0=1;2=10"));
measureRepository.addRawMeasure(LEAF_REF, CLASS_COMPLEXITY_DISTRIBUTION_KEY, newMeasureBuilder().create("0=1;2=10"));

underTest.execute();

assertThat(dbTester.countRowsOfTable("project_measures")).isEqualTo(1);

List<Map<String, Object>> dtos = selectSnapshots();
private void preparePortfolio() {
// tree of components
Component portfolio = ViewsComponent.builder(VIEW, REF_1).setUuid("view-uuid")
.addChildren(
ViewsComponent.builder(SUBVIEW, REF_2).setUuid("subview-uuid")
.addChildren(
ViewsComponent.builder(PROJECT_VIEW, REF_3).setUuid("project-uuid")
.build())
.build())
.build();
treeRootHolder.setRoot(portfolio);

Map<String, Object> dto = dtos.get(0);
assertThat(dto.get("analysisUuid")).isEqualTo(ANALYSIS_UUID);
assertThat(dto.get("componentUuid")).isEqualTo(rootDto.uuid());
assertThat(dto.get("textValue")).isEqualTo("0=1;2=10");
// components as persisted in db
ComponentDto viewDto = insertComponent("view-key", "view-uuid");
ComponentDto subViewDto = insertComponent("subview-key", "subview-uuid");
ComponentDto projectDto = insertComponent("project-key", "project-uuid");
db.components().insertSnapshot(viewDto, s -> s.setUuid(ANALYSIS_UUID));
}

@Test
public void insert_developer_measure_from_report() {
setupReportComponents();

metricRepository.add(1, INT_METRIC);

Developer developer = new DumbDeveloper("DEV1");
dbIdsRepository.setDeveloperId(developer, 10);
measureRepository.addRawMeasure(ROOT_REF, INT_METRIC_KEY, newMeasureBuilder().forDeveloper(developer).create(1));
private void assertThatMeasureIsNotPersisted(String componentUuid, Metric metric) {
assertThat(selectMeasure(componentUuid, metric)).isEmpty();
}

underTest.execute();
private void assertThatMeasuresAreNotPersisted(String componentUuid) {
assertThatMeasureIsNotPersisted(componentUuid, STRING_METRIC);
assertThatMeasureIsNotPersisted(componentUuid, INT_METRIC);
}

assertThat(dbTester.countRowsOfTable("project_measures")).isEqualTo(1);
List<Map<String, Object>> dtos = selectSnapshots();
Map<String, Object> dto = dtos.get(0);
private void execute(boolean persistDirectories) {
new PersistMeasuresStep(dbClient, metricRepository, new MeasureToMeasureDto(analysisMetadataHolder, treeRootHolder), treeRootHolder, measureRepository, persistDirectories)
.execute();
}

assertValue(dto, 1d);
assertThat(dto.get("developerId")).isEqualTo(10L);
private Optional<MeasureDto> selectMeasure(String componentUuid, Metric metric) {
return dbClient.measureDao().selectMeasure(db.getSession(), ANALYSIS_UUID, componentUuid, metric.getKey());
}

private ComponentDto addComponent(String key, String uuid) {
private ComponentDto insertComponent(String key, String uuid) {
ComponentDto componentDto = new ComponentDto()
.setOrganizationUuid("org1")
.setDbKey(key)
@@ -387,23 +233,12 @@ public class PersistMeasuresStepTest extends BaseStepTest {
.setUuidPath(uuid + ".")
.setRootUuid(uuid)
.setProjectUuid(uuid);
dbClient.componentDao().insert(dbTester.getSession(), componentDto);
dbClient.componentDao().insert(db.getSession(), componentDto);
return componentDto;
}

private List<Map<String, Object>> selectSnapshots() {
return dbTester
.select(
"SELECT analysis_uuid as \"analysisUuid\", component_uuid as \"componentUuid\", metric_id as \"metricId\", person_id as \"developerId\", "
+
"value as \"value\", text_value as \"textValue\", " +
"variation_value_1 as \"variation_value\"" +
"FROM project_measures " +
"ORDER by id asc");
}

@Override
protected ComputationStep step() {
return underTest;
return new PersistMeasuresStep(dbClient, metricRepository, new MeasureToMeasureDto(analysisMetadataHolder, treeRootHolder), treeRootHolder, measureRepository, true);
}
}

+ 2
- 5
server/sonar-server/src/test/java/org/sonar/server/duplication/ws/ShowActionTest.java Datei anzeigen

@@ -29,7 +29,6 @@ import org.sonar.api.server.ws.WebService;
import org.sonar.api.web.UserRole;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.server.component.TestComponentFinder;
@@ -113,9 +112,8 @@ public class ShowActionTest {
ComponentDto project = db.components().insertMainBranch();
userSessionRule.addProjectPermission(UserRole.CODEVIEWER, project);
ComponentDto branch = db.components().insertProjectBranch(project);
SnapshotDto analysis = db.components().insertSnapshot(newAnalysis(branch));
ComponentDto file = db.components().insertComponent(newFileDto(branch));
db.measures().insertMeasure(file, analysis, dataMetric, m -> m.setData(format("<duplications>\n" +
db.measures().insertLiveMeasure(file, dataMetric, m -> m.setData(format("<duplications>\n" +
" <g>\n" +
" <b s=\"31\" l=\"5\" r=\"%s\"/>\n" +
" <b s=\"20\" l=\"5\" r=\"%s\"/>\n" +
@@ -224,14 +222,13 @@ public class ShowActionTest {
ComponentDto project = db.components().insertPrivateProject();
userSessionRule.addProjectPermission(UserRole.CODEVIEWER, project);
ComponentDto file = db.components().insertComponent(newFileDto(project).setDbKey("foo.js"));
SnapshotDto snapshot = db.components().insertSnapshot(newAnalysis(project));
String xml = "<duplications>\n" +
" <g>\n" +
" <b s=\"31\" l=\"5\" r=\"foo.js\"/>\n" +
" <b s=\"20\" l=\"5\" r=\"foo.js\"/>\n" +
" </g>\n" +
"</duplications>\n";
db.measures().insertMeasure(file, snapshot, dataMetric, m -> m.setData(xml));
db.measures().insertLiveMeasure(file, dataMetric, m -> m.setData(xml));

TestRequest request = requestFactory.apply(file);
TestResponse result = request.execute();

+ 103
- 96
server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentActionTest.java Datei anzeigen

@@ -29,7 +29,7 @@ import org.sonar.api.web.UserRole;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.measure.MeasureDto;
import org.sonar.db.measure.LiveMeasureDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.server.component.TestComponentFinder;
@@ -52,7 +52,7 @@ import static org.sonar.api.utils.DateUtils.parseDateTime;
import static org.sonar.api.web.UserRole.USER;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.component.ComponentTesting.newProjectCopy;
import static org.sonar.db.component.SnapshotTesting.newAnalysis;
import static org.sonar.server.computation.task.projectanalysis.metric.Metric.MetricType.INT;
import static org.sonar.test.JsonAssert.assertJson;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.DEPRECATED_PARAM_COMPONENT_ID;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_ADDITIONAL_FIELDS;
@@ -92,10 +92,10 @@ public class ComponentActionTest {
@Test
public void provided_project() {
ComponentDto project = db.components().insertPrivateProject();
logAsUser(project);
insertNclocMetric();
userSession.addProjectPermission(UserRole.USER, project);
MetricDto metric = db.measures().insertMetric(m -> m.setValueType(INT.name()));

ComponentWsResponse response = newRequest(project.getKey(), "ncloc");
ComponentWsResponse response = newRequest(project.getKey(), metric.getKey());

assertThat(response.getMetrics().getMetricsCount()).isEqualTo(1);
assertThat(response.getPeriods().getPeriodsCount()).isEqualTo(0);
@@ -105,13 +105,13 @@ public class ComponentActionTest {
@Test
public void without_additional_fields() {
ComponentDto project = db.components().insertPrivateProject();
logAsUser(project);
userSession.addProjectPermission(UserRole.USER, project);
db.components().insertSnapshot(project);
insertNclocMetric();
MetricDto metric = db.measures().insertMetric(m -> m.setValueType(INT.name()));

String response = ws.newRequest()
.setParam(PARAM_COMPONENT, project.getKey())
.setParam(PARAM_METRIC_KEYS, "ncloc")
.setParam(PARAM_METRIC_KEYS, metric.getKey())
.execute().getInput();

assertThat(response)
@@ -122,12 +122,12 @@ public class ComponentActionTest {
@Test
public void branch() {
ComponentDto project = db.components().insertPrivateProject();
logAsUser(project);
userSession.addProjectPermission(UserRole.USER, project);
ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("my_branch"));
SnapshotDto analysis = db.components().insertSnapshot(branch);
ComponentDto file = db.components().insertComponent(newFileDto(branch));
MetricDto complexity = insertComplexityMetric();
MeasureDto measure = db.measures().insertMeasure(file, analysis, complexity, m -> m.setValue(12.0d).setVariation(2.0d));
MetricDto complexity = db.measures().insertMetric(m1 -> m1.setKey("complexity").setValueType(INT.name()));
LiveMeasureDto measure = db.measures().insertLiveMeasure(file, complexity, m -> m.setValue(12.0d).setVariation(2.0d));

ComponentWsResponse response = ws.newRequest()
.setParam(PARAM_COMPONENT, file.getKey())
@@ -149,9 +149,9 @@ public class ComponentActionTest {
ComponentDto view = db.components().insertView();
db.components().insertSnapshot(view);
ComponentDto projectCopy = db.components().insertComponent(newProjectCopy("project-uuid-copy", project, view));
insertNclocMetric();
MetricDto metric = db.measures().insertMetric(m -> m.setValueType(INT.name()));

ComponentWsResponse response = newRequest(projectCopy.getKey(), "ncloc");
ComponentWsResponse response = newRequest(projectCopy.getKey(), metric.getKey());

assertThat(response.getComponent().getRefId()).isEqualTo(project.uuid());
assertThat(response.getComponent().getRefKey()).isEqualTo(project.getKey());
@@ -160,11 +160,11 @@ public class ComponentActionTest {
@Test
public void return_deprecated_id_in_the_response() {
ComponentDto project = db.components().insertPrivateProject();
logAsUser(project);
userSession.addProjectPermission(UserRole.USER, project);
db.components().insertSnapshot(project);
insertNclocMetric();
MetricDto metric = db.measures().insertMetric(m -> m.setValueType(INT.name()));

ComponentWsResponse response = newRequest(project.getKey(), "ncloc");
ComponentWsResponse response = newRequest(project.getKey(), metric.getKey());

assertThat(response.getComponent().getId()).isEqualTo(project.uuid());
}
@@ -172,13 +172,13 @@ public class ComponentActionTest {
@Test
public void use_deprecated_component_id_parameter() {
ComponentDto project = db.components().insertPrivateProject();
logAsUser(project);
userSession.addProjectPermission(UserRole.USER, project);
userSession.addProjectPermission(USER, project);
insertNclocMetric();
MetricDto metric = db.measures().insertMetric(m -> m.setValueType(INT.name()));

ComponentWsResponse response = ws.newRequest()
.setParam("componentId", project.uuid())
.setParam(PARAM_METRIC_KEYS, "ncloc")
.setParam(PARAM_METRIC_KEYS, metric.getKey())
.executeProtobuf(ComponentWsResponse.class);

assertThat(response.getComponent().getKey()).isEqualTo(project.getDbKey());
@@ -187,13 +187,13 @@ public class ComponentActionTest {
@Test
public void use_deprecated_component_key_parameter() {
ComponentDto project = db.components().insertPrivateProject();
logAsUser(project);
userSession.addProjectPermission(UserRole.USER, project);
userSession.addProjectPermission(USER, project);
insertNclocMetric();
MetricDto metric = db.measures().insertMetric(m -> m.setValueType(INT.name()));

ComponentWsResponse response = ws.newRequest()
.setParam("componentKey", project.getKey())
.setParam(PARAM_METRIC_KEYS, "ncloc")
.setParam(PARAM_METRIC_KEYS, metric.getKey())
.executeProtobuf(ComponentWsResponse.class);

assertThat(response.getComponent().getKey()).isEqualTo(project.getDbKey());
@@ -202,12 +202,11 @@ public class ComponentActionTest {
@Test
public void metric_without_a_domain() {
ComponentDto project = db.components().insertPrivateProject();
logAsUser(project);
SnapshotDto analysis = db.getDbClient().snapshotDao().insert(db.getSession(), newAnalysis(project));
userSession.addProjectPermission(UserRole.USER, project);
MetricDto metricWithoutDomain = db.measures().insertMetric(m -> m
.setValueType(Measure.ValueType.INT.name())
.setDomain(null));
db.measures().insertMeasure(project, analysis, metricWithoutDomain);
db.measures().insertLiveMeasure(project, metricWithoutDomain);

ComponentWsResponse response = ws.newRequest()
.setParam(PARAM_COMPONENT, project.getKey())
@@ -215,36 +214,58 @@ public class ComponentActionTest {
.setParam(PARAM_ADDITIONAL_FIELDS, "metrics")
.executeProtobuf(ComponentWsResponse.class);

assertThat(response.getComponent().getMeasures(0).getMetric()).isEqualTo(metricWithoutDomain.getKey());
assertThat(response.getComponent().getMeasuresList()).extracting(Measures.Measure::getMetric).containsExactly(metricWithoutDomain.getKey());
Common.Metric responseMetric = response.getMetrics().getMetrics(0);
assertThat(responseMetric.getKey()).isEqualTo(metricWithoutDomain.getKey());
assertThat(responseMetric.hasDomain()).isFalse();
}

@Test
public void use_best_values() {
ComponentDto project = db.components().insertPrivateProject();
ComponentDto file = db.components().insertComponent(newFileDto(project));
userSession.addProjectPermission(UserRole.USER, project);
MetricDto metric = db.measures().insertMetric(m -> m
.setValueType(Measure.ValueType.INT.name())
.setBestValue(7.0d)
.setOptimizedBestValue(true)
.setDomain(null));

ComponentWsResponse response = ws.newRequest()
.setParam(PARAM_COMPONENT, file.getKey())
.setParam(PARAM_METRIC_KEYS, metric.getKey())
.setParam(PARAM_ADDITIONAL_FIELDS, "metrics")
.executeProtobuf(ComponentWsResponse.class);

assertThat(response.getComponent().getMeasuresList())
.extracting(Measures.Measure::getMetric, Measures.Measure::getValue)
.containsExactly(tuple(metric.getKey(), "7"));
}

@Test
public void fail_when_developer_is_not_found() {
ComponentDto project = db.components().insertPrivateProject();
logAsUser(project);
userSession.addProjectPermission(UserRole.USER, project);
db.components().insertSnapshot(project);

insertNclocMetric();
MetricDto metric = db.measures().insertMetric(m -> m.setValueType(INT.name()));

expectedException.expect(NotFoundException.class);
expectedException.expectMessage("Component id 'unknown-developer-id' not found");
expectedException.expectMessage("The Developer Cockpit feature has been dropped. The specified developer cannot be found.");

ws.newRequest()
.setParam(PARAM_COMPONENT, project.getKey())
.setParam(PARAM_METRIC_KEYS, "ncloc")
.setParam(PARAM_METRIC_KEYS, metric.getKey())
.setParam(PARAM_DEVELOPER_ID, "unknown-developer-id").executeProtobuf(ComponentWsResponse.class);
}

@Test
public void fail_when_a_metric_is_not_found() {
ComponentDto project = db.components().insertPrivateProject();
logAsUser(project);
userSession.addProjectPermission(UserRole.USER, project);
db.components().insertSnapshot(project);
insertNclocMetric();
insertComplexityMetric();
db.measures().insertMetric(m -> m.setKey("ncloc").setValueType(INT.name()));
db.measures().insertMetric(m -> m.setKey("complexity").setValueType(INT.name()));

expectedException.expect(NotFoundException.class);
expectedException.expectMessage("The following metric keys are not found: unknown-metric, another-unknown-metric");
@@ -255,7 +276,7 @@ public class ComponentActionTest {
@Test
public void fail_when_empty_metric_keys_parameter() {
ComponentDto project = db.components().insertPrivateProject();
logAsUser(project);
userSession.addProjectPermission(UserRole.USER, project);
db.components().insertSnapshot(project);

expectedException.expect(BadRequestException.class);
@@ -269,39 +290,39 @@ public class ComponentActionTest {
userSession.logIn();
ComponentDto project = db.components().insertPrivateProject();
db.components().insertSnapshot(project);
insertNclocMetric();
MetricDto metric = db.measures().insertMetric(m -> m.setValueType(INT.name()));

expectedException.expect(ForbiddenException.class);

newRequest(project.getKey(), "ncloc");
newRequest(project.getKey(), metric.getKey());
}

@Test
public void fail_when_component_does_not_exist() {
insertNclocMetric();
MetricDto metric = db.measures().insertMetric(m -> m.setValueType(INT.name()));

expectedException.expect(NotFoundException.class);
expectedException.expectMessage("Component key 'project-key' not found");

ws.newRequest()
.setParam(PARAM_COMPONENT, "project-key")
.setParam(PARAM_METRIC_KEYS, "ncloc")
.setParam(PARAM_METRIC_KEYS, metric.getKey())
.execute();
}

@Test
public void fail_when_component_is_removed() {
ComponentDto project = db.components().insertPrivateProject(p -> p.setEnabled(false));
logAsUser(project);
userSession.addProjectPermission(UserRole.USER, project);
userSession.addProjectPermission(USER, project);
insertNclocMetric();
MetricDto metric = db.measures().insertMetric(m -> m.setValueType(INT.name()));

expectedException.expect(NotFoundException.class);
expectedException.expectMessage(String.format("Component key '%s' not found", project.getKey()));

ws.newRequest()
.setParam(PARAM_COMPONENT, project.getKey())
.setParam(PARAM_METRIC_KEYS, "ncloc")
.setParam(PARAM_METRIC_KEYS, metric.getKey())
.execute();
}

@@ -345,14 +366,14 @@ public class ComponentActionTest {
ComponentDto project = db.components().insertMainBranch(organization);
userSession.logIn().addProjectPermission(UserRole.USER, project);
ComponentDto branch = db.components().insertProjectBranch(project);
insertNclocMetric();
MetricDto metric = db.measures().insertMetric(m -> m.setValueType(INT.name()));

expectedException.expect(NotFoundException.class);
expectedException.expectMessage(format("Component key '%s' not found", branch.getDbKey()));

ws.newRequest()
.setParam(PARAM_COMPONENT, branch.getDbKey())
.setParam(PARAM_METRIC_KEYS, "ncloc")
.setParam(PARAM_METRIC_KEYS, metric.getKey())
.execute();
}

@@ -362,21 +383,21 @@ public class ComponentActionTest {
ComponentDto project = db.components().insertMainBranch(organization);
userSession.logIn().addProjectPermission(UserRole.USER, project);
ComponentDto branch = db.components().insertProjectBranch(project);
insertNclocMetric();
MetricDto metric = db.measures().insertMetric(m -> m.setValueType(INT.name()));

expectedException.expect(NotFoundException.class);
expectedException.expectMessage(format("Component id '%s' not found", branch.uuid()));

ws.newRequest()
.setParam(DEPRECATED_PARAM_COMPONENT_ID, branch.uuid())
.setParam(PARAM_METRIC_KEYS, "ncloc")
.setParam(PARAM_METRIC_KEYS, metric.getKey())
.execute();
}

@Test
public void json_example() {
ComponentDto project = db.components().insertPrivateProject();
logAsUser(project);
userSession.addProjectPermission(UserRole.USER, project);
SnapshotDto analysis = db.components().insertSnapshot(project,
s -> s.setPeriodDate(parseDateTime("2016-01-11T10:49:50+0100").getTime())
.setPeriodMode("previous_version")
@@ -386,66 +407,36 @@ public class ComponentActionTest {
.setName("ElementImpl.java")
.setLanguage("java")
.setPath("src/main/java/com/sonarsource/markdown/impl/ElementImpl.java"));
MetricDto complexity = insertComplexityMetric();
db.measures().insertMeasure(file, analysis, complexity,
m -> m.setValue(12.0d)
.setVariation(2.0d)
.setData(null));
MetricDto ncloc = insertNclocMetric();
db.measures().insertMeasure(file, analysis, ncloc,
m -> m.setValue(114.0d)
.setVariation(3.0d)
.setData(null));
MetricDto newViolations = insertNewViolationMetric();
db.measures().insertMeasure(file, analysis, newViolations,
m -> m.setVariation(25.0d)
.setValue(null)
.setData(null));

String response = ws.newRequest()
.setParam(PARAM_COMPONENT, file.getKey())
.setParam(PARAM_METRIC_KEYS, "ncloc, complexity, new_violations")
.setParam(PARAM_ADDITIONAL_FIELDS, "metrics,periods")
.execute()
.getInput();

assertJson(response).isSimilarTo(getClass().getResource("component-example.json"));
}

private ComponentWsResponse newRequest(String componentKey, String metricKeys) {
return ws.newRequest()
.setParam(PARAM_COMPONENT, componentKey)
.setParam(PARAM_METRIC_KEYS, metricKeys)
.setParam(PARAM_ADDITIONAL_FIELDS, "metrics,periods")
.executeProtobuf(ComponentWsResponse.class);
}

private MetricDto insertNclocMetric() {
return db.measures().insertMetric(m -> m.setKey("ncloc")
.setShortName("Lines of code")
.setDescription("Non Commenting Lines of Code")
.setDomain("Size")
MetricDto complexity = db.measures().insertMetric(m -> m.setKey("complexity")
.setShortName("Complexity")
.setDescription("Cyclomatic complexity")
.setDomain("Complexity")
.setValueType("INT")
.setDirection(-1)
.setQualitative(false)
.setHidden(false)
.setUserManaged(false));
}
db.measures().insertLiveMeasure(file, complexity,
m -> m.setValue(12.0d)
.setVariation(2.0d)
.setData((String) null));

private MetricDto insertComplexityMetric() {
return db.measures().insertMetric(m -> m.setKey("complexity")
.setShortName("Complexity")
.setDescription("Cyclomatic complexity")
.setDomain("Complexity")
MetricDto ncloc = db.measures().insertMetric(m1 -> m1.setKey("ncloc")
.setShortName("Lines of code")
.setDescription("Non Commenting Lines of Code")
.setDomain("Size")
.setValueType("INT")
.setDirection(-1)
.setQualitative(false)
.setHidden(false)
.setUserManaged(false));
}
db.measures().insertLiveMeasure(file, ncloc,
m -> m.setValue(114.0d)
.setVariation(3.0d)
.setData((String) null));

private MetricDto insertNewViolationMetric() {
return db.measures().insertMetric(m -> m.setKey("new_violations")
MetricDto newViolations = db.measures().insertMetric(m -> m.setKey("new_violations")
.setShortName("New issues")
.setDescription("New Issues")
.setDomain("Issues")
@@ -454,10 +445,26 @@ public class ComponentActionTest {
.setQualitative(true)
.setHidden(false)
.setUserManaged(false));
}
db.measures().insertLiveMeasure(file, newViolations,
m -> m.setVariation(25.0d)
.setValue(null)
.setData((String) null));

private void logAsUser(ComponentDto project) {
userSession.addProjectPermission(UserRole.USER, project);
String response = ws.newRequest()
.setParam(PARAM_COMPONENT, file.getKey())
.setParam(PARAM_METRIC_KEYS, "ncloc, complexity, new_violations")
.setParam(PARAM_ADDITIONAL_FIELDS, "metrics,periods")
.execute()
.getInput();

assertJson(response).isSimilarTo(getClass().getResource("component-example.json"));
}

private ComponentWsResponse newRequest(String componentKey, String metricKeys) {
return ws.newRequest()
.setParam(PARAM_COMPONENT, componentKey)
.setParam(PARAM_METRIC_KEYS, metricKeys)
.setParam(PARAM_ADDITIONAL_FIELDS, "metrics,periods")
.executeProtobuf(ComponentWsResponse.class);
}
}

+ 60
- 81
server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeActionTest.java Datei anzeigen

@@ -37,7 +37,7 @@ import org.sonar.db.component.ComponentDbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ResourceTypesRule;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.measure.MeasureDto;
import org.sonar.db.measure.LiveMeasureDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.metric.MetricTesting;
import org.sonar.db.organization.OrganizationDto;
@@ -73,7 +73,6 @@ import static org.sonar.db.component.ComponentTesting.newDirectory;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.component.ComponentTesting.newProjectCopy;
import static org.sonar.db.component.SnapshotTesting.newAnalysis;
import static org.sonar.db.measure.MeasureTesting.newMeasureDto;
import static org.sonar.server.measure.ws.ComponentTreeAction.LEAVES_STRATEGY;
import static org.sonar.server.measure.ws.ComponentTreeAction.METRIC_PERIOD_SORT;
import static org.sonar.server.measure.ws.ComponentTreeAction.METRIC_SORT;
@@ -142,33 +141,19 @@ public class ComponentTreeActionTest {
.setQualifier(DIRECTORY));

MetricDto complexity = insertComplexityMetric();
dbClient.measureDao().insert(dbSession,
newMeasureDto(complexity, file1, analysis)
.setValue(12.0d),
newMeasureDto(complexity, dir, analysis)
.setValue(35.0d)
.setVariation(0.0d),
newMeasureDto(complexity, project, analysis)
.setValue(42.0d));
db.measures().insertLiveMeasure(file1, complexity, m -> m.setValue(12.0d));
db.measures().insertLiveMeasure(dir, complexity, m -> m.setValue(35.0d).setVariation(0.0d));
db.measures().insertLiveMeasure(project, complexity, m -> m.setValue(42.0d));

MetricDto ncloc = insertNclocMetric();
dbClient.measureDao().insert(dbSession,
newMeasureDto(ncloc, file1, analysis)
.setValue(114.0d),
newMeasureDto(ncloc, dir, analysis)
.setValue(217.0d)
.setVariation(0.0d),
newMeasureDto(ncloc, project, analysis)
.setValue(1984.0d));
db.measures().insertLiveMeasure(file1, ncloc, m -> m.setValue(114.0d));
db.measures().insertLiveMeasure(dir, ncloc, m -> m.setValue(217.0d).setVariation(0.0d));
db.measures().insertLiveMeasure(project, ncloc, m -> m.setValue(1984.0d));

MetricDto newViolations = insertNewViolationsMetric();
dbClient.measureDao().insert(dbSession,
newMeasureDto(newViolations, file1, analysis)
.setVariation(25.0d),
newMeasureDto(newViolations, dir, analysis)
.setVariation(25.0d),
newMeasureDto(newViolations, project, analysis)
.setVariation(255.0d));
db.measures().insertLiveMeasure(file1, newViolations, m -> m.setVariation(25.0d));
db.measures().insertLiveMeasure(dir, newViolations, m -> m.setVariation(25.0d));
db.measures().insertLiveMeasure(project, newViolations, m -> m.setVariation(255.0d));

db.commit();

@@ -206,17 +191,16 @@ public class ComponentTreeActionTest {
.setPeriodMode("last_version")
.setPeriodDate(System.currentTimeMillis()));
userSession.anonymous().addProjectPermission(UserRole.USER, project);
ComponentDto directoryDto = newDirectory(project, "directory-uuid", "path/to/directory").setName("directory-1");
componentDb.insertComponent(directoryDto);
ComponentDto file = newFileDto(directoryDto, null, "file-uuid").setName("file-1");
ComponentDto directory = newDirectory(project, "directory-uuid", "path/to/directory").setName("directory-1");
componentDb.insertComponent(directory);
ComponentDto file = newFileDto(directory, null, "file-uuid").setName("file-1");
componentDb.insertComponent(file);
MetricDto ncloc = insertNclocMetric();
MetricDto coverage = insertCoverageMetric();
dbClient.measureDao().insert(dbSession,
newMeasureDto(ncloc, file, projectSnapshot).setValue(5.0d).setVariation(4.0d),
newMeasureDto(coverage, file, projectSnapshot).setValue(15.5d),
newMeasureDto(coverage, directoryDto, projectSnapshot).setValue(15.0d));
db.commit();
db.measures().insertLiveMeasure(file, ncloc, m -> m.setValue(5.0d).setVariation(4.0d));
db.measures().insertLiveMeasure(file, coverage, m -> m.setValue(15.5d));
db.measures().insertLiveMeasure(directory, coverage, m -> m.setValue(15.5d));

ComponentTreeWsResponse response = ws.newRequest()
.setParam(PARAM_COMPONENT, project.getKey())
@@ -237,9 +221,9 @@ public class ComponentTreeActionTest {
ComponentDto project = db.components().insertPrivateProject();
SnapshotDto projectSnapshot = db.components().insertSnapshot(project);
userSession.anonymous().addProjectPermission(UserRole.USER, project);
ComponentDto directoryDto = newDirectory(project, "directory-uuid", "path/to/directory").setName("directory-1");
componentDb.insertComponent(directoryDto);
ComponentDto file = newFileDto(directoryDto, null, "file-uuid").setName("file-1");
ComponentDto directory = newDirectory(project, "directory-uuid", "path/to/directory").setName("directory-1");
componentDb.insertComponent(directory);
ComponentDto file = newFileDto(directory, null, "file-uuid").setName("file-1");
componentDb.insertComponent(file);
MetricDto coverage = insertCoverageMetric();
dbClient.metricDao().insert(dbSession, MetricTesting.newMetricDto()
@@ -253,10 +237,9 @@ public class ComponentTreeActionTest {
.setOptimizedBestValue(true)
.setBestValue(1984.0d)
.setValueType(INT.name()));
dbClient.measureDao().insert(dbSession,
newMeasureDto(coverage, file, projectSnapshot).setValue(15.5d),
newMeasureDto(coverage, directoryDto, projectSnapshot).setValue(42.0d));
db.commit();
db.measures().insertLiveMeasure(file, coverage, m -> m.setValue(15.5d));
db.measures().insertLiveMeasure(directory, coverage, m -> m.setValue(42.0d));

ComponentTreeWsResponse response = ws.newRequest()
.setParam(PARAM_COMPONENT, project.getKey())
@@ -284,17 +267,17 @@ public class ComponentTreeActionTest {
.setPeriodDate(parseDateTime("2016-01-11T10:49:50+0100").getTime())
.setPeriodMode("previous_version")
.setPeriodParam("1.0-SNAPSHOT"));
ComponentDto directoryDto = newDirectory(project, "directory-uuid", "path/to/directory").setName("directory-1");
componentDb.insertComponent(directoryDto);
ComponentDto file = newFileDto(directoryDto, null, "file-uuid").setName("file-1");
ComponentDto directory = newDirectory(project, "directory-uuid", "path/to/directory").setName("directory-1");
componentDb.insertComponent(directory);
ComponentDto file = newFileDto(directory, null, "file-uuid").setName("file-1");
componentDb.insertComponent(file);
MetricDto metric = dbClient.metricDao().insert(dbSession, newMetricDto()
.setKey(NEW_SECURITY_RATING_KEY)
.setOptimizedBestValue(true)
.setBestValue(1d)
.setValueType(RATING.name()));
dbClient.measureDao().insert(dbSession, newMeasureDto(metric, directoryDto, projectSnapshot).setVariation(2d));
db.commit();
db.measures().insertLiveMeasure(directory, metric, m -> m.setVariation(2d));

ComponentTreeWsResponse response = ws.newRequest()
.setParam(PARAM_COMPONENT, project.getKey())
@@ -322,17 +305,16 @@ public class ComponentTreeActionTest {
ComponentDto file2 = componentDb.insertComponent(newFileDto(project, null, "file-uuid-2").setName("file-1"));
ComponentDto file1 = componentDb.insertComponent(newFileDto(project, null, "file-uuid-1").setName("file-1"));
MetricDto coverage = insertCoverageMetric();
dbClient.measureDao().insert(dbSession,
newMeasureDto(coverage, file1, projectSnapshot).setValue(1.0d),
newMeasureDto(coverage, file2, projectSnapshot).setValue(2.0d),
newMeasureDto(coverage, file3, projectSnapshot).setValue(3.0d),
newMeasureDto(coverage, file4, projectSnapshot).setValue(4.0d),
newMeasureDto(coverage, file5, projectSnapshot).setValue(5.0d),
newMeasureDto(coverage, file6, projectSnapshot).setValue(6.0d),
newMeasureDto(coverage, file7, projectSnapshot).setValue(7.0d),
newMeasureDto(coverage, file8, projectSnapshot).setValue(8.0d),
newMeasureDto(coverage, file9, projectSnapshot).setValue(9.0d));
db.commit();
db.measures().insertLiveMeasure(file1, coverage, m -> m.setValue(1.0d));
db.measures().insertLiveMeasure(file2, coverage, m -> m.setValue(2.0d));
db.measures().insertLiveMeasure(file3, coverage, m -> m.setValue(3.0d));
db.measures().insertLiveMeasure(file4, coverage, m -> m.setValue(4.0d));
db.measures().insertLiveMeasure(file5, coverage, m -> m.setValue(5.0d));
db.measures().insertLiveMeasure(file6, coverage, m -> m.setValue(6.0d));
db.measures().insertLiveMeasure(file7, coverage, m -> m.setValue(7.0d));
db.measures().insertLiveMeasure(file8, coverage, m -> m.setValue(8.0d));
db.measures().insertLiveMeasure(file9, coverage, m -> m.setValue(9.0d));

ComponentTreeWsResponse response = ws.newRequest()
.setParam(PARAM_COMPONENT, project.getKey())
@@ -361,11 +343,10 @@ public class ComponentTreeActionTest {
ComponentDto file2 = componentDb.insertComponent(newFileDto(project, null, "file-uuid-2"));
MetricDto ncloc = newMetricDto().setKey("ncloc").setValueType(INT.name()).setDirection(1);
dbClient.metricDao().insert(dbSession, ncloc);
dbClient.measureDao().insert(dbSession,
newMeasureDto(ncloc, file1, projectSnapshot).setValue(1.0d),
newMeasureDto(ncloc, file2, projectSnapshot).setValue(2.0d),
newMeasureDto(ncloc, file3, projectSnapshot).setValue(3.0d));
db.commit();
db.measures().insertLiveMeasure(file1, ncloc, m -> m.setValue(1.0d));
db.measures().insertLiveMeasure(file2, ncloc, m -> m.setValue(2.0d));
db.measures().insertLiveMeasure(file3, ncloc, m -> m.setValue(3.0d));

ComponentTreeWsResponse response = ws.newRequest()
.setParam(PARAM_COMPONENT, project.getKey())
@@ -392,12 +373,11 @@ public class ComponentTreeActionTest {
componentDb.insertComponent(file4);
MetricDto ncloc = newMetricDto().setKey("ncloc").setValueType(INT.name()).setDirection(1);
dbClient.metricDao().insert(dbSession, ncloc);
dbClient.measureDao().insert(dbSession,
newMeasureDto(ncloc, file1, projectSnapshot).setValue(1.0d),
newMeasureDto(ncloc, file2, projectSnapshot).setValue(2.0d),
newMeasureDto(ncloc, file3, projectSnapshot).setValue(3.0d),
// measure on period 1
newMeasureDto(ncloc, file4, projectSnapshot).setVariation(4.0d));
db.measures().insertLiveMeasure(file1, ncloc, m -> m.setData((String) null).setValue(1.0d).setVariation(null));
db.measures().insertLiveMeasure(file2, ncloc, m -> m.setData((String) null).setValue(2.0d).setVariation(null));
db.measures().insertLiveMeasure(file3, ncloc, m -> m.setData((String) null).setValue(3.0d).setVariation(null));
// measure on period 1
db.measures().insertLiveMeasure(file4, ncloc, m -> m.setData((String) null).setValue(null).setVariation(4.0d));
db.commit();

ComponentTreeWsResponse response = ws.newRequest()
@@ -423,11 +403,10 @@ public class ComponentTreeActionTest {
ComponentDto file2 = componentDb.insertComponent(newFileDto(project, null, "file-uuid-2"));
MetricDto ncloc = newMetricDto().setKey("ncloc").setValueType(INT.name()).setDirection(1);
dbClient.metricDao().insert(dbSession, ncloc);
dbClient.measureDao().insert(dbSession,
newMeasureDto(ncloc, file1, projectSnapshot).setVariation(1.0d),
newMeasureDto(ncloc, file2, projectSnapshot).setVariation(2.0d),
newMeasureDto(ncloc, file3, projectSnapshot).setVariation(3.0d));
db.commit();
db.measures().insertLiveMeasure(file1, ncloc, m -> m.setVariation(1.0d));
db.measures().insertLiveMeasure(file2, ncloc, m -> m.setVariation(2.0d));
db.measures().insertLiveMeasure(file3, ncloc, m -> m.setVariation(3.0d));

ComponentTreeWsResponse response = ws.newRequest()
.setParam(PARAM_COMPONENT, project.getKey())
@@ -450,12 +429,11 @@ public class ComponentTreeActionTest {
ComponentDto file1 = componentDb.insertComponent(newFileDto(project, null, "file-uuid-1"));
MetricDto ncloc = newMetricDto().setKey("new_ncloc").setValueType(INT.name()).setDirection(1);
dbClient.metricDao().insert(dbSession, ncloc);
dbClient.measureDao().insert(dbSession,
newMeasureDto(ncloc, file1, projectSnapshot).setVariation(1.0d),
newMeasureDto(ncloc, file2, projectSnapshot).setVariation(2.0d),
newMeasureDto(ncloc, file3, projectSnapshot).setVariation(3.0d),
// file 4 measure is on absolute value
newMeasureDto(ncloc, file4, projectSnapshot).setValue(4.0d));
db.measures().insertLiveMeasure(file1, ncloc, m -> m.setData((String) null).setValue(null).setVariation(1.0d));
db.measures().insertLiveMeasure(file2, ncloc, m -> m.setData((String) null).setValue(null).setVariation(2.0d));
db.measures().insertLiveMeasure(file3, ncloc, m -> m.setData((String) null).setValue(null).setVariation(3.0d));
// file 4 measure is on absolute value
db.measures().insertLiveMeasure(file4, ncloc, m -> m.setData((String) null).setValue(4.0d).setVariation(null));
db.commit();

ComponentTreeWsResponse response = ws.newRequest()
@@ -492,12 +470,13 @@ public class ComponentTreeActionTest {

@Test
public void branch() {
ComponentDto project = db.components().insertPrivateProject();
OrganizationDto organization = db.organizations().insert();
ComponentDto project = db.components().insertPrivateProject(organization);
ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("my_branch"));
SnapshotDto analysis = db.components().insertSnapshot(branch);
ComponentDto file = db.components().insertComponent(newFileDto(branch));
MetricDto complexity = insertComplexityMetric();
MeasureDto measure = db.measures().insertMeasure(file, analysis, complexity, m -> m.setValue(12.0d));
MetricDto complexity = db.measures().insertMetric(m -> m.setValueType(INT.name()));
LiveMeasureDto measure = db.measures().insertLiveMeasure(file, complexity, m -> m.setValue(12.0d));

ComponentTreeWsResponse response = ws.newRequest()
.setParam(PARAM_COMPONENT, file.getKey())
@@ -518,7 +497,7 @@ public class ComponentTreeActionTest {
SnapshotDto analysis = db.components().insertSnapshot(project);
ComponentDto file = componentDb.insertComponent(newFileDto(project));
MetricDto ncloc = insertNclocMetric();
db.measures().insertMeasure(file, analysis, ncloc, m -> m.setValue(2d));
db.measures().insertLiveMeasure(file, ncloc, m -> m.setValue(2d));

ComponentTreeWsResponse response = ws.newRequest()
.setParam(PARAM_COMPONENT, project.getKey())
@@ -565,7 +544,7 @@ public class ComponentTreeActionTest {
MetricDto metricWithoutDomain = db.measures().insertMetric(m -> m
.setValueType(Metric.ValueType.INT.name())
.setDomain(null));
db.measures().insertMeasure(project, analysis, metricWithoutDomain);
db.measures().insertLiveMeasure(project, metricWithoutDomain);

ComponentTreeWsResponse result = ws.newRequest()
.setParam(PARAM_COMPONENT, project.getKey())
@@ -582,11 +561,11 @@ public class ComponentTreeActionTest {
@Test
public void reference_component() {
ComponentDto project = db.components().insertPrivateProject();
ComponentDto view = db.components().insertView();
ComponentDto view = db.components().insertPrivatePortfolio(db.getDefaultOrganization());
SnapshotDto viewAnalysis = db.components().insertSnapshot(view);
ComponentDto projectCopy = db.components().insertComponent(newProjectCopy(project, view));
MetricDto ncloc = insertNclocMetric();
db.measures().insertMeasure(projectCopy, viewAnalysis, ncloc, m -> m.setValue(5d));
db.measures().insertLiveMeasure(projectCopy, ncloc, m -> m.setValue(5d));

ComponentTreeWsResponse result = ws.newRequest()
.setParam(PARAM_COMPONENT, view.getKey())
@@ -903,7 +882,7 @@ public class ComponentTreeActionTest {
.setShortName("Lines of code")
.setDescription("Non Commenting Lines of Code")
.setDomain("Size")
.setValueType("INT")
.setValueType(INT.name())
.setDirection(-1)
.setQualitative(false)
.setHidden(false)
@@ -918,7 +897,7 @@ public class ComponentTreeActionTest {
.setShortName("Complexity")
.setDescription("Cyclomatic complexity")
.setDomain("Complexity")
.setValueType("INT")
.setValueType(INT.name())
.setDirection(-1)
.setQualitative(false)
.setHidden(false)

+ 4
- 4
server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeSortTest.java Datei anzeigen

@@ -30,7 +30,7 @@ import org.sonar.api.measures.Metric.ValueType;
import org.sonar.api.resources.Qualifiers;
import org.sonar.core.util.Uuids;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.measure.MeasureDto;
import org.sonar.db.measure.LiveMeasureDto;
import org.sonar.db.metric.MetricDto;

import static com.google.common.collect.Lists.newArrayList;
@@ -78,9 +78,9 @@ public class ComponentTreeSortTest {
// same number than path field
double currentValue = 9;
for (ComponentDto component : components) {
measuresByComponentUuidAndMetric.put(component.uuid(), violationsMetric, createFromMeasureDto(new MeasureDto().setValue(currentValue)
measuresByComponentUuidAndMetric.put(component.uuid(), violationsMetric, createFromMeasureDto(new LiveMeasureDto().setValue(currentValue)
.setVariation(-currentValue)));
measuresByComponentUuidAndMetric.put(component.uuid(), sqaleIndexMetric, createFromMeasureDto(new MeasureDto().setData(String.valueOf(currentValue))));
measuresByComponentUuidAndMetric.put(component.uuid(), sqaleIndexMetric, createFromMeasureDto(new LiveMeasureDto().setData(String.valueOf(currentValue))));
currentValue--;
}
}
@@ -167,7 +167,7 @@ public class ComponentTreeSortTest {
for (int i = 0; i < components.size(); i++) {
ComponentDto component = components.get(i);
String alertStatus = statuses.get(i % 3);
measuresByComponentUuidAndMetric.put(component.uuid(), metrics.get(0), createFromMeasureDto(new MeasureDto().setData(alertStatus)));
measuresByComponentUuidAndMetric.put(component.uuid(), metrics.get(0), createFromMeasureDto(new LiveMeasureDto().setData(alertStatus)));
}
ComponentTreeRequest wsRequest = newRequest(newArrayList(METRIC_SORT, NAME_SORT), true, CoreMetrics.ALERT_STATUS_KEY);


+ 123
- 262
server/sonar-server/src/test/java/org/sonar/server/measure/ws/SearchActionTest.java Datei anzeigen

@@ -20,29 +20,21 @@
package org.sonar.server.measure.ws;

import com.google.common.base.Joiner;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javax.annotation.Nullable;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.measures.Metric;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.System2;
import org.sonar.api.web.UserRole;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.ws.TestRequest;
@@ -57,16 +49,12 @@ import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.tuple;
import static org.sonar.api.utils.DateUtils.parseDateTime;
import static org.sonar.db.component.ComponentTesting.newApplication;
import static org.sonar.api.measures.Metric.ValueType.FLOAT;
import static org.sonar.api.measures.Metric.ValueType.INT;
import static org.sonar.db.component.ComponentTesting.newDirectory;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.component.ComponentTesting.newModuleDto;
import static org.sonar.db.component.ComponentTesting.newSubView;
import static org.sonar.db.component.ComponentTesting.newView;
import static org.sonar.db.component.SnapshotTesting.newAnalysis;
import static org.sonar.db.measure.MeasureTesting.newMeasureDto;
import static org.sonar.db.metric.MetricTesting.newMetricDto;
import static org.sonar.test.JsonAssert.assertJson;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_KEYS;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_PROJECT_KEYS;
@@ -77,25 +65,41 @@ public class SearchActionTest {
public ExpectedException expectedException = ExpectedException.none();

@Rule
public UserSessionRule userSession = UserSessionRule.standalone();
public UserSessionRule userSession = UserSessionRule.standalone().logIn();

@Rule
public DbTester db = DbTester.create(System2.INSTANCE);

private DbClient dbClient = db.getDbClient();
private DbSession dbSession = db.getSession();
private UserDto user;
private WsActionTester ws = new WsActionTester(new SearchAction(userSession, dbClient));

@Before
public void setUp() throws Exception {
user = db.users().insertUser("john");
userSession.logIn(user);
}
private WsActionTester ws = new WsActionTester(new SearchAction(userSession, db.getDbClient()));

@Test
public void json_example() {
List<String> projectKeys = insertJsonExampleData();
OrganizationDto organization = db.organizations().insert();

ComponentDto project1 = db.components().insertPrivateProject(organization, p -> p.setDbKey("MY_PROJECT_1").setName("Project 1"));
ComponentDto project2 = db.components().insertPrivateProject(organization, p -> p.setDbKey("MY_PROJECT_2").setName("Project 2"));
ComponentDto project3 = db.components().insertPrivateProject(organization, p -> p.setDbKey("MY_PROJECT_3").setName("Project 3"));

userSession.addProjectPermission(UserRole.USER, project1);
userSession.addProjectPermission(UserRole.USER, project2);
userSession.addProjectPermission(UserRole.USER, project3);

MetricDto complexity = db.measures().insertMetric(m -> m.setKey("complexity").setValueType(INT.name()));
db.measures().insertLiveMeasure(project1, complexity, m -> m.setValue(12.0d));
db.measures().insertLiveMeasure(project2, complexity, m -> m.setValue(35.0d).setVariation(0.0d));
db.measures().insertLiveMeasure(project3, complexity, m -> m.setValue(42.0d));

MetricDto ncloc = db.measures().insertMetric(m -> m.setKey("ncloc").setValueType(INT.name()));
db.measures().insertLiveMeasure(project1, ncloc, m -> m.setValue(114.0d));
db.measures().insertLiveMeasure(project2, ncloc, m -> m.setValue(217.0d).setVariation(0.0d));
db.measures().insertLiveMeasure(project3, ncloc, m -> m.setValue(1984.0d));

MetricDto newViolations = db.measures().insertMetric(m -> m.setKey("new_violations").setValueType(INT.name()));
db.measures().insertLiveMeasure(project1, newViolations, m -> m.setVariation(25.0d));
db.measures().insertLiveMeasure(project2, newViolations, m -> m.setVariation(25.0d));
db.measures().insertLiveMeasure(project3, newViolations, m -> m.setVariation(255.0d));

List<String> projectKeys = Arrays.asList(project1.getDbKey(), project2.getDbKey(), project3.getDbKey());

String result = ws.newRequest()
.setParam(PARAM_PROJECT_KEYS, Joiner.on(",").join(projectKeys))
@@ -108,40 +112,34 @@ public class SearchActionTest {

@Test
public void return_measures() throws Exception {
ComponentDto project = ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization());
SnapshotDto projectSnapshot = db.components().insertProjectAndSnapshot(project);
setBrowsePermissionOnUser(project);
MetricDto coverage = insertCoverageMetric();
dbClient.measureDao().insert(dbSession, newMeasureDto(coverage, project, projectSnapshot).setValue(15.5d));
db.commit();
ComponentDto project = db.components().insertPrivateProject(db.getDefaultOrganization());
userSession.addProjectPermission(UserRole.USER, project);
MetricDto coverage = db.measures().insertMetric(m -> m.setValueType(FLOAT.name()));
db.measures().insertLiveMeasure(project, coverage, m -> m.setValue(15.5d));

SearchWsResponse result = call(singletonList(project.getDbKey()), singletonList("coverage"));
SearchWsResponse result = call(singletonList(project.getDbKey()), singletonList(coverage.getKey()));

List<Measure> measures = result.getMeasuresList();
assertThat(measures).hasSize(1);
Measure measure = measures.get(0);
assertThat(measure.getMetric()).isEqualTo("coverage");
assertThat(measure.getMetric()).isEqualTo(coverage.getKey());
assertThat(measure.getValue()).isEqualTo("15.5");
}

@Test
public void return_measures_on_leak_period() throws Exception {
ComponentDto project = ComponentTesting.newPrivateProjectDto(db.organizations().insert());
SnapshotDto projectSnapshot = db.components().insertProjectAndSnapshot(project);
setBrowsePermissionOnUser(project);
MetricDto coverage = insertCoverageMetric();
dbClient.measureDao().insert(dbSession,
newMeasureDto(coverage, project, projectSnapshot)
.setValue(15.5d)
.setVariation(10d));
db.commit();

SearchWsResponse result = call(singletonList(project.getDbKey()), singletonList("coverage"));
OrganizationDto organization = db.organizations().insert();
ComponentDto project = db.components().insertPrivateProject(organization);
userSession.addProjectPermission(UserRole.USER, project);
MetricDto coverage = db.measures().insertMetric(m -> m.setValueType(FLOAT.name()));
db.measures().insertLiveMeasure(project, coverage, m -> m.setValue(15.5d).setVariation(10d));

SearchWsResponse result = call(singletonList(project.getDbKey()), singletonList(coverage.getKey()));

List<Measure> measures = result.getMeasuresList();
assertThat(measures).hasSize(1);
Measure measure = measures.get(0);
assertThat(measure.getMetric()).isEqualTo("coverage");
assertThat(measure.getMetric()).isEqualTo(coverage.getKey());
assertThat(measure.getValue()).isEqualTo("15.5");
assertThat(measure.getPeriods().getPeriodsValueList())
.extracting(Measures.PeriodValue::getIndex, Measures.PeriodValue::getValue)
@@ -150,130 +148,121 @@ public class SearchActionTest {

@Test
public void sort_by_metric_key_then_project_name() throws Exception {
MetricDto coverage = insertCoverageMetric();
MetricDto complexity = insertComplexityMetric();
OrganizationDto organizationDto = db.organizations().insert();
ComponentDto project1 = ComponentTesting.newPrivateProjectDto(organizationDto).setName("C");
SnapshotDto projectSnapshot1 = db.components().insertProjectAndSnapshot(project1);
ComponentDto project2 = ComponentTesting.newPrivateProjectDto(organizationDto).setName("A");
SnapshotDto projectSnapshot2 = db.components().insertProjectAndSnapshot(project2);
ComponentDto project3 = ComponentTesting.newPrivateProjectDto(organizationDto).setName("B");
SnapshotDto projectSnapshot3 = db.components().insertProjectAndSnapshot(project3);
setBrowsePermissionOnUser(project1, project2, project3);
dbClient.measureDao().insert(dbSession, newMeasureDto(coverage, project1, projectSnapshot1).setValue(5.5d));
dbClient.measureDao().insert(dbSession, newMeasureDto(coverage, project2, projectSnapshot2).setValue(6.5d));
dbClient.measureDao().insert(dbSession, newMeasureDto(coverage, project3, projectSnapshot3).setValue(7.5d));
dbClient.measureDao().insert(dbSession, newMeasureDto(complexity, project1, projectSnapshot1).setValue(10d));
dbClient.measureDao().insert(dbSession, newMeasureDto(complexity, project2, projectSnapshot2).setValue(15d));
dbClient.measureDao().insert(dbSession, newMeasureDto(complexity, project3, projectSnapshot3).setValue(20d));
db.commit();

SearchWsResponse result = call(asList(project1.getDbKey(), project2.getDbKey(), project3.getDbKey()), asList("coverage", "complexity"));
MetricDto coverage = db.measures().insertMetric(m -> m.setKey("coverage").setValueType(FLOAT.name()));
MetricDto complexity = db.measures().insertMetric(m -> m.setKey("complexity").setValueType(INT.name()));
OrganizationDto organization = db.organizations().insert();
ComponentDto project1 = db.components().insertPrivateProject(organization, p -> p.setName("C"));
ComponentDto project2 = db.components().insertPrivateProject(organization, p -> p.setName("A"));
ComponentDto project3 = db.components().insertPrivateProject(organization, p -> p.setName("B"));
userSession.addProjectPermission(UserRole.USER, project1);
userSession.addProjectPermission(UserRole.USER, project2);
userSession.addProjectPermission(UserRole.USER, project3);
db.measures().insertLiveMeasure(project1, coverage, m -> m.setValue(5.5d));
db.measures().insertLiveMeasure(project2, coverage, m -> m.setValue(6.5d));
db.measures().insertLiveMeasure(project3, coverage, m -> m.setValue(7.5d));
db.measures().insertLiveMeasure(project1, complexity, m -> m.setValue(10d));
db.measures().insertLiveMeasure(project2, complexity, m -> m.setValue(15d));
db.measures().insertLiveMeasure(project3, complexity, m -> m.setValue(20d));

SearchWsResponse result = call(asList(project1.getDbKey(), project2.getDbKey(), project3.getDbKey()), asList(coverage.getKey(), complexity.getKey()));

assertThat(result.getMeasuresList()).extracting(Measure::getMetric, Measure::getComponent)
.containsExactly(
tuple("complexity", project2.getDbKey()), tuple("complexity", project3.getDbKey()), tuple("complexity", project1.getDbKey()),
tuple("coverage", project2.getDbKey()), tuple("coverage", project3.getDbKey()), tuple("coverage", project1.getDbKey()));
tuple(complexity.getKey(), project2.getDbKey()), tuple(complexity.getKey(), project3.getDbKey()), tuple(complexity.getKey(), project1.getDbKey()),
tuple(coverage.getKey(), project2.getDbKey()), tuple(coverage.getKey(), project3.getDbKey()), tuple(coverage.getKey(), project1.getDbKey()));
}

@Test
public void return_measures_on_view() throws Exception {
ComponentDto view = newView(db.getDefaultOrganization());
SnapshotDto viewSnapshot = db.components().insertProjectAndSnapshot(view);
MetricDto coverage = insertCoverageMetric();
dbClient.measureDao().insert(dbSession, newMeasureDto(coverage, view, viewSnapshot).setValue(15.5d));
db.commit();
OrganizationDto organization = db.organizations().insert();
ComponentDto view = db.components().insertPrivatePortfolio(organization);
userSession.addProjectPermission(UserRole.USER, view);
MetricDto coverage = db.measures().insertMetric(m -> m.setValueType(FLOAT.name()));
db.measures().insertLiveMeasure(view, coverage, m -> m.setValue(15.5d));

SearchWsResponse result = call(singletonList(view.getDbKey()), singletonList("coverage"));
SearchWsResponse result = call(singletonList(view.getDbKey()), singletonList(coverage.getKey()));

List<Measure> measures = result.getMeasuresList();
assertThat(measures).hasSize(1);
Measure measure = measures.get(0);
assertThat(measure.getMetric()).isEqualTo("coverage");
assertThat(measure.getMetric()).isEqualTo(coverage.getKey());
assertThat(measure.getValue()).isEqualTo("15.5");
}


@Test
public void return_measures_on_application() throws Exception {
ComponentDto application = newApplication(db.getDefaultOrganization());
SnapshotDto viewSnapshot = db.components().insertProjectAndSnapshot(application);
MetricDto coverage = insertCoverageMetric();
dbClient.measureDao().insert(dbSession, newMeasureDto(coverage, application, viewSnapshot).setValue(15.5d));
db.commit();
OrganizationDto organization = db.organizations().insert();
ComponentDto application = db.components().insertPrivateApplication(organization);
userSession.addProjectPermission(UserRole.USER, application);
MetricDto coverage = db.measures().insertMetric(m -> m.setValueType(FLOAT.name()));
db.measures().insertLiveMeasure(application, coverage, m -> m.setValue(15.5d));

SearchWsResponse result = call(singletonList(application.getDbKey()), singletonList("coverage"));
SearchWsResponse result = call(singletonList(application.getDbKey()), singletonList(coverage.getKey()));

List<Measure> measures = result.getMeasuresList();
assertThat(measures).hasSize(1);
Measure measure = measures.get(0);
assertThat(measure.getMetric()).isEqualTo("coverage");
assertThat(measure.getMetric()).isEqualTo(coverage.getKey());
assertThat(measure.getValue()).isEqualTo("15.5");
}

@Test
public void return_measures_on_sub_view() throws Exception {
ComponentDto view = newView(db.getDefaultOrganization());
SnapshotDto viewSnapshot = db.components().insertProjectAndSnapshot(view);
OrganizationDto organization = db.organizations().insert();
ComponentDto view = db.components().insertPrivatePortfolio(organization);
ComponentDto subView = db.components().insertComponent(newSubView(view));
MetricDto coverage = insertCoverageMetric();
dbClient.measureDao().insert(dbSession, newMeasureDto(coverage, subView, viewSnapshot).setValue(15.5d));
db.commit();
userSession.addProjectPermission(UserRole.USER, subView);
MetricDto metric = db.measures().insertMetric(m -> m.setValueType(FLOAT.name()));
db.measures().insertLiveMeasure(subView, metric, m -> m.setValue(15.5d));

SearchWsResponse result = call(singletonList(subView.getDbKey()), singletonList("coverage"));
SearchWsResponse result = call(singletonList(subView.getDbKey()), singletonList(metric.getKey()));

List<Measure> measures = result.getMeasuresList();
assertThat(measures).hasSize(1);
Measure measure = measures.get(0);
assertThat(measure.getMetric()).isEqualTo("coverage");
assertThat(measure.getMetric()).isEqualTo(metric.getKey());
assertThat(measure.getValue()).isEqualTo("15.5");
}

@Test
public void only_returns_authorized_projects() {
MetricDto metricDto = insertComplexityMetric();
ComponentDto project1 = ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization());
SnapshotDto projectSnapshot1 = db.components().insertProjectAndSnapshot(project1);
ComponentDto project2 = ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization());
SnapshotDto projectSnapshot2 = db.components().insertProjectAndSnapshot(project2);
dbClient.measureDao().insert(dbSession,
newMeasureDto(metricDto, project1, projectSnapshot1).setValue(15.5d),
newMeasureDto(metricDto, project2, projectSnapshot2).setValue(42.0d));
db.commit();
setBrowsePermissionOnUser(project1);

SearchWsResponse result = call(asList(project1.getDbKey(), project2.getDbKey()), singletonList("complexity"));
MetricDto metric = db.measures().insertMetric(m -> m.setValueType(FLOAT.name()));
ComponentDto project1 = db.components().insertPrivateProject(db.getDefaultOrganization());
ComponentDto project2 = db.components().insertPrivateProject(db.getDefaultOrganization());
db.measures().insertLiveMeasure(project1, metric, m -> m.setValue(15.5d));
db.measures().insertLiveMeasure(project2, metric, m -> m.setValue(42.0d));
Arrays.stream(new ComponentDto[]{project1}).forEach(p -> userSession.addProjectPermission(UserRole.USER, p));

SearchWsResponse result = call(asList(project1.getDbKey(), project2.getDbKey()), singletonList(metric.getKey()));

assertThat(result.getMeasuresList()).extracting(Measure::getComponent).containsOnly(project1.getDbKey());
}

@Test
public void do_not_verify_permissions_if_user_is_root() {
MetricDto metricDto = insertComplexityMetric();
ComponentDto project1 = ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization());
SnapshotDto projectSnapshot1 = db.components().insertProjectAndSnapshot(project1);
dbClient.measureDao().insert(dbSession, newMeasureDto(metricDto, project1, projectSnapshot1).setValue(15.5d));
db.commit();
MetricDto metric = db.measures().insertMetric(m -> m.setValueType(FLOAT.name()));
ComponentDto project1 = db.components().insertPrivateProject(db.getDefaultOrganization());
db.measures().insertLiveMeasure(project1, metric, m -> m.setValue(15.5d));

userSession.setNonRoot();
SearchWsResponse result = call(asList(project1.getDbKey()), singletonList("complexity"));
SearchWsResponse result = call(singletonList(project1.getDbKey()), singletonList(metric.getKey()));
assertThat(result.getMeasuresCount()).isEqualTo(0);

userSession.setRoot();
result = call(asList(project1.getDbKey()), singletonList("complexity"));
result = call(singletonList(project1.getDbKey()), singletonList(metric.getKey()));
assertThat(result.getMeasuresCount()).isEqualTo(1);
}

@Test
public void does_not_return_branch_when_using_db_key() {
MetricDto coverage = insertCoverageMetric();
MetricDto coverage = db.measures().insertMetric(m -> m.setValueType(FLOAT.name()));
ComponentDto project = db.components().insertMainBranch();
ComponentDto branch = db.components().insertProjectBranch(project);
SnapshotDto analysis = db.components().insertSnapshot(branch);
db.measures().insertMeasure(branch, analysis, coverage, m -> m.setValue(10d));
setBrowsePermissionOnUser(project);
db.measures().insertLiveMeasure(branch, coverage, m -> m.setValue(10d));
userSession.addProjectPermission(UserRole.USER, project);

SearchWsResponse result = call(asList(branch.getDbKey()), singletonList(coverage.getKey()));
SearchWsResponse result = call(singletonList(branch.getDbKey()), singletonList(coverage.getKey()));

assertThat(result.getMeasuresList()).isEmpty();
}
@@ -281,7 +270,7 @@ public class SearchActionTest {
@Test
public void fail_if_no_metric() {
ComponentDto project = db.components().insertPrivateProject();
setBrowsePermissionOnUser(project);
userSession.addProjectPermission(UserRole.USER, project);

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("The 'metricKeys' parameter is missing");
@@ -292,7 +281,7 @@ public class SearchActionTest {
@Test
public void fail_if_empty_metric() {
ComponentDto project = db.components().insertPrivateProject();
setBrowsePermissionOnUser(project);
userSession.addProjectPermission(UserRole.USER, project);

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Metric keys must be provided");
@@ -303,33 +292,33 @@ public class SearchActionTest {
@Test
public void fail_if_unknown_metric() {
ComponentDto project = db.components().insertPrivateProject();
setBrowsePermissionOnUser(project);
insertComplexityMetric();
userSession.addProjectPermission(UserRole.USER, project);
MetricDto metric = db.measures().insertMetric();

expectedException.expect(BadRequestException.class);
expectedException.expectMessage("The following metrics are not found: ncloc, violations");

call(singletonList(project.getDbKey()), newArrayList("violations", "complexity", "ncloc"));
call(singletonList(project.getDbKey()), newArrayList("violations", metric.getKey(), "ncloc"));
}

@Test
public void fail_if_no_project() {
insertComplexityMetric();
MetricDto metric = db.measures().insertMetric();

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Project keys must be provided");

call(null, singletonList("complexity"));
call(null, singletonList(metric.getKey()));
}

@Test
public void fail_if_empty_project_key() {
insertComplexityMetric();
MetricDto metric = db.measures().insertMetric();

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Project keys must be provided");

call(emptyList(), singletonList("complexity"));
call(emptyList(), singletonList(metric.getKey()));
}

@Test
@@ -338,12 +327,12 @@ public class SearchActionTest {
.mapToObj(i -> db.components().insertPrivateProject())
.map(ComponentDto::getDbKey)
.collect(Collectors.toList());
insertComplexityMetric();
MetricDto metric = db.measures().insertMetric();

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("101 projects provided, more than maximum authorized (100)");

call(keys, singletonList("complexity"));
call(keys, singletonList(metric.getKey()));
}

@Test
@@ -352,48 +341,48 @@ public class SearchActionTest {
.mapToObj(i -> db.components().insertPrivateProject())
.map(ComponentDto::getDbKey)
.collect(Collectors.toList());
insertComplexityMetric();
MetricDto metric = db.measures().insertMetric();

call(keys, singletonList("complexity"));
call(keys, singletonList(metric.getKey()));
}

@Test
public void fail_if_module() {
ComponentDto project = db.components().insertPrivateProject();
ComponentDto module = db.components().insertComponent(newModuleDto(project));
setBrowsePermissionOnUser(project);
insertComplexityMetric();
userSession.addProjectPermission(UserRole.USER, project);
MetricDto metric = db.measures().insertMetric();

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Only component of qualifiers [TRK, APP, VW, SVW] are allowed");

call(singletonList(module.getDbKey()), singletonList("complexity"));
call(singletonList(module.getDbKey()), singletonList(metric.getKey()));
}

@Test
public void fail_if_directory() {
ComponentDto project = db.components().insertPrivateProject();
ComponentDto dir = db.components().insertComponent(newDirectory(project, "dir"));
setBrowsePermissionOnUser(project);
insertComplexityMetric();
userSession.addProjectPermission(UserRole.USER, project);
MetricDto metric = db.measures().insertMetric();

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Only component of qualifiers [TRK, APP, VW, SVW] are allowed");

call(singletonList(dir.getDbKey()), singletonList("complexity"));
call(singletonList(dir.getDbKey()), singletonList(metric.getKey()));
}

@Test
public void fail_if_file() {
ComponentDto project = db.components().insertPrivateProject();
ComponentDto file = db.components().insertComponent(newFileDto(project));
setBrowsePermissionOnUser(project);
insertComplexityMetric();
userSession.addProjectPermission(UserRole.USER, project);
MetricDto metric = db.measures().insertMetric();

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Only component of qualifiers [TRK, APP, VW, SVW] are allowed");

call(singletonList(file.getDbKey()), singletonList("complexity"));
call(singletonList(file.getDbKey()), singletonList(metric.getKey()));
}

@Test
@@ -418,132 +407,4 @@ public class SearchActionTest {
}
return request.executeProtobuf(SearchWsResponse.class);
}

private static MetricDto newMetricDtoWithoutOptimization() {
return newMetricDto()
.setWorstValue(null)
.setBestValue(null)
.setOptimizedBestValue(false)
.setUserManaged(false);
}

private MetricDto insertNewViolationsMetric() {
MetricDto metric = dbClient.metricDao().insert(dbSession, newMetricDtoWithoutOptimization()
.setKey("new_violations")
.setShortName("New issues")
.setDescription("New Issues")
.setDomain("Issues")
.setValueType("INT")
.setDirection(-1)
.setQualitative(true)
.setHidden(false)
.setUserManaged(false)
.setOptimizedBestValue(true)
.setBestValue(0.0d));
db.commit();
return metric;
}

private MetricDto insertNclocMetric() {
MetricDto metric = dbClient.metricDao().insert(dbSession, newMetricDtoWithoutOptimization()
.setKey("ncloc")
.setShortName("Lines of code")
.setDescription("Non Commenting Lines of Code")
.setDomain("Size")
.setValueType("INT")
.setDirection(-1)
.setQualitative(false)
.setHidden(false)
.setUserManaged(false));
db.commit();
return metric;
}

private MetricDto insertComplexityMetric() {
MetricDto metric = dbClient.metricDao().insert(dbSession, newMetricDtoWithoutOptimization()
.setKey("complexity")
.setShortName("Complexity")
.setDescription("Cyclomatic complexity")
.setDomain("Complexity")
.setValueType("INT")
.setDirection(-1)
.setQualitative(false)
.setHidden(false)
.setUserManaged(false));
db.commit();
return metric;
}

private MetricDto insertCoverageMetric() {
MetricDto metric = dbClient.metricDao().insert(dbSession, newMetricDtoWithoutOptimization()
.setKey("coverage")
.setShortName("Coverage")
.setDescription("Code Coverage")
.setDomain("Coverage")
.setValueType(Metric.ValueType.FLOAT.name())
.setDirection(1)
.setQualitative(false)
.setHidden(false)
.setUserManaged(false));
db.commit();
return metric;
}

private List<String> insertJsonExampleData() {
List<String> projectKeys = new ArrayList<>();
OrganizationDto organizationDto = db.organizations().insert();
ComponentDto project1 = ComponentTesting.newPrivateProjectDto(organizationDto).setDbKey("MY_PROJECT_1").setName("Project 1");
ComponentDto project2 = ComponentTesting.newPrivateProjectDto(organizationDto).setDbKey("MY_PROJECT_2").setName("Project 2");
ComponentDto project3 = ComponentTesting.newPrivateProjectDto(organizationDto).setDbKey("MY_PROJECT_3").setName("Project 3");
projectKeys.addAll(asList(project1.getDbKey(), project2.getDbKey(), project3.getDbKey()));
db.components().insertComponents(project1, project2, project3);
SnapshotDto projectSnapshot1 = dbClient.snapshotDao().insert(dbSession, newAnalysis(project1)
.setPeriodDate(parseDateTime("2016-01-11T10:49:50+0100").getTime())
.setPeriodMode("previous_version")
.setPeriodParam("1.0-SNAPSHOT"));
SnapshotDto projectSnapshot2 = dbClient.snapshotDao().insert(dbSession, newAnalysis(project2)
.setPeriodDate(parseDateTime("2016-01-11T10:49:50+0100").getTime())
.setPeriodMode("previous_version")
.setPeriodParam("1.0-SNAPSHOT"));
SnapshotDto projectSnapshot3 = dbClient.snapshotDao().insert(dbSession, newAnalysis(project3)
.setPeriodDate(parseDateTime("2016-01-11T10:49:50+0100").getTime())
.setPeriodMode("previous_version")
.setPeriodParam("1.0-SNAPSHOT"));

MetricDto complexity = insertComplexityMetric();
dbClient.measureDao().insert(dbSession,
newMeasureDto(complexity, project1, projectSnapshot1)
.setValue(12.0d),
newMeasureDto(complexity, project2, projectSnapshot2)
.setValue(35.0d)
.setVariation(0.0d),
newMeasureDto(complexity, project3, projectSnapshot3)
.setValue(42.0d));

MetricDto ncloc = insertNclocMetric();
dbClient.measureDao().insert(dbSession,
newMeasureDto(ncloc, project1, projectSnapshot1)
.setValue(114.0d),
newMeasureDto(ncloc, project2, projectSnapshot2)
.setValue(217.0d)
.setVariation(0.0d),
newMeasureDto(ncloc, project3, projectSnapshot3)
.setValue(1984.0d));

MetricDto newViolations = insertNewViolationsMetric();
dbClient.measureDao().insert(dbSession,
newMeasureDto(newViolations, project1, projectSnapshot1)
.setVariation(25.0d),
newMeasureDto(newViolations, project2, projectSnapshot2)
.setVariation(25.0d),
newMeasureDto(newViolations, project3, projectSnapshot3)
.setVariation(255.0d));
db.commit();
setBrowsePermissionOnUser(project1, project2, project3);
return projectKeys;
}

private void setBrowsePermissionOnUser(ComponentDto... projects) {
Arrays.stream(projects).forEach(p -> userSession.addProjectPermission(UserRole.USER, p));
}
}

+ 0
- 0
server/sonar-server/src/test/java/org/sonar/server/measure/ws/SearchHistoryActionTest.java Datei anzeigen


Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.

Laden…
Abbrechen
Speichern