]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10670 Count ncloc of whole instance with biggest long-living branch
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Fri, 11 May 2018 15:25:22 +0000 (17:25 +0200)
committerSonarTech <sonartech@sonarsource.com>
Thu, 24 May 2018 18:20:48 +0000 (20:20 +0200)
24 files changed:
server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/measure/LiveMeasureMapper.java
server/sonar-db-dao/src/main/resources/org/sonar/db/measure/LiveMeasureMapper.xml
server/sonar-db-dao/src/test/java/org/sonar/db/measure/LiveMeasureDaoTest.java
server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/XooProjectBuilder.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/edition/ws/FormDataAction.java
server/sonar-server/src/main/java/org/sonar/server/measure/index/ProjectMeasuresIndex.java
server/sonar-server/src/main/java/org/sonar/server/measure/index/ProjectMeasuresStatistics.java
server/sonar-server/src/main/java/org/sonar/server/telemetry/TelemetryData.java
server/sonar-server/src/main/java/org/sonar/server/telemetry/TelemetryDataJsonWriter.java
server/sonar-server/src/main/java/org/sonar/server/telemetry/TelemetryDataLoader.java
server/sonar-server/src/test/java/org/sonar/server/edition/ws/FormDataActionTest.java
server/sonar-server/src/test/java/org/sonar/server/measure/index/ProjectMeasuresIndexTest.java
server/sonar-server/src/test/java/org/sonar/server/platform/ws/ClusterSystemInfoWriterTest.java
server/sonar-server/src/test/java/org/sonar/server/platform/ws/StandaloneSystemInfoWriterTest.java
server/sonar-server/src/test/java/org/sonar/server/telemetry/TelemetryDaemonTest.java
server/sonar-server/src/test/resources/org/sonar/server/telemetry/telemetry-example.json
tests/src/test/java/org/sonarqube/tests/ce/CeWorkersPauseTest.java
tests/src/test/java/org/sonarqube/tests/component/ModuleTest.java
tests/src/test/java/org/sonarqube/tests/project/ProjectInfoTest.java
tests/src/test/java/org/sonarqube/tests/project/ProjectKeyUpdateTest.java
tests/src/test/java/org/sonarqube/tests/serverSystem/BlueGreenTest.java
tests/src/test/java/org/sonarqube/tests/telemetry/TelemetryUploadTest.java
tests/src/test/java/util/XooProjectBuilder.java [deleted file]

index 14e8636561c870d4b4e64bc229d4dfcc3736c6e0..52cfdb646aec3d2cb11fc1f7b270544f6f65ccd4 100644 (file)
@@ -29,9 +29,12 @@ 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.BranchType;
 import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.KeyType;
 
 import static java.util.Collections.singletonList;
