]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6843 Read source lines db when nothing in report 522/head
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Mon, 21 Sep 2015 14:09:18 +0000 (16:09 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Tue, 22 Sep 2015 15:05:11 +0000 (17:05 +0200)
server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/PersistFileSourcesStepTest.java
server/sonar-server/src/main/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulator.java
server/sonar-server/src/main/java/org/sonar/server/computation/issue/TrackerRawInputFactory.java
server/sonar-server/src/main/java/org/sonar/server/computation/source/SourceLinesRepository.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/source/SourceLinesRepositoryImpl.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistFileSourcesStep.java
server/sonar-server/src/test/java/org/sonar/server/computation/issue/IntegrateIssuesVisitorTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/issue/TrackerRawInputFactoryTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/source/SourceLinesRepositoryRule.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/source/SourceLinesRepositoryTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistFileSourcesStepTest.java

index eadde392bb5808cd05ee8603845fb51745c461dd..c8d5df5374664914b5a44ae4ed2a81d36190885b 100644 (file)
@@ -31,19 +31,19 @@ import org.junit.rules.TemporaryFolder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.utils.System2;
-import org.sonar.core.util.Uuids;
 import org.sonar.batch.protocol.Constants;
 import org.sonar.batch.protocol.output.BatchReport;
 import org.sonar.batch.protocol.output.BatchReportWriter;
+import org.sonar.core.util.Uuids;
+import org.sonar.db.DbClient;
 import org.sonar.db.DbTester;
-import org.sonar.db.source.FileSourceDao;
 import org.sonar.server.computation.batch.BatchReportDirectoryHolderImpl;
 import org.sonar.server.computation.batch.BatchReportReaderImpl;
 import org.sonar.server.computation.batch.TreeRootHolderRule;
 import org.sonar.server.computation.component.Component;
 import org.sonar.server.computation.component.ReportComponent;
+import org.sonar.server.computation.source.SourceLinesRepositoryImpl;
 import org.sonar.server.computation.step.PersistFileSourcesStep;
-import org.sonar.server.db.DbClient;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
@@ -75,13 +75,15 @@ public class PersistFileSourcesStepTest {
 
   private void persistFileSources(File reportDir) {
     LOGGER.info("Persist file sources");
-    DbClient dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new FileSourceDao(dbTester.myBatis()));
+    DbClient dbClient = dbTester.getDbClient();
 
     long start = System.currentTimeMillis();
 
     BatchReportDirectoryHolderImpl batchReportDirectoryHolder = new BatchReportDirectoryHolderImpl();
     batchReportDirectoryHolder.setDirectory(reportDir);
-    PersistFileSourcesStep step = new PersistFileSourcesStep(dbClient, System2.INSTANCE, treeRootHolder, new BatchReportReaderImpl(batchReportDirectoryHolder));
+    org.sonar.server.computation.batch.BatchReportReader batchReportReader = new BatchReportReaderImpl(batchReportDirectoryHolder);
+    PersistFileSourcesStep step = new PersistFileSourcesStep(dbClient, System2.INSTANCE, treeRootHolder, batchReportReader,
+      new SourceLinesRepositoryImpl(dbClient, batchReportReader));
     step.execute();
 
     long end = System.currentTimeMillis();
index 453aef13d87a5fc34632d3a3053276ffefcdaf11..8ed0f9a2f1cd2470d9b1ca45e8fdadb96a933a76 100644 (file)
@@ -24,8 +24,8 @@ import java.util.List;
 import org.sonar.core.issue.tracking.Tracker;
 import org.sonar.core.platform.ContainerPopulator;
 import org.sonar.server.computation.CeTask;
-import org.sonar.server.computation.ComputationTempFolderProvider;
 import org.sonar.server.computation.ComputationStepExecutor;
+import org.sonar.server.computation.ComputationTempFolderProvider;
 import org.sonar.server.computation.analysis.ReportAnalysisMetadataHolder;
 import org.sonar.server.computation.batch.BatchReportDirectoryHolderImpl;
 import org.sonar.server.computation.batch.BatchReportReaderImpl;
@@ -77,6 +77,7 @@ import org.sonar.server.computation.qualitygate.QualityGateHolderImpl;
 import org.sonar.server.computation.qualitygate.QualityGateServiceImpl;
 import org.sonar.server.computation.qualityprofile.ActiveRulesHolderImpl;
 import org.sonar.server.computation.source.LastCommitVisitor;
+import org.sonar.server.computation.source.SourceLinesRepositoryImpl;
 import org.sonar.server.computation.sqale.SqaleMeasuresVisitor;
 import org.sonar.server.computation.sqale.SqaleRatingSettings;
 import org.sonar.server.computation.step.ComputationSteps;
@@ -108,9 +109,9 @@ public final class ReportComputeEngineContainerPopulator implements ContainerPop
       ComputationStepExecutor.class,
       new ComputationTempFolderProvider(),
 
-    MetricModule.class,
+      MetricModule.class,
 
-    // holders
+      // holders
       ReportAnalysisMetadataHolder.class,
       BatchReportDirectoryHolderImpl.class,
       ReportTreeRootHolderImpl.class,
@@ -121,9 +122,9 @@ public final class ReportComputeEngineContainerPopulator implements ContainerPop
       ActiveRulesHolderImpl.class,
       MeasureComputersHolderImpl.class,
 
-    BatchReportReaderImpl.class,
+      BatchReportReaderImpl.class,
 
-    // repositories
+      // repositories
       LanguageRepositoryImpl.class,
       MeasureRepositoryImpl.class,
       EventRepositoryImpl.class,
@@ -131,8 +132,9 @@ public final class ReportComputeEngineContainerPopulator implements ContainerPop
       DbIdsRepositoryImpl.class,
       QualityGateServiceImpl.class,
       EvaluationResultTextConverterImpl.class,
+      SourceLinesRepositoryImpl.class,
 
-    // issues
+      // issues
       RuleCacheLoader.class,
       RuleRepositoryImpl.class,
       ScmAccountToUserLoader.class,
@@ -144,7 +146,7 @@ public final class ReportComputeEngineContainerPopulator implements ContainerPop
       ComponentsWithUnprocessedIssues.class,
       ComponentIssuesRepositoryImpl.class,
 
-    // common rules
+      // common rules
       CommonRuleEngineImpl.class,
       BranchCoverageRule.class,
       LineCoverageRule.class,
@@ -153,7 +155,7 @@ public final class ReportComputeEngineContainerPopulator implements ContainerPop
       TestErrorRule.class,
       SkippedTestRule.class,
 
-    // order is important: DebtAggregator then NewDebtAggregator (new debt requires debt)
+      // order is important: DebtAggregator then NewDebtAggregator (new debt requires debt)
       DebtCalculator.class,
       DebtAggregator.class,
       NewDebtCalculator.class,
@@ -162,7 +164,7 @@ public final class ReportComputeEngineContainerPopulator implements ContainerPop
       RuleTagsCopier.class,
       IssueCounter.class,
 
-    // visitors : order is important, measure computers must be executed at the end in order to access to every measures / issues
+      // visitors : order is important, measure computers must be executed at the end in order to access to every measures / issues
       LoadComponentUuidsHavingOpenIssuesVisitor.class,
       IntegrateIssuesVisitor.class,
       CloseIssuesOnRemovedComponentsVisitor.class,
@@ -170,14 +172,14 @@ public final class ReportComputeEngineContainerPopulator implements ContainerPop
       LastCommitVisitor.class,
       MeasureComputersVisitor.class,
 
-    UpdateConflictResolver.class,
+      UpdateConflictResolver.class,
       TrackerBaseInputFactory.class,
       TrackerRawInputFactory.class,
       Tracker.class,
       TrackerExecution.class,
       BaseIssuesLoader.class,
 
-    // views
+      // views
       ViewIndex.class);
   }
 
