From 129962bb39832f0312884c9d2bb4c61dd77111e3 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Mon, 21 Sep 2015 16:09:18 +0200 Subject: [PATCH] SONAR-6843 Read source lines db when nothing in report --- .../benchmark/PersistFileSourcesStepTest.java | 12 +- ...ReportComputeEngineContainerPopulator.java | 24 ++-- .../issue/TrackerRawInputFactory.java | 12 +- .../source/SourceLinesRepository.java | 36 ++++++ .../source/SourceLinesRepositoryImpl.java | 85 +++++++++++++ .../step/PersistFileSourcesStep.java | 7 +- .../issue/IntegrateIssuesVisitorTest.java | 6 +- .../issue/TrackerRawInputFactoryTest.java | 14 +- .../source/SourceLinesRepositoryRule.java | 55 ++++++++ .../source/SourceLinesRepositoryTest.java | 120 ++++++++++++++++++ .../step/PersistFileSourcesStepTest.java | 15 ++- 11 files changed, 351 insertions(+), 35 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/source/SourceLinesRepository.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/source/SourceLinesRepositoryImpl.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/source/SourceLinesRepositoryRule.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/source/SourceLinesRepositoryTest.java diff --git a/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/PersistFileSourcesStepTest.java b/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/PersistFileSourcesStepTest.java index eadde392bb5..c8d5df53746 100644 --- a/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/PersistFileSourcesStepTest.java +++ b/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/PersistFileSourcesStepTest.java @@ -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(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulator.java b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulator.java index 453aef13d87..8ed0f9a2f1c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulator.java @@ -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); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/TrackerRawInputFactory.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/TrackerRawInputFactory.java index 5cce8011da4..a7203902344 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/TrackerRawInputFactory.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/TrackerRawInputFactory.java @@ -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 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 index 00000000000..fc589bd0cf3 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/source/SourceLinesRepository.java @@ -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 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 index 00000000000..8238f0c1cd4 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/source/SourceLinesRepositoryImpl.java @@ -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 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> 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 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 { + INSTANCE; + + @Override + public String apply(@Nonnull DbFileSources.Line line) { + return line.getSource(); + } + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistFileSourcesStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistFileSourcesStep.java index 305a1969068..56757271ef8 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistFileSourcesStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistFileSourcesStep.java @@ -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 linesIterator = reportReader.readFileSource(fileRef); + CloseableIterator linesIterator = sourceLinesRepository.readLines(file); LineReaders lineReaders = new LineReaders(reportReader, fileRef); try { ComputeFileSourceData computeFileSourceData = new ComputeFileSourceData(linesIterator, lineReaders.readers(), component.getLines()); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IntegrateIssuesVisitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IntegrateIssuesVisitorTest.java index d0c04e06a3b..df20c65ceb9 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IntegrateIssuesVisitorTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IntegrateIssuesVisitorTest.java @@ -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 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()); + fileSourceRepository, new CommonRuleEngineImpl()), new Tracker()); IssueCache issueCache; IssueLifecycle issueLifecycle = mock(IssueLifecycle.class); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/TrackerRawInputFactoryTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/TrackerRawInputFactoryTest.java index 47a989b3425..70718ddd5a7 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/TrackerRawInputFactoryTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/TrackerRawInputFactoryTest.java @@ -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 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 index 00000000000..cb2c7b3cf32 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/source/SourceLinesRepositoryRule.java @@ -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 lines = new ArrayList<>(); + + @Override + protected void after() { + lines.clear(); + } + + @Override + public CloseableIterator 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 index 00000000000..7da426075fb --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/source/SourceLinesRepositoryTest.java @@ -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(); + } +} + diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistFileSourcesStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistFileSourcesStepTest.java index ad860e1a4ad..1d2c2e45e97 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistFileSourcesStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistFileSourcesStepTest.java @@ -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 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 { -- 2.39.5