]> source.dussan.org Git - sonarqube.git/commitdiff
Refactor persisting of sources
authorDuarte Meneses <duarte.meneses@sonarsource.com>
Wed, 15 Aug 2018 07:46:56 +0000 (09:46 +0200)
committersonartech <sonartech@sonarsource.com>
Wed, 19 Sep 2018 08:51:39 +0000 (10:51 +0200)
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/formula/FormulaExecutorComponentVisitor.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/source/ComputeFileSourceData.java [deleted file]
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/source/FileSourceDataComputer.java [new file with mode: 0644]
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/source/SourceLineReadersFactory.java [new file with mode: 0644]
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/source/SourceLinesHashRepositoryImpl.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/source/linereader/LineReader.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistFileSourcesStep.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/source/ComputeFileSourceDataTest.java [deleted file]
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/source/FileSourceDataComputerTest.java [new file with mode: 0644]
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/source/SourceLineReadersFactoryTest.java [new file with mode: 0644]
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistFileSourcesStepTest.java

index 5cd56d376523a823a64ee378565f894884886b08..8f172573644e6a9e866cb2a8fd9b7fb64da11950 100644 (file)
@@ -27,13 +27,12 @@ import java.util.Map;
 import javax.annotation.CheckForNull;
 import org.sonar.ce.task.projectanalysis.component.Component;
 import org.sonar.ce.task.projectanalysis.component.ComponentVisitor;
-import org.sonar.ce.task.projectanalysis.component.PathAwareVisitorAdapter;
-import org.sonar.ce.task.projectanalysis.metric.Metric;
-import org.sonar.ce.task.projectanalysis.metric.MetricRepository;
 import org.sonar.ce.task.projectanalysis.component.CrawlerDepthLimit;
+import org.sonar.ce.task.projectanalysis.component.PathAwareVisitorAdapter;
 import org.sonar.ce.task.projectanalysis.measure.Measure;
 import org.sonar.ce.task.projectanalysis.measure.MeasureRepository;
-
+import org.sonar.ce.task.projectanalysis.metric.Metric;
+import org.sonar.ce.task.projectanalysis.metric.MetricRepository;
 
 import static java.util.Objects.requireNonNull;
 
@@ -217,7 +216,7 @@ public class FormulaExecutorComponentVisitor extends PathAwareVisitorAdapter<For
     }
   }
 