index 5cce8011da49b4ff86212b22e84eae6ff99c2d4a..a72039023449aa94384705f0a6addfb98a63626a 100644 (file)
@@ -19,7 +19,6 @@
  */
 package org.sonar.server.computation.issue;
 
-import com.google.common.collect.Lists;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -39,18 +38,23 @@ import org.sonar.server.computation.batch.BatchReportReader;
 import org.sonar.server.computation.component.Component;
 import org.sonar.server.computation.component.ReportTreeRootHolder;
 import org.sonar.server.computation.issue.commonrule.CommonRuleEngine;
+import org.sonar.server.computation.source.SourceLinesRepository;
 import org.sonar.server.rule.CommonRuleKeys;
 
+import static com.google.common.collect.Lists.newArrayList;
+
 public class TrackerRawInputFactory {
 
   private final ReportTreeRootHolder treeRootHolder;
   private final BatchReportReader reportReader;
+  private final SourceLinesRepository sourceLinesRepository;
   private final CommonRuleEngine commonRuleEngine;
 
   public TrackerRawInputFactory(ReportTreeRootHolder treeRootHolder, BatchReportReader reportReader,
-    CommonRuleEngine commonRuleEngine) {
+    SourceLinesRepository sourceLinesRepository, CommonRuleEngine commonRuleEngine) {
     this.treeRootHolder = treeRootHolder;
     this.reportReader = reportReader;
+    this.sourceLinesRepository = sourceLinesRepository;
     this.commonRuleEngine = commonRuleEngine;
   }
 
@@ -69,7 +73,7 @@ public class TrackerRawInputFactory {
     protected LineHashSequence loadLineHashSequence() {
       Iterable<String> lines;
       if (component.getType() == Component.Type.FILE) {
-        lines = Lists.newArrayList(reportReader.readFileSource(component.getReportAttributes().getRef()));
+        lines = newArrayList(sourceLinesRepository.readLines(component));
       } else {
         lines = Collections.emptyList();
       }
@@ -133,7 +137,7 @@ public class TrackerRawInputFactory {
         dbLocationsBuilder.setTextRange(convertTextRange(reportIssue.getTextRange()));
       }
       for (BatchReport.Flow flow : reportIssue.getFlowList()) {
-        if (flow.getLocationCount()>0) {
+        if (flow.getLocationCount() > 0) {
           DbIssues.Flow.Builder dbFlowBuilder = DbIssues.Flow.newBuilder();
           for (BatchReport.IssueLocation location : flow.getLocationList()) {
             dbFlowBuilder.addLocation(convertLocation(location));
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/source/SourceLinesRepository.java b/server/sonar-server/src/main/java/org/sonar/server/computation/source/SourceLinesRepository.java
new file mode 100644 (file)
index 0000000..fc589bd
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.computation.source;
+
+import org.sonar.core.util.CloseableIterator;
+import org.sonar.server.computation.component.Component;
+
+public interface SourceLinesRepository {
+
+  /**
+   * Return lines from a given component. If file sources is not in the report then we read it from the database.
+   *
+   * @throws NullPointerException if argument is {@code null}
+   * @throws IllegalArgumentException if component is not a {@link org.sonar.server.computation.component.Component.Type#FILE}
+   * @throws IllegalStateException if the file has no source code in the report and in the database
+   */
+  CloseableIterator<String> readLines(Component component);
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/source/SourceLinesRepositoryImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/source/SourceLinesRepositoryImpl.java
new file mode 100644 (file)
index 0000000..8238f0c
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.computation.source;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import javax.annotation.Nonnull;
+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.server.computation.batch.BatchReportReader;
+import org.sonar.server.computation.component.Component;
+
+import static com.google.common.collect.FluentIterable.from;
+import static org.sonar.server.computation.component.Component.Type.FILE;
+
+public class SourceLinesRepositoryImpl implements SourceLinesRepository {
+
+  private final DbClient dbClient;
+  private final BatchReportReader reportReader;
+
+  public SourceLinesRepositoryImpl(DbClient dbClient, BatchReportReader reportReader) {
+    this.dbClient = dbClient;
+    this.reportReader = reportReader;
+  }
+
+  @Override
+  public CloseableIterator<String> readLines(Component component) {
+    Preconditions.checkNotNull(component, "Component should not be bull");
+    if (!component.getType().equals(FILE)) {
+      throw new IllegalArgumentException(String.format("Component '%s' is not a file", component));
+    }
+
+    Optional<CloseableIterator<String>> linesIteratorOptional = reportReader.readFileSource(component.getReportAttributes().getRef());
+    if (linesIteratorOptional.isPresent()) {
+      return linesIteratorOptional.get();
+    }
+    DbSession session = dbClient.openSession(false);
+    try {
+      return readLinesFromDb(session, component);
+    } finally {
+      dbClient.closeSession(session);
+    }
+  }
+
+  private CloseableIterator<String> readLinesFromDb(DbSession session, Component component) {
+    FileSourceDto dto = dbClient.fileSourceDao().selectSourceByFileUuid(session, component.getUuid());
+    if (dto == null) {
+      throw new IllegalStateException(String.format("The file '%s' has no source", component));
+    }
+    DbFileSources.Data data = dto.getSourceData();
+    return CloseableIterator.from(from(data.getLinesList()).transform(LineToRaw.INSTANCE).iterator());
+  }
+
+  private enum LineToRaw implements Function<DbFileSources.Line, String> {
+    INSTANCE;
+
+    @Override
+    public String apply(@Nonnull DbFileSources.Line line) {
+      return line.getSource();
+    }
+  }
+
+}
index 305a19690681d47c6aeaa9dafe9d7c91eeb4dd54..56757271ef8a30a32b72a206bd623a645de1a086 100644 (file)
@@ -49,6 +49,7 @@ import org.sonar.server.computation.source.DuplicationLineReader;
 import org.sonar.server.computation.source.HighlightingLineReader;
 import org.sonar.server.computation.source.LineReader;
 import org.sonar.server.computation.source.ScmLineReader;
+import org.sonar.server.computation.source.SourceLinesRepository;
 import org.sonar.server.computation.source.SymbolsLineReader;
 
 import static org.sonar.server.computation.component.ComponentVisitor.Order.PRE_ORDER;
@@ -59,12 +60,14 @@ public class PersistFileSourcesStep implements ComputationStep {
   private final System2 system2;
   private final TreeRootHolder treeRootHolder;
   private final BatchReportReader reportReader;
+  private final SourceLinesRepository sourceLinesRepository;
 
-  public PersistFileSourcesStep(DbClient dbClient, System2 system2, TreeRootHolder treeRootHolder, BatchReportReader reportReader) {
+  public PersistFileSourcesStep(DbClient dbClient, System2 system2, TreeRootHolder treeRootHolder, BatchReportReader reportReader, SourceLinesRepository sourceLinesRepository) {
     this.dbClient = dbClient;
     this.system2 = system2;
     this.treeRootHolder = treeRootHolder;
     this.reportReader = reportReader;
+    this.sourceLinesRepository = sourceLinesRepository;
   }
 
   @Override
@@ -108,7 +111,7 @@ public class PersistFileSourcesStep implements ComputationStep {
     public void visitFile(Component file) {
       int fileRef = file.getReportAttributes().getRef();
       BatchReport.Component component = reportReader.readComponent(fileRef);
-      CloseableIterator<String> linesIterator = reportReader.readFileSource(fileRef);
+      CloseableIterator<String> linesIterator = sourceLinesRepository.readLines(file);
       LineReaders lineReaders = new LineReaders(reportReader, fileRef);
       try {
         ComputeFileSourceData computeFileSourceData = new ComputeFileSourceData(linesIterator, lineReaders.readers(), component.getLines());
index d0c04e06a3b6f1419505e9202da129758d5580f3..df20c65ceb99514eb7dae0facb7ec62edd8e2ce8 100644 (file)
@@ -47,6 +47,7 @@ import org.sonar.server.computation.component.Component;
 import org.sonar.server.computation.component.TypeAwareVisitor;
 import org.sonar.server.computation.issue.commonrule.CommonRuleEngineImpl;
 import org.sonar.server.computation.qualityprofile.ActiveRulesHolderRule;
+import org.sonar.server.computation.source.SourceLinesRepositoryRule;
 import org.sonar.server.issue.IssueTesting;
 
 import static com.google.common.collect.Lists.newArrayList;
@@ -98,11 +99,14 @@ public class IntegrateIssuesVisitorTest {
   @Rule
   public ComponentIssuesRepositoryRule componentIssuesRepository = new ComponentIssuesRepositoryRule(treeRootHolder);
 
+  @Rule
+  public SourceLinesRepositoryRule fileSourceRepository = new SourceLinesRepositoryRule();
+
   ArgumentCaptor<DefaultIssue> defaultIssueCaptor = ArgumentCaptor.forClass(DefaultIssue.class);
 
   BaseIssuesLoader baseIssuesLoader = new BaseIssuesLoader(treeRootHolder, dbTester.getDbClient(), ruleRepositoryRule, activeRulesHolderRule);
   TrackerExecution tracker = new TrackerExecution(new TrackerBaseInputFactory(baseIssuesLoader, dbTester.getDbClient()), new TrackerRawInputFactory(treeRootHolder, reportReader,
-    new CommonRuleEngineImpl()), new Tracker<DefaultIssue, DefaultIssue>());
+    fileSourceRepository, new CommonRuleEngineImpl()), new Tracker<DefaultIssue, DefaultIssue>());
   IssueCache issueCache;
 
   IssueLifecycle issueLifecycle = mock(IssueLifecycle.class);
index 47a989b34252d9c0346ccb51219dfa38b085e704..70718ddd5a7e2c8293ccec8272ead767a86a059e 100644 (file)
@@ -35,6 +35,7 @@ import org.sonar.server.computation.batch.TreeRootHolderRule;
 import org.sonar.server.computation.component.Component;
 import org.sonar.server.computation.component.ReportComponent;
 import org.sonar.server.computation.issue.commonrule.CommonRuleEngine;
+import org.sonar.server.computation.source.SourceLinesRepositoryRule;
 import org.sonar.server.rule.CommonRuleKeys;
 
 import static java.util.Arrays.asList;
@@ -53,13 +54,16 @@ public class TrackerRawInputFactoryTest {
   @Rule
   public BatchReportReaderRule reportReader = new BatchReportReaderRule();
 
+  @Rule
+  public SourceLinesRepositoryRule fileSourceRepository = new SourceLinesRepositoryRule();
+
   CommonRuleEngine commonRuleEngine = mock(CommonRuleEngine.class);
 
-  TrackerRawInputFactory underTest = new TrackerRawInputFactory(treeRootHolder, reportReader, commonRuleEngine);
+  TrackerRawInputFactory underTest = new TrackerRawInputFactory(treeRootHolder, reportReader, fileSourceRepository, commonRuleEngine);
 
   @Test
   public void load_source_hash_sequences() throws Exception {
-    reportReader.putFileSourceLines(FILE.getReportAttributes().getRef(), "line 1;", "line 2;");
+    fileSourceRepository.addLine("line 1;").addLine("line 2;");
     Input<DefaultIssue> input = underTest.create(FILE);
 
     assertThat(input.getLineHashSequence()).isNotNull();
@@ -80,7 +84,7 @@ public class TrackerRawInputFactoryTest {
 
   @Test
   public void load_issues() throws Exception {
-    reportReader.putFileSourceLines(FILE.getReportAttributes().getRef(), "line 1;", "line 2;");
+    fileSourceRepository.addLine("line 1;").addLine("line 2;");
     BatchReport.Issue reportIssue = BatchReport.Issue.newBuilder()
       .setLine(2)
       .setMsg("the message")
@@ -111,7 +115,7 @@ public class TrackerRawInputFactoryTest {
 
   @Test
   public void ignore_report_issues_on_common_rules() throws Exception {
-    reportReader.putFileSourceLines(FILE.getReportAttributes().getRef(), "line 1;", "line 2;");
+    fileSourceRepository.addLine("line 1;").addLine("line 2;");
     BatchReport.Issue reportIssue = BatchReport.Issue.newBuilder()
       .setMsg("the message")
       .setRuleRepository(CommonRuleKeys.commonRepositoryForLang("java"))
@@ -127,7 +131,7 @@ public class TrackerRawInputFactoryTest {
 
   @Test
   public void load_issues_of_compute_engine_common_rules() throws Exception {
-    reportReader.putFileSourceLines(FILE.getReportAttributes().getRef(), "line 1;", "line 2;");
+    fileSourceRepository.addLine("line 1;").addLine("line 2;");
     DefaultIssue ceIssue = new DefaultIssue()
       .setRuleKey(RuleKey.of(CommonRuleKeys.commonRepositoryForLang("java"), "InsufficientCoverage"))
       .setMessage("not enough coverage")
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/source/SourceLinesRepositoryRule.java b/server/sonar-server/src/test/java/org/sonar/server/computation/source/SourceLinesRepositoryRule.java
new file mode 100644 (file)
index 0000000..cb2c7b3
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.computation.source;
+
+import com.google.common.base.Preconditions;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.rules.ExternalResource;
+import org.sonar.core.util.CloseableIterator;
+import org.sonar.server.computation.component.Component;
+
+import static org.sonar.server.computation.component.Component.Type.FILE;
+
+public class SourceLinesRepositoryRule extends ExternalResource implements SourceLinesRepository {
+
+  private List<String> lines = new ArrayList<>();
+
+  @Override
+  protected void after() {
+    lines.clear();
+  }
+
+  @Override
+  public CloseableIterator<String> readLines(Component component) {
+    Preconditions.checkNotNull(component, "Component should not be bull");
+    if (!component.getType().equals(FILE)) {
+      throw new IllegalArgumentException(String.format("Component '%s' is not a file", component));
+    }
+    return CloseableIterator.from(lines.iterator());
+  }
+
+  public SourceLinesRepositoryRule addLine(String line){
+    lines.add(line);
+    return this;
+  }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/source/SourceLinesRepositoryTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/source/SourceLinesRepositoryTest.java
new file mode 100644 (file)
index 0000000..7da4260
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.computation.source;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.protobuf.DbFileSources;
+import org.sonar.db.source.FileSourceDto;
+import org.sonar.server.computation.batch.BatchReportReaderRule;
+import org.sonar.server.computation.component.Component;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.server.computation.component.ReportComponent.builder;
+
+public class SourceLinesRepositoryTest {
+
+  static final String FILE_UUID = "FILE_UUID";
+  static final String FILE_KEY = "FILE_KEY";
+  static final int FILE_REF = 2;
+
+  static final Component FILE = builder(Component.Type.FILE, FILE_REF)
+    .setKey(FILE_KEY)
+    .setUuid(FILE_UUID)
+    .build();
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  @Rule
+  public DbTester db = DbTester.create(System2.INSTANCE);
+
+  @Rule
+  public BatchReportReaderRule reportReader = new BatchReportReaderRule();
+
+  DbSession session = db.getSession();
+
+  SourceLinesRepositoryImpl underTest = new SourceLinesRepositoryImpl(db.getDbClient(), reportReader);
+
+  @Test
+  public void read_lines_from_report() throws Exception {
+    reportReader.putFileSourceLines(FILE_REF, "line1", "line2");
+
+    assertThat(underTest.readLines(FILE)).containsOnly("line1", "line2");
+  }
+
+  @Test
+  public void not_fail_to_read_lines_on_empty_file_from_report() throws Exception {
+    // File exist but there's no line
+    reportReader.putFileSourceLines(FILE_REF);
+
+    // Should not try to read source file from the db
+    assertThat(underTest.readLines(FILE)).isEmpty();
+  }
+
+  @Test
+  public void read_lines_from_database() throws Exception {
+    insertFileSourceInDb("line1", "line2");
+
+    assertThat(underTest.readLines(FILE)).containsOnly("line1", "line2");
+  }
+
+  @Test
+  public void read_from_report_even_if_source_exist_in_db() throws Exception {
+    reportReader.putFileSourceLines(FILE_REF, "report line1", "report line2");
+    insertFileSourceInDb("db line1", "db line2");
+
+    assertThat(underTest.readLines(FILE)).containsOnly("report line1", "report line2");
+  }
+
+  @Test
+  public void fail_with_NPE_to_read_lines_on_null_component() throws Exception {
+    thrown.expect(NullPointerException.class);
+    thrown.expectMessage("Component should not be bull");
+
+    underTest.readLines(null);
+  }
+
+  @Test
+  public void fail_with_IAE_to_read_lines_on_not_file_component() throws Exception {
+    thrown.expect(IllegalArgumentException.class);
+    thrown.expectMessage("Component 'ReportComponent{ref=123, key='NotFile', type=PROJECT}' is not a file");
+
+    underTest.readLines(builder(Component.Type.PROJECT, 123).setKey("NotFile").build());
+  }
+
+  private void insertFileSourceInDb(String... lines) {
+    DbFileSources.Data.Builder dataBuilder = DbFileSources.Data.newBuilder();
+    for (int i = 0; i < lines.length; i++) {
+      dataBuilder.addLinesBuilder().setLine(i + 1).setSource(lines[i]).build();
+    }
+    db.getDbClient().fileSourceDao().insert(session,
+      new FileSourceDto()
+        .setFileUuid(FILE_UUID).setProjectUuid("PROJECT_UUID")
+        .setSourceData(dataBuilder.build()));
+    session.commit();
+  }
+}
+
index ad860e1a4ad223683abb19ba40eab9baf5af216a..1d2c2e45e97de8344957c7730ad1010d591bea75 100644 (file)
@@ -21,7 +21,6 @@
 package org.sonar.server.computation.step;
 
 import com.google.common.base.Optional;
-import java.util.List;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -41,6 +40,7 @@ import org.sonar.server.computation.batch.TreeRootHolderRule;
 import org.sonar.server.computation.component.Component;
 import org.sonar.server.computation.component.ReportComponent;
 import org.sonar.server.computation.language.LanguageRepository;
+import org.sonar.server.computation.source.SourceLinesRepositoryRule;
 import org.sonar.test.DbTests;
 
 import static com.google.common.collect.Lists.newArrayList;
@@ -69,6 +69,9 @@ public class PersistFileSourcesStepTest extends BaseStepTest {
   @Rule
   public BatchReportReaderRule reportReader = new BatchReportReaderRule();
 
+  @Rule
+  public SourceLinesRepositoryRule fileSourceRepository = new SourceLinesRepositoryRule();
+
   DbClient dbClient = dbTester.getDbClient();
   DbSession session = dbTester.getSession();
 
@@ -80,7 +83,7 @@ public class PersistFileSourcesStepTest extends BaseStepTest {
   public void setup() {
     dbTester.truncateTables();
     when(system2.now()).thenReturn(now);
-    underTest = new PersistFileSourcesStep(dbClient, system2, treeRootHolder, reportReader);
+    underTest = new PersistFileSourcesStep(dbClient, system2, treeRootHolder, reportReader, fileSourceRepository);
   }
 
   @Override
@@ -126,10 +129,10 @@ public class PersistFileSourcesStepTest extends BaseStepTest {
     reportReader.putComponent(BatchReport.Component.newBuilder()
       .setRef(FILE_REF)
       .setType(Constants.ComponentType.FILE)
-      // Lines is set to 3 but only 2 lines are read from the file -> the last lines should be added
+        // Lines is set to 3 but only 2 lines are read from the file -> the last lines should be added
       .setLines(3)
       .build());
-    reportReader.putFileSourceLines(FILE_REF, "line1", "line2");
+    fileSourceRepository.addLine("line1").addLine("line2");
 
     underTest.execute();
 
@@ -438,11 +441,9 @@ public class PersistFileSourcesStepTest extends BaseStepTest {
       .setLines(numberOfLines)
       .build());
 
-    List<String> lines = newArrayList();
     for (int i = 1; i <= numberOfLines; i++) {
-      lines.add("line" + i);
+      fileSourceRepository.addLine("line" + i);
     }
-    reportReader.putFileSourceLines(FILE_REF, lines);
   }
 
   private static class EmptyLanguageRepository implements LanguageRepository {