]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-13785 Complete migration of file sources with a DB migration
authorDuarte Meneses <duarte.meneses@sonarsource.com>
Thu, 20 Aug 2020 15:08:59 +0000 (10:08 -0500)
committersonartech <sonartech@sonarsource.com>
Mon, 24 Aug 2020 20:06:06 +0000 (20:06 +0000)
16 files changed:
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/dbmigration/DbMigrationModule.java [deleted file]
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/dbmigration/PopulateFileSourceLineCount.java [deleted file]
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/dbmigration/ProjectAnalysisDataChange.java [deleted file]
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/dbmigration/ProjectAnalysisDataChanges.java [deleted file]
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/dbmigration/ProjectAnalysisDataChangesImpl.java [deleted file]
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/dbmigration/package-info.java [deleted file]
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/DbMigrationsStep.java [deleted file]
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/dbmigration/DbMigrationModuleTest.java [deleted file]
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/dbmigration/PopulateFileSourceLineCountTest.java [deleted file]
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/dbmigration/ProjectAnalysisDataChangesImplTest.java [deleted file]
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/DbMigrationsStepTest.java [deleted file]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/PopulateFileSourceLineCount.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/PopulateFileSourceLineCountTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v85/PopulateFileSourceLineCountTest/schema.sql [new file with mode: 0644]

index cfc3da144b578b7f33e1c50011dafdf106aec883..7cd9fc307c3627be34da8bbb61d478a2182c88e4 100644 (file)
@@ -38,7 +38,6 @@ import org.sonar.ce.task.projectanalysis.component.ReferenceBranchComponentUuids
 import org.sonar.ce.task.projectanalysis.component.ReportModulesPath;
 import org.sonar.ce.task.projectanalysis.component.SiblingComponentsWithOpenIssues;
 import org.sonar.ce.task.projectanalysis.component.TreeRootHolderImpl;
-import org.sonar.ce.task.projectanalysis.dbmigration.DbMigrationModule;
 import org.sonar.ce.task.projectanalysis.duplication.CrossProjectDuplicationStatusHolderImpl;
 import org.sonar.ce.task.projectanalysis.duplication.DuplicationMeasures;
 import org.sonar.ce.task.projectanalysis.duplication.DuplicationRepositoryImpl;
@@ -182,7 +181,6 @@ public final class ProjectAnalysisTaskContainerPopulator implements ContainerPop
       // File System
       new ComputationTempFolderProvider(),
 