-  private class CreateMeasureContextImpl implements CreateMeasureContext {
+  private static class CreateMeasureContextImpl implements CreateMeasureContext {
     private final Component component;
     private final Metric metric;
 
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/source/ComputeFileSourceData.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/source/ComputeFileSourceData.java
deleted file mode 100644 (file)
index 37abda3..0000000
+++ /dev/null
@@ -1,92 +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 org.sonar.ce.task.projectanalysis.source;
-
-import java.util.Iterator;
-import java.util.List;
-import org.sonar.core.hash.SourceHashComputer;
-import org.sonar.db.protobuf.DbFileSources;
-import org.sonar.ce.task.projectanalysis.source.SourceLinesHashRepositoryImpl.LineHashesComputer;
-import org.sonar.ce.task.projectanalysis.source.linereader.LineReader;
-
-public class ComputeFileSourceData {
-  private final List<LineReader> lineReaders;
-  private final Iterator<String> linesIterator;
-  private final SourceHashComputer sourceHashComputer;
-  private final LineHashesComputer lineHashesComputer;
-
-  public ComputeFileSourceData(Iterator<String> sourceLinesIterator, List<LineReader> dataLineReaders, LineHashesComputer lineHashesComputer) {
-    this.lineReaders = dataLineReaders;
-    this.linesIterator = sourceLinesIterator;
-    this.lineHashesComputer = lineHashesComputer;
-    this.sourceHashComputer = new SourceHashComputer();
-  }
-
-  public Data compute() {
-    DbFileSources.Data.Builder fileSourceBuilder = DbFileSources.Data.newBuilder();
-    int currentLine = 0;
-
-    while (linesIterator.hasNext()) {
-      currentLine++;
-      read(fileSourceBuilder, currentLine, linesIterator.next(), linesIterator.hasNext());
-    }
-
-    return new Data(fileSourceBuilder.build(), lineHashesComputer.getResult(), sourceHashComputer.getHash());
-  }
-
-  private void read(DbFileSources.Data.Builder fileSourceBuilder, int currentLine, String lineSource, boolean hasNextLine) {
-    sourceHashComputer.addLine(lineSource, hasNextLine);
-    lineHashesComputer.addLine(lineSource);
-
-    DbFileSources.Line.Builder lineBuilder = fileSourceBuilder
-      .addLinesBuilder()
-      .setSource(lineSource)
-      .setLine(currentLine);
-
-    for (LineReader lineReader : lineReaders) {
-      lineReader.read(lineBuilder);
-    }
-  }
-
-  public static class Data {
-    private final DbFileSources.Data fileSourceData;
-    private final List<String> lineHashes;
-    private final String srcHash;
-
-    private Data(DbFileSources.Data fileSourceData, List<String> lineHashes, String srcHash) {
-      this.fileSourceData = fileSourceData;
-      this.lineHashes = lineHashes;
-      this.srcHash = srcHash;
-    }
-
-    public String getSrcHash() {
-      return srcHash;
-    }
-
-    public List<String> getLineHashes() {
-      return lineHashes;
-    }
-
-    public DbFileSources.Data getFileSourceData() {
-      return fileSourceData;
-    }
-  }
-
-}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/source/FileSourceDataComputer.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/source/FileSourceDataComputer.java
new file mode 100644 (file)
index 0000000..de2ebfe
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * 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.sonar.ce.task.projectanalysis.source;
+
+import java.util.List;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.sonar.ce.task.projectanalysis.component.Component;
+import org.sonar.ce.task.projectanalysis.scm.Changeset;
+import org.sonar.core.hash.SourceHashComputer;
+import org.sonar.core.util.CloseableIterator;
+import org.sonar.db.protobuf.DbFileSources;
+
+public class FileSourceDataComputer {
+  private final SourceLinesRepository sourceLinesRepository;
+  private final SourceLineReadersFactory sourceLineReadersFactory;
+  private final SourceLinesHashRepository sourceLinesHash;
+  private final SourceHashComputer sourceHashComputer;
+
+  public FileSourceDataComputer(SourceLinesRepository sourceLinesRepository, SourceLineReadersFactory sourceLineReadersFactory, SourceLinesHashRepository sourceLinesHash) {
+    this.sourceLinesRepository = sourceLinesRepository;
+    this.sourceLineReadersFactory = sourceLineReadersFactory;
+    this.sourceLinesHash = sourceLinesHash;
+    this.sourceHashComputer = new SourceHashComputer();
+  }
+
+  public Data compute(Component file) {
+    try (CloseableIterator<String> linesIterator = sourceLinesRepository.readLines(file);
+      SourceLineReadersFactory.LineReaders lineReaders = sourceLineReadersFactory.getLineReaders(file)) {
+
+      SourceLinesHashRepositoryImpl.LineHashesComputer lineHashesComputer = sourceLinesHash.getLineHashesComputerToPersist(file);
+      DbFileSources.Data.Builder fileSourceBuilder = DbFileSources.Data.newBuilder();
+      int currentLine = 0;
+
+      while (linesIterator.hasNext()) {
+        currentLine++;
+        read(fileSourceBuilder, lineHashesComputer, lineReaders, currentLine, linesIterator.next(), linesIterator.hasNext());
+      }
+
+      return new Data(fileSourceBuilder.build(), lineHashesComputer.getResult(), sourceHashComputer.getHash(), lineReaders.getLatestChangeWithRevision());
+    }
+  }
+
+  private void read(DbFileSources.Data.Builder fileSourceBuilder, SourceLinesHashRepositoryImpl.LineHashesComputer lineHashesComputer,
+    SourceLineReadersFactory.LineReaders lineReaders, int currentLine, String lineSource, boolean hasNextLine) {
+    sourceHashComputer.addLine(lineSource, hasNextLine);
+    lineHashesComputer.addLine(lineSource);
+
+    DbFileSources.Line.Builder lineBuilder = fileSourceBuilder
+      .addLinesBuilder()
+      .setSource(lineSource)
+      .setLine(currentLine);
+
+    lineReaders.read(lineBuilder);
+  }
+
+  public static class Data {
+    private final DbFileSources.Data fileSourceData;
+    private final List<String> lineHashes;
+    private final String srcHash;
+    private final Changeset latestChangeWithRevision;
+
+    public Data(DbFileSources.Data fileSourceData, List<String> lineHashes, String srcHash, @Nullable Changeset latestChangeWithRevision) {
+      this.fileSourceData = fileSourceData;
+      this.lineHashes = lineHashes;
+      this.srcHash = srcHash;
+      this.latestChangeWithRevision = latestChangeWithRevision;
+    }
+
+    public String getSrcHash() {
+      return srcHash;
+    }
+
+    public List<String> getLineHashes() {
+      return lineHashes;
+    }
+
+    public DbFileSources.Data getLineData() {
+      return fileSourceData;
+    }
+
+    @CheckForNull
+    public Changeset getLatestChangeWithRevision() {
+      return latestChangeWithRevision;
+    }
+  }
+
+}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/source/SourceLineReadersFactory.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/source/SourceLineReadersFactory.java
new file mode 100644 (file)
index 0000000..82f3858
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * 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.sonar.ce.task.projectanalysis.source;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.sonar.ce.task.projectanalysis.batch.BatchReportReader;
+import org.sonar.ce.task.projectanalysis.component.Component;
+import org.sonar.ce.task.projectanalysis.duplication.DuplicationRepository;
+import org.sonar.ce.task.projectanalysis.scm.Changeset;
+import org.sonar.ce.task.projectanalysis.scm.ScmInfo;
+import org.sonar.ce.task.projectanalysis.scm.ScmInfoRepository;
+import org.sonar.ce.task.projectanalysis.source.linereader.CoverageLineReader;
+import org.sonar.ce.task.projectanalysis.source.linereader.DuplicationLineReader;
+import org.sonar.ce.task.projectanalysis.source.linereader.HighlightingLineReader;
+import org.sonar.ce.task.projectanalysis.source.linereader.LineReader;
+import org.sonar.ce.task.projectanalysis.source.linereader.RangeOffsetConverter;
+import org.sonar.ce.task.projectanalysis.source.linereader.ScmLineReader;
+import org.sonar.ce.task.projectanalysis.source.linereader.SymbolsLineReader;
+import org.sonar.core.util.CloseableIterator;
+import org.sonar.db.protobuf.DbFileSources;
+import org.sonar.scanner.protocol.output.ScannerReport;
+
+public class SourceLineReadersFactory {
+  private final BatchReportReader reportReader;
+  private final ScmInfoRepository scmInfoRepository;
+  private final DuplicationRepository duplicationRepository;
+
+  SourceLineReadersFactory(BatchReportReader reportReader, ScmInfoRepository scmInfoRepository, DuplicationRepository duplicationRepository) {
+    this.reportReader = reportReader;
+    this.scmInfoRepository = scmInfoRepository;
+    this.duplicationRepository = duplicationRepository;
+  }
+
+  public LineReaders getLineReaders(Component component) {
+    List<LineReader> readers = new ArrayList<>();
+    List<CloseableIterator<?>> closeables = new ArrayList<>();
+    ScmLineReader scmLineReader = null;
+
+    int componentRef = component.getReportAttributes().getRef();
+    CloseableIterator<ScannerReport.LineCoverage> coverageIt = reportReader.readComponentCoverage(componentRef);
+    closeables.add(coverageIt);
+    readers.add(new CoverageLineReader(coverageIt));
+
+    Optional<ScmInfo> scmInfoOptional = scmInfoRepository.getScmInfo(component);
+    if (scmInfoOptional.isPresent()) {
+      scmLineReader = new ScmLineReader(scmInfoOptional.get());
+      readers.add(scmLineReader);
+    }
+
+    RangeOffsetConverter rangeOffsetConverter = new RangeOffsetConverter();
+    CloseableIterator<ScannerReport.SyntaxHighlightingRule> highlightingIt = reportReader.readComponentSyntaxHighlighting(componentRef);
+    closeables.add(highlightingIt);
+    readers.add(new HighlightingLineReader(component, highlightingIt, rangeOffsetConverter));
+
+    CloseableIterator<ScannerReport.Symbol> symbolsIt = reportReader.readComponentSymbols(componentRef);
+    closeables.add(symbolsIt);
+    readers.add(new SymbolsLineReader(component, symbolsIt, rangeOffsetConverter));
+    readers.add(new DuplicationLineReader(duplicationRepository.getDuplications(component)));
+
+    return new LineReaders(readers, scmLineReader, closeables);
+  }
+
+  static class LineReaders implements AutoCloseable, LineReader {
+    final List<LineReader> readers;
+    @Nullable
+    final ScmLineReader scmLineReader;
+    final List<CloseableIterator<?>> closeables;
+
+    LineReaders(List<LineReader> readers, @Nullable ScmLineReader scmLineReader, List<CloseableIterator<?>> closeables) {
+      this.readers = readers;
+      this.scmLineReader = scmLineReader;
+      this.closeables = closeables;
+    }
+
+    @Override public void close() {
+      for (CloseableIterator<?> reportIterator : closeables) {
+        reportIterator.close();
+      }
+    }
+
+    @Override public void read(DbFileSources.Line.Builder lineBuilder) {
+      for (LineReader r : readers) {
+        r.read(lineBuilder);
+      }
+    }
+
+    @CheckForNull
+    public Changeset getLatestChangeWithRevision() {
+      return scmLineReader == null ? null : scmLineReader.getLatestChangeWithRevision();
+    }
+  }
+
+}
index 5bf405516f2bc684928c2e4798240662fc47a3b5..52e7b043bf9b3824e4cba4b6d7bb4fdcda74362a 100644 (file)
@@ -27,7 +27,6 @@ import org.sonar.core.hash.LineRange;
 import org.sonar.core.hash.SourceLineHashesComputer;
 import org.sonar.core.util.CloseableIterator;
 import org.sonar.db.source.LineHashVersion;
-import org.sonar.ce.task.projectanalysis.component.Component;
 
 public class SourceLinesHashRepositoryImpl implements SourceLinesHashRepository {
   private final SourceLinesRepository sourceLinesRepository;
@@ -95,7 +94,7 @@ public class SourceLinesHashRepositoryImpl implements SourceLinesHashRepository
     return processor.getResult();
   }
 
-  public static interface LineHashesComputer {
+  public interface LineHashesComputer {
     void addLine(String line);
 
     List<String> getResult();
index 2057c701a1a656c6c97e47873884204901e2bd0b..fa1a4153e02384887f2590a294039d7b2bbaa582 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.ce.task.projectanalysis.source.linereader;
 
 import org.sonar.db.protobuf.DbFileSources;
 
+@FunctionalInterface
 public interface LineReader {
 
   void read(DbFileSources.Line.Builder lineBuilder);
index ca933bd5a7ac1520e2c9496fabf22f62853ba50b..50c61d5f9412f4c5bbe32867e6a1e0fb88d019bc 100644 (file)
 package org.sonar.ce.task.projectanalysis.step;
 
 import com.google.common.collect.ImmutableMap;
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import org.apache.commons.codec.digest.DigestUtils;
 import org.apache.commons.lang.ObjectUtils;
 import org.sonar.api.utils.System2;
-import org.sonar.ce.task.projectanalysis.batch.BatchReportReader;
 import org.sonar.ce.task.projectanalysis.component.Component;
 import org.sonar.ce.task.projectanalysis.component.CrawlerDepthLimit;
 import org.sonar.ce.task.projectanalysis.component.DepthTraversalTypeAwareCrawler;
 import org.sonar.ce.task.projectanalysis.component.TreeRootHolder;
 import org.sonar.ce.task.projectanalysis.component.TypeAwareVisitorAdapter;
-import org.sonar.ce.task.projectanalysis.duplication.DuplicationRepository;
 import org.sonar.ce.task.projectanalysis.scm.Changeset;
-import org.sonar.ce.task.projectanalysis.scm.ScmInfo;
-import org.sonar.ce.task.projectanalysis.scm.ScmInfoRepository;
-import org.sonar.ce.task.projectanalysis.source.ComputeFileSourceData;
+import org.sonar.ce.task.projectanalysis.source.FileSourceDataComputer;
 import org.sonar.ce.task.projectanalysis.source.SourceLinesHashRepository;
-import org.sonar.ce.task.projectanalysis.source.SourceLinesHashRepositoryImpl.LineHashesComputer;
-import org.sonar.ce.task.projectanalysis.source.SourceLinesRepository;
-import org.sonar.ce.task.projectanalysis.source.linereader.CoverageLineReader;
-import org.sonar.ce.task.projectanalysis.source.linereader.DuplicationLineReader;
-import org.sonar.ce.task.projectanalysis.source.linereader.HighlightingLineReader;
-import org.sonar.ce.task.projectanalysis.source.linereader.LineReader;
-import org.sonar.ce.task.projectanalysis.source.linereader.RangeOffsetConverter;
-import org.sonar.ce.task.projectanalysis.source.linereader.ScmLineReader;
-import org.sonar.ce.task.projectanalysis.source.linereader.SymbolsLineReader;
 import org.sonar.ce.task.step.ComputationStep;
-import org.sonar.core.util.CloseableIterator;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.protobuf.DbFileSources;
 import org.sonar.db.source.FileSourceDto;
 import org.sonar.db.source.FileSourceDto.Type;
-import org.sonar.scanner.protocol.output.ScannerReport;
 
 import static org.sonar.ce.task.projectanalysis.component.ComponentVisitor.Order.PRE_ORDER;
 
 public class PersistFileSourcesStep implements ComputationStep {
-
   private final DbClient dbClient;
   private final System2 system2;
   private final TreeRootHolder treeRootHolder;
-  private final BatchReportReader reportReader;
-  private final SourceLinesRepository sourceLinesRepository;
-  private final ScmInfoRepository scmInfoRepository;
-  private final DuplicationRepository duplicationRepository;
   private final SourceLinesHashRepository sourceLinesHash;
+  private final FileSourceDataComputer fileSourceDataComputer;
 
-  public PersistFileSourcesStep(DbClient dbClient, System2 system2, TreeRootHolder treeRootHolder, BatchReportReader reportReader, SourceLinesRepository sourceLinesRepository,
-    ScmInfoRepository scmInfoRepository, DuplicationRepository duplicationRepository, SourceLinesHashRepository sourceLinesHash) {
+  public PersistFileSourcesStep(DbClient dbClient, System2 system2, TreeRootHolder treeRootHolder,
+    SourceLinesHashRepository sourceLinesHash, FileSourceDataComputer fileSourceDataComputer) {
     this.dbClient = dbClient;
     this.system2 = system2;
     this.treeRootHolder = treeRootHolder;
-    this.reportReader = reportReader;
-    this.sourceLinesRepository = sourceLinesRepository;
-    this.scmInfoRepository = scmInfoRepository;
-    this.duplicationRepository = duplicationRepository;
     this.sourceLinesHash = sourceLinesHash;
+    this.fileSourceDataComputer = fileSourceDataComputer;
   }
 
   @Override
@@ -95,7 +71,6 @@ public class PersistFileSourcesStep implements ComputationStep {
   }
 
   private class FileSourceVisitor extends TypeAwareVisitorAdapter {
-
     private final DbSession session;
 
     private Map<String, FileSourceDto> previousFileSourcesByUuid = new HashMap<>();
@@ -118,22 +93,19 @@ public class PersistFileSourcesStep implements ComputationStep {
 
     @Override
     public void visitFile(Component file) {
-      try (CloseableIterator<String> linesIterator = sourceLinesRepository.readLines(file);
-        LineReaders lineReaders = new LineReaders(reportReader, scmInfoRepository, duplicationRepository, file)) {
-        LineHashesComputer lineHashesComputer = sourceLinesHash.getLineHashesComputerToPersist(file);
-        ComputeFileSourceData computeFileSourceData = new ComputeFileSourceData(linesIterator, lineReaders.readers(), lineHashesComputer);
-        ComputeFileSourceData.Data fileSourceData = computeFileSourceData.compute();
-        persistSource(fileSourceData, file, lineReaders.getLatestChangeWithRevision());
+      try {
+        FileSourceDataComputer.Data fileSourceData = fileSourceDataComputer.compute(file);
+        persistSource(fileSourceData, file, fileSourceData.getLatestChangeWithRevision());
       } catch (Exception e) {
         throw new IllegalStateException(String.format("Cannot persist sources of %s", file.getKey()), e);
       }
     }
 
-    private void persistSource(ComputeFileSourceData.Data fileSourceData, Component file, @Nullable Changeset latestChangeWithRevision) {
-      DbFileSources.Data fileData = fileSourceData.getFileSourceData();
+    private void persistSource(FileSourceDataComputer.Data fileSourceData, Component file, @Nullable Changeset latestChangeWithRevision) {
+      DbFileSources.Data lineData = fileSourceData.getLineData();
 
-      byte[] data = FileSourceDto.encodeSourceData(fileData);
-      String dataHash = DigestUtils.md5Hex(data);
+      byte[] binaryData = FileSourceDto.encodeSourceData(lineData);
+      String dataHash = DigestUtils.md5Hex(binaryData);
       String srcHash = fileSourceData.getSrcHash();
       List<String> lineHashes = fileSourceData.getLineHashes();
       Integer lineHashesVersion = sourceLinesHash.getLineHashesVersion(file);
@@ -144,7 +116,7 @@ public class PersistFileSourcesStep implements ComputationStep {
           .setProjectUuid(projectUuid)
           .setFileUuid(file.getUuid())
           .setDataType(Type.SOURCE)
-          .setBinaryData(data)
+          .setBinaryData(binaryData)
           .setSrcHash(srcHash)
           .setDataHash(dataHash)
           .setLineHashes(lineHashes)
@@ -160,10 +132,10 @@ public class PersistFileSourcesStep implements ComputationStep {
         boolean srcHashUpdated = !srcHash.equals(previousDto.getSrcHash());
         String revision = computeRevision(latestChangeWithRevision);
         boolean revisionUpdated = !ObjectUtils.equals(revision, previousDto.getRevision());
-        boolean lineHashesVersionUpdated = previousDto.getLineHashesVersion() != lineHashesVersion;
+        boolean lineHashesVersionUpdated = !previousDto.getLineHashesVersion().equals(lineHashesVersion);
         if (binaryDataUpdated || srcHashUpdated || revisionUpdated || lineHashesVersionUpdated) {
           previousDto
-            .setBinaryData(data)
+            .setBinaryData(binaryData)
             .setDataHash(dataHash)
             .setSrcHash(srcHash)
             .setLineHashes(lineHashes)
@@ -185,54 +157,6 @@ public class PersistFileSourcesStep implements ComputationStep {
     }
   }
 
-  private static class LineReaders implements AutoCloseable {
-    private final List<LineReader> readers = new ArrayList<>();
-    private final List<CloseableIterator<?>> closeables = new ArrayList<>();
-    @CheckForNull
-    private final ScmLineReader scmLineReader;
-
-    LineReaders(BatchReportReader reportReader, ScmInfoRepository scmInfoRepository, DuplicationRepository duplicationRepository, Component component) {
-      int componentRef = component.getReportAttributes().getRef();
-      CloseableIterator<ScannerReport.LineCoverage> coverageIt = reportReader.readComponentCoverage(componentRef);
-      closeables.add(coverageIt);
-      readers.add(new CoverageLineReader(coverageIt));
-
-      Optional<ScmInfo> scmInfoOptional = scmInfoRepository.getScmInfo(component);
-      if (scmInfoOptional.isPresent()) {
-        this.scmLineReader = new ScmLineReader(scmInfoOptional.get());
-        readers.add(scmLineReader);
-      } else {
-        this.scmLineReader = null;
-      }
-
-      RangeOffsetConverter rangeOffsetConverter = new RangeOffsetConverter();
-      CloseableIterator<ScannerReport.SyntaxHighlightingRule> highlightingIt = reportReader.readComponentSyntaxHighlighting(componentRef);
-      closeables.add(highlightingIt);
-      readers.add(new HighlightingLineReader(component, highlightingIt, rangeOffsetConverter));
-
-      CloseableIterator<ScannerReport.Symbol> symbolsIt = reportReader.readComponentSymbols(componentRef);
-      closeables.add(symbolsIt);
-      readers.add(new SymbolsLineReader(component, symbolsIt, rangeOffsetConverter));
-      readers.add(new DuplicationLineReader(duplicationRepository.getDuplications(component)));
-    }
-
-    List<LineReader> readers() {
-      return readers;
-    }
-
-    @Override
-    public void close() {
-      for (CloseableIterator<?> reportIterator : closeables) {
-        reportIterator.close();
-      }
-    }
-
-    @CheckForNull
-    public Changeset getLatestChangeWithRevision() {
-      return scmLineReader == null ? null : scmLineReader.getLatestChangeWithRevision();
-    }
-  }
-
   @Override
   public String getDescription() {
     return "Persist sources";
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/source/ComputeFileSourceDataTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/source/ComputeFileSourceDataTest.java
deleted file mode 100644 (file)
index 3bdaf73..0000000
+++ /dev/null
@@ -1,86 +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 org.sonar.ce.task.projectanalysis.source;
-
-import com.google.common.collect.Lists;
-import org.junit.Test;
-import org.sonar.ce.task.projectanalysis.source.SourceLinesHashRepositoryImpl.LineHashesComputer;
-import org.sonar.ce.task.projectanalysis.source.linereader.LineReader;
-import org.sonar.db.protobuf.DbFileSources;
-
-import static com.google.common.collect.Lists.newArrayList;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-public class ComputeFileSourceDataTest {
-
-  private LineHashesComputer lineHashesComputer = mock(LineHashesComputer.class);
-
-  @Test
-  public void compute_one_line() {
-    when(lineHashesComputer.getResult()).thenReturn(Lists.newArrayList("137f72c3708c6bd0de00a0e5a69c699b"));
-    ComputeFileSourceData computeFileSourceData = new ComputeFileSourceData(
-      newArrayList("line1").iterator(),
-      Lists.newArrayList(new MockLineReader()),
-      lineHashesComputer);
-
-    ComputeFileSourceData.Data data = computeFileSourceData.compute();
-    assertThat(data.getLineHashes()).containsOnly("137f72c3708c6bd0de00a0e5a69c699b");
-    assertThat(data.getSrcHash()).isEqualTo("137f72c3708c6bd0de00a0e5a69c699b");
-    assertThat(data.getFileSourceData().getLinesList()).hasSize(1);
-    assertThat(data.getFileSourceData().getLines(0).getHighlighting()).isEqualTo("h-1");
-
-    verify(lineHashesComputer).addLine("line1");
-    verify(lineHashesComputer).getResult();
-    verifyNoMoreInteractions(lineHashesComputer);
-  }
-
-  @Test
-  public void compute_two_lines() {
-    when(lineHashesComputer.getResult()).thenReturn(Lists.newArrayList("137f72c3708c6bd0de00a0e5a69c699b", "e6251bcf1a7dc3ba5e7933e325bbe605"));
-
-    ComputeFileSourceData computeFileSourceData = new ComputeFileSourceData(
-      newArrayList("line1", "line2").iterator(),
-      Lists.newArrayList(new MockLineReader()),
-      lineHashesComputer);
-
-    ComputeFileSourceData.Data data = computeFileSourceData.compute();
-    assertThat(data.getLineHashes()).containsOnly("137f72c3708c6bd0de00a0e5a69c699b", "e6251bcf1a7dc3ba5e7933e325bbe605");
-    assertThat(data.getSrcHash()).isEqualTo("ee5a58024a155466b43bc559d953e018");
-    assertThat(data.getFileSourceData().getLinesList()).hasSize(2);
-    assertThat(data.getFileSourceData().getLines(0).getHighlighting()).isEqualTo("h-1");
-    assertThat(data.getFileSourceData().getLines(1).getHighlighting()).isEqualTo("h-2");
-
-    verify(lineHashesComputer).addLine("line1");
-    verify(lineHashesComputer).addLine("line2");
-    verify(lineHashesComputer).getResult();
-    verifyNoMoreInteractions(lineHashesComputer);
-  }
-
-  private static class MockLineReader implements LineReader {
-    @Override
-    public void read(DbFileSources.Line.Builder lineBuilder) {
-      lineBuilder.setHighlighting("h-" + lineBuilder.getLine());
-    }
-  }
-}
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/source/FileSourceDataComputerTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/source/FileSourceDataComputerTest.java
new file mode 100644 (file)
index 0000000..7e05224
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * 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.sonar.ce.task.projectanalysis.source;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.ce.task.projectanalysis.component.Component;
+import org.sonar.ce.task.projectanalysis.component.ReportComponent;
+import org.sonar.ce.task.projectanalysis.source.SourceLinesHashRepositoryImpl.LineHashesComputer;
+import org.sonar.ce.task.projectanalysis.source.linereader.LineReader;
+import org.sonar.ce.task.projectanalysis.source.linereader.ScmLineReader;
+import org.sonar.core.util.CloseableIterator;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+public class FileSourceDataComputerTest {
+  private static final Component FILE = ReportComponent.builder(Component.Type.FILE, 1).build();
+
+  private SourceLinesRepository sourceLinesRepository = mock(SourceLinesRepository.class);
+  private LineHashesComputer lineHashesComputer = mock(LineHashesComputer.class);
+  private SourceLineReadersFactory sourceLineReadersFactory = mock(SourceLineReadersFactory.class);
+  private SourceLinesHashRepository sourceLinesHashRepository = mock(SourceLinesHashRepository.class);
+  private ScmLineReader scmLineReader = mock(ScmLineReader.class);
+  private CloseableIterator closeableIterator = mock(CloseableIterator.class);
+
+  private FileSourceDataComputer fileSourceDataComputer;
+
+  @Before
+  public void before() {
+    when(sourceLinesHashRepository.getLineHashesComputerToPersist(FILE)).thenReturn(lineHashesComputer);
+    LineReader reader = line -> line.setHighlighting("h-" + line.getLine());
+    SourceLineReadersFactory.LineReaders lineReaders = new SourceLineReadersFactory.LineReaders(Collections.singletonList(reader), scmLineReader,
+      Collections.singletonList(closeableIterator));
+    when(sourceLineReadersFactory.getLineReaders(FILE)).thenReturn(lineReaders);
+
+    fileSourceDataComputer = new FileSourceDataComputer(sourceLinesRepository, sourceLineReadersFactory, sourceLinesHashRepository);
+  }
+
+  @Test
+  public void compute_one_line() {
+    List<String> lineHashes = Collections.singletonList("lineHash");
+    when(sourceLinesRepository.readLines(FILE)).thenReturn(CloseableIterator.from(Collections.singletonList("line1").iterator()));
+    when(lineHashesComputer.getResult()).thenReturn(lineHashes);
+
+    FileSourceDataComputer.Data data = fileSourceDataComputer.compute(FILE);
+
+    assertThat(data.getLineHashes()).isEqualTo(lineHashes);
+    assertThat(data.getSrcHash()).isEqualTo("137f72c3708c6bd0de00a0e5a69c699b");
+    assertThat(data.getLineData().getLinesList()).hasSize(1);
+    assertThat(data.getLineData().getLines(0).getHighlighting()).isEqualTo("h-1");
+
+    verify(lineHashesComputer).addLine("line1");
+    verify(lineHashesComputer).getResult();
+    verify(closeableIterator).close();
+    verifyNoMoreInteractions(lineHashesComputer);
+  }
+
+  @Test
+  public void compute_two_lines() {
+    List<String> lineHashes = Arrays.asList("137f72c3708c6bd0de00a0e5a69c699b", "e6251bcf1a7dc3ba5e7933e325bbe605");
+    when(sourceLinesRepository.readLines(FILE)).thenReturn(CloseableIterator.from(Arrays.asList("line1", "line2").iterator()));
+    when(lineHashesComputer.getResult()).thenReturn(lineHashes);
+
+    FileSourceDataComputer.Data data = fileSourceDataComputer.compute(FILE);
+
+    assertThat(data.getLineHashes()).isEqualTo(lineHashes);
+    assertThat(data.getSrcHash()).isEqualTo("ee5a58024a155466b43bc559d953e018");
+    assertThat(data.getLineData().getLinesList()).hasSize(2);
+    assertThat(data.getLineData().getLines(0).getHighlighting()).isEqualTo("h-1");
+    assertThat(data.getLineData().getLines(1).getHighlighting()).isEqualTo("h-2");
+
+    verify(lineHashesComputer).addLine("line1");
+    verify(lineHashesComputer).addLine("line2");
+    verify(lineHashesComputer).getResult();
+    verify(closeableIterator).close();
+    verifyNoMoreInteractions(lineHashesComputer);
+  }
+
+}
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/source/SourceLineReadersFactoryTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/source/SourceLineReadersFactoryTest.java
new file mode 100644 (file)
index 0000000..2942a4b
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * 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.sonar.ce.task.projectanalysis.source;
+
+import java.util.Arrays;
+import java.util.Collections;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule;
+import org.sonar.ce.task.projectanalysis.component.Component;
+import org.sonar.ce.task.projectanalysis.component.FileAttributes;
+import org.sonar.ce.task.projectanalysis.component.ReportComponent;
+import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
+import org.sonar.ce.task.projectanalysis.duplication.DuplicationRepositoryRule;
+import org.sonar.ce.task.projectanalysis.scm.Changeset;
+import org.sonar.ce.task.projectanalysis.scm.ScmInfoRepositoryRule;
+import org.sonar.ce.task.projectanalysis.source.linereader.LineReader;
+import org.sonar.ce.task.projectanalysis.source.linereader.ScmLineReader;
+import org.sonar.core.util.CloseableIterator;
+import org.sonar.db.protobuf.DbFileSources;
+import org.sonar.scanner.protocol.output.ScannerReport;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+public class SourceLineReadersFactoryTest {
+  private static final int FILE1_REF = 3;
+  private static final String PROJECT_UUID = "PROJECT";
+  private static final String PROJECT_KEY = "PROJECT_KEY";
+  private static final String FILE1_UUID = "FILE1";
+  private static final long NOW = 123456789L;
+
+  @Rule
+  public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
+  @Rule
+  public BatchReportReaderRule reportReader = new BatchReportReaderRule();
+  @Rule
+  public ScmInfoRepositoryRule scmInfoRepository = new ScmInfoRepositoryRule();
+  @Rule
+  public DuplicationRepositoryRule duplicationRepository = DuplicationRepositoryRule.create(treeRootHolder);
+
+  private SourceLineReadersFactory underTest;
+
+  @Before
+  public void setUp() {
+    underTest = new SourceLineReadersFactory(reportReader, scmInfoRepository, duplicationRepository);
+  }
+
+  @Test
+  public void should_create_readers() {
+    initBasicReport(10);
+    SourceLineReadersFactory.LineReaders lineReaders = underTest.getLineReaders(fileComponent());
+
+    assertThat(lineReaders).isNotNull();
+    assertThat(lineReaders.closeables).hasSize(3);
+    assertThat(lineReaders.readers).hasSize(4);
+  }
+
+  @Test
+  public void line_readers_should_close_all_closeables() {
+    LineReader r1 = mock(LineReader.class);
+    LineReader r2 = mock(LineReader.class);
+    CloseableIterator c1 = mock(CloseableIterator.class);
+    CloseableIterator c2 = mock(CloseableIterator.class);
+
+    SourceLineReadersFactory.LineReaders lineReaders = new SourceLineReadersFactory.LineReaders(Arrays.asList(r1, r2), null, Arrays.asList(c1, c2));
+    lineReaders.close();
+
+    verify(c1).close();
+    verify(c2).close();
+    verifyNoMoreInteractions(c1, c2);
+    verifyZeroInteractions(r1, r2);
+  }
+
+  @Test
+  public void line_readers_should_call_all_readers() {
+    LineReader r1 = mock(LineReader.class);
+    LineReader r2 = mock(LineReader.class);
+    CloseableIterator c1 = mock(CloseableIterator.class);
+    CloseableIterator c2 = mock(CloseableIterator.class);
+
+    SourceLineReadersFactory.LineReaders lineReaders = new SourceLineReadersFactory.LineReaders(Arrays.asList(r1, r2), null, Arrays.asList(c1, c2));
+    DbFileSources.Line.Builder builder = DbFileSources.Line.newBuilder();
+    lineReaders.read(builder);
+
+    verify(r1).read(builder);
+    verify(r2).read(builder);
+    verifyNoMoreInteractions(r1, r2);
+    verifyZeroInteractions(c1, c2);
+  }
+
+  @Test
+  public void should_delegate_latest_changeset() {
+    ScmLineReader scmLineReader = mock(ScmLineReader.class);
+    Changeset changeset = Changeset.newChangesetBuilder().setDate(0L).build();
+    when(scmLineReader.getLatestChangeWithRevision()).thenReturn(changeset);
+    SourceLineReadersFactory.LineReaders lineReaders = new SourceLineReadersFactory.LineReaders(Collections.emptyList(), scmLineReader, Collections.emptyList());
+    assertThat(lineReaders.getLatestChangeWithRevision()).isEqualTo(changeset);
+  }
+
+  @Test
+  public void should_not_delegate_latest_changeset() {
+    SourceLineReadersFactory.LineReaders lineReaders = new SourceLineReadersFactory.LineReaders(Collections.emptyList(), null, Collections.emptyList());
+    assertThat(lineReaders.getLatestChangeWithRevision()).isNull();
+  }
+
+  private Component fileComponent() {
+    return ReportComponent.builder(Component.Type.FILE, FILE1_REF).build();
+  }
+
+  private void initBasicReport(int numberOfLines) {
+    treeRootHolder.setRoot(ReportComponent.builder(Component.Type.PROJECT, 1).setUuid(PROJECT_UUID).setKey(PROJECT_KEY).addChildren(
+      ReportComponent.builder(Component.Type.MODULE, 2).setUuid("MODULE").setKey("MODULE_KEY").addChildren(
+        ReportComponent.builder(Component.Type.FILE, FILE1_REF).setUuid(FILE1_UUID).setKey("MODULE_KEY:src/Foo.java")
+          .setFileAttributes(new FileAttributes(false, null, numberOfLines)).build())
+        .build())
+      .build());
+
+    reportReader.putComponent(ScannerReport.Component.newBuilder()
+      .setRef(1)
+      .setType(ScannerReport.Component.ComponentType.PROJECT)
+      .addChildRef(2)
+      .build());
+    reportReader.putComponent(ScannerReport.Component.newBuilder()
+      .setRef(2)
+      .setType(ScannerReport.Component.ComponentType.MODULE)
+      .addChildRef(FILE1_REF)
+      .build());
+    reportReader.putComponent(ScannerReport.Component.newBuilder()
+      .setRef(FILE1_REF)
+      .setType(ScannerReport.Component.ComponentType.FILE)
+      .setLines(numberOfLines)
+      .build());
+
+    // for (int i = 1; i <= numberOfLines; i++) {
+    // fileSourceRepository.addLine(FILE1_REF, "line" + i);
+    // }
+  }
+
+}
index 245602dcb3f3a8a0dce5369a808c96cfee1ddb9a..7d2b6bd644ce40289b223fb0abc4c65abcf14eaf 100644 (file)
  */
 package org.sonar.ce.task.projectanalysis.step;
 
-import com.google.common.collect.Lists;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
+import java.util.function.Consumer;
+import org.apache.commons.codec.digest.DigestUtils;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.mockito.Mockito;
 import org.sonar.api.utils.System2;
-import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule;
 import org.sonar.ce.task.projectanalysis.component.Component;
 import org.sonar.ce.task.projectanalysis.component.FileAttributes;
 import org.sonar.ce.task.projectanalysis.component.ReportComponent;
 import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
-import org.sonar.ce.task.projectanalysis.duplication.Duplication;
-import org.sonar.ce.task.projectanalysis.duplication.DuplicationRepositoryRule;
-import org.sonar.ce.task.projectanalysis.duplication.InnerDuplicate;
-import org.sonar.ce.task.projectanalysis.duplication.TextBlock;
 import org.sonar.ce.task.projectanalysis.scm.Changeset;
-import org.sonar.ce.task.projectanalysis.scm.ScmInfoRepositoryRule;
+import org.sonar.ce.task.projectanalysis.source.FileSourceDataComputer;
 import org.sonar.ce.task.projectanalysis.source.SourceLinesHashRepository;
 import org.sonar.ce.task.projectanalysis.source.SourceLinesHashRepositoryImpl;
-import org.sonar.ce.task.projectanalysis.source.SourceLinesRepositoryRule;
 import org.sonar.ce.task.step.ComputationStep;
 import org.sonar.ce.task.step.TestComputationStepContext;
 import org.sonar.db.DbClient;
@@ -51,12 +47,7 @@ import org.sonar.db.protobuf.DbFileSources;
 import org.sonar.db.source.FileSourceDto;
 import org.sonar.db.source.FileSourceDto.Type;
 import org.sonar.db.source.LineHashVersion;
-import org.sonar.scanner.protocol.output.ScannerReport;
-import org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType;
-import org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType;
 
-import static com.google.common.collect.ImmutableList.of;
-import static com.google.common.collect.Lists.newArrayList;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -68,6 +59,7 @@ public class PersistFileSourcesStepTest extends BaseStepTest {
   private static final String PROJECT_KEY = "PROJECT_KEY";
   private static final String FILE1_UUID = "FILE1";
   private static final long NOW = 123456789L;
+  private static final long PAST = 15000L;
 
   private System2 system2 = mock(System2.class);
 
@@ -77,18 +69,11 @@ public class PersistFileSourcesStepTest extends BaseStepTest {
   public DbTester dbTester = DbTester.create(system2);
   @Rule
   public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
-  @Rule
-  public BatchReportReaderRule reportReader = new BatchReportReaderRule();
-  @Rule
-  public ScmInfoRepositoryRule scmInfoRepository = new ScmInfoRepositoryRule();
-  @Rule
-  public SourceLinesRepositoryRule fileSourceRepository = new SourceLinesRepositoryRule();
-  @Rule
-  public DuplicationRepositoryRule duplicationRepository = DuplicationRepositoryRule.create(treeRootHolder);
 
   private SourceLinesHashRepository sourceLinesHashRepository = mock(SourceLinesHashRepository.class);
   private SourceLinesHashRepositoryImpl.LineHashesComputer lineHashesComputer = mock(SourceLinesHashRepositoryImpl.LineHashesComputer.class);
 
+  private FileSourceDataComputer fileSourceDataComputer = mock(FileSourceDataComputer.class);
   private DbClient dbClient = dbTester.getDbClient();
   private DbSession session = dbTester.getSession();
 
@@ -98,8 +83,8 @@ public class PersistFileSourcesStepTest extends BaseStepTest {
   public void setup() {
     when(system2.now()).thenReturn(NOW);
     when(sourceLinesHashRepository.getLineHashesComputerToPersist(Mockito.any(Component.class))).thenReturn(lineHashesComputer);
-    underTest = new PersistFileSourcesStep(dbClient, system2, treeRootHolder, reportReader, fileSourceRepository, scmInfoRepository,
-      duplicationRepository, sourceLinesHashRepository);
+    underTest = new PersistFileSourcesStep(dbClient, system2, treeRootHolder, sourceLinesHashRepository, fileSourceDataComputer);
+    initBasicReport(1);
   }
 
   @Override
@@ -109,8 +94,16 @@ public class PersistFileSourcesStepTest extends BaseStepTest {
 
   @Test
   public void persist_sources() {
-    initBasicReport(2);
-    when(lineHashesComputer.getResult()).thenReturn(Lists.newArrayList("137f72c3708c6bd0de00a0e5a69c699b", "e6251bcf1a7dc3ba5e7933e325bbe605"));
+    List<String> lineHashes = Arrays.asList("137f72c3708c6bd0de00a0e5a69c699b", "e6251bcf1a7dc3ba5e7933e325bbe605");
+    String sourceHash = "ee5a58024a155466b43bc559d953e018";
+    DbFileSources.Data fileSourceData = DbFileSources.Data.newBuilder()
+      .addAllLines(Arrays.asList(
+        DbFileSources.Line.newBuilder().setSource("line1").setLine(1).build(),
+        DbFileSources.Line.newBuilder().setSource("line2").setLine(2).build()
+      ))
+      .build();
+    when(fileSourceDataComputer.compute(fileComponent())).thenReturn(new FileSourceDataComputer.Data(fileSourceData, lineHashes, sourceHash, null));
+
     underTest.execute(new TestComputationStepContext());
 
     assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1);
@@ -134,8 +127,10 @@ public class PersistFileSourcesStepTest extends BaseStepTest {
 
   @Test
   public void persist_source_hashes() {
-    initBasicReport(2);
-    when(lineHashesComputer.getResult()).thenReturn(Lists.newArrayList("137f72c3708c6bd0de00a0e5a69c699b", "e6251bcf1a7dc3ba5e7933e325bbe605"));
+    List<String> lineHashes = Arrays.asList("137f72c3708c6bd0de00a0e5a69c699b", "e6251bcf1a7dc3ba5e7933e325bbe605");
+    String sourceHash = "ee5a58024a155466b43bc559d953e018";
+    setComputedData(DbFileSources.Data.newBuilder().build(), lineHashes, sourceHash, null);
+
     underTest.execute(new TestComputationStepContext());
 
     assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1);
@@ -146,72 +141,67 @@ public class PersistFileSourcesStepTest extends BaseStepTest {
 
   @Test
   public void persist_coverage() {
-    initBasicReport(1);
-
-    reportReader.putCoverage(FILE1_REF, newArrayList(ScannerReport.LineCoverage.newBuilder()
-      .setLine(1)
-      .setConditions(10)
-      .setHits(true)
-      .setCoveredConditions(2)
-      .build()));
+    DbFileSources.Data dbData = DbFileSources.Data.newBuilder().addLines(
+      DbFileSources.Line.newBuilder()
+        .setConditions(10)
+        .setCoveredConditions(2)
+        .setLineHits(1)
+        .setLine(1)
+        .build())
+      .build();
+    setComputedData(dbData);
 
     underTest.execute(new TestComputationStepContext());
 
     assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1);
     FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID);
-    DbFileSources.Data data = fileSourceDto.getSourceData();
-
-    assertThat(data.getLinesList()).hasSize(1);
+    assertThat(fileSourceDto.getSourceData()).isEqualTo(dbData);
+  }
 
-    assertThat(data.getLines(0).getLineHits()).isEqualTo(1);
-    assertThat(data.getLines(0).getConditions()).isEqualTo(10);
-    assertThat(data.getLines(0).getCoveredConditions()).isEqualTo(2);
+  private Component fileComponent() {
+    return ReportComponent.builder(Component.Type.FILE, FILE1_REF).build();
   }
 
   @Test
   public void persist_scm() {
-    initBasicReport(1);
-    scmInfoRepository.setScmInfo(FILE1_REF, Changeset.newChangesetBuilder()
-      .setAuthor("john")
-      .setDate(123456789L)
-      .setRevision("rev-1")
-      .build());
+    DbFileSources.Data dbData = DbFileSources.Data.newBuilder().addLines(
+      DbFileSources.Line.newBuilder()
+        .setScmAuthor("john")
+        .setScmDate(123456789L)
+        .setScmRevision("rev-1")
+        .build())
+      .build();
+    setComputedData(dbData);
 
     underTest.execute(new TestComputationStepContext());
 
     assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1);
     FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID);
-
-    assertThat(fileSourceDto.getRevision()).isEqualTo("rev-1");
-
-    DbFileSources.Data data = fileSourceDto.getSourceData();
-
-    assertThat(data.getLinesList()).hasSize(1);
-
-    assertThat(data.getLines(0).getScmAuthor()).isEqualTo("john");
-    assertThat(data.getLines(0).getScmDate()).isEqualTo(123456789L);
-    assertThat(data.getLines(0).getScmRevision()).isEqualTo("rev-1");
+    assertThat(fileSourceDto.getSourceData()).isEqualTo(dbData);
+    assertThat(fileSourceDto.getRevision()).isNull();
   }
 
   @Test
   public void persist_scm_some_lines() {
-    initBasicReport(3);
-    scmInfoRepository.setScmInfo(FILE1_REF, Changeset.newChangesetBuilder()
-      .setAuthor("john")
-      .setDate(123456789L)
-      .setRevision("rev-1")
-      .build(),
-      Changeset.newChangesetBuilder()
-        .setDate(223456789L)
-        .build());
+    DbFileSources.Data dbData = DbFileSources.Data.newBuilder().addAllLines(Arrays.asList(
+      DbFileSources.Line.newBuilder()
+        .setScmAuthor("john")
+        .setScmDate(123456789L)
+        .setScmRevision("rev-1")
+        .build(),
+      DbFileSources.Line.newBuilder()
+        .setScmDate(223456789L)
+        .build(),
+      DbFileSources.Line.newBuilder()
+        .build()
+    )).build();
+    setComputedData(dbData);
 
     underTest.execute(new TestComputationStepContext());
 
     assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1);
     FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID);
 
-    assertThat(fileSourceDto.getRevision()).isEqualTo("rev-1");
-
     DbFileSources.Data data = fileSourceDto.getSourceData();
 
     assertThat(data.getLinesList()).hasSize(3);
@@ -227,86 +217,67 @@ public class PersistFileSourcesStepTest extends BaseStepTest {
     assertThat(data.getLines(2).getScmAuthor()).isEmpty();
     assertThat(data.getLines(2).getScmDate()).isEqualTo(0);
     assertThat(data.getLines(2).getScmRevision()).isEmpty();
-
   }
 
   @Test
   public void persist_highlighting() {
-    initBasicReport(1);
-
-    reportReader.putSyntaxHighlighting(FILE1_REF, newArrayList(ScannerReport.SyntaxHighlightingRule.newBuilder()
-      .setRange(ScannerReport.TextRange.newBuilder()
-        .setStartLine(1).setEndLine(1)
-        .setStartOffset(2).setEndOffset(4)
-        .build())
-      .setType(HighlightingType.ANNOTATION)
-      .build()));
+    DbFileSources.Data dbData = DbFileSources.Data.newBuilder().addLines(
+      DbFileSources.Line.newBuilder()
+        .setHighlighting("2,4,a")
+        .build()
+    ).build();
+    setComputedData(dbData);
 
     underTest.execute(new TestComputationStepContext());
 
     assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1);
     FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID);
     DbFileSources.Data data = fileSourceDto.getSourceData();
-
+    assertThat(data).isEqualTo(dbData);
     assertThat(data.getLinesList()).hasSize(1);
-
     assertThat(data.getLines(0).getHighlighting()).isEqualTo("2,4,a");
   }
 
   @Test
   public void persist_symbols() {
-    initBasicReport(3);
-
-    reportReader.putSymbols(FILE1_REF, newArrayList(
-      ScannerReport.Symbol.newBuilder()
-        .setDeclaration(ScannerReport.TextRange.newBuilder()
-          .setStartLine(1).setEndLine(1).setStartOffset(2).setEndOffset(4)
-          .build())
-        .addReference(ScannerReport.TextRange.newBuilder()
-          .setStartLine(3).setEndLine(3).setStartOffset(1).setEndOffset(3)
-          .build())
-        .build()));
+    DbFileSources.Data dbData = DbFileSources.Data.newBuilder().addAllLines(Arrays.asList(
+      DbFileSources.Line.newBuilder()
+        .setSymbols("2,4,1")
+        .build(),
+      DbFileSources.Line.newBuilder().build(),
+      DbFileSources.Line.newBuilder()
+        .setSymbols("1,3,1")
+        .build()
+    )).build();
+    setComputedData(dbData);
 
     underTest.execute(new TestComputationStepContext());
 
     assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1);
     FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID);
-    DbFileSources.Data data = fileSourceDto.getSourceData();
-
-    assertThat(data.getLinesList()).hasSize(3);
-
-    assertThat(data.getLines(0).getSymbols()).isEqualTo("2,4,1");
-    assertThat(data.getLines(1).getSymbols()).isEmpty();
-    assertThat(data.getLines(2).getSymbols()).isEqualTo("1,3,1");
+    assertThat(fileSourceDto.getSourceData()).isEqualTo(dbData);
   }
 
   @Test
   public void persist_duplication() {
-    initBasicReport(1);
-
-    duplicationRepository.add(
-      FILE1_REF,
-      new Duplication(new TextBlock(1, 2), Arrays.asList(new InnerDuplicate(new TextBlock(3, 4)))));
+    DbFileSources.Data dbData = DbFileSources.Data.newBuilder().addLines(
+      DbFileSources.Line.newBuilder()
+        .addDuplication(2)
+        .build()
+    ).build();
+    setComputedData(dbData);
 
     underTest.execute(new TestComputationStepContext());
 
     assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1);
     FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID);
-    DbFileSources.Data data = fileSourceDto.getSourceData();
-
-    assertThat(data.getLinesList()).hasSize(1);
-
-    assertThat(data.getLines(0).getDuplicationList()).hasSize(1);
+    assertThat(fileSourceDto.getSourceData()).isEqualTo(dbData);
   }
 
   @Test
   public void save_revision() {
-    initBasicReport(1);
-    scmInfoRepository.setScmInfo(FILE1_REF, Changeset.newChangesetBuilder()
-      .setAuthor("john")
-      .setDate(123456789L)
-      .setRevision("rev-1")
-      .build());
+    Changeset latest = Changeset.newChangesetBuilder().setDate(0L).setRevision("rev-1").build();
+    setComputedData(DbFileSources.Data.newBuilder().build(), Collections.singletonList("lineHashes"), "srcHash", latest);
 
     underTest.execute(new TestComputationStepContext());
 
@@ -316,7 +287,7 @@ public class PersistFileSourcesStepTest extends BaseStepTest {
 
   @Test
   public void not_save_revision() {
-    initBasicReport(1);
+    setComputedData(DbFileSources.Data.newBuilder().build());
 
     underTest.execute(new TestComputationStepContext());
 
@@ -326,41 +297,20 @@ public class PersistFileSourcesStepTest extends BaseStepTest {
 
   @Test
   public void not_update_sources_when_nothing_has_changed() {
-    // Existing sources
-    long past = 150000L;
-    String srcHash = "137f72c3708c6bd0de00a0e5a69c699b";
-    List<String> lineHashes = of("137f72c3708c6bd0de00a0e5a69c699b");
-    String dataHash = "29f25900140c94db38035128cb6de6a2";
-
-    dbClient.fileSourceDao().insert(dbTester.getSession(), new FileSourceDto()
-      .setProjectUuid(PROJECT_UUID)
-      .setFileUuid(FILE1_UUID)
-      .setSrcHash(srcHash)
-      .setLineHashes(lineHashes)
-      .setDataHash(dataHash)
-      .setLineHashesVersion(LineHashVersion.WITHOUT_SIGNIFICANT_CODE.getDbValue())
-      .setSourceData(DbFileSources.Data.newBuilder()
-        .addLines(DbFileSources.Line.newBuilder()
-          .setLine(1)
-          .setSource("line1")
-          .build())
-        .build())
-      .setCreatedAt(past)
-      .setUpdatedAt(past));
+    dbClient.fileSourceDao().insert(dbTester.getSession(), createDto());
     dbTester.getSession().commit();
 
-    // Sources from the report
-    initBasicReport(1);
+    Changeset changeset = Changeset.newChangesetBuilder().setDate(1L).setRevision("rev-1").build();
+    setComputedData(DbFileSources.Data.newBuilder().build(), Collections.singletonList("lineHash"), "sourceHash", changeset);
 
     underTest.execute(new TestComputationStepContext());
 
     assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1);
     FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID);
-    assertThat(fileSourceDto.getSrcHash()).isEqualTo(srcHash);
-    assertThat(fileSourceDto.getLineHashes()).isEqualTo(lineHashes);
-    assertThat(fileSourceDto.getDataHash()).isEqualTo(dataHash);
-    assertThat(fileSourceDto.getCreatedAt()).isEqualTo(past);
-    assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(past);
+    assertThat(fileSourceDto.getSrcHash()).isEqualTo("sourceHash");
+    assertThat(fileSourceDto.getLineHashes()).isEqualTo(Collections.singletonList("lineHash"));
+    assertThat(fileSourceDto.getCreatedAt()).isEqualTo(PAST);
+    assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(PAST);
   }
 
   @Test
@@ -372,7 +322,7 @@ public class PersistFileSourcesStepTest extends BaseStepTest {
       .setFileUuid(FILE1_UUID)
       .setDataType(Type.SOURCE)
       .setSrcHash("5b4bd9815cdb17b8ceae19eb1810c34c")
-      .setLineHashes(of("6438c669e0d0de98e6929c2cc0fac474", ""))
+      .setLineHashes(Collections.singletonList("6438c669e0d0de98e6929c2cc0fac474"))
       .setDataHash("6cad150e3d065976c230cddc5a09efaa")
       .setSourceData(DbFileSources.Data.newBuilder()
         .addLines(DbFileSources.Line.newBuilder()
@@ -385,13 +335,18 @@ public class PersistFileSourcesStepTest extends BaseStepTest {
       .setRevision("rev-0"));
     dbTester.getSession().commit();
 
-    initBasicReport(1);
+    DbFileSources.Data newSourceData = DbFileSources.Data.newBuilder()
+      .addLines(DbFileSources.Line.newBuilder()
+        .setLine(1)
+        .setSource("old line")
+        .setScmDate(123456789L)
+        .setScmRevision("rev-1")
+        .setScmAuthor("john")
+        .build())
+      .build();
 
-    scmInfoRepository.setScmInfo(FILE1_REF, Changeset.newChangesetBuilder()
-      .setAuthor("john")
-      .setDate(123456789L)
-      .setRevision("rev-1")
-      .build());
+    Changeset changeset = Changeset.newChangesetBuilder().setDate(1L).setRevision("rev-1").build();
+    setComputedData(newSourceData, Collections.singletonList("6438c669e0d0de98e6929c2cc0fac474"), "5b4bd9815cdb17b8ceae19eb1810c34c", changeset);
 
     underTest.execute(new TestComputationStepContext());
 
@@ -404,106 +359,79 @@ public class PersistFileSourcesStepTest extends BaseStepTest {
 
   @Test
   public void update_sources_when_src_hash_is_missing() {
-    // Existing sources
-    long past = 150000L;
-    dbClient.fileSourceDao().insert(dbTester.getSession(), new FileSourceDto()
-      .setProjectUuid(PROJECT_UUID)
-      .setFileUuid(FILE1_UUID)
-      .setDataType(Type.SOURCE)
-      // Source hash is missing, update will be made
-      .setLineHashes(of("137f72c3708c6bd0de00a0e5a69c699b"))
-      .setDataHash("29f25900140c94db38035128cb6de6a2")
-      .setSourceData(DbFileSources.Data.newBuilder()
-        .addLines(DbFileSources.Line.newBuilder()
-          .setLine(1)
-          .setSource("line")
-          .build())
-        .build())
-      .setCreatedAt(past)
-      .setUpdatedAt(past));
+    dbClient.fileSourceDao().insert(dbTester.getSession(), createDto(dto -> dto.setSrcHash(null)));
     dbTester.getSession().commit();
 
-    initBasicReport(1);
+    DbFileSources.Data sourceData = DbFileSources.Data.newBuilder().build();
+    setComputedData(sourceData, Collections.singletonList("lineHash"), "newSourceHash", null);
 
     underTest.execute(new TestComputationStepContext());
 
     assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1);
     FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID);
-    assertThat(fileSourceDto.getCreatedAt()).isEqualTo(past);
+    assertThat(fileSourceDto.getCreatedAt()).isEqualTo(PAST);
     assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(NOW);
-    assertThat(fileSourceDto.getSrcHash()).isEqualTo("137f72c3708c6bd0de00a0e5a69c699b");
+    assertThat(fileSourceDto.getSrcHash()).isEqualTo("newSourceHash");
   }
 
   @Test
   public void update_sources_when_revision_is_missing() {
-    // Existing sources
-    long past = 150000L;
-    dbClient.fileSourceDao().insert(dbTester.getSession(), new FileSourceDto()
-      .setProjectUuid(PROJECT_UUID)
-      .setFileUuid(FILE1_UUID)
-      .setDataType(Type.SOURCE)
-      .setSrcHash("137f72c3708c6bd0de00a0e5a69c699b")
-      .setLineHashes(of("137f72c3708c6bd0de00a0e5a69c699b"))
-      .setDataHash("8e84c0d961cfe364e43833c4cc4ddef5")
-      // Revision is missing, update will be made
-      .setSourceData(DbFileSources.Data.newBuilder()
-        .addLines(DbFileSources.Line.newBuilder()
-          .setLine(1)
-          .setSource("line")
-          .build())
+    DbFileSources.Data sourceData = DbFileSources.Data.newBuilder()
+      .addLines(DbFileSources.Line.newBuilder()
+        .setLine(1)
+        .setSource("line")
         .build())
-      .setCreatedAt(past)
-      .setUpdatedAt(past));
-    dbTester.getSession().commit();
+      .build();
 
-    scmInfoRepository.setScmInfo(FILE1_REF, Changeset.newChangesetBuilder()
-      .setAuthor("john")
-      .setDate(123456789L)
-      .setRevision("rev-1")
-      .build());
+    dbClient.fileSourceDao().insert(dbTester.getSession(), createDto(dto -> dto.setRevision(null)));
+    dbTester.getSession().commit();
 
-    initBasicReport(1);
+    Changeset changeset = Changeset.newChangesetBuilder().setDate(1L).setRevision("revision").build();
+    setComputedData(sourceData, Collections.singletonList("137f72c3708c6bd0de00a0e5a69c699b"), "29f25900140c94db38035128cb6de6a2", changeset);
 
     underTest.execute(new TestComputationStepContext());
 
     assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1);
     FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID);
-    assertThat(fileSourceDto.getCreatedAt()).isEqualTo(past);
+    assertThat(fileSourceDto.getCreatedAt()).isEqualTo(PAST);
     assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(NOW);
-    assertThat(fileSourceDto.getRevision()).isEqualTo("rev-1");
+    assertThat(fileSourceDto.getRevision()).isEqualTo("revision");
   }
 
-  @Test
-  public void clear_revision_when_no_ChangeSet() {
-    // Existing sources
-    long past = 150000L;
-    dbClient.fileSourceDao().insert(dbTester.getSession(), new FileSourceDto()
+  private FileSourceDto createDto() {
+    return createDto(dto -> {
+    });
+  }
+
+  private FileSourceDto createDto(Consumer<FileSourceDto> modifier) {
+    DbFileSources.Data sourceData = DbFileSources.Data.newBuilder().build();
+    byte[] data = FileSourceDto.encodeSourceData(sourceData);
+    String dataHash = DigestUtils.md5Hex(data);
+
+    FileSourceDto dto = new FileSourceDto()
       .setProjectUuid(PROJECT_UUID)
       .setFileUuid(FILE1_UUID)
       .setDataType(Type.SOURCE)
-      .setSrcHash("137f72c3708c6bd0de00a0e5a69c699b")
-      .setLineHashes(of("137f72c3708c6bd0de00a0e5a69c699b"))
-      .setDataHash("8e84c0d961cfe364e43833c4cc4ddef5")
-      // Revision is missing, update will be made
-      .setSourceData(DbFileSources.Data.newBuilder()
-        .addLines(DbFileSources.Line.newBuilder()
-          .setLine(1)
-          .setSource("line")
-          .build())
-        .build())
-      .setCreatedAt(past)
-      .setUpdatedAt(past));
-    dbTester.getSession().commit();
+      .setSrcHash("sourceHash")
+      .setLineHashes(Collections.singletonList("lineHash"))
+      .setDataHash(dataHash)
+      .setRevision("rev-1")
+      .setSourceData(sourceData)
+      .setCreatedAt(PAST)
+      .setUpdatedAt(PAST);
 
-    initBasicReport(1);
+    modifier.accept(dto);
+    return dto;
+  }
 
-    underTest.execute(new TestComputationStepContext());
+  private void setComputedData(DbFileSources.Data data, List<String> lineHashes, String sourceHash, Changeset latestChangeWithRevision) {
+    FileSourceDataComputer.Data computedData = new FileSourceDataComputer.Data(data, lineHashes, sourceHash, latestChangeWithRevision);
+    when(fileSourceDataComputer.compute(fileComponent())).thenReturn(computedData);
+  }
 
-    assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1);
-    FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID);
-    assertThat(fileSourceDto.getCreatedAt()).isEqualTo(past);
-    assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(NOW);
-    assertThat(fileSourceDto.getRevision()).isNull();
+  private void setComputedData(DbFileSources.Data data) {
+    FileSourceDataComputer.Data computedData = new FileSourceDataComputer.Data(data, Collections.emptyList(), "", null);
+    when(fileSourceDataComputer.compute(fileComponent())).thenReturn(computedData);
   }
 
   private void initBasicReport(int numberOfLines) {
@@ -513,25 +441,5 @@ public class PersistFileSourcesStepTest extends BaseStepTest {
           .setFileAttributes(new FileAttributes(false, null, numberOfLines)).build())
         .build())
       .build());
-
-    reportReader.putComponent(ScannerReport.Component.newBuilder()
-      .setRef(1)
-      .setType(ComponentType.PROJECT)
-      .addChildRef(2)
-      .build());
-    reportReader.putComponent(ScannerReport.Component.newBuilder()
-      .setRef(2)
-      .setType(ComponentType.MODULE)
-      .addChildRef(FILE1_REF)
-      .build());
-    reportReader.putComponent(ScannerReport.Component.newBuilder()
-      .setRef(FILE1_REF)
-      .setType(ComponentType.FILE)
-      .setLines(numberOfLines)
-      .build());
-
-    for (int i = 1; i <= numberOfLines; i++) {
-      fileSourceRepository.addLine(FILE1_REF, "line" + i);
-    }
   }
 }