+import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY;
 import static org.sonar.db.DatabaseUtils.executeLargeInputs;
 
 public class LiveMeasureDao implements Dao {
@@ -78,6 +81,17 @@ public class LiveMeasureDao implements Dao {
     mapper(dbSession).selectTreeByQuery(query, baseComponent.uuid(), query.getUuidPath(baseComponent), resultHandler);
   }
 
+  /**
+   * Example:
+   * If Main Branch = 0 LOCs (provisioned but never analyzed) and the "largest long-lived branch" is 120 LOCs, I'm expecting to consider the value 120.
+   * If Main Branch = 100 LOCs and the "largest long-lived branch" is 120 LOCs, I'm expecting to consider the value 120.
+   * If Main Branch = 100 LOCs and the "largest long-lived branch" is 80 LOCs, I'm expecting to consider the value 100.
+   */
+  public long sumNclocOfBiggestLongLivingBranch(DbSession dbSession) {
+    Long ncloc = mapper(dbSession).sumNclocOfBiggestLongLivingBranch(NCLOC_KEY, KeyType.BRANCH, BranchType.LONG);
+    return ncloc == null ? 0L : ncloc;
+  }
+
   public void insert(DbSession dbSession, LiveMeasureDto dto) {
     mapper(dbSession).insert(dto, Uuids.create(), null, system2.now());
   }
index b6a115470dc506e9c84da8f6e9d91196ba2f36f8..c0fca2f6a1ea71fea308d81e3044913dda8e6fa7 100644 (file)
@@ -24,6 +24,8 @@ import java.util.List;
 import javax.annotation.Nullable;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.session.ResultHandler;
+import org.sonar.db.component.BranchType;
+import org.sonar.db.component.KeyType;
 
 public interface LiveMeasureMapper {
 
@@ -41,6 +43,11 @@ public interface LiveMeasureMapper {
     @Param("baseUuidPath") String baseUuidPath,
     ResultHandler<LiveMeasureDto> resultHandler);
 
+  Long sumNclocOfBiggestLongLivingBranch(
+    @Param("ncloc") String nclocKey,
+    @Param("branch") KeyType branchOrPullRequest,
+    @Param("branchType") BranchType branchType);
+
   void insert(
     @Param("dto") LiveMeasureDto dto,
     @Param("uuid") String uuid,
index 38c973317367ba88caf16654cb8faceb8dbb618b..6b571f5dc713b13bba5e4366c44a97f1734d710d 100644 (file)
     </foreach>
   </select>
 
+  <select id="sumNclocOfBiggestLongLivingBranch" parameterType="map" resultType="long">
+    select sum(sumncloc.maxncloc) from (
+    select b.project_uuid as projectUuid, max(lm.value) as maxncloc
+    from live_measures lm
+    inner join metrics m on m.id = lm.metric_id
+    inner join projects p on p.uuid = lm.component_uuid
+    inner join project_branches b on b.uuid = p.uuid
+    where
+    m.name = #{ncloc, jdbcType=VARCHAR}
+    and p.enabled = ${_true}
+    and p.scope = 'PRJ'
+    and p.qualifier = 'TRK'
+    and p.copy_component_uuid is null
+    and b.branch_type = #{branchType, jdbcType=VARCHAR}
+    and b.key_type = #{branch, jdbcType=VARCHAR}
+    group by b.project_uuid
+    ) sumncloc
+  </select>
+
   <insert id="insert" parameterType="map" useGeneratedKeys="false">
     insert into live_measures (
     uuid,
index a37fc2ce3b3f4aee3e4479616975bc6ad82b1c71..ed1b1e10e0f795d29306065120eedf4c97db020c 100644 (file)
@@ -27,8 +27,10 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.sonar.api.utils.System2;
 import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchType;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.metric.MetricDto;
+import org.sonar.db.organization.OrganizationDto;
 
 import static java.util.Arrays.asList;
 import static java.util.Collections.emptyList;
@@ -36,6 +38,7 @@ import static java.util.Collections.singleton;
 import static java.util.Collections.singletonList;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.groups.Tuple.tuple;
+import static org.sonar.api.measures.Metric.ValueType.INT;
 import static org.sonar.db.component.ComponentTesting.newFileDto;
 import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
 import static org.sonar.db.measure.MeasureTesting.newLiveMeasure;
@@ -54,7 +57,7 @@ public class LiveMeasureDaoTest {
   }
 
   @Test
-  public void test_selectByComponentUuidsAndMetricIds() {
+  public void selectByComponentUuidsAndMetricIds() {
     LiveMeasureDto measure1 = newLiveMeasure().setMetricId(metric.getId());
     LiveMeasureDto measure2 = newLiveMeasure().setMetricId(metric.getId());
     underTest.insert(db.getSession(), measure1);
@@ -94,7 +97,7 @@ public class LiveMeasureDaoTest {
   }
 
   @Test
-  public void test_selectByComponentUuidsAndMetricKeys() {
+  public void selectByComponentUuidsAndMetricKeys() {
     LiveMeasureDto measure1 = newLiveMeasure().setMetricId(metric.getId());
     LiveMeasureDto measure2 = newLiveMeasure().setMetricId(metric.getId());
     underTest.insert(db.getSession(), measure1);
@@ -133,7 +136,7 @@ public class LiveMeasureDaoTest {
   }
 
   @Test
-  public void test_selectMeasure() {
+  public void selectMeasure() {
     MetricDto metric = db.measures().insertMetric();
     LiveMeasureDto stored = newLiveMeasure().setMetricId(metric.getId());
     underTest.insert(db.getSession(), stored);
@@ -198,6 +201,39 @@ public class LiveMeasureDaoTest {
       .contains(project.uuid(), file.uuid(), metric.getId(), 3.14, 0.1, "text_value", "text_value");
   }
 
+  @Test
+  public void countNcloc() {
+    OrganizationDto organization = db.organizations().insert();
+    MetricDto ncloc = db.measures().insertMetric(m -> m.setKey("ncloc").setValueType(INT.toString()));
+    MetricDto lines = db.measures().insertMetric(m -> m.setKey("lines").setValueType(INT.toString()));
+
+    ComponentDto simpleProject = db.components().insertMainBranch(organization);
+    db.measures().insertLiveMeasure(simpleProject, ncloc, m -> m.setValue(10d));
+
+    ComponentDto projectWithBiggerLongLivingBranch = db.components().insertMainBranch(organization);
+    ComponentDto bigLongLivingBranch = db.components().insertProjectBranch(projectWithBiggerLongLivingBranch, b -> b.setBranchType(BranchType.LONG));
+    db.measures().insertLiveMeasure(projectWithBiggerLongLivingBranch, ncloc, m -> m.setValue(100d));
+    db.measures().insertLiveMeasure(bigLongLivingBranch, ncloc, m -> m.setValue(200d));
+
+    ComponentDto projectWithLinesButNoLoc = db.components().insertMainBranch(organization);
+    db.measures().insertLiveMeasure(projectWithLinesButNoLoc, lines, m -> m.setValue(365d));
+    db.measures().insertLiveMeasure(projectWithLinesButNoLoc, ncloc, m -> m.setValue(0d));
+
+    long result = underTest.sumNclocOfBiggestLongLivingBranch(db.getSession());
+
+    assertThat(result).isEqualTo(10L + 200L);
+  }
+
+  @Test
+  public void countNcloc_empty() {
+    db.measures().insertMetric(m -> m.setKey("ncloc").setValueType(INT.toString()));
+    db.measures().insertMetric(m -> m.setKey("lines").setValueType(INT.toString()));
+
+    long result = underTest.sumNclocOfBiggestLongLivingBranch(db.getSession());
+
+    assertThat(result).isEqualTo(0L);
+  }
+
   @Test
   public void insert_data() {
     byte[] data = "text_value".getBytes(StandardCharsets.UTF_8);
@@ -215,7 +251,7 @@ public class LiveMeasureDaoTest {
   }
 
   @Test
-  public void test_insertOrUpdate() {
+  public void insertOrUpdate() {
     // insert
     LiveMeasureDto dto = newLiveMeasure();
     underTest.insertOrUpdate(db.getSession(), dto, "foo");
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/XooProjectBuilder.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/XooProjectBuilder.java
new file mode 100644 (file)
index 0000000..36deec6
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonarqube.qa.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.StringUtils;
+
+import static java.util.Arrays.asList;
+
+public class XooProjectBuilder {
+  private final String key;
+  private final List<String> moduleKeys = new ArrayList<>();
+  private int filesPerModule = 1;
+  private Properties projectProperties = new Properties();
+
+  public XooProjectBuilder(String projectKey) {
+    this.key = projectKey;
+  }
+
+  public XooProjectBuilder addModules(String key, String... otherKeys) {
+    this.moduleKeys.add(key);
+    this.moduleKeys.addAll(asList(otherKeys));
+    return this;
+  }
+
+  public XooProjectBuilder setFilesPerModule(int i) {
+    this.filesPerModule = i;
+    return this;
+  }
+
+  public XooProjectBuilder addProjectProperties(String... keyValueProperties) {
+    for (int i = 0; i < keyValueProperties.length; i += 2) {
+      this.projectProperties.setProperty(keyValueProperties[i], keyValueProperties[i + 1]);
+    }
+    return this;
+  }
+
+  public File build(File dir) {
+    for (String moduleKey : moduleKeys) {
+      generateModule(moduleKey, new File(dir, moduleKey), new Properties());
+    }
+    projectProperties.setProperty("sonar.modules", StringUtils.join(moduleKeys, ","));
+    generateModule(key, dir, projectProperties);
+    return dir;
+  }
+
+  private void generateModule(String key, File dir, Properties additionalProps) {
+    try {
+      File sourceDir = new File(dir, "src");
+      FileUtils.forceMkdir(sourceDir);
+      for (int i = 0; i < filesPerModule; i++) {
+        File sourceFile = new File(sourceDir, "File" + i + ".xoo");
+        FileUtils.write(sourceFile, "content of " + sourceFile.getName());
+
+        File measuresFile = new File(sourceFile + ".measures");
+        FileUtils.write(measuresFile, "ncloc:10\n" +
+          "comment_lines:3\n");
+      }
+      Properties props = new Properties();
+      props.setProperty("sonar.projectKey", key);
+      props.setProperty("sonar.projectName", key);
+      props.setProperty("sonar.projectVersion", "1.0");
+      props.setProperty("sonar.sources", sourceDir.getName());
+      props.putAll(additionalProps);
+      File propsFile = new File(dir, "sonar-project.properties");
+      try (OutputStream output = FileUtils.openOutputStream(propsFile)) {
+        props.store(output, "generated");
+      }
+    } catch (IOException e) {
+      throw new IllegalStateException(e);
+    }
+  }
+}
index ebb732e198030f93bed60857752f5bfde220ff6d..601b5b9de8839eb751e30257625a0f0d657be48b 100644 (file)
@@ -23,20 +23,22 @@ import org.sonar.api.platform.Server;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
-import org.sonar.server.measure.index.ProjectMeasuresIndex;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
 import org.sonar.server.user.UserSession;
-import org.sonar.server.ws.WsUtils;
 import org.sonarqube.ws.Editions.FormDataResponse;
 
+import static org.sonar.server.ws.WsUtils.writeProtobuf;
+
 public class FormDataAction implements EditionsWsAction {
   private final UserSession userSession;
   private final Server server;
-  private final ProjectMeasuresIndex measuresIndex;
+  private final DbClient dbClient;
 
-  public FormDataAction(UserSession userSession, Server server, ProjectMeasuresIndex measuresIndex) {
+  public FormDataAction(UserSession userSession, Server server, DbClient dbClient) {
     this.userSession = userSession;
     this.server = server;
-    this.measuresIndex = measuresIndex;
+    this.dbClient = dbClient;
   }
 
   @Override
@@ -56,13 +58,16 @@ public class FormDataAction implements EditionsWsAction {
       .checkLoggedIn()
       .checkIsSystemAdministrator();
 
-    String serverId = server.getId();
-    long nloc = measuresIndex.searchTelemetryStatistics().getNcloc();
-
     FormDataResponse responsePayload = FormDataResponse.newBuilder()
-      .setNcloc(nloc)
-      .setServerId(serverId)
+      .setNcloc(computeNcloc())
+      .setServerId(server.getId())
       .build();
-    WsUtils.writeProtobuf(responsePayload, request, response);
+    writeProtobuf(responsePayload, request, response);
+  }
+
+  private long computeNcloc() {
+    try (DbSession dbSession = dbClient.openSession(false)) {
+      return dbClient.liveMeasureDao().sumNclocOfBiggestLongLivingBranch(dbSession);
+    }
   }
 }
index 93fe07f6d40bf3f9310ffb82958c7065bb7f8672..a759e0326c5b4fe971ef165f2010fd038e86156f 100644 (file)
@@ -75,7 +75,6 @@ import static org.elasticsearch.search.sort.SortOrder.DESC;
 import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
 import static org.sonar.api.measures.CoreMetrics.COVERAGE_KEY;
 import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES_DENSITY_KEY;
-import static org.sonar.api.measures.CoreMetrics.LINES_KEY;
 import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY;
 import static org.sonar.api.measures.CoreMetrics.NEW_COVERAGE_KEY;
 import static org.sonar.api.measures.CoreMetrics.NEW_DUPLICATED_LINES_DENSITY_KEY;
@@ -105,8 +104,8 @@ import static org.sonarqube.ws.client.project.ProjectsWsParameters.FILTER_LANGUA
 import static org.sonarqube.ws.client.project.ProjectsWsParameters.FILTER_TAGS;
 import static org.sonarqube.ws.client.project.ProjectsWsParameters.MAX_PAGE_SIZE;
 
-@ComputeEngineSide
 @ServerSide
+@ComputeEngineSide
 public class ProjectMeasuresIndex {
 
   public static final List<String> SUPPORTED_FACETS = ImmutableList.of(
@@ -203,16 +202,15 @@ public class ProjectMeasuresIndex {
         .order(Terms.Order.count(false))
         .subAggregation(sum(FIELD_DISTRIB_NCLOC).field(FIELD_DISTRIB_NCLOC))));
 
-    Stream.of(LINES_KEY, NCLOC_KEY)
-      .forEach(metric -> request.addAggregation(AggregationBuilders.nested(metric, FIELD_MEASURES)
-        .subAggregation(AggregationBuilders.filter(metric + "_filter", termQuery(FIELD_MEASURES_KEY, metric))
-          .subAggregation(sum(metric + "_filter_sum").field(FIELD_MEASURES_VALUE)))));
+    request.addAggregation(AggregationBuilders.nested(NCLOC_KEY, FIELD_MEASURES)
+      .subAggregation(AggregationBuilders.filter(NCLOC_KEY + "_filter", termQuery(FIELD_MEASURES_KEY, NCLOC_KEY))
+        .subAggregation(sum(NCLOC_KEY + "_filter_sum").field(FIELD_MEASURES_VALUE))));
 
     ProjectMeasuresStatistics.Builder statistics = ProjectMeasuresStatistics.builder();
 
     SearchResponse response = request.get();
     statistics.setProjectCount(response.getHits().getTotalHits());
-    Stream.of(LINES_KEY, NCLOC_KEY)
+    Stream.of(NCLOC_KEY)
       .map(metric -> (Nested) response.getAggregations().get(metric))
       .map(nested -> (Filter) nested.getAggregations().get(nested.getName() + "_filter"))
       .map(filter -> (Sum) filter.getAggregations().get(filter.getName() + "_sum"))
index 369ac649da0897e26119ae6f5606bc4abdfbf5d7..0012e40383e4ba29bdf701ba8047df757c4af6a1 100644 (file)
@@ -22,19 +22,16 @@ package org.sonar.server.measure.index;
 import java.util.Map;
 
 import static java.util.Objects.requireNonNull;
-import static org.sonar.api.measures.CoreMetrics.LINES_KEY;
 import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY;
 
 public class ProjectMeasuresStatistics {
   private final long projectCount;
-  private final long lines;
   private final long ncloc;
   private final Map<String, Long> projectCountByLanguage;
   private final Map<String, Long> nclocByLanguage;
 
   private ProjectMeasuresStatistics(Builder builder) {
     projectCount = builder.projectCount;
-    lines = builder.lines;
     ncloc = builder.ncloc;
     projectCountByLanguage = builder.projectCountByLanguage;
     nclocByLanguage = builder.nclocByLanguage;
@@ -44,10 +41,10 @@ public class ProjectMeasuresStatistics {
     return projectCount;
   }
 
-  public long getLines() {
-    return lines;
-  }
-
+  /**
+   * @deprecated since 7.2 Global Ncloc computation should rely on org.sonar.db.measure.LiveMeasureDao#countNcloc(org.sonar.db.DbSession)
+   */
+  @Deprecated
   public long getNcloc() {
     return ncloc;
   }
@@ -66,7 +63,6 @@ public class ProjectMeasuresStatistics {
 
   public static class Builder {
     private Long projectCount;
-    private Long lines;
     private Long ncloc;
     private Map<String, Long> projectCountByLanguage;
     private Map<String, Long> nclocByLanguage;
@@ -81,15 +77,10 @@ public class ProjectMeasuresStatistics {
     }
 
     public Builder setSum(String metric, long value) {
-      switch (metric) {
-        case LINES_KEY:
-          this.lines = value;
-          break;
-        case NCLOC_KEY:
-          this.ncloc = value;
-          break;
-        default:
-          throw new IllegalStateException("Metric not supported: " + metric);
+      if (NCLOC_KEY.equals(metric)) {
+        this.ncloc = value;
+      } else {
+        throw new IllegalStateException("Metric not supported: " + metric);
       }
       return this;
     }
@@ -105,7 +96,6 @@ public class ProjectMeasuresStatistics {
 
     public ProjectMeasuresStatistics build() {
       requireNonNull(projectCount);
-      requireNonNull(lines);
       requireNonNull(ncloc);
       requireNonNull(projectCountByLanguage);
       requireNonNull(nclocByLanguage);
index 5b08138c7298f7b7bc8ccd335f8f47120f66aca2..a8790069333cf8094073b703c7196f8e3493f7e4 100644 (file)
@@ -28,7 +28,6 @@ public class TelemetryData {
   private final String serverId;
   private final String version;
   private final Map<String, String> plugins;
-  private final long lines;
   private final long ncloc;
   private final long userCount;
   private final long projectCount;
@@ -41,8 +40,7 @@ public class TelemetryData {
     serverId = builder.serverId;
     version = builder.version;
     plugins = builder.plugins;
-    lines = builder.projectMeasuresStatistics.getLines();
-    ncloc = builder.projectMeasuresStatistics.getNcloc();
+    ncloc = builder.ncloc;
     userCount = builder.userCount;
     projectCount = builder.projectMeasuresStatistics.getProjectCount();
     usingBranches = builder.usingBranches;
@@ -63,10 +61,6 @@ public class TelemetryData {
     return plugins;
   }
 
-  public long getLines() {
-    return lines;
-  }
-
   public long getNcloc() {
     return ncloc;
   }
@@ -106,6 +100,7 @@ public class TelemetryData {
     private Map<String, String> plugins;
     private Database database;
     private ProjectMeasuresStatistics projectMeasuresStatistics;
+    private Long ncloc;
     private Boolean usingBranches;
 
     private Builder() {
@@ -122,16 +117,24 @@ public class TelemetryData {
       return this;
     }
 
-    void setUserCount(long userCount) {
+    Builder setUserCount(long userCount) {
       this.userCount = userCount;
+      return this;
     }
 
-    void setPlugins(Map<String, String> plugins) {
+    Builder setPlugins(Map<String, String> plugins) {
       this.plugins = plugins;
+      return this;
     }
 
-    void setProjectMeasuresStatistics(ProjectMeasuresStatistics projectMeasuresStatistics) {
+    Builder setProjectMeasuresStatistics(ProjectMeasuresStatistics projectMeasuresStatistics) {
       this.projectMeasuresStatistics = projectMeasuresStatistics;
+      return this;
+    }
+
+    Builder setNcloc(long ncloc) {
+      this.ncloc = ncloc;
+      return this;
     }
 
     Builder setDatabase(Database database) {
@@ -149,6 +152,7 @@ public class TelemetryData {
       requireNonNull(version);
       requireNonNull(plugins);
       requireNonNull(projectMeasuresStatistics);
+      requireNonNull(ncloc);
       requireNonNull(database);
       requireNonNull(usingBranches);
 
index 1e35468805a990bae6ff49f9a49e063f14fc446d..1b3df1148cdc2e8321e94df77b678bc432542569 100644 (file)
@@ -21,7 +21,6 @@ package org.sonar.server.telemetry;
 
 import org.sonar.api.utils.text.JsonWriter;
 
-import static org.sonar.api.measures.CoreMetrics.LINES_KEY;
 import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY;
 
 public class TelemetryDataJsonWriter {
@@ -50,7 +49,6 @@ public class TelemetryDataJsonWriter {
     json.prop("userCount", statistics.getUserCount());
     json.prop("projectCount", statistics.getProjectCount());
     json.prop("usingBranches", statistics.isUsingBranches());
-    json.prop(LINES_KEY, statistics.getLines());
     json.prop(NCLOC_KEY, statistics.getNcloc());
     json.name("projectCountByLanguage");
     json.beginArray();
index 8cbca030f51924233566f4c94af5da8b9d52be9c..601b0898092bfa7f046fdf57558e752dc8130830 100644 (file)
@@ -23,6 +23,7 @@ import java.sql.DatabaseMetaData;
 import java.sql.SQLException;
 import java.util.Map;
 import java.util.function.Function;
+import org.sonar.api.ce.ComputeEngineSide;
 import org.sonar.api.platform.Server;
 import org.sonar.api.server.ServerSide;
 import org.sonar.core.platform.PluginInfo;
@@ -37,6 +38,7 @@ import org.sonar.server.telemetry.TelemetryData.Database;
 import org.sonar.server.user.index.UserIndex;
 import org.sonar.server.user.index.UserQuery;
 
+@ComputeEngineSide
 @ServerSide
 public class TelemetryDataLoader {
   private final Server server;
@@ -68,6 +70,7 @@ public class TelemetryDataLoader {
     try (DbSession dbSession = dbClient.openSession(false)) {
       data.setDatabase(loadDatabaseMetadata(dbSession));
       data.setUsingBranches(dbClient.branchDao().hasNonMainBranches(dbSession));
+      data.setNcloc(dbClient.liveMeasureDao().sumNclocOfBiggestLongLivingBranch(dbSession));
     }
 
     return data.build();
index 120ea0c39ba39d19d36e67d0f3354022363d4871..7a6e8e68dbc48e6d340763b2fbc634a0d74ba837 100644 (file)
  */
 package org.sonar.server.edition.ws;
 
-import com.tngtech.java.junit.dataprovider.DataProvider;
 import com.tngtech.java.junit.dataprovider.DataProviderRunner;
-import java.io.IOException;
-import java.util.Arrays;
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.junit.runner.RunWith;
 import org.sonar.api.platform.Server;
 import org.sonar.api.server.ws.WebService;
-import org.sonar.server.edition.EditionManagementState;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.metric.MetricDto;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.UnauthorizedException;
-import org.sonar.server.measure.index.ProjectMeasuresIndex;
-import org.sonar.server.measure.index.ProjectMeasuresStatistics;
 import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.ws.TestRequest;
 import org.sonar.server.ws.WsActionTester;
-import org.sonar.test.JsonAssert;
-import org.sonarqube.ws.MediaTypes;
 import org.sonarqube.ws.Editions.FormDataResponse;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
-import static org.sonar.server.edition.EditionManagementState.PendingStatus.NONE;
+import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY;
+import static org.sonar.api.measures.Metric.ValueType.INT;
+import static org.sonar.test.JsonAssert.assertJson;
 
 @RunWith(DataProviderRunner.class)
 public class FormDataActionTest {
+
   @Rule
   public ExpectedException expectedException = ExpectedException.none();
   @Rule
   public UserSessionRule userSessionRule = UserSessionRule.standalone();
+  @Rule
+  public DbTester db = DbTester.create();
 
   private Server server = mock(Server.class);
-  private ProjectMeasuresStatistics stats = mock(ProjectMeasuresStatistics.class);
-  private ProjectMeasuresIndex measuresIndex = mock(ProjectMeasuresIndex.class);
-  private FormDataAction underTest = new FormDataAction(userSessionRule, server, measuresIndex);
-  private WsActionTester actionTester = new WsActionTester(underTest);
-
-  @Before
-  public void setUp() {
-    when(measuresIndex.searchTelemetryStatistics()).thenReturn(stats);
-  }
+  private DbClient dbClient = db.getDbClient();
+  private FormDataAction underTest = new FormDataAction(userSessionRule, server, dbClient);
+
+  private WsActionTester ws = new WsActionTester(underTest);
 
   @Test
-  public void verify_definition() {
-    WebService.Action def = actionTester.getDef();
+  public void definition() {
+    WebService.Action def = ws.getDef();
 
     assertThat(def.key()).isEqualTo("form_data");
     assertThat(def.since()).isEqualTo("6.7");
@@ -80,7 +75,7 @@ public class FormDataActionTest {
   @Test
   public void request_fails_if_user_not_logged_in() {
     userSessionRule.anonymous();
-    TestRequest request = actionTester.newRequest();
+    TestRequest request = ws.newRequest();
 
     expectedException.expect(UnauthorizedException.class);
     expectedException.expectMessage("Authentication is required");
@@ -91,7 +86,7 @@ public class FormDataActionTest {
   @Test
   public void request_fails_if_user_is_not_system_administer() {
     userSessionRule.logIn();
-    TestRequest request = actionTester.newRequest();
+    TestRequest request = ws.newRequest();
 
     expectedException.expect(ForbiddenException.class);
     expectedException.expectMessage("Insufficient privileges");
@@ -100,37 +95,36 @@ public class FormDataActionTest {
   }
 
   @Test
-  public void verify_example() {
+  public void json_example() {
     userSessionRule.logIn().setSystemAdministrator();
     when(server.getId()).thenReturn("uuid");
-    when(stats.getNcloc()).thenReturn(12345L);
+    setNcloc(12345L);
 
-    TestRequest request = actionTester.newRequest();
+    String result = ws.newRequest().execute().getInput();
 
-    JsonAssert.assertJson(request.execute().getInput()).isSimilarTo(actionTester.getDef().responseExampleAsString());
+    assertJson(result).isSimilarTo(ws.getDef().responseExampleAsString());
   }
 
   @Test
-  public void returns_server_id_and_nloc() throws IOException {
+  public void returns_server_id_and_nloc() {
     userSessionRule.logIn().setSystemAdministrator();
     when(server.getId()).thenReturn("myserver");
-    when(stats.getNcloc()).thenReturn(1000L);
+    long ncloc = 256L;
+    setNcloc(ncloc);
 
     FormDataResponse expectedResponse = FormDataResponse.newBuilder()
       .setServerId("myserver")
-      .setNcloc(1000L)
+      .setNcloc(ncloc)
       .build();
 
-    TestRequest request = actionTester.newRequest().setMediaType(MediaTypes.PROTOBUF);
+    FormDataResponse result = ws.newRequest().executeProtobuf(FormDataResponse.class);
 
-    assertThat(FormDataResponse.parseFrom(request.execute().getInputStream())).isEqualTo(expectedResponse);
+    assertThat(result).isEqualTo(expectedResponse);
   }
 
-  @DataProvider
-  public static Object[][] notNonePendingInstallationStatuses() {
-    return Arrays.stream(EditionManagementState.PendingStatus.values())
-      .filter(s -> s != NONE)
-      .map(s -> new Object[] {s})
-      .toArray(Object[][]::new);
+  private void setNcloc(double ncloc) {
+    ComponentDto project = db.components().insertMainBranch();
+    MetricDto nclocMetric = db.measures().insertMetric(m -> m.setValueType(INT.toString()).setKey(NCLOC_KEY));
+    db.measures().insertLiveMeasure(project, nclocMetric, m -> m.setValue(ncloc));
   }
 }
index e452edbcf09cd33c66c44181de7bf05331e8918f..8d8627d429b96c3dec6eeede0a6e03842b8c8165 100644 (file)
@@ -1373,18 +1373,16 @@ public class ProjectMeasuresIndexTest {
   @Test
   public void search_statistics() {
     es.putDocuments(INDEX_TYPE_PROJECT_MEASURES,
-      newDoc("lines", 10, "ncloc", 20, "coverage", 80)
+      newDoc("lines", 10, "coverage", 80)
         .setLanguages(Arrays.asList("java", "cs", "js"))
         .setNclocLanguageDistributionFromMap(ImmutableMap.of("java", 200, "cs", 250, "js", 50)),
-      newDoc("lines", 20, "ncloc", 30, "coverage", 80)
+      newDoc("lines", 20, "coverage", 80)
         .setLanguages(Arrays.asList("java", "python", "kotlin"))
         .setNclocLanguageDistributionFromMap(ImmutableMap.of("java", 300, "python", 100, "kotlin", 404)));
 
     ProjectMeasuresStatistics result = underTest.searchTelemetryStatistics();
 
     assertThat(result.getProjectCount()).isEqualTo(2);
-    assertThat(result.getLines()).isEqualTo(30);
-    assertThat(result.getNcloc()).isEqualTo(50);
     assertThat(result.getProjectCountByLanguage()).containsOnly(
       entry("java", 2L), entry("cs", 1L), entry("js", 1L), entry("python", 1L), entry("kotlin", 1L));
     assertThat(result.getNclocByLanguage()).containsOnly(
index d4d870f41f703b5a3b1ff9b891c5de10b190f5c7..90feb1f3bc4ad2a643ad438fbd12e5e45a122442 100644 (file)
@@ -70,7 +70,7 @@ public class ClusterSystemInfoWriterTest {
       + "\"Application Nodes\":[{\"Name\":\"appNodes\",\"\":{\"name\":\"appNodes\"}}],"
       + "\"Search Nodes\":[{\"Name\":\"searchNodes\",\"\":{\"name\":\"searchNodes\"}}],"
       + "\"Statistics\":{\"id\":\"\",\"version\":\"\",\"database\":{\"name\":\"\",\"version\":\"\"},\"plugins\":[],"
-      + "\"userCount\":0,\"projectCount\":0,\"usingBranches\":false,\"lines\":0,\"ncloc\":0,\"projectCountByLanguage\":[],\"nclocByLanguage\":[]}}");
+      + "\"userCount\":0,\"projectCount\":0,\"usingBranches\":false,\"ncloc\":0,\"projectCountByLanguage\":[],\"nclocByLanguage\":[]}}");
   }
 
   private static NodeInfo createNodeInfo(String name) {
index a228ff739ad4e80ddf3bf41c3a5f6c22ff6a687c..1f2ef68501188b33f35e84fd4fc5403a15720d34 100644 (file)
@@ -79,7 +79,7 @@ public class StandaloneSystemInfoWriterTest {
     // response does not contain empty "Section Three"
     assertThat(writer.toString()).isEqualTo("{\"Health\":\"GREEN\",\"Health Causes\":[],\"Section One\":{\"foo\":\"bar\"},\"Section Two\":{\"one\":1,\"two\":2}," +
       "\"Statistics\":{\"id\":\"\",\"version\":\"\",\"database\":{\"name\":\"\",\"version\":\"\"},\"plugins\":[],\"userCount\":0,\"projectCount\":0,\"usingBranches\":false," +
-      "\"lines\":0,\"ncloc\":0,\"projectCountByLanguage\":[],\"nclocByLanguage\":[]}}");
+      "\"ncloc\":0,\"projectCountByLanguage\":[],\"nclocByLanguage\":[]}}");
   }
 
   private void logInAsSystemAdministrator() {
index 0ed4e2289b4362351ad72073681a35bf4286b19f..d9171b08155d33e4a1d73e288ce67fd927a02fcc 100644 (file)
@@ -163,7 +163,7 @@ public class TelemetryDaemonTest {
   }
 
   @Test
-  public void exclude_branches() throws IOException {
+  public void take_biggest_long_living_branches() throws IOException {
     initTelemetrySettingsToDefaultValues();
     settings.setProperty("sonar.telemetry.frequencyInSeconds", "1");
     server.setId("AU-TpxcB-iU5OvuD2FL7").setVersion("7.5.4");
@@ -181,7 +181,7 @@ public class TelemetryDaemonTest {
     ArgumentCaptor<String> jsonCaptor = ArgumentCaptor.forClass(String.class);
     verify(client, timeout(2_000).atLeastOnce()).upload(jsonCaptor.capture());
     assertJson(jsonCaptor.getValue()).isSimilarTo("{\n" +
-      "  \"ncloc\": 10\n" +
+      "  \"ncloc\": 20\n" +
       "}\n");
   }
 
index 5ec6050cba7cc50894eeb4412095e8bc4d0cb69f..1d80c0c7ed29c94e44386d989c3bc30cb84b9b9e 100644 (file)
@@ -22,7 +22,6 @@
   "userCount": 3,
   "projectCount": 2,
   "usingBranches": true,
-  "lines": 500,
   "ncloc": 300,
   "projectCountByLanguage": [
     {
index fc4f73387d4274b2f30f65e01418cfdee3369714..56460f7808ad5640a6829b595465b38d43f70fbc 100644 (file)
@@ -34,10 +34,10 @@ import org.junit.rules.TemporaryFolder;
 import org.junit.rules.TestRule;
 import org.junit.rules.Timeout;
 import org.sonarqube.qa.util.Tester;
+import org.sonarqube.qa.util.XooProjectBuilder;
 import org.sonarqube.tests.Category4Suite;
 import org.sonarqube.ws.Ce;
 import org.sonarqube.ws.client.ce.ActivityStatusRequest;
-import util.XooProjectBuilder;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
index 37029fd7b5741b67e664c056516364538a5e8e86..a56ef4b24cf457bc429e6f5a16db4009ba50c57a 100644 (file)
@@ -28,10 +28,10 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 import org.sonarqube.qa.util.Tester;
+import org.sonarqube.qa.util.XooProjectBuilder;
 import org.sonarqube.ws.client.components.ShowRequest;
 import org.sonarqube.ws.client.components.TreeRequest;
 import org.sonarqube.ws.client.projects.UpdateKeyRequest;
-import util.XooProjectBuilder;
 
 import static java.util.Arrays.asList;
 import static org.assertj.core.api.Assertions.assertThat;
index 407acd2478fea4e6061b4c2c5ee520e3cdbc1af0..69070092154f99b299f2a405d83ce79f5b0e5e5d 100644 (file)
@@ -28,10 +28,10 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 import org.sonarqube.qa.util.Tester;
+import org.sonarqube.qa.util.XooProjectBuilder;
 import org.sonarqube.ws.Components.Component;
 import org.sonarqube.ws.Organizations;
 import org.sonarqube.ws.client.components.ShowRequest;
-import util.XooProjectBuilder;
 
 import static org.apache.commons.lang.StringUtils.repeat;
 import static org.assertj.core.api.Assertions.assertThat;
index 1afbc28366a186a8854ba7dc710109e0f2cd63ea..b5b9ff2cbc16e363c99ff405b26389edc81206b3 100644 (file)
@@ -37,6 +37,7 @@ import org.junit.rules.TemporaryFolder;
 import org.junit.rules.TestRule;
 import org.junit.rules.Timeout;
 import org.sonarqube.qa.util.Tester;
+import org.sonarqube.qa.util.XooProjectBuilder;
 import org.sonarqube.ws.Components;
 import org.sonarqube.ws.Organizations;
 import org.sonarqube.ws.Projects;
@@ -49,7 +50,6 @@ import org.sonarqube.ws.client.components.TreeRequest;
 import org.sonarqube.ws.client.projects.BulkUpdateKeyRequest;
 import org.sonarqube.ws.client.projects.UpdateKeyRequest;
 import util.ItUtils;
-import util.XooProjectBuilder;
 
 import static java.util.Arrays.asList;
 import static org.assertj.core.api.Assertions.assertThat;
index cc81e8e6074293d33b384c246a3d1b0d7c4c1747..5a9c01d65fee1c36923cc1dc8c83c25dcbc7e210 100644 (file)
@@ -33,13 +33,13 @@ import org.junit.rules.TemporaryFolder;
 import org.junit.rules.TestRule;
 import org.junit.rules.Timeout;
 import org.sonarqube.qa.util.Tester;
+import org.sonarqube.qa.util.XooProjectBuilder;
 import org.sonarqube.ws.Ce;
 import org.sonarqube.ws.Projects;
 import org.sonarqube.ws.client.ce.ActivityStatusRequest;
 import org.sonarqube.ws.client.plugins.UninstallRequest;
 import org.sonarqube.ws.client.qualityprofiles.AddProjectRequest;
 import util.ItUtils;
-import util.XooProjectBuilder;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static util.ItUtils.newOrchestratorBuilder;
index fd17f38511261b356ca0e263349d85dbb1b76a28..5257a8847cf6f8d45852a7f0232213f07b38b691 100644 (file)
@@ -91,7 +91,6 @@ public class TelemetryUploadTest {
     List<String> plugins = ((List<Map<String, String>>) json.get("plugins")).stream().map(p -> p.get("name")).collect(Collectors.toList());
     assertThat(plugins).contains("xoo");
     assertThat(getInteger(json.get("ncloc"))).isEqualTo(13 * 2 + 7);
-    assertThat(getInteger(json.get("lines"))).isEqualTo(17 * 3);
     List<Map<String, String>> projectCountByLanguage = (List<Map<String, String>>) json.get("projectCountByLanguage");
     assertThat(projectCountByLanguage).extracting(p -> p.get("language"), p -> getInteger(p.get("count")))
       .contains(tuple("xoo", 2), tuple("xoo2", 1));
diff --git a/tests/src/test/java/util/XooProjectBuilder.java b/tests/src/test/java/util/XooProjectBuilder.java
deleted file mode 100644 (file)
index c9e8c82..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package util;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Properties;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.lang.StringUtils;
-
-import static java.util.Arrays.asList;
-
-public class XooProjectBuilder {
-  private final String key;
-  private final List<String> moduleKeys = new ArrayList<>();
-  private int filesPerModule = 1;
-
-  public XooProjectBuilder(String projectKey) {
-    this.key = projectKey;
-  }
-
-  public XooProjectBuilder addModules(String key, String... otherKeys) {
-    this.moduleKeys.add(key);
-    this.moduleKeys.addAll(asList(otherKeys));
-    return this;
-  }
-
-  public XooProjectBuilder setFilesPerModule(int i) {
-    this.filesPerModule = i;
-    return this;
-  }
-
-  public File build(File dir) {
-    for (String moduleKey : moduleKeys) {
-      generateModule(moduleKey, new File(dir, moduleKey), new Properties());
-    }
-    Properties additionalProps = new Properties();
-    additionalProps.setProperty("sonar.modules", StringUtils.join(moduleKeys, ","));
-    generateModule(key, dir, additionalProps);
-    return dir;
-  }
-
-  private void generateModule(String key, File dir, Properties additionalProps) {
-    try {
-      File sourceDir = new File(dir, "src");
-      FileUtils.forceMkdir(sourceDir);
-      for (int i = 0; i < filesPerModule; i++) {
-        File sourceFile = new File(sourceDir, "File" + i + ".xoo");
-        FileUtils.write(sourceFile, "content of " + sourceFile.getName());
-
-        File measuresFile = new File(sourceFile + ".measures");
-        FileUtils.write(measuresFile, "ncloc:10\n" +
-          "comment_lines:3\n");
-      }
-      Properties props = new Properties();
-      props.setProperty("sonar.projectKey", key);
-      props.setProperty("sonar.projectName", key);
-      props.setProperty("sonar.projectVersion", "1.0");
-      props.setProperty("sonar.sources", sourceDir.getName());
-      props.putAll(additionalProps);
-      File propsFile = new File(dir, "sonar-project.properties");
-      try (OutputStream output = FileUtils.openOutputStream(propsFile)) {
-        props.store(output, "generated");
-      }
-    } catch (IOException e) {
-      throw new IllegalStateException(e);
-    }
-  }
-}