]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-11151 Load changed lines from the report
authorDuarte Meneses <duarte.meneses@sonarsource.com>
Fri, 10 Aug 2018 13:04:29 +0000 (15:04 +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/batch/BatchReportReader.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/batch/BatchReportReaderImpl.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/source/NewLinesRepository.java [new file with mode: 0644]
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/batch/BatchReportReaderRule.java
sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/output/ScannerReportReader.java

index 961add8e05f5519758814817cccab5349911fcbe..f8bc0774d7af44ddb9bfcfeeeb6fb1e6dd9c0e94 100644 (file)
@@ -39,7 +39,7 @@ public interface BatchReportReader {
   ScannerReport.Component readComponent(int componentRef);
 
   CloseableIterator<ScannerReport.Issue> readComponentIssues(int componentRef);
-  
+
   CloseableIterator<ScannerReport.ExternalIssue> readComponentExternalIssues(int componentRef);
 
   CloseableIterator<ScannerReport.Duplication> readComponentDuplications(int componentRef);
@@ -62,6 +62,8 @@ public interface BatchReportReader {
   CloseableIterator<ScannerReport.CoverageDetail> readCoverageDetails(int testFileRef);
 
   CloseableIterator<ScannerReport.ContextProperty> readContextProperties();
-  
+
   Optional<CloseableIterator<ScannerReport.LineSgnificantCode>> readComponentSignificantCode(int fileRef);
+
+  Optional<ScannerReport.ChangedLines> readComponentChangedLines(int fileRef);
 }
index ff33320a682c868b56ecbc6686f6f0dd394cb73f..680baaeca43525561c7c8faabe0515c54edeb6b3 100644 (file)
@@ -26,7 +26,6 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
 import java.util.NoSuchElementException;
 import java.util.Optional;
 import javax.annotation.CheckForNull;
@@ -111,7 +110,7 @@ public class BatchReportReaderImpl implements BatchReportReader {
     ensureInitialized();
     return delegate.readComponentIssues(componentRef);
   }
-  
+
   @Override
   public CloseableIterator<ScannerReport.ExternalIssue> readComponentExternalIssues(int componentRef) {
     ensureInitialized();
@@ -257,7 +256,7 @@ public class BatchReportReaderImpl implements BatchReportReader {
       fileInputStream.close();
     }
   }
-  
+
   public boolean hasSignificantCode(int fileRef) {
     return delegate.hasSignificantCode(fileRef);
   }
@@ -267,4 +266,8 @@ public class BatchReportReaderImpl implements BatchReportReader {
     ensureInitialized();
     return Optional.ofNullable(delegate.readComponentSignificantCode(fileRef));
   }
+
+  @Override public Optional<ScannerReport.ChangedLines> readComponentChangedLines(int fileRef) {
+    return Optional.ofNullable(delegate.readComponentChangedLines(fileRef));
+  }
 }
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/source/NewLinesRepository.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/source/NewLinesRepository.java
new file mode 100644 (file)
index 0000000..d2d0c9b
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * 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.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import javax.annotation.Nullable;
+import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
+import org.sonar.ce.task.projectanalysis.batch.BatchReportReader;
+import org.sonar.ce.task.projectanalysis.component.Component;
+import org.sonar.ce.task.projectanalysis.period.Period;
+import org.sonar.ce.task.projectanalysis.period.PeriodHolder;
+import org.sonar.ce.task.projectanalysis.scm.Changeset;
+import org.sonar.ce.task.projectanalysis.scm.ScmInfo;
+import org.sonar.ce.task.projectanalysis.scm.ScmInfoRepository;
+
+public class NewLinesRepository {
+  private final BatchReportReader reportReader;
+  private final AnalysisMetadataHolder analysisMetadataHolder;
+  private final ScmInfoRepository scmInfoRepository;
+  @Nullable
+  private final Period period;
+  private final Map<Component, Optional<Set<Integer>>> changedLinesCache = new HashMap<>();
+
+  public NewLinesRepository(BatchReportReader reportReader, AnalysisMetadataHolder analysisMetadataHolder, PeriodHolder periodHolder, ScmInfoRepository scmInfoRepository) {
+    this.reportReader = reportReader;
+    this.analysisMetadataHolder = analysisMetadataHolder;
+    this.scmInfoRepository = scmInfoRepository;
+    this.period = periodHolder.getPeriod();
+  }
+
+  public boolean newLinesAvailable(Component component) {
+    return getNewLines(component).isPresent();
+  }
+
+  public boolean isLineNew(Component component, int line) {
+    return getNewLines(component).map(s -> s.contains(line))
+      .orElseThrow(() -> new IllegalStateException("No data about new lines available"));
+  }
+
+  public Optional<Set<Integer>> getNewLines(Component component) {
+    return changedLinesCache.computeIfAbsent(component, this::computeNewLines);
+  }
+
+  private Optional<Set<Integer>> computeNewLines(Component component) {
+    Optional<Set<Integer>> reportChangedLines = getChangedLinesFromReport(component);
+    if (reportChangedLines.isPresent()) {
+      return reportChangedLines;
+    }
+    return generateNewLinesFromScm(component);
+  }
+
+  private Optional<Set<Integer>> generateNewLinesFromScm(Component component) {
+    if (period == null) {
+      return Optional.empty();
+    }
+
+    Optional<ScmInfo> scmInfoOpt = scmInfoRepository.getScmInfo(component);
+    if (!scmInfoOpt.isPresent()) {
+      return Optional.empty();
+    }
+
+    ScmInfo scmInfo = scmInfoOpt.get();
+    Map<Integer, Changeset> allChangesets = scmInfo.getAllChangesets();
+    Set<Integer> lines = new HashSet<>();
+
+    for (Map.Entry<Integer, Changeset> e : allChangesets.entrySet()) {
+      if (isLineInPeriod(e.getValue().getDate(), period)) {
+        lines.add(e.getKey());
+      }
+    }
+    return Optional.of(lines);
+  }
+
+  /**
+   * A line belongs to a Period if its date is older than the SNAPSHOT's date of the period.
+   */
+  private static boolean isLineInPeriod(long lineDate, Period period) {
+    return lineDate > period.getSnapshotDate();
+  }
+
+  private Optional<Set<Integer>> getChangedLinesFromReport(Component component) {
+    if (analysisMetadataHolder.isPullRequest() || analysisMetadataHolder.isShortLivingBranch()) {
+      return reportReader.readComponentChangedLines(component.getReportAttributes().getRef())
+        .map(c -> new HashSet<>(c.getLineList()));
+    }
+
+    return Optional.empty();
+  }
+}
index 2b4b234c32c56ea72ed611cb1030bc5db7710d64..0572a9b7a3cdb8d097f03a647699c55dcf6e7b2a 100644 (file)
@@ -55,6 +55,7 @@ public class BatchReportReaderRule implements TestRule, BatchReportReader {
   private Map<Integer, List<ScannerReport.Test>> tests = new HashMap<>();
   private Map<Integer, List<ScannerReport.CoverageDetail>> coverageDetails = new HashMap<>();
   private Map<Integer, List<ScannerReport.LineSgnificantCode>> significantCode = new HashMap<>();
+  private Map<Integer, ScannerReport.ChangedLines> changedLines = new HashMap<>();
 
   @Override
   public Statement apply(final Statement statement, Description description) {
@@ -233,6 +234,15 @@ public class BatchReportReaderRule implements TestRule, BatchReportReader {
     return list == null ? Optional.empty() : Optional.of(CloseableIterator.from(list.iterator()));
   }
 
+  public BatchReportReaderRule putChangedLines(int fileRef, ScannerReport.ChangedLines fileChangedLines) {
+    changedLines.put(fileRef, fileChangedLines);
+    return this;
+  }
+
+  @Override public Optional<ScannerReport.ChangedLines> readComponentChangedLines(int fileRef) {
+    return Optional.ofNullable(changedLines.get(fileRef));
+  }
+
   @Override
   public CloseableIterator<ScannerReport.SyntaxHighlightingRule> readComponentSyntaxHighlighting(int fileRef) {
     return closeableIterator(this.syntaxHighlightings.get(fileRef));
index 7da8a34470e96219fe744f8ea369cc078ef6ead2..65c7bb79a05df113e9bf09bcc7a2ebca4f60d63e 100644 (file)
@@ -129,6 +129,15 @@ public class ScannerReportReader {
     return null;
   }
 
+  @CheckForNull
+  public ScannerReport.ChangedLines readComponentChangedLines(int fileRef) {
+    File file = fileStructure.fileFor(FileStructure.Domain.CHANGED_LINES, fileRef);
+    if (fileExists(file)) {
+      return Protobuf.read(file, ScannerReport.ChangedLines.parser());
+    }
+    return null;
+  }
+
   public boolean hasSignificantCode(int fileRef) {
     File file = fileStructure.fileFor(FileStructure.Domain.SGNIFICANT_CODE, fileRef);
     return fileExists(file);