-      DbMigrationModule.class,
       ReportModulesPath.class,
       MetricModule.class,
 
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/dbmigration/DbMigrationModule.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/dbmigration/DbMigrationModule.java
deleted file mode 100644 (file)
index 07dc053..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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.ce.task.projectanalysis.dbmigration;
-
-import org.sonar.core.platform.Module;
-
-public class DbMigrationModule extends Module {
-  @Override
-  protected void configureModule() {
-    add(ProjectAnalysisDataChangesImpl.class);
-    ProjectAnalysisDataChangesImpl.getDataChangeClasses().forEach(this::add);
-  }
-}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/dbmigration/PopulateFileSourceLineCount.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/dbmigration/PopulateFileSourceLineCount.java
deleted file mode 100644 (file)
index ec64720..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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.ce.task.projectanalysis.dbmigration;
-
-import com.google.common.collect.Iterables;
-import java.sql.SQLException;
-import org.sonar.ce.task.CeTask;
-import org.sonar.db.Database;
-import org.sonar.db.source.FileSourceDto;
-import org.sonar.server.platform.db.migration.step.DataChange;
-import org.sonar.server.platform.db.migration.step.MassUpdate;
-import org.sonar.server.platform.db.migration.step.Select;
-import org.sonar.server.platform.db.migration.step.SqlStatement;
-
-import static org.sonar.db.source.FileSourceDto.LINE_COUNT_NOT_POPULATED;
-
-public class PopulateFileSourceLineCount extends DataChange implements ProjectAnalysisDataChange {
-  private final CeTask ceTask;
-
-  public PopulateFileSourceLineCount(Database database, CeTask ceTask) {
-    super(database);
-    this.ceTask = ceTask;
-  }
-
-  @Override
-  protected void execute(Context context) throws SQLException {
-    String componentUuid = ceTask.getComponent().get().getUuid();
-    Long unInitializedFileSources = context.prepareSelect("select count(1) from file_sources where line_count = ? and project_uuid = ?")
-      .setInt(1, LINE_COUNT_NOT_POPULATED)
-      .setString(2, componentUuid)
-      .get(row -> row.getLong(1));
-
-    if (unInitializedFileSources != null && unInitializedFileSources > 0) {
-      MassUpdate massUpdate = context.prepareMassUpdate();
-      massUpdate.select("select uuid,line_hashes from file_sources where line_count = ? and project_uuid = ?")
-        .setInt(1, LINE_COUNT_NOT_POPULATED)
-        .setString(2, componentUuid);
-      massUpdate.update("update file_sources set line_count = ? where uuid = ?");
-      massUpdate.rowPluralName("line counts of sources of project " + componentUuid);
-      massUpdate.execute(PopulateFileSourceLineCount::handle);
-    }
-  }
-
-  private static boolean handle(Select.Row row, SqlStatement update) throws SQLException {
-    String rowUuid = row.getString(1);
-    String rawData = row.getNullableString(2);
-
-    int lineCount = rawData == null ? 0 : Iterables.size(FileSourceDto.LINES_HASHES_SPLITTER.split(rawData));
-    update.setInt(1, lineCount);
-    update.setString(2, rowUuid);
-    return true;
-  }
-}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/dbmigration/ProjectAnalysisDataChange.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/dbmigration/ProjectAnalysisDataChange.java
deleted file mode 100644 (file)
index d959da5..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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.ce.task.projectanalysis.dbmigration;
-
-import org.sonar.server.platform.db.migration.step.MigrationStep;
-
-/**
- * Marker interface of {@link MigrationStep} for the implementations to be run in
- * {@link org.sonar.ce.task.projectanalysis.step.DbMigrationsStep DbMigrationsStep}.
- * <p>
- * {@link MigrationStep} execute during project report analysis should perform <strong>only data change operations</strong>.
- */
-public interface ProjectAnalysisDataChange extends MigrationStep {
-
-}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/dbmigration/ProjectAnalysisDataChanges.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/dbmigration/ProjectAnalysisDataChanges.java
deleted file mode 100644 (file)
index 441f476..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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.ce.task.projectanalysis.dbmigration;
-
-import java.util.List;
-
-public interface ProjectAnalysisDataChanges {
-  /**
-   * @return {@link ProjectAnalysisDataChange} instances to be executed in order.
-   */
-  List<ProjectAnalysisDataChange> getDataChanges();
-}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/dbmigration/ProjectAnalysisDataChangesImpl.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/dbmigration/ProjectAnalysisDataChangesImpl.java
deleted file mode 100644 (file)
index b493dee..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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.ce.task.projectanalysis.dbmigration;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.collect.ImmutableList.of;
-import static org.sonar.core.util.stream.MoreCollectors.toList;
-import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
-
-/**
- * Implementation of {@link ProjectAnalysisDataChanges} based on an ordered list of {@link ProjectAnalysisDataChange}
- * classes and the {@link ProjectAnalysisDataChange} instances which can be injected by the container.
- */
-public class ProjectAnalysisDataChangesImpl implements ProjectAnalysisDataChanges {
-  private static final List<Class<? extends ProjectAnalysisDataChange>> DATA_CHANGE_CLASSES_IN_ORDER_OF_EXECUTION = of(
-    PopulateFileSourceLineCount.class);
-  private final List<ProjectAnalysisDataChange> dataChangeInstances;
-
-  public ProjectAnalysisDataChangesImpl(ProjectAnalysisDataChange[] dataChanges) {
-    checkArgument(dataChanges.length == DATA_CHANGE_CLASSES_IN_ORDER_OF_EXECUTION.size(),
-      "Number of ProjectAnalysisDataChange instance available (%s) is inconsistent with the number of declared ProjectAnalysisDataChange types (%s)",
-      dataChanges.length,
-      DATA_CHANGE_CLASSES_IN_ORDER_OF_EXECUTION.size());
-    Map<? extends Class<? extends ProjectAnalysisDataChange>, ProjectAnalysisDataChange> dataChangesByClass = Arrays.stream(dataChanges)
-      .collect(uniqueIndex(ProjectAnalysisDataChange::getClass));
-    dataChangeInstances = DATA_CHANGE_CLASSES_IN_ORDER_OF_EXECUTION.stream()
-      .map(dataChangesByClass::get)
-      .filter(Objects::nonNull)
-      .collect(toList(DATA_CHANGE_CLASSES_IN_ORDER_OF_EXECUTION.size()));
-    checkState(dataChangeInstances.size() == DATA_CHANGE_CLASSES_IN_ORDER_OF_EXECUTION.size(),
-      "Some of the ProjectAnalysisDataChange type declared have no instance in the container");
-  }
-
-  static List<Class<? extends ProjectAnalysisDataChange>> getDataChangeClasses() {
-    return DATA_CHANGE_CLASSES_IN_ORDER_OF_EXECUTION;
-  }
-
-  @Override
-  public List<ProjectAnalysisDataChange> getDataChanges() {
-    return dataChangeInstances;
-  }
-}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/dbmigration/package-info.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/dbmigration/package-info.java
deleted file mode 100644 (file)
index 79bb110..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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.
- */
-@ParametersAreNonnullByDefault
-package org.sonar.ce.task.projectanalysis.dbmigration;
-
-import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/DbMigrationsStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/DbMigrationsStep.java
deleted file mode 100644 (file)
index 85cc9ec..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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.ce.task.projectanalysis.step;
-
-import java.sql.SQLException;
-import org.sonar.ce.task.projectanalysis.dbmigration.ProjectAnalysisDataChange;
-import org.sonar.ce.task.projectanalysis.dbmigration.ProjectAnalysisDataChanges;
-import org.sonar.ce.task.step.ComputationStep;
-
-public class DbMigrationsStep implements ComputationStep {
-  private final ProjectAnalysisDataChanges dataChanges;
-
-  public DbMigrationsStep(ProjectAnalysisDataChanges dataChanges) {
-    this.dataChanges = dataChanges;
-  }
-
-  @Override
-  public String getDescription() {
-    return "Execute DB migrations for current project";
-  }
-
-  @Override
-  public void execute(ComputationStep.Context context) {
-    dataChanges.getDataChanges().forEach(DbMigrationsStep::execute);
-  }
-
-  private static void execute(ProjectAnalysisDataChange dataChange) {
-    try {
-      dataChange.execute();
-    } catch (SQLException e) {
-      throw new IllegalStateException("Failed to perform DB migration for project", e);
-    }
-  }
-
-}
index c7d5ca07703a7ed06bf5e08f12405c483cd872f8..9eda77e3c9a0acf69779adb904247f9ae2264083 100644 (file)
@@ -39,7 +39,6 @@ public class ReportComputationSteps extends AbstractComputationSteps {
     ExtractReportStep.class,
     PersistScannerContextStep.class,
     PersistAnalysisWarningsStep.class,
-    DbMigrationsStep.class,
     GenerateAnalysisUuid.class,
 
     // Builds Component tree
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/dbmigration/DbMigrationModuleTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/dbmigration/DbMigrationModuleTest.java
deleted file mode 100644 (file)
index 31cf66b..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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.ce.task.projectanalysis.dbmigration;
-
-import java.util.Objects;
-import org.junit.Test;
-import org.sonar.core.platform.ComponentContainer;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class DbMigrationModuleTest {
-  private DbMigrationModule underTest = new DbMigrationModule();
-
-  @Test
-  public void module_configure_ProjectAnalysisDataChanges_implementation() {
-    ComponentContainer container = new ComponentContainer();
-
-    underTest.configure(container);
-
-    assertThat(container.getPicoContainer().getComponentAdapters(ProjectAnalysisDataChanges.class))
-      .hasSize(1);
-  }
-
-  @Test
-  public void module_includes_ProjectAnalysisDataChange_classes() {
-    ComponentContainer container = new ComponentContainer();
-
-    underTest.configure(container);
-
-    assertThat(ProjectAnalysisDataChangesImpl.getDataChangeClasses()
-      .stream()
-      .map(t -> container.getPicoContainer().getComponentAdapter(t))
-      .filter(Objects::nonNull)).hasSize(ProjectAnalysisDataChangesImpl.getDataChangeClasses().size());
-  }
-}
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/dbmigration/PopulateFileSourceLineCountTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/dbmigration/PopulateFileSourceLineCountTest.java
deleted file mode 100644 (file)
index 139ce95..0000000
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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.ce.task.projectanalysis.dbmigration;
-
-import java.sql.SQLException;
-import java.util.Optional;
-import java.util.Random;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-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.ce.task.CeTask;
-import org.sonar.core.util.SequenceUuidFactory;
-import org.sonar.core.util.UuidFactory;
-import org.sonar.db.DbTester;
-
-import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.db.source.FileSourceDto.LINE_COUNT_NOT_POPULATED;
-
-public class PopulateFileSourceLineCountTest {
-
-  @Rule
-  public ExpectedException expectedException = ExpectedException.none();
-
-  @Rule
-  public DbTester db = DbTester.create(System2.INSTANCE);
-
-  private UuidFactory uuidFactory = new SequenceUuidFactory();
-
-  private Random random = new Random();
-  private CeTask ceTask = mock(CeTask.class);
-  private PopulateFileSourceLineCount underTest = new PopulateFileSourceLineCount(db.database(), ceTask);
-
-  @Test
-  public void execute_has_no_effect_on_empty_table() throws SQLException {
-    String projectUuid = randomAlphanumeric(4);
-    when(ceTask.getComponent()).thenReturn(newComponent(projectUuid));
-
-    underTest.execute();
-  }
-
-  @Test
-  public void execute_populates_line_count_of_any_type() throws SQLException {
-    String projectUuid = randomAlphanumeric(4);
-    String fileUuid = randomAlphanumeric(5);
-    when(ceTask.getComponent()).thenReturn(newComponent(projectUuid));
-    int lineCount = 1 + random.nextInt(15);
-    insertUnpopulatedFileSource(projectUuid, fileUuid, lineCount);
-    assertThat(getLineCountByFileUuid(fileUuid)).isEqualTo(LINE_COUNT_NOT_POPULATED);
-
-    underTest.execute();
-
-    assertThat(getLineCountByFileUuid(fileUuid)).isEqualTo(lineCount);
-  }
-
-  @Test
-  public void execute_changes_only_file_source_with_LINE_COUNT_NOT_POPULATED_value() throws SQLException {
-    String projectUuid = randomAlphanumeric(4);
-    String fileUuid1 = randomAlphanumeric(5);
-    String fileUuid2 = randomAlphanumeric(6);
-    String fileUuid3 = randomAlphanumeric(7);
-    int lineCountFile1 = 100 + random.nextInt(15);
-    int lineCountFile2 = 50 + random.nextInt(15);
-    int lineCountFile3 = 150 + random.nextInt(15);
-
-    when(ceTask.getComponent()).thenReturn(newComponent(projectUuid));
-    insertPopulatedFileSource(projectUuid, fileUuid1, lineCountFile1);
-    int badLineCountFile2 = insertInconsistentPopulatedFileSource(projectUuid, fileUuid2, lineCountFile2);
-    insertUnpopulatedFileSource(projectUuid, fileUuid3, lineCountFile3);
-    assertThat(getLineCountByFileUuid(fileUuid1)).isEqualTo(lineCountFile1);
-    assertThat(getLineCountByFileUuid(fileUuid2)).isEqualTo(badLineCountFile2);
-    assertThat(getLineCountByFileUuid(fileUuid3)).isEqualTo(LINE_COUNT_NOT_POPULATED);
-
-    underTest.execute();
-
-    assertThat(getLineCountByFileUuid(fileUuid1)).isEqualTo(lineCountFile1);
-    assertThat(getLineCountByFileUuid(fileUuid2)).isEqualTo(badLineCountFile2);
-    assertThat(getLineCountByFileUuid(fileUuid3)).isEqualTo(lineCountFile3);
-  }
-
-  @Test
-  public void execute_changes_only_file_source_of_CeTask_component_uuid() throws SQLException {
-    String projectUuid1 = randomAlphanumeric(4);
-    String projectUuid2 = randomAlphanumeric(5);
-    String fileUuid1 = randomAlphanumeric(6);
-    String fileUuid2 = randomAlphanumeric(7);
-    int lineCountFile1 = 100 + random.nextInt(15);
-    int lineCountFile2 = 30 + random.nextInt(15);
-
-    when(ceTask.getComponent()).thenReturn(newComponent(projectUuid1));
-    insertUnpopulatedFileSource(projectUuid1, fileUuid1, lineCountFile1);
-    insertUnpopulatedFileSource(projectUuid2, fileUuid2, lineCountFile2);
-
-    underTest.execute();
-
-    assertThat(getLineCountByFileUuid(fileUuid1)).isEqualTo(lineCountFile1);
-    assertThat(getLineCountByFileUuid(fileUuid2)).isEqualTo(LINE_COUNT_NOT_POPULATED);
-  }
-
-  @Test
-  public void execute_set_line_count_to_zero_when_file_source_has_no_line_hashes() throws SQLException {
-    String projectUuid = randomAlphanumeric(4);
-    String fileUuid1 = randomAlphanumeric(5);
-
-    when(ceTask.getComponent()).thenReturn(newComponent(projectUuid));
-    insertFileSource(projectUuid, fileUuid1, null, LINE_COUNT_NOT_POPULATED);
-
-    underTest.execute();
-
-    assertThat(getLineCountByFileUuid(fileUuid1)).isZero();
-  }
-
-  @Test
-  public void execute_set_line_count_to_1_when_file_source_has_empty_line_hashes() throws SQLException {
-    String projectUuid = randomAlphanumeric(4);
-    String fileUuid1 = randomAlphanumeric(5);
-
-    when(ceTask.getComponent()).thenReturn(newComponent(projectUuid));
-    insertFileSource(projectUuid, fileUuid1, "", LINE_COUNT_NOT_POPULATED);
-
-    underTest.execute();
-
-    assertThat(getLineCountByFileUuid(fileUuid1)).isEqualTo(1);
-  }
-
-  private int getLineCountByFileUuid(String fileUuid) {
-    Long res = (Long) db.selectFirst("select line_count as \"LINE_COUNT\" from file_sources where file_uuid = '" + fileUuid + "'")
-      .get("LINE_COUNT");
-    return res.intValue();
-  }
-
-  private void insertUnpopulatedFileSource(String projectUuid, String fileUuid, int numberOfHashes) {
-    String lineHashes = generateLineHashes(numberOfHashes);
-
-    insertFileSource(projectUuid, fileUuid, lineHashes, LINE_COUNT_NOT_POPULATED);
-  }
-
-  private void insertPopulatedFileSource(String projectUuid, String fileUuid, int lineCount) {
-    String lineHashes = generateLineHashes(lineCount);
-
-    insertFileSource(projectUuid, fileUuid, lineHashes, lineCount);
-  }
-
-  private int insertInconsistentPopulatedFileSource(String projectUuid, String fileUuid, int lineCount) {
-    String lineHashes = generateLineHashes(lineCount);
-    int badLineCount = lineCount + random.nextInt(6);
-
-    insertFileSource(projectUuid, fileUuid, lineHashes, badLineCount);
-
-    return badLineCount;
-  }
-
-  private static String generateLineHashes(int numberOfHashes) {
-    return IntStream.range(0, numberOfHashes)
-      .mapToObj(String::valueOf)
-      .collect(Collectors.joining("\n"));
-  }
-
-  private void insertFileSource(String projectUuid, String fileUuid, @Nullable String lineHashes, int lineCount) {
-    db.executeInsert(
-      "FILE_SOURCES",
-      "UUID", uuidFactory.create(),
-      "PROJECT_UUID", projectUuid,
-      "FILE_UUID", fileUuid,
-      "LINE_HASHES", lineHashes,
-      "LINE_COUNT", lineCount,
-      "CREATED_AT", 1_222_333L,
-      "UPDATED_AT", 1_222_333L);
-    db.commit();
-  }
-
-  private static Optional<CeTask.Component> newComponent(String projectUuid) {
-    return Optional.of(new CeTask.Component(projectUuid, "key_" + projectUuid, "name_" + projectUuid));
-  }
-}
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/dbmigration/ProjectAnalysisDataChangesImplTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/dbmigration/ProjectAnalysisDataChangesImplTest.java
deleted file mode 100644 (file)
index 0648383..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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.ce.task.projectanalysis.dbmigration;
-
-import java.util.List;
-import java.util.stream.Collectors;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.ce.task.CeTask;
-import org.sonar.db.Database;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-
-public class ProjectAnalysisDataChangesImplTest {
-  @Rule
-  public ExpectedException expectedException = ExpectedException.none();
-
-  @Test
-  public void constructor_throws_IAE_if_argument_is_empty() {
-    ProjectAnalysisDataChange[] empty = new ProjectAnalysisDataChange[0];
-    int expectedArraySize = ProjectAnalysisDataChangesImpl.getDataChangeClasses().size();
-
-    expectedException.expect(IllegalArgumentException.class);
-    expectedException.expectMessage("Number of ProjectAnalysisDataChange instance available (0) is inconsistent with " +
-      "the number of declared ProjectAnalysisDataChange types (" + expectedArraySize + ")");
-
-    new ProjectAnalysisDataChangesImpl(empty);
-  }
-
-  @Test
-  public void constructor_throws_ISE_if_an_instance_of_declared_class_is_missing() {
-    ProjectAnalysisDataChange[] wrongInstance = new ProjectAnalysisDataChange[] {
-      mock(ProjectAnalysisDataChange.class)
-    };
-
-    expectedException.expect(IllegalStateException.class);
-    expectedException.expectMessage("Some of the ProjectAnalysisDataChange type declared have no instance in the container");
-
-    new ProjectAnalysisDataChangesImpl(wrongInstance);
-
-  }
-
-  @Test
-  public void getDataChanges_returns_instances_of_classes_in_order_defined_by_getDataChangeClasses() {
-    Database database = mock(Database.class);
-    CeTask ceTask = mock(CeTask.class);
-    ProjectAnalysisDataChangesImpl underTest = new ProjectAnalysisDataChangesImpl(new ProjectAnalysisDataChange[] {
-      new PopulateFileSourceLineCount(database, ceTask)
-    });
-
-    List<ProjectAnalysisDataChange> dataChanges = underTest.getDataChanges();
-
-    List<? extends Class<?>> dataChangeClasses = dataChanges
-      .stream()
-      .map(ProjectAnalysisDataChange::getClass)
-      .collect(Collectors.toList());
-    assertThat(dataChangeClasses).isEqualTo(ProjectAnalysisDataChangesImpl.getDataChangeClasses());
-  }
-}
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/DbMigrationsStepTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/DbMigrationsStepTest.java
deleted file mode 100644 (file)
index 81d9de5..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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.ce.task.projectanalysis.step;
-
-import com.google.common.collect.ImmutableList;
-import java.sql.SQLException;
-import java.util.Arrays;
-import java.util.Random;
-import java.util.stream.IntStream;
-import org.junit.Test;
-import org.mockito.InOrder;
-import org.mockito.Mockito;
-import org.sonar.ce.task.projectanalysis.dbmigration.ProjectAnalysisDataChange;
-import org.sonar.ce.task.projectanalysis.dbmigration.ProjectAnalysisDataChanges;
-import org.sonar.ce.task.step.TestComputationStepContext;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.fail;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class DbMigrationsStepTest {
-  private ProjectAnalysisDataChanges projectAnalysisDataChanges = mock(ProjectAnalysisDataChanges.class);
-
-  private DbMigrationsStep underTest = new DbMigrationsStep(projectAnalysisDataChanges);
-
-  @Test
-  public void execute_has_no_effect_if_there_is_no_DataChange() {
-    underTest.execute(new TestComputationStepContext());
-  }
-
-  @Test
-  public void execute_calls_execute_on_DataChange_instances_in_order_provided_by_ProjectAnalysisDataChanges() {
-    ProjectAnalysisDataChange[] dataChanges = IntStream.range(0, 5 + new Random().nextInt(5))
-      .mapToObj(i -> mock(ProjectAnalysisDataChange.class))
-      .toArray(ProjectAnalysisDataChange[]::new);
-    InOrder inOrder = Mockito.inOrder(dataChanges);
-    when(projectAnalysisDataChanges.getDataChanges()).thenReturn(Arrays.asList(dataChanges));
-
-    underTest.execute(new TestComputationStepContext());
-
-    Arrays.stream(dataChanges).forEach(t -> {
-      try {
-        inOrder.verify(t).execute();
-      } catch (SQLException e) {
-        throw new RuntimeException("mock execute method throw an exception??!!??", e);
-      }
-    });
-  }
-
-  @Test
-  public void execute_stops_executing_and_throws_ISE_at_first_failing_DataChange() throws SQLException {
-    ProjectAnalysisDataChange okMock1 = mock(ProjectAnalysisDataChange.class);
-    ProjectAnalysisDataChange okMock2 = mock(ProjectAnalysisDataChange.class);
-    ProjectAnalysisDataChange failingMock1 = mock(ProjectAnalysisDataChange.class);
-    SQLException expected = new SQLException("Faiking DataChange throwing a SQLException");
-    doThrow(expected).when(failingMock1).execute();
-    ProjectAnalysisDataChange okMock3 = mock(ProjectAnalysisDataChange.class);
-    ProjectAnalysisDataChange failingMock2 = mock(ProjectAnalysisDataChange.class);
-    doThrow(new SQLException("Faiking another failing DataChange throwing a SQLException but which should never be thrown"))
-      .when(failingMock2)
-      .execute();
-    ProjectAnalysisDataChange okMock4 = mock(ProjectAnalysisDataChange.class);
-    InOrder inOrder = Mockito.inOrder(okMock1, okMock2, failingMock1, okMock3, failingMock2, okMock4);
-    when(projectAnalysisDataChanges.getDataChanges()).thenReturn(ImmutableList.of(
-      okMock1, okMock2, failingMock1, okMock3, failingMock2, okMock4));
-
-    try {
-      underTest.execute(new TestComputationStepContext());
-      fail("A IllegalStateException should have been thrown");
-    } catch (IllegalStateException e) {
-      assertThat(e)
-        .hasCause(expected);
-      inOrder.verify(okMock1).execute();
-      inOrder.verify(okMock2).execute();
-      inOrder.verify(failingMock1).execute();
-      inOrder.verifyNoMoreInteractions();
-    }
-  }
-
-  @Test
-  public void verify_description() {
-    assertThat(underTest.getDescription()).isNotEmpty();
-  }
-}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/PopulateFileSourceLineCount.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v85/PopulateFileSourceLineCount.java
new file mode 100644 (file)
index 0000000..d2f0562
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.v85;
+
+import java.sql.SQLException;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.step.DataChange;
+import org.sonar.server.platform.db.migration.step.MassUpdate;
+import org.sonar.server.platform.db.migration.step.Select;
+import org.sonar.server.platform.db.migration.step.SqlStatement;
+
+public class PopulateFileSourceLineCount extends DataChange {
+  static final int LINE_COUNT_NOT_POPULATED = -1;
+  private static final String NEW_LINE = "\n";
+
+  public PopulateFileSourceLineCount(Database database) {
+    super(database);
+  }
+
+  @Override
+  protected void execute(Context context) throws SQLException {
+    MassUpdate massUpdate = context.prepareMassUpdate();
+    massUpdate.select("select uuid, line_hashes from file_sources where line_count = ?").setInt(1, LINE_COUNT_NOT_POPULATED);
+    massUpdate.update("update file_sources set line_count = ? where uuid = ?");
+    massUpdate.rowPluralName("line counts of file sources");
+    massUpdate.execute(PopulateFileSourceLineCount::handle);
+  }
+
+  private static boolean handle(Select.Row row, SqlStatement update) throws SQLException {
+    String rowUuid = row.getString(1);
+    String rawData = row.getNullableString(2);
+
+    int lineCount = rawData == null ? 0 : (StringUtils.countMatches(rawData, NEW_LINE) + 1);
+    update.setInt(1, lineCount);
+    update.setString(2, rowUuid);
+    return true;
+  }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/PopulateFileSourceLineCountTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v85/PopulateFileSourceLineCountTest.java
new file mode 100644 (file)
index 0000000..a8f3ba4
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.v85;
+
+import java.sql.SQLException;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import javax.annotation.Nullable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.core.util.SequenceUuidFactory;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.db.CoreDbTester;
+
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.server.platform.db.migration.version.v85.PopulateFileSourceLineCount.LINE_COUNT_NOT_POPULATED;
+
+public class PopulateFileSourceLineCountTest {
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  @Rule
+  public CoreDbTester db = CoreDbTester.createForSchema(PopulateFileSourceLineCountTest.class, "schema.sql");
+
+  private UuidFactory uuidFactory = new SequenceUuidFactory();
+
+  private PopulateFileSourceLineCount underTest = new PopulateFileSourceLineCount(db.database());
+
+  @Test
+  public void execute_has_no_effect_on_empty_table() throws SQLException {
+    underTest.execute();
+    assertThat(db.countRowsOfTable("file_sources")).isZero();
+  }
+
+  @Test
+  public void execute_populates_line_count_of_any_type() throws SQLException {
+    String projectUuid = randomAlphanumeric(4);
+    String fileUuid = randomAlphanumeric(5);
+    int lineCount = 10;
+    insertUnpopulatedFileSource(projectUuid, fileUuid, lineCount);
+    assertThat(getLineCountByFileUuid(fileUuid)).isEqualTo(LINE_COUNT_NOT_POPULATED);
+
+    underTest.execute();
+
+    assertThat(getLineCountByFileUuid(fileUuid)).isEqualTo(lineCount);
+  }
+
+  @Test
+  public void execute_changes_only_file_source_with_LINE_COUNT_NOT_POPULATED_value() throws SQLException {
+    String projectUuid = randomAlphanumeric(4);
+    String fileUuid1 = randomAlphanumeric(5);
+    String fileUuid2 = randomAlphanumeric(6);
+    String fileUuid3 = randomAlphanumeric(7);
+    int lineCountFile1 = 10;
+    int lineCountFile2 = 50;
+    int lineCountFile3 = 150;
+
+    insertPopulatedFileSource(projectUuid, fileUuid1, lineCountFile1);
+    int badLineCountFile2 = insertInconsistentPopulatedFileSource(projectUuid, fileUuid2, lineCountFile2);
+    insertUnpopulatedFileSource(projectUuid, fileUuid3, lineCountFile3);
+    assertThat(getLineCountByFileUuid(fileUuid1)).isEqualTo(lineCountFile1);
+    assertThat(getLineCountByFileUuid(fileUuid2)).isEqualTo(badLineCountFile2);
+    assertThat(getLineCountByFileUuid(fileUuid3)).isEqualTo(LINE_COUNT_NOT_POPULATED);
+
+    underTest.execute();
+
+    assertThat(getLineCountByFileUuid(fileUuid1)).isEqualTo(lineCountFile1);
+    assertThat(getLineCountByFileUuid(fileUuid2)).isEqualTo(badLineCountFile2);
+    assertThat(getLineCountByFileUuid(fileUuid3)).isEqualTo(lineCountFile3);
+  }
+
+  @Test
+  public void execute_set_line_count_to_zero_when_file_source_has_no_line_hashes() throws SQLException {
+    String projectUuid = randomAlphanumeric(4);
+    String fileUuid1 = randomAlphanumeric(5);
+
+    insertFileSource(projectUuid, fileUuid1, null, LINE_COUNT_NOT_POPULATED);
+
+    underTest.execute();
+
+    assertThat(getLineCountByFileUuid(fileUuid1)).isZero();
+  }
+
+  @Test
+  public void execute_set_line_count_to_1_when_file_source_has_empty_line_hashes() throws SQLException {
+    String projectUuid = randomAlphanumeric(4);
+    String fileUuid1 = randomAlphanumeric(5);
+
+    insertFileSource(projectUuid, fileUuid1, "", LINE_COUNT_NOT_POPULATED);
+
+    underTest.execute();
+
+    assertThat(getLineCountByFileUuid(fileUuid1)).isEqualTo(1);
+  }
+
+  private int getLineCountByFileUuid(String fileUuid) {
+    Long res = (Long) db.selectFirst("select line_count as \"LINE_COUNT\" from file_sources where file_uuid = '" + fileUuid + "'")
+      .get("LINE_COUNT");
+    return res.intValue();
+  }
+
+  private void insertUnpopulatedFileSource(String projectUuid, String fileUuid, int numberOfHashes) {
+    String lineHashes = generateLineHashes(numberOfHashes);
+
+    insertFileSource(projectUuid, fileUuid, lineHashes, LINE_COUNT_NOT_POPULATED);
+  }
+
+  private void insertPopulatedFileSource(String projectUuid, String fileUuid, int lineCount) {
+    String lineHashes = generateLineHashes(lineCount);
+
+    insertFileSource(projectUuid, fileUuid, lineHashes, lineCount);
+  }
+
+  private int insertInconsistentPopulatedFileSource(String projectUuid, String fileUuid, int lineCount) {
+    String lineHashes = generateLineHashes(lineCount);
+    int badLineCount = lineCount + 6;
+
+    insertFileSource(projectUuid, fileUuid, lineHashes, badLineCount);
+
+    return badLineCount;
+  }
+
+  private static String generateLineHashes(int numberOfHashes) {
+    return IntStream.range(0, numberOfHashes)
+      .mapToObj(String::valueOf)
+      .collect(Collectors.joining("\n"));
+  }
+
+  private void insertFileSource(String projectUuid, String fileUuid, @Nullable String lineHashes, int lineCount) {
+    db.executeInsert(
+      "FILE_SOURCES",
+      "UUID", uuidFactory.create(),
+      "PROJECT_UUID", projectUuid,
+      "FILE_UUID", fileUuid,
+      "LINE_HASHES", lineHashes,
+      "LINE_COUNT", lineCount,
+      "CREATED_AT", 1_222_333L,
+      "UPDATED_AT", 1_222_333L);
+  }
+}
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v85/PopulateFileSourceLineCountTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v85/PopulateFileSourceLineCountTest/schema.sql
new file mode 100644 (file)
index 0000000..261f299
--- /dev/null
@@ -0,0 +1,18 @@
+CREATE TABLE "FILE_SOURCES"(
+    "PROJECT_UUID" VARCHAR(50) NOT NULL,
+    "FILE_UUID" VARCHAR(50) NOT NULL,
+    "LINE_HASHES" CLOB(2147483647),
+    "LINE_HASHES_VERSION" INTEGER,
+    "DATA_HASH" VARCHAR(50),
+    "SRC_HASH" VARCHAR(50),
+    "REVISION" VARCHAR(100),
+    "LINE_COUNT" INTEGER NOT NULL,
+    "BINARY_DATA" BLOB,
+    "CREATED_AT" BIGINT NOT NULL,
+    "UPDATED_AT" BIGINT NOT NULL,
+    "UUID" VARCHAR(40) NOT NULL
+);
+ALTER TABLE "FILE_SOURCES" ADD CONSTRAINT "PK_FILE_SOURCES" PRIMARY KEY("UUID");
+CREATE UNIQUE INDEX "FILE_SOURCES_FILE_UUID" ON "FILE_SOURCES"("FILE_UUID");
+CREATE INDEX "FILE_SOURCES_PROJECT_UUID" ON "FILE_SOURCES"("PROJECT_UUID");
+CREATE INDEX "FILE_SOURCES_UPDATED_AT" ON "FILE_SOURCES"("UPDATED_AT");