]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-11394 Calculate estimated overall duplication in SLB
authorDuarte Meneses <duarte.meneses@sonarsource.com>
Mon, 22 Oct 2018 08:06:52 +0000 (10:06 +0200)
committerSonarTech <sonartech@sonarsource.com>
Wed, 7 Nov 2018 19:21:03 +0000 (20:21 +0100)
19 files changed:
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/duplication/DuplicationMeasures.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/duplication/InExtendedProjectDuplicate.java [new file with mode: 0644]
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/duplication/InProjectDuplicate.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/source/linereader/DuplicationLineReader.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/DuplicationDataMeasuresStep.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadDuplicationsFromReportStep.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/duplication/DuplicationMeasuresTest.java [new file with mode: 0644]
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/duplication/ReportDuplicationMeasuresTest.java [deleted file]
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/duplication/ViewsDuplicationMeasuresTest.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/DuplicationDataMeasuresStepTest.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadDuplicationsFromReportStepTest.java
server/sonar-server/src/main/java/org/sonar/server/duplication/ws/Duplication.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsParser.java
server/sonar-server/src/main/java/org/sonar/server/duplication/ws/ShowResponseBuilder.java
server/sonar-server/src/test/java/org/sonar/server/duplication/ws/DuplicationsParserTest.java
server/sonar-server/src/test/java/org/sonar/server/duplication/ws/ShowResponseBuilderTest.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/CpdExecutor.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/cpd/CpdExecutorTest.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorContextTest.java

index d78c5a9dc9b6250b6306e3a3955be18ad98697dd..be7e5e52ef7e0496ea8e65199545a704f793a3d9 100644 (file)
@@ -35,19 +35,16 @@ import org.sonar.ce.task.projectanalysis.formula.Formula;
 import org.sonar.ce.task.projectanalysis.formula.FormulaExecutorComponentVisitor;
 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 com.google.common.collect.FluentIterable.from;
 import static com.google.common.collect.Iterables.isEmpty;
 import static java.util.Objects.requireNonNull;
-import static org.sonar.api.measures.CoreMetrics.COMMENT_LINES_KEY;
 import static org.sonar.api.measures.CoreMetrics.DUPLICATED_BLOCKS_KEY;
 import static org.sonar.api.measures.CoreMetrics.DUPLICATED_FILES_KEY;
 import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES_DENSITY_KEY;
 import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES_KEY;
 import static org.sonar.api.measures.CoreMetrics.LINES_KEY;
-import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY;
 
 public class DuplicationMeasures {
   protected final ImmutableList<Formula> formulas;
@@ -63,7 +60,7 @@ public class DuplicationMeasures {
     this.measureRepository = measureRepository;
     // will be null for views
     this.duplicationRepository = duplicationRepository;
-    this.formulas = ImmutableList.of(new DuplicationFormula(metricRepository, measureRepository));
+    this.formulas = ImmutableList.of(new DuplicationFormula());
   }
 
   /**
@@ -76,7 +73,7 @@ public class DuplicationMeasures {
   public void execute() {
     new PathAwareCrawler<>(
       FormulaExecutorComponentVisitor.newBuilder(metricRepository, measureRepository).buildFor(formulas))
-      .visit(treeRootHolder.getRoot());
+      .visit(treeRootHolder.getReportTreeRoot());
   }
 
   protected DuplicationCounter createCounter() {
@@ -88,6 +85,7 @@ public class DuplicationMeasures {
     private final DuplicationRepository duplicationRepository;
     protected int fileCount = 0;
     protected int blockCount = 0;
+    protected int dupLineCount = 0;
     protected int lineCount = 0;
 
     protected DuplicationCounter() {
@@ -102,6 +100,7 @@ public class DuplicationMeasures {
     public void aggregate(DuplicationCounter counter) {
       this.fileCount += counter.fileCount;
       this.blockCount += counter.blockCount;
+      this.dupLineCount += counter.dupLineCount;
       this.lineCount += counter.lineCount;
     }
 
@@ -116,6 +115,8 @@ public class DuplicationMeasures {
     }
 
     protected void initializeForFile(Component file) {
+      // don't use measure since it won't be available for some files in the report tree in SLB
+      this.lineCount = file.getFileAttributes().getLines();
       Iterable<Duplication> duplications = requireNonNull(this.duplicationRepository, "DuplicationRepository missing")
         .getDuplications(file);
       if (isEmpty(duplications)) {
@@ -136,7 +137,8 @@ public class DuplicationMeasures {
 
       this.fileCount += 1;
       this.blockCount += blocks;
-      this.lineCount += duplicatedLineNumbers.size();
+      this.dupLineCount += duplicatedLineNumbers.size();
+
     }
 
     private static void addLines(TextBlock textBlock, Set<Integer> duplicatedLineNumbers) {
@@ -148,7 +150,8 @@ public class DuplicationMeasures {
     private void initializeForProjectView(CounterInitializationContext context) {
       fileCount += getMeasure(context, DUPLICATED_FILES_KEY);
       blockCount += getMeasure(context, DUPLICATED_BLOCKS_KEY);
-      lineCount += getMeasure(context, DUPLICATED_LINES_KEY);
+      dupLineCount += getMeasure(context, DUPLICATED_LINES_KEY);
+      lineCount += getMeasure(context, LINES_KEY);
     }
 
     private static int getMeasure(CounterInitializationContext context, String metricKey) {
@@ -161,18 +164,6 @@ public class DuplicationMeasures {
   }
 
   private final class DuplicationFormula implements Formula<DuplicationCounter> {
-    private final MeasureRepository measureRepository;
-    private final Metric nclocMetric;
-    private final Metric linesMetric;
-    private final Metric commentLinesMetric;
-
-    private DuplicationFormula(MetricRepository metricRepository, MeasureRepository measureRepository) {
-      this.measureRepository = measureRepository;
-      this.nclocMetric = metricRepository.getByKey(NCLOC_KEY);
-      this.linesMetric = metricRepository.getByKey(LINES_KEY);
-      this.commentLinesMetric = metricRepository.getByKey(COMMENT_LINES_KEY);
-    }
-
     @Override
     public DuplicationCounter createNewCounter() {
       return createCounter();
@@ -184,7 +175,7 @@ public class DuplicationMeasures {
         case DUPLICATED_FILES_KEY:
           return Optional.of(Measure.newMeasureBuilder().create(counter.fileCount));
         case DUPLICATED_LINES_KEY:
-          return Optional.of(Measure.newMeasureBuilder().create(counter.lineCount));
+          return Optional.of(Measure.newMeasureBuilder().create(counter.dupLineCount));
         case DUPLICATED_LINES_DENSITY_KEY:
           return createDuplicatedLinesDensityMeasure(counter, context);
         case DUPLICATED_BLOCKS_KEY:
@@ -195,29 +186,15 @@ public class DuplicationMeasures {
     }
 
     private Optional<Measure> createDuplicatedLinesDensityMeasure(DuplicationCounter counter, CreateMeasureContext context) {
-      int duplicatedLines = counter.lineCount;
-      java.util.Optional<Integer> nbLines = getNbLinesFromLocOrNcloc(context);
-      if (nbLines.isPresent() && nbLines.get() > 0) {
-        double density = Math.min(100d, 100d * duplicatedLines / nbLines.get());
+      int duplicatedLines = counter.dupLineCount;
+      int nbLines = counter.lineCount;
+      if (nbLines > 0) {
+        double density = Math.min(100d, 100d * duplicatedLines / nbLines);
         return Optional.of(Measure.newMeasureBuilder().create(density, context.getMetric().getDecimalScale()));
       }
       return Optional.absent();
     }
 
-    private java.util.Optional<Integer> getNbLinesFromLocOrNcloc(CreateMeasureContext context) {
-      Optional<Measure> lines = measureRepository.getRawMeasure(context.getComponent(), linesMetric);
-      if (lines.isPresent()) {
-        return java.util.Optional.of(lines.get().getIntValue());
-      }
-      Optional<Measure> nclocs = measureRepository.getRawMeasure(context.getComponent(), nclocMetric);
-      if (nclocs.isPresent()) {
-        Optional<Measure> commentLines = measureRepository.getRawMeasure(context.getComponent(), commentLinesMetric);
-        int nbLines = nclocs.get().getIntValue();
-        return java.util.Optional.of(commentLines.isPresent() ? (nbLines + commentLines.get().getIntValue()) : nbLines);
-      }
-      return java.util.Optional.empty();
-    }
-
     @Override
     public String[] getOutputMetricKeys() {
       return new String[] {DUPLICATED_FILES_KEY, DUPLICATED_LINES_KEY, DUPLICATED_LINES_DENSITY_KEY, DUPLICATED_BLOCKS_KEY};
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/duplication/InExtendedProjectDuplicate.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/duplication/InExtendedProjectDuplicate.java
new file mode 100644 (file)
index 0000000..53a1367
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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.duplication;
+
+import org.sonar.ce.task.projectanalysis.component.Component;
+
+public class InExtendedProjectDuplicate extends InProjectDuplicate {
+  public InExtendedProjectDuplicate(Component file, TextBlock textBlock) {
+    super(file, textBlock);
+  }
+
+  @Override
+  public String toString() {
+    return "InExtendedProjectDuplicate{" +
+      "file=" + file +
+      ", textBlock=" + getTextBlock() +
+      '}';
+  }
+}
index 45b9f33803dd4c2f7cd95e6117f2fdce163c6fd6..bcaf0617c5e98b3c784e5c7d607b7191c8fce7f0 100644 (file)
@@ -30,7 +30,7 @@ import static java.util.Objects.requireNonNull;
 
 @Immutable
 public class InProjectDuplicate extends AbstractDuplicate {
-  private final Component file;
+  protected final Component file;
 
   public InProjectDuplicate(Component file, TextBlock textBlock) {
     super(textBlock);
index 4e0184134eff27a6cae3c240632a3ce0466c9563..b3c05f3660a2964a2803655e400cecbd728470c9 100644 (file)
  */
 package org.sonar.ce.task.projectanalysis.source.linereader;
 
-import com.google.common.base.Function;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Ordering;
 import java.util.ArrayList;
-import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import org.sonar.ce.task.projectanalysis.duplication.Duplicate;
 import org.sonar.ce.task.projectanalysis.duplication.Duplication;
 import org.sonar.ce.task.projectanalysis.duplication.InnerDuplicate;
 import org.sonar.ce.task.projectanalysis.duplication.TextBlock;
+import org.sonar.core.util.stream.MoreCollectors;
 import org.sonar.db.protobuf.DbFileSources;
 
 import static com.google.common.collect.FluentIterable.from;
@@ -49,29 +51,27 @@ public class DuplicationLineReader implements LineReader {
   @Override
   public Optional<ReadError> read(DbFileSources.Line.Builder lineBuilder) {
     Predicate<Map.Entry<TextBlock, Integer>> containsLine = new TextBlockContainsLine(lineBuilder.getLine());
-    for (Integer textBlockIndex : from(duplicatedTextBlockIndexByTextBlock.entrySet())
+    // list is sorted to cope with the non-guaranteed order of Map entries which would trigger false detection of changes
+    // in {@link DbFileSources.Line#getDuplicationList()}
+    duplicatedTextBlockIndexByTextBlock.entrySet().stream()
       .filter(containsLine)
-      .transform(MapEntryToBlockId.INSTANCE)
-      // list is sorted to cope with the non-guaranteed order of Map entries which would trigger false detection of changes
-      // in {@link DbFileSources.Line#getDuplicationList()}
-      .toSortedList(Ordering.natural())) {
-      lineBuilder.addDuplication(textBlockIndex);
-    }
+      .map(MapEntryToBlockId.INSTANCE)
+      .sorted(Comparator.naturalOrder())
+      .forEach(lineBuilder::addDuplication);
+
     return Optional.empty();
   }
 
   /**
-   *
    * <p>
    * This method uses the natural order of TextBlocks to ensure that given the same set of TextBlocks, they get the same
    * index. It avoids false detections of changes in {@link DbFileSources.Line#getDuplicationList()}.
    * </p>
    */
   private static Map<TextBlock, Integer> createIndexOfDuplicatedTextBlocks(Iterable<Duplication> duplications) {
-    List<TextBlock> duplicatedTextBlocks = extractAllDuplicatedTextBlocks(duplications);
-    Collections.sort(duplicatedTextBlocks);
-    return from(duplicatedTextBlocks)
-      .toMap(new TextBlockIndexGenerator());
+    return extractAllDuplicatedTextBlocks(duplications)
+      .stream().sorted()
+      .collect(Collectors.toMap(e -> e, new TextBlockIndexGenerator(), MoreCollectors.mergeNotSupportedMerger(), LinkedHashMap::new));
   }
 
   /**
@@ -102,7 +102,7 @@ public class DuplicationLineReader implements LineReader {
     }
 
     @Override
-    public boolean apply(@Nonnull Map.Entry<TextBlock, Integer> input) {
+    public boolean test(@Nonnull Map.Entry<TextBlock, Integer> input) {
       return isLineInBlock(input.getKey(), line);
     }
 
index 4446004e861f16fb8d0d4fba4248bdabeccface1..1fc73c04092aacf621c76a495f0f0fc8307dbbc8 100644 (file)
@@ -29,6 +29,7 @@ import org.sonar.ce.task.projectanalysis.duplication.CrossProjectDuplicate;
 import org.sonar.ce.task.projectanalysis.duplication.Duplicate;
 import org.sonar.ce.task.projectanalysis.duplication.Duplication;
 import org.sonar.ce.task.projectanalysis.duplication.DuplicationRepository;
+import org.sonar.ce.task.projectanalysis.duplication.InExtendedProjectDuplicate;
 import org.sonar.ce.task.projectanalysis.duplication.InProjectDuplicate;
 import org.sonar.ce.task.projectanalysis.duplication.InnerDuplicate;
 import org.sonar.ce.task.projectanalysis.duplication.TextBlock;
@@ -89,14 +90,14 @@ public class DuplicationDataMeasuresStep implements ComputationStep {
         Measure.newMeasureBuilder().create(duplicationXml));
     }
 
-    private String createXmlDuplications(String componentKey, Iterable<Duplication> duplications) {
+    private String createXmlDuplications(String componentDbKey, Iterable<Duplication> duplications) {
       StringBuilder xml = new StringBuilder();
       xml.append("<duplications>");
       for (Duplication duplication : duplications) {
         xml.append("<g>");
-        appendDuplication(xml, componentKey, duplication.getOriginal());
+        appendDuplication(xml, componentDbKey, duplication.getOriginal(), false);
         for (Duplicate duplicate : duplication.getDuplicates()) {
-          processDuplicationBlock(xml, duplicate, componentKey);
+          processDuplicationBlock(xml, duplicate, componentDbKey);
         }
         xml.append("</g>");
       }
@@ -104,15 +105,18 @@ public class DuplicationDataMeasuresStep implements ComputationStep {
       return xml.toString();
     }
 
-    private void processDuplicationBlock(StringBuilder xml, Duplicate duplicate, String componentKey) {
+    private void processDuplicationBlock(StringBuilder xml, Duplicate duplicate, String componentDbKey) {
       if (duplicate instanceof InnerDuplicate) {
-        // Duplication is on a the same file
-        appendDuplication(xml, componentKey, duplicate);
+        // Duplication is on the same file
+        appendDuplication(xml, componentDbKey, duplicate);
+      } else if (duplicate instanceof InExtendedProjectDuplicate) {
+        // Duplication is on a different file that is not saved in the DB
+        appendDuplication(xml, ((InExtendedProjectDuplicate) duplicate).getFile().getDbKey(), duplicate.getTextBlock(), true);
       } else if (duplicate instanceof InProjectDuplicate) {
         // Duplication is on a different file
         appendDuplication(xml, ((InProjectDuplicate) duplicate).getFile().getDbKey(), duplicate);
       } else if (duplicate instanceof CrossProjectDuplicate) {
-        // componentKey is only set for cross project duplications
+        // Only componentKey is set for cross project duplications
         String crossProjectComponentKey = ((CrossProjectDuplicate) duplicate).getFileKey();
         appendDuplication(xml, crossProjectComponentKey, duplicate);
       } else {
@@ -120,15 +124,16 @@ public class DuplicationDataMeasuresStep implements ComputationStep {
       }
     }
 
-    private void appendDuplication(StringBuilder xml, String componentKey, Duplicate duplicate) {
-      appendDuplication(xml, componentKey, duplicate.getTextBlock());
+    private void appendDuplication(StringBuilder xml, String componentDbKey, Duplicate duplicate) {
+      appendDuplication(xml, componentDbKey, duplicate.getTextBlock(), false);
     }
 
-    private void appendDuplication(StringBuilder xml, String componentKey, TextBlock textBlock) {
+    private void appendDuplication(StringBuilder xml, String componentDbKey, TextBlock textBlock, boolean onlyText) {
       int length = textBlock.getEnd() - textBlock.getStart() + 1;
       xml.append("<b s=\"").append(textBlock.getStart())
         .append("\" l=\"").append(length)
-        .append("\" r=\"").append(StringEscapeUtils.escapeXml(componentKey))
+        .append("\" t=\"").append(onlyText)
+        .append("\" r=\"").append(StringEscapeUtils.escapeXml(componentDbKey))
         .append("\"/>");
     }
   }
index bd3119ae4bc2c48198320eca467a94b25e1135af..f8a7fefffa55e52e67a6ef8301a20b9c9c66659a 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.ce.task.projectanalysis.step;
 
 import com.google.common.base.Function;
 import javax.annotation.Nonnull;
+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.component.CrawlerDepthLimit;
@@ -31,6 +32,7 @@ import org.sonar.ce.task.projectanalysis.duplication.DetailedTextBlock;
 import org.sonar.ce.task.projectanalysis.duplication.Duplicate;
 import org.sonar.ce.task.projectanalysis.duplication.Duplication;
 import org.sonar.ce.task.projectanalysis.duplication.DuplicationRepository;
+import org.sonar.ce.task.projectanalysis.duplication.InExtendedProjectDuplicate;
 import org.sonar.ce.task.projectanalysis.duplication.InProjectDuplicate;
 import org.sonar.ce.task.projectanalysis.duplication.InnerDuplicate;
 import org.sonar.ce.task.projectanalysis.duplication.TextBlock;
@@ -46,11 +48,14 @@ import static com.google.common.collect.FluentIterable.from;
  */
 public class LoadDuplicationsFromReportStep implements ComputationStep {
   private final TreeRootHolder treeRootHolder;
+  private final AnalysisMetadataHolder analysisMetadataHolder;
   private final BatchReportReader batchReportReader;
   private final DuplicationRepository duplicationRepository;
 
-  public LoadDuplicationsFromReportStep(TreeRootHolder treeRootHolder, BatchReportReader batchReportReader, DuplicationRepository duplicationRepository) {
+  public LoadDuplicationsFromReportStep(TreeRootHolder treeRootHolder, AnalysisMetadataHolder analysisMetadataHolder, BatchReportReader batchReportReader,
+    DuplicationRepository duplicationRepository) {
     this.treeRootHolder = treeRootHolder;
+    this.analysisMetadataHolder = analysisMetadataHolder;
     this.batchReportReader = batchReportReader;
     this.duplicationRepository = duplicationRepository;
   }
@@ -80,7 +85,11 @@ public class LoadDuplicationsFromReportStep implements ComputationStep {
       if (input.getOtherFileRef() != 0) {
         checkArgument(input.getOtherFileRef() != file.getReportAttributes().getRef(), "file and otherFile references can not be the same");
         Component otherComponent = treeRootHolder.getReportTreeComponentByRef(input.getOtherFileRef());
-        return new InProjectDuplicate(otherComponent, convert(input.getRange()));
+        if ((analysisMetadataHolder.isShortLivingBranch() || analysisMetadataHolder.isPullRequest()) && otherComponent.getStatus() == Component.Status.SAME) {
+          return new InExtendedProjectDuplicate(otherComponent, convert(input.getRange()));
+        } else {
+          return new InProjectDuplicate(otherComponent, convert(input.getRange()));
+        }
       }
       return new InnerDuplicate(convert(input.getRange()));
     }
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/duplication/DuplicationMeasuresTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/duplication/DuplicationMeasuresTest.java
new file mode 100644 (file)
index 0000000..c413ed4
--- /dev/null
@@ -0,0 +1,387 @@
+/*
+ * 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.duplication;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.ce.task.projectanalysis.component.FileAttributes;
+import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
+import org.sonar.ce.task.projectanalysis.measure.MeasureRepositoryRule;
+import org.sonar.ce.task.projectanalysis.metric.MetricRepositoryRule;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.guava.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.api.measures.CoreMetrics.DUPLICATED_BLOCKS;
+import static org.sonar.api.measures.CoreMetrics.DUPLICATED_BLOCKS_KEY;
+import static org.sonar.api.measures.CoreMetrics.DUPLICATED_FILES;
+import static org.sonar.api.measures.CoreMetrics.DUPLICATED_FILES_KEY;
+import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES;
+import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES_DENSITY;
+import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES_DENSITY_KEY;
+import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES_KEY;
+import static org.sonar.ce.task.projectanalysis.component.Component.Type.DIRECTORY;
+import static org.sonar.ce.task.projectanalysis.component.Component.Type.FILE;
+import static org.sonar.ce.task.projectanalysis.component.Component.Type.MODULE;
+import static org.sonar.ce.task.projectanalysis.component.Component.Type.PROJECT;
+import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
+import static org.sonar.ce.task.projectanalysis.measure.Measure.newMeasureBuilder;
+
+public class DuplicationMeasuresTest {
+  private static final int ROOT_REF = 1;
+  private static final int MODULE_REF = 12;
+  private static final int SUB_MODULE_1_REF = 123;
+  private static final int SUB_MODULE_2_REF = 126;
+  private static final int DIRECTORY_REF = 1234;
+  private static final int FILE_1_REF = 12341;
+  private static final int FILE_2_REF = 12342;
+  private static final int FILE_3_REF = 1261;
+  private static final int FILE_4_REF = 1262;
+  private static final FileAttributes FILE_1_ATTRS = mock(FileAttributes.class);
+  private static final FileAttributes FILE_2_ATTRS = mock(FileAttributes.class);
+  private static final FileAttributes FILE_3_ATTRS = mock(FileAttributes.class);
+  private static final FileAttributes FILE_4_ATTRS = mock(FileAttributes.class);
+
+  private static final String SOME_FILE_KEY = "some file key";
+
+  @Rule
+  public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule()
+    .setRoot(
+      builder(PROJECT, ROOT_REF)
+        .addChildren(
+          builder(MODULE, MODULE_REF)
+            .addChildren(
+              builder(MODULE, SUB_MODULE_1_REF)
+                .addChildren(
+                  builder(DIRECTORY, DIRECTORY_REF)
+                    .addChildren(
+                      builder(FILE, FILE_1_REF).setFileAttributes(FILE_1_ATTRS).build(),
+                      builder(FILE, FILE_2_REF).setFileAttributes(FILE_2_ATTRS).build())
+                    .build())
+                .build(),
+              builder(MODULE, SUB_MODULE_2_REF)
+                .addChildren(
+                  builder(FILE, FILE_3_REF).setFileAttributes(FILE_3_ATTRS).build(),
+                  builder(FILE, FILE_4_REF).setFileAttributes(FILE_4_ATTRS).build())
+                .build())
+            .build())
+        .build());
+  @Rule
+  public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
+    .add(DUPLICATED_BLOCKS)
+    .add(DUPLICATED_FILES)
+    .add(DUPLICATED_LINES)
+    .add(DUPLICATED_LINES_DENSITY);
+  @Rule
+  public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
+  @Rule
+  public DuplicationRepositoryRule duplicationRepository = DuplicationRepositoryRule.create(treeRootHolder);
+
+  private DuplicationMeasures underTest = new DuplicationMeasures(treeRootHolder, metricRepository, measureRepository, duplicationRepository);
+
+  @Test
+  public void compute_duplicated_blocks_one_for_original_one_for_each_InnerDuplicate() {
+    TextBlock original = new TextBlock(1, 1);
+    duplicationRepository.addDuplication(FILE_1_REF, original, new TextBlock(2, 2), new TextBlock(3, 3), new TextBlock(2, 3));
+
+    underTest.execute();
+
+    assertRawMeasureValue(FILE_1_REF, DUPLICATED_BLOCKS_KEY, 4);
+  }
+
+  @Test
+  public void compute_duplicated_blocks_does_not_count_blocks_only_once_it_assumes_consistency_from_duplication_data() {
+    duplicationRepository.addDuplication(FILE_1_REF, new TextBlock(1, 1), new TextBlock(3, 3));
+    duplicationRepository.addDuplication(FILE_1_REF, new TextBlock(2, 2), new TextBlock(3, 3));
+
+    underTest.execute();
+
+    assertRawMeasureValue(FILE_1_REF, DUPLICATED_BLOCKS_KEY, 4);
+  }
+
+  @Test
+  public void compute_duplicated_blocks_one_for_original_and_ignores_InProjectDuplicate() {
+    duplicationRepository.addDuplication(FILE_1_REF, new TextBlock(1, 1), FILE_2_REF, new TextBlock(2, 2));
+
+    underTest.execute();
+
+    assertRawMeasureValue(FILE_1_REF, DUPLICATED_BLOCKS_KEY, 1);
+  }
+
+  @Test
+  public void compute_duplicated_blocks_one_for_original_and_ignores_CrossProjectDuplicate() {
+    duplicationRepository.addDuplication(FILE_1_REF, new TextBlock(1, 1), SOME_FILE_KEY, new TextBlock(2, 2));
+
+    underTest.execute();
+
+    assertRawMeasureValue(FILE_1_REF, DUPLICATED_BLOCKS_KEY, 1);
+  }
+
+  @Test
+  public void compute_and_aggregate_duplicated_blocks_from_single_duplication() {
+    addDuplicatedBlock(FILE_1_REF, 10);
+    addDuplicatedBlock(FILE_2_REF, 40);
+    addDuplicatedBlock(FILE_4_REF, 5);
+
+    underTest.execute();
+
+    assertRawMeasureValue(FILE_1_REF, DUPLICATED_BLOCKS_KEY, 10);
+    assertRawMeasureValue(FILE_2_REF, DUPLICATED_BLOCKS_KEY, 40);
+    assertRawMeasureValue(FILE_3_REF, DUPLICATED_BLOCKS_KEY, 0);
+    assertRawMeasureValue(FILE_4_REF, DUPLICATED_BLOCKS_KEY, 5);
+    assertRawMeasureValue(DIRECTORY_REF, DUPLICATED_BLOCKS_KEY, 50);
+    assertRawMeasureValue(SUB_MODULE_1_REF, DUPLICATED_BLOCKS_KEY, 50);
+    assertRawMeasureValue(SUB_MODULE_2_REF, DUPLICATED_BLOCKS_KEY, 5);
+    assertRawMeasureValue(MODULE_REF, DUPLICATED_BLOCKS_KEY, 55);
+    assertRawMeasureValue(ROOT_REF, DUPLICATED_BLOCKS_KEY, 55);
+  }
+
+  @Test
+  public void compute_and_aggregate_duplicated_blocks_to_zero_when_no_duplication() {
+    underTest.execute();
+
+    assertComputedAndAggregatedToZeroInt(DUPLICATED_BLOCKS_KEY);
+  }
+
+  @Test
+  public void compute_duplicated_lines_counts_lines_from_original_and_InnerDuplicate_of_a_single_line() {
+    TextBlock original = new TextBlock(1, 1);
+    duplicationRepository.addDuplication(FILE_1_REF, original, new TextBlock(2, 2));
+
+    underTest.execute();
+
+    assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_KEY, 2);
+  }
+
+  @Test
+  public void compute_duplicated_lines_counts_lines_from_original_and_ignores_InProjectDuplicate() {
+    TextBlock original = new TextBlock(1, 1);
+    duplicationRepository.addDuplication(FILE_1_REF, original, FILE_2_REF, new TextBlock(2, 2));
+
+    underTest.execute();
+
+    assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_KEY, 1);
+  }
+
+  @Test
+  public void compute_duplicated_lines_counts_lines_from_original_and_ignores_CrossProjectDuplicate() {
+    TextBlock original = new TextBlock(1, 1);
+    duplicationRepository.addDuplication(FILE_1_REF, original, SOME_FILE_KEY, new TextBlock(2, 2));
+
+    underTest.execute();
+
+    assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_KEY, 1);
+  }
+
+  @Test
+  public void compute_duplicated_lines_counts_lines_from_original_and_InnerDuplicate() {
+    TextBlock original = new TextBlock(1, 5);
+    duplicationRepository.addDuplication(FILE_1_REF, original, new TextBlock(10, 11));
+
+    underTest.execute();
+
+    assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_KEY, 7);
+  }
+
+  @Test
+  public void compute_duplicated_lines_counts_lines_from_original_and_InnerDuplicate_only_once() {
+    TextBlock original = new TextBlock(1, 12);
+    duplicationRepository.addDuplication(FILE_1_REF, original, new TextBlock(10, 11), new TextBlock(11, 15));
+    duplicationRepository.addDuplication(FILE_1_REF, new TextBlock(2, 2), new TextBlock(96, 96));
+
+    underTest.execute();
+
+    assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_KEY, 16);
+  }
+
+  @Test
+  public void compute_and_aggregate_duplicated_files() {
+    addDuplicatedBlock(FILE_1_REF, 2);
+    addDuplicatedBlock(FILE_3_REF, 10);
+    addDuplicatedBlock(FILE_4_REF, 50);
+
+    underTest.execute();
+
+    assertRawMeasureValue(FILE_1_REF, DUPLICATED_FILES_KEY, 1);
+    assertRawMeasureValue(FILE_2_REF, DUPLICATED_FILES_KEY, 0);
+    assertRawMeasureValue(FILE_3_REF, DUPLICATED_FILES_KEY, 1);
+    assertRawMeasureValue(FILE_4_REF, DUPLICATED_FILES_KEY, 1);
+    assertRawMeasureValue(DIRECTORY_REF, DUPLICATED_FILES_KEY, 1);
+    assertRawMeasureValue(SUB_MODULE_1_REF, DUPLICATED_FILES_KEY, 1);
+    assertRawMeasureValue(SUB_MODULE_2_REF, DUPLICATED_FILES_KEY, 2);
+    assertRawMeasureValue(MODULE_REF, DUPLICATED_FILES_KEY, 3);
+    assertRawMeasureValue(ROOT_REF, DUPLICATED_FILES_KEY, 3);
+  }
+
+  @Test
+  public void compute_and_aggregate_zero_duplicated_files_when_no_duplication_data() {
+    underTest.execute();
+
+    assertComputedAndAggregatedToZeroInt(DUPLICATED_FILES_KEY);
+  }
+
+  @Test
+  public void compute_and_aggregate_duplicated_lines() {
+    addDuplicatedBlock(FILE_1_REF, 10);
+    addDuplicatedBlock(FILE_2_REF, 9);
+    addDuplicatedBlock(FILE_4_REF, 7);
+
+    underTest.execute();
+
+    assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_KEY, 10);
+    assertRawMeasureValue(FILE_2_REF, DUPLICATED_LINES_KEY, 9);
+    assertRawMeasureValue(FILE_3_REF, DUPLICATED_LINES_KEY, 0);
+    assertRawMeasureValue(FILE_4_REF, DUPLICATED_LINES_KEY, 7);
+    assertRawMeasureValue(DIRECTORY_REF, DUPLICATED_LINES_KEY, 19);
+    assertRawMeasureValue(SUB_MODULE_1_REF, DUPLICATED_LINES_KEY, 19);
+    assertRawMeasureValue(SUB_MODULE_2_REF, DUPLICATED_LINES_KEY, 7);
+    assertRawMeasureValue(MODULE_REF, DUPLICATED_LINES_KEY, 26);
+    assertRawMeasureValue(ROOT_REF, DUPLICATED_LINES_KEY, 26);
+  }
+
+  @Test
+  public void compute_and_aggregate_zero_duplicated_line_when_no_duplication() {
+    underTest.execute();
+
+    assertComputedAndAggregatedToZeroInt(DUPLICATED_LINES_KEY);
+  }
+
+  @Test
+  public void compute_and_aggregate_duplicated_lines_density_using_lines() {
+    addDuplicatedBlock(FILE_1_REF, 2);
+    addDuplicatedBlock(FILE_2_REF, 3);
+
+    when(FILE_1_ATTRS.getLines()).thenReturn(10);
+    when(FILE_2_ATTRS.getLines()).thenReturn(40);
+
+    underTest.execute();
+
+    assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 20d);
+    assertRawMeasureValue(FILE_2_REF, DUPLICATED_LINES_DENSITY_KEY, 7.5d);
+    assertNoRawMeasure(FILE_3_REF, DUPLICATED_LINES_DENSITY_KEY);
+    assertNoRawMeasure(FILE_4_REF, DUPLICATED_LINES_DENSITY_KEY);
+    assertRawMeasureValue(DIRECTORY_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
+    assertRawMeasureValue(SUB_MODULE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
+    assertNoRawMeasure(SUB_MODULE_2_REF, DUPLICATED_LINES_DENSITY_KEY);
+    assertRawMeasureValue(MODULE_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
+    assertRawMeasureValue(ROOT_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
+  }
+
+  @Test
+  public void compute_zero_percent_duplicated_lines_density_when_there_is_no_duplication() {
+    when(FILE_1_ATTRS.getLines()).thenReturn(10);
+    when(FILE_2_ATTRS.getLines()).thenReturn(40);
+
+    underTest.execute();
+
+    assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 0d);
+    assertRawMeasureValue(FILE_2_REF, DUPLICATED_LINES_DENSITY_KEY, 0d);
+    assertNoRawMeasure(FILE_3_REF, DUPLICATED_LINES_DENSITY_KEY);
+    assertNoRawMeasure(FILE_4_REF, DUPLICATED_LINES_DENSITY_KEY);
+    assertRawMeasureValue(DIRECTORY_REF, DUPLICATED_LINES_DENSITY_KEY, 0d);
+    assertRawMeasureValue(SUB_MODULE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 0d);
+    assertNoRawMeasure(SUB_MODULE_2_REF, DUPLICATED_LINES_DENSITY_KEY);
+    assertRawMeasureValue(MODULE_REF, DUPLICATED_LINES_DENSITY_KEY, 0d);
+    assertRawMeasureValue(ROOT_REF, DUPLICATED_LINES_DENSITY_KEY, 0d);
+  }
+
+  @Test
+  public void not_compute_duplicated_lines_density_when_lines_is_zero() {
+    when(FILE_1_ATTRS.getLines()).thenReturn(0);
+    when(FILE_2_ATTRS.getLines()).thenReturn(0);
+    underTest.execute();
+    assertNoRawMeasures(DUPLICATED_LINES_DENSITY_KEY);
+  }
+
+  @Test
+  public void compute_100_percent_duplicated_lines_density() {
+    addDuplicatedBlock(FILE_1_REF, 2);
+    addDuplicatedBlock(FILE_2_REF, 3);
+
+    when(FILE_1_ATTRS.getLines()).thenReturn(2);
+    when(FILE_2_ATTRS.getLines()).thenReturn(3);
+
+    underTest.execute();
+
+    assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 100d);
+    assertRawMeasureValue(FILE_2_REF, DUPLICATED_LINES_DENSITY_KEY, 100d);
+    assertNoRawMeasure(FILE_3_REF, DUPLICATED_LINES_DENSITY_KEY);
+    assertNoRawMeasure(FILE_4_REF, DUPLICATED_LINES_DENSITY_KEY);
+    assertRawMeasureValue(DIRECTORY_REF, DUPLICATED_LINES_DENSITY_KEY, 100d);
+    assertRawMeasureValue(SUB_MODULE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 100d);
+    assertNoRawMeasure(SUB_MODULE_2_REF, DUPLICATED_LINES_DENSITY_KEY);
+    assertRawMeasureValue(MODULE_REF, DUPLICATED_LINES_DENSITY_KEY, 100d);
+    assertRawMeasureValue(ROOT_REF, DUPLICATED_LINES_DENSITY_KEY, 100d);
+  }
+
+  /**
+   * Adds duplication blocks of a single line (each line is specific to its block).
+   * This is a very simple use case, convenient for unit tests but more realistic and complex use cases must be tested separately.
+   */
+  private void addDuplicatedBlock(int fileRef, int blockCount) {
+    checkArgument(blockCount > 1, "BlockCount can not be less than 2");
+    TextBlock original = new TextBlock(1, 1);
+    TextBlock[] duplicates = new TextBlock[blockCount - 1];
+    for (int i = 10; i < blockCount + 9; i++) {
+      duplicates[i - 10] = new TextBlock(i, i);
+    }
+    duplicationRepository.addDuplication(fileRef, original, duplicates);
+  }
+
+  private void addRawMeasure(int componentRef, String metricKey, int value) {
+    measureRepository.addRawMeasure(componentRef, metricKey, newMeasureBuilder().create(value));
+  }
+
+  private void assertNoRawMeasures(String metricKey) {
+    assertThat(measureRepository.getAddedRawMeasures(FILE_1_REF).get(metricKey)).isEmpty();
+    assertThat(measureRepository.getAddedRawMeasures(FILE_2_REF).get(metricKey)).isEmpty();
+    assertThat(measureRepository.getAddedRawMeasures(DIRECTORY_REF).get(metricKey)).isEmpty();
+    assertThat(measureRepository.getAddedRawMeasures(SUB_MODULE_1_REF).get(metricKey)).isEmpty();
+    assertThat(measureRepository.getAddedRawMeasures(MODULE_REF).get(metricKey)).isEmpty();
+    assertThat(measureRepository.getAddedRawMeasures(ROOT_REF).get(metricKey)).isEmpty();
+  }
+
+  private void assertNoRawMeasure(int componentRef, String metricKey) {
+    assertThat(measureRepository.getAddedRawMeasure(componentRef, metricKey)).isAbsent();
+  }
+
+  private void assertRawMeasureValue(int componentRef, String metricKey, int value) {
+    assertThat(measureRepository.getAddedRawMeasure(componentRef, metricKey).get().getIntValue()).isEqualTo(value);
+  }
+
+  private void assertRawMeasureValue(int componentRef, String metricKey, double value) {
+    assertThat(measureRepository.getAddedRawMeasure(componentRef, metricKey).get().getDoubleValue()).isEqualTo(value);
+  }
+
+  private void assertComputedAndAggregatedToZeroInt(String metricKey) {
+    assertRawMeasureValue(FILE_1_REF, metricKey, 0);
+    assertRawMeasureValue(FILE_2_REF, metricKey, 0);
+    assertRawMeasureValue(FILE_3_REF, metricKey, 0);
+    assertRawMeasureValue(FILE_4_REF, metricKey, 0);
+    assertRawMeasureValue(DIRECTORY_REF, metricKey, 0);
+    assertRawMeasureValue(SUB_MODULE_1_REF, metricKey, 0);
+    assertRawMeasureValue(SUB_MODULE_2_REF, metricKey, 0);
+    assertRawMeasureValue(MODULE_REF, metricKey, 0);
+    assertRawMeasureValue(ROOT_REF, metricKey, 0);
+  }
+
+}
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/duplication/ReportDuplicationMeasuresTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/duplication/ReportDuplicationMeasuresTest.java
deleted file mode 100644 (file)
index 2547908..0000000
+++ /dev/null
@@ -1,492 +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.duplication;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
-import org.sonar.ce.task.projectanalysis.measure.MeasureRepositoryRule;
-import org.sonar.ce.task.projectanalysis.metric.MetricRepositoryRule;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.guava.api.Assertions.assertThat;
-import static org.sonar.api.measures.CoreMetrics.COMMENT_LINES;
-import static org.sonar.api.measures.CoreMetrics.COMMENT_LINES_KEY;
-import static org.sonar.api.measures.CoreMetrics.DUPLICATED_BLOCKS;
-import static org.sonar.api.measures.CoreMetrics.DUPLICATED_BLOCKS_KEY;
-import static org.sonar.api.measures.CoreMetrics.DUPLICATED_FILES;
-import static org.sonar.api.measures.CoreMetrics.DUPLICATED_FILES_KEY;
-import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES;
-import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES_DENSITY;
-import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES_DENSITY_KEY;
-import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES_KEY;
-import static org.sonar.api.measures.CoreMetrics.LINES;
-import static org.sonar.api.measures.CoreMetrics.LINES_KEY;
-import static org.sonar.api.measures.CoreMetrics.NCLOC;
-import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY;
-import static org.sonar.ce.task.projectanalysis.component.Component.Type.DIRECTORY;
-import static org.sonar.ce.task.projectanalysis.component.Component.Type.FILE;
-import static org.sonar.ce.task.projectanalysis.component.Component.Type.MODULE;
-import static org.sonar.ce.task.projectanalysis.component.Component.Type.PROJECT;
-import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
-import static org.sonar.ce.task.projectanalysis.measure.Measure.newMeasureBuilder;
-
-public class ReportDuplicationMeasuresTest {
-  private static final int ROOT_REF = 1;
-  private static final int MODULE_REF = 12;
-  private static final int SUB_MODULE_1_REF = 123;
-  private static final int SUB_MODULE_2_REF = 126;
-  private static final int DIRECTORY_REF = 1234;
-  private static final int FILE_1_REF = 12341;
-  private static final int FILE_2_REF = 12342;
-  private static final int FILE_3_REF = 1261;
-  private static final int FILE_4_REF = 1262;
-  private static final String SOME_FILE_KEY = "some file key";
-
-  @Rule
-  public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule()
-    .setRoot(
-      builder(PROJECT, ROOT_REF)
-        .addChildren(
-          builder(MODULE, MODULE_REF)
-            .addChildren(
-              builder(MODULE, SUB_MODULE_1_REF)
-                .addChildren(
-                  builder(DIRECTORY, DIRECTORY_REF)
-                    .addChildren(
-                      builder(FILE, FILE_1_REF).build(),
-                      builder(FILE, FILE_2_REF).build())
-                    .build())
-                .build(),
-              builder(MODULE, SUB_MODULE_2_REF)
-                .addChildren(
-                  builder(FILE, FILE_3_REF).build(),
-                  builder(FILE, FILE_4_REF).build())
-                .build())
-            .build())
-        .build());
-  @Rule
-  public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
-    .add(LINES)
-    .add(NCLOC)
-    .add(COMMENT_LINES)
-    .add(DUPLICATED_BLOCKS)
-    .add(DUPLICATED_FILES)
-    .add(DUPLICATED_LINES)
-    .add(DUPLICATED_LINES_DENSITY);
-  @Rule
-  public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
-  @Rule
-  public DuplicationRepositoryRule duplicationRepository = DuplicationRepositoryRule.create(treeRootHolder);
-
-  private DuplicationMeasures underTest = new DuplicationMeasures(treeRootHolder, metricRepository, measureRepository, duplicationRepository);
-
-  @Test
-  public void compute_duplicated_blocks_one_for_original_one_for_each_InnerDuplicate() {
-    TextBlock original = new TextBlock(1, 1);
-    duplicationRepository.addDuplication(FILE_1_REF, original, new TextBlock(2, 2), new TextBlock(3, 3), new TextBlock(2, 3));
-
-    underTest.execute();
-
-    assertRawMeasureValue(FILE_1_REF, DUPLICATED_BLOCKS_KEY, 4);
-  }
-
-  @Test
-  public void compute_duplicated_blocks_does_not_count_blocks_only_once_it_assumes_consistency_from_duplication_data() {
-    duplicationRepository.addDuplication(FILE_1_REF, new TextBlock(1, 1), new TextBlock(3, 3));
-    duplicationRepository.addDuplication(FILE_1_REF, new TextBlock(2, 2), new TextBlock(3, 3));
-
-    underTest.execute();
-
-    assertRawMeasureValue(FILE_1_REF, DUPLICATED_BLOCKS_KEY, 4);
-  }
-
-  @Test
-  public void compute_duplicated_blocks_one_for_original_and_ignores_InProjectDuplicate() {
-    duplicationRepository.addDuplication(FILE_1_REF, new TextBlock(1, 1), FILE_2_REF, new TextBlock(2, 2));
-
-    underTest.execute();
-
-    assertRawMeasureValue(FILE_1_REF, DUPLICATED_BLOCKS_KEY, 1);
-  }
-
-  @Test
-  public void compute_duplicated_blocks_one_for_original_and_ignores_CrossProjectDuplicate() {
-    duplicationRepository.addDuplication(FILE_1_REF, new TextBlock(1, 1), SOME_FILE_KEY, new TextBlock(2, 2));
-
-    underTest.execute();
-
-    assertRawMeasureValue(FILE_1_REF, DUPLICATED_BLOCKS_KEY, 1);
-  }
-
-  @Test
-  public void compute_and_aggregate_duplicated_blocks_from_single_duplication() {
-    addDuplicatedBlock(FILE_1_REF, 10);
-    addDuplicatedBlock(FILE_2_REF, 40);
-    addDuplicatedBlock(FILE_4_REF, 5);
-
-    underTest.execute();
-
-    assertRawMeasureValue(FILE_1_REF, DUPLICATED_BLOCKS_KEY, 10);
-    assertRawMeasureValue(FILE_2_REF, DUPLICATED_BLOCKS_KEY, 40);
-    assertRawMeasureValue(FILE_3_REF, DUPLICATED_BLOCKS_KEY, 0);
-    assertRawMeasureValue(FILE_4_REF, DUPLICATED_BLOCKS_KEY, 5);
-    assertRawMeasureValue(DIRECTORY_REF, DUPLICATED_BLOCKS_KEY, 50);
-    assertRawMeasureValue(SUB_MODULE_1_REF, DUPLICATED_BLOCKS_KEY, 50);
-    assertRawMeasureValue(SUB_MODULE_2_REF, DUPLICATED_BLOCKS_KEY, 5);
-    assertRawMeasureValue(MODULE_REF, DUPLICATED_BLOCKS_KEY, 55);
-    assertRawMeasureValue(ROOT_REF, DUPLICATED_BLOCKS_KEY, 55);
-  }
-
-  @Test
-  public void compute_and_aggregate_duplicated_blocks_to_zero_when_no_duplication() {
-    underTest.execute();
-
-    assertComputedAndAggregatedToZeroInt(DUPLICATED_BLOCKS_KEY);
-  }
-
-  @Test
-  public void compute_duplicated_lines_counts_lines_from_original_and_InnerDuplicate_of_a_single_line() {
-    TextBlock original = new TextBlock(1, 1);
-    duplicationRepository.addDuplication(FILE_1_REF, original, new TextBlock(2, 2));
-
-    underTest.execute();
-
-    assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_KEY, 2);
-  }
-
-  @Test
-  public void compute_duplicated_lines_counts_lines_from_original_and_ignores_InProjectDuplicate() {
-    TextBlock original = new TextBlock(1, 1);
-    duplicationRepository.addDuplication(FILE_1_REF, original, FILE_2_REF, new TextBlock(2, 2));
-
-    underTest.execute();
-
-    assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_KEY, 1);
-  }
-
-  @Test
-  public void compute_duplicated_lines_counts_lines_from_original_and_ignores_CrossProjectDuplicate() {
-    TextBlock original = new TextBlock(1, 1);
-    duplicationRepository.addDuplication(FILE_1_REF, original, SOME_FILE_KEY, new TextBlock(2, 2));
-
-    underTest.execute();
-
-    assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_KEY, 1);
-  }
-
-  @Test
-  public void compute_duplicated_lines_counts_lines_from_original_and_InnerDuplicate() {
-    TextBlock original = new TextBlock(1, 5);
-    duplicationRepository.addDuplication(FILE_1_REF, original, new TextBlock(10, 11));
-
-    underTest.execute();
-
-    assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_KEY, 7);
-  }
-
-  @Test
-  public void compute_duplicated_lines_counts_lines_from_original_and_InnerDuplicate_only_once() {
-    TextBlock original = new TextBlock(1, 12);
-    duplicationRepository.addDuplication(FILE_1_REF, original, new TextBlock(10, 11), new TextBlock(11, 15));
-    duplicationRepository.addDuplication(FILE_1_REF, new TextBlock(2, 2), new TextBlock(96, 96));
-
-    underTest.execute();
-
-    assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_KEY, 16);
-  }
-
-  @Test
-  public void compute_and_aggregate_duplicated_files() {
-    addDuplicatedBlock(FILE_1_REF, 2);
-    addDuplicatedBlock(FILE_3_REF, 10);
-    addDuplicatedBlock(FILE_4_REF, 50);
-
-    underTest.execute();
-
-    assertRawMeasureValue(FILE_1_REF, DUPLICATED_FILES_KEY, 1);
-    assertRawMeasureValue(FILE_2_REF, DUPLICATED_FILES_KEY, 0);
-    assertRawMeasureValue(FILE_3_REF, DUPLICATED_FILES_KEY, 1);
-    assertRawMeasureValue(FILE_4_REF, DUPLICATED_FILES_KEY, 1);
-    assertRawMeasureValue(DIRECTORY_REF, DUPLICATED_FILES_KEY, 1);
-    assertRawMeasureValue(SUB_MODULE_1_REF, DUPLICATED_FILES_KEY, 1);
-    assertRawMeasureValue(SUB_MODULE_2_REF, DUPLICATED_FILES_KEY, 2);
-    assertRawMeasureValue(MODULE_REF, DUPLICATED_FILES_KEY, 3);
-    assertRawMeasureValue(ROOT_REF, DUPLICATED_FILES_KEY, 3);
-  }
-
-  @Test
-  public void compute_and_aggregate_zero_duplicated_files_when_no_duplication_data() {
-    underTest.execute();
-
-    assertComputedAndAggregatedToZeroInt(DUPLICATED_FILES_KEY);
-  }
-
-  @Test
-  public void compute_and_aggregate_duplicated_lines() {
-    addDuplicatedBlock(FILE_1_REF, 10);
-    addDuplicatedBlock(FILE_2_REF, 9);
-    addDuplicatedBlock(FILE_4_REF, 7);
-
-    underTest.execute();
-
-    assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_KEY, 10);
-    assertRawMeasureValue(FILE_2_REF, DUPLICATED_LINES_KEY, 9);
-    assertRawMeasureValue(FILE_3_REF, DUPLICATED_LINES_KEY, 0);
-    assertRawMeasureValue(FILE_4_REF, DUPLICATED_LINES_KEY, 7);
-    assertRawMeasureValue(DIRECTORY_REF, DUPLICATED_LINES_KEY, 19);
-    assertRawMeasureValue(SUB_MODULE_1_REF, DUPLICATED_LINES_KEY, 19);
-    assertRawMeasureValue(SUB_MODULE_2_REF, DUPLICATED_LINES_KEY, 7);
-    assertRawMeasureValue(MODULE_REF, DUPLICATED_LINES_KEY, 26);
-    assertRawMeasureValue(ROOT_REF, DUPLICATED_LINES_KEY, 26);
-  }
-
-  @Test
-  public void compute_and_aggregate_zero_duplicated_line_when_no_duplication() {
-    underTest.execute();
-
-    assertComputedAndAggregatedToZeroInt(DUPLICATED_LINES_KEY);
-  }
-
-  @Test
-  public void compute_and_aggregate_duplicated_lines_density_using_lines() {
-    addDuplicatedBlock(FILE_1_REF, 2);
-    addDuplicatedBlock(FILE_2_REF, 3);
-
-    addRawMeasure(FILE_1_REF, LINES_KEY, 10);
-    addRawMeasure(FILE_2_REF, LINES_KEY, 40);
-    addRawMeasure(DIRECTORY_REF, LINES_KEY, 50);
-    addRawMeasure(SUB_MODULE_1_REF, LINES_KEY, 50);
-    addRawMeasure(MODULE_REF, LINES_KEY, 50);
-    addRawMeasure(ROOT_REF, LINES_KEY, 50);
-
-    underTest.execute();
-
-    assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 20d);
-    assertRawMeasureValue(FILE_2_REF, DUPLICATED_LINES_DENSITY_KEY, 7.5d);
-    assertNoRawMeasure(FILE_3_REF, DUPLICATED_LINES_DENSITY_KEY);
-    assertNoRawMeasure(FILE_4_REF, DUPLICATED_LINES_DENSITY_KEY);
-    assertRawMeasureValue(DIRECTORY_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
-    assertRawMeasureValue(SUB_MODULE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
-    assertNoRawMeasure(SUB_MODULE_2_REF, DUPLICATED_LINES_DENSITY_KEY);
-    assertRawMeasureValue(MODULE_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
-    assertRawMeasureValue(ROOT_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
-  }
-
-  @Test
-  public void compute_and_aggregate_duplicated_lines_density_using_nclocs_and_comment_lines() {
-    addDuplicatedBlock(FILE_1_REF, 2);
-    addDuplicatedBlock(FILE_2_REF, 3);
-
-    addRawMeasure(FILE_1_REF, COMMENT_LINES_KEY, 2);
-    addRawMeasure(FILE_2_REF, COMMENT_LINES_KEY, 10);
-    addRawMeasure(DIRECTORY_REF, COMMENT_LINES_KEY, 12);
-    addRawMeasure(SUB_MODULE_1_REF, COMMENT_LINES_KEY, 12);
-    addRawMeasure(MODULE_REF, COMMENT_LINES_KEY, 12);
-    addRawMeasure(ROOT_REF, COMMENT_LINES_KEY, 12);
-
-    addRawMeasure(FILE_1_REF, NCLOC_KEY, 8);
-    addRawMeasure(FILE_2_REF, NCLOC_KEY, 30);
-    addRawMeasure(DIRECTORY_REF, NCLOC_KEY, 38);
-    addRawMeasure(SUB_MODULE_1_REF, NCLOC_KEY, 38);
-    addRawMeasure(MODULE_REF, NCLOC_KEY, 38);
-    addRawMeasure(ROOT_REF, NCLOC_KEY, 38);
-
-    underTest.execute();
-
-    assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 20d);
-    assertRawMeasureValue(FILE_2_REF, DUPLICATED_LINES_DENSITY_KEY, 7.5d);
-    assertNoRawMeasure(FILE_3_REF, DUPLICATED_LINES_DENSITY_KEY);
-    assertNoRawMeasure(FILE_4_REF, DUPLICATED_LINES_DENSITY_KEY);
-    assertRawMeasureValue(DIRECTORY_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
-    assertRawMeasureValue(SUB_MODULE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
-    assertNoRawMeasure(SUB_MODULE_2_REF, DUPLICATED_LINES_DENSITY_KEY);
-    assertRawMeasureValue(MODULE_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
-    assertRawMeasureValue(ROOT_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
-  }
-
-  @Test
-  public void compute_duplicated_lines_density_using_only_nclocs() {
-    addDuplicatedBlock(FILE_1_REF, 2);
-    addDuplicatedBlock(FILE_2_REF, 3);
-
-    addRawMeasure(FILE_1_REF, NCLOC_KEY, 10);
-    addRawMeasure(FILE_2_REF, NCLOC_KEY, 40);
-    addRawMeasure(DIRECTORY_REF, NCLOC_KEY, 50);
-    addRawMeasure(SUB_MODULE_1_REF, NCLOC_KEY, 50);
-    addRawMeasure(MODULE_REF, NCLOC_KEY, 50);
-    addRawMeasure(ROOT_REF, NCLOC_KEY, 50);
-
-    underTest.execute();
-
-    assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 20d);
-    assertRawMeasureValue(FILE_2_REF, DUPLICATED_LINES_DENSITY_KEY, 7.5d);
-    assertNoRawMeasure(FILE_3_REF, DUPLICATED_LINES_DENSITY_KEY);
-    assertNoRawMeasure(FILE_4_REF, DUPLICATED_LINES_DENSITY_KEY);
-    assertRawMeasureValue(DIRECTORY_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
-    assertRawMeasureValue(SUB_MODULE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
-    assertNoRawMeasure(SUB_MODULE_2_REF, DUPLICATED_LINES_DENSITY_KEY);
-    assertRawMeasureValue(MODULE_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
-    assertRawMeasureValue(ROOT_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
-  }
-
-  @Test
-  public void compute_zero_percent_duplicated_lines_density_when_there_is_no_duplication() {
-    addRawMeasure(FILE_1_REF, COMMENT_LINES_KEY, 2);
-    addRawMeasure(FILE_2_REF, COMMENT_LINES_KEY, 10);
-    addRawMeasure(DIRECTORY_REF, COMMENT_LINES_KEY, 12);
-    addRawMeasure(SUB_MODULE_1_REF, COMMENT_LINES_KEY, 12);
-    addRawMeasure(MODULE_REF, COMMENT_LINES_KEY, 12);
-    addRawMeasure(ROOT_REF, COMMENT_LINES_KEY, 12);
-
-    addRawMeasure(FILE_1_REF, NCLOC_KEY, 8);
-    addRawMeasure(FILE_2_REF, NCLOC_KEY, 30);
-    addRawMeasure(DIRECTORY_REF, NCLOC_KEY, 38);
-    addRawMeasure(SUB_MODULE_1_REF, NCLOC_KEY, 38);
-    addRawMeasure(MODULE_REF, NCLOC_KEY, 38);
-    addRawMeasure(ROOT_REF, NCLOC_KEY, 38);
-
-    underTest.execute();
-
-    assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 0d);
-    assertRawMeasureValue(FILE_2_REF, DUPLICATED_LINES_DENSITY_KEY, 0d);
-    assertNoRawMeasure(FILE_3_REF, DUPLICATED_LINES_DENSITY_KEY);
-    assertNoRawMeasure(FILE_4_REF, DUPLICATED_LINES_DENSITY_KEY);
-    assertRawMeasureValue(DIRECTORY_REF, DUPLICATED_LINES_DENSITY_KEY, 0d);
-    assertRawMeasureValue(SUB_MODULE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 0d);
-    assertNoRawMeasure(SUB_MODULE_2_REF, DUPLICATED_LINES_DENSITY_KEY);
-    assertRawMeasureValue(MODULE_REF, DUPLICATED_LINES_DENSITY_KEY, 0d);
-    assertRawMeasureValue(ROOT_REF, DUPLICATED_LINES_DENSITY_KEY, 0d);
-  }
-
-  @Test
-  public void not_compute_duplicated_lines_density_when_lines_is_zero() {
-    addRawMeasure(FILE_1_REF, LINES_KEY, 0);
-    addRawMeasure(FILE_2_REF, LINES_KEY, 0);
-    addRawMeasure(DIRECTORY_REF, LINES_KEY, 0);
-    addRawMeasure(SUB_MODULE_1_REF, LINES_KEY, 0);
-    addRawMeasure(MODULE_REF, LINES_KEY, 0);
-    addRawMeasure(ROOT_REF, LINES_KEY, 0);
-
-    underTest.execute();
-
-    assertNoRawMeasures(DUPLICATED_LINES_DENSITY_KEY);
-  }
-
-  @Test
-  public void not_compute_duplicated_lines_density_when_ncloc_and_comment_are_zero() {
-    addRawMeasure(FILE_1_REF, COMMENT_LINES_KEY, 0);
-    addRawMeasure(FILE_2_REF, COMMENT_LINES_KEY, 0);
-    addRawMeasure(DIRECTORY_REF, COMMENT_LINES_KEY, 0);
-    addRawMeasure(SUB_MODULE_1_REF, COMMENT_LINES_KEY, 0);
-    addRawMeasure(MODULE_REF, COMMENT_LINES_KEY, 0);
-    addRawMeasure(ROOT_REF, COMMENT_LINES_KEY, 0);
-
-    addRawMeasure(FILE_1_REF, NCLOC_KEY, 0);
-    addRawMeasure(FILE_2_REF, NCLOC_KEY, 0);
-    addRawMeasure(DIRECTORY_REF, NCLOC_KEY, 0);
-    addRawMeasure(SUB_MODULE_1_REF, NCLOC_KEY, 0);
-    addRawMeasure(MODULE_REF, NCLOC_KEY, 0);
-    addRawMeasure(ROOT_REF, NCLOC_KEY, 0);
-
-    underTest.execute();
-
-    assertNoRawMeasures(DUPLICATED_LINES_DENSITY_KEY);
-  }
-
-  @Test
-  public void compute_100_percent_duplicated_lines_density() {
-    addDuplicatedBlock(FILE_1_REF, 2);
-    addDuplicatedBlock(FILE_2_REF, 3);
-
-    addRawMeasure(FILE_1_REF, LINES_KEY, 2);
-    addRawMeasure(FILE_2_REF, LINES_KEY, 3);
-    addRawMeasure(DIRECTORY_REF, LINES_KEY, 5);
-    addRawMeasure(SUB_MODULE_1_REF, LINES_KEY, 5);
-    addRawMeasure(MODULE_REF, LINES_KEY, 5);
-    addRawMeasure(ROOT_REF, LINES_KEY, 5);
-
-    underTest.execute();
-
-    assertRawMeasureValue(FILE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 100d);
-    assertRawMeasureValue(FILE_2_REF, DUPLICATED_LINES_DENSITY_KEY, 100d);
-    assertNoRawMeasure(FILE_3_REF, DUPLICATED_LINES_DENSITY_KEY);
-    assertNoRawMeasure(FILE_4_REF, DUPLICATED_LINES_DENSITY_KEY);
-    assertRawMeasureValue(DIRECTORY_REF, DUPLICATED_LINES_DENSITY_KEY, 100d);
-    assertRawMeasureValue(SUB_MODULE_1_REF, DUPLICATED_LINES_DENSITY_KEY, 100d);
-    assertNoRawMeasure(SUB_MODULE_2_REF, DUPLICATED_LINES_DENSITY_KEY);
-    assertRawMeasureValue(MODULE_REF, DUPLICATED_LINES_DENSITY_KEY, 100d);
-    assertRawMeasureValue(ROOT_REF, DUPLICATED_LINES_DENSITY_KEY, 100d);
-  }
-
-  /**
-   * Adds duplication blocks of a single line (each line is specific to its block).
-   *
-   * This is a very simple use case, convenient for unit tests but more realistic and complex use cases must be tested separately.
-   */
-  private void addDuplicatedBlock(int fileRef, int blockCount) {
-    checkArgument(blockCount > 1, "BlockCount can not be less than 2");
-    TextBlock original = new TextBlock(1, 1);
-    TextBlock[] duplicates = new TextBlock[blockCount - 1];
-    for (int i = 10; i < blockCount + 9; i++) {
-      duplicates[i - 10] = new TextBlock(i, i);
-    }
-    duplicationRepository.addDuplication(fileRef, original, duplicates);
-  }
-
-  private void addRawMeasure(int componentRef, String metricKey, int value) {
-    measureRepository.addRawMeasure(componentRef, metricKey, newMeasureBuilder().create(value));
-  }
-
-  private void assertNoRawMeasures(String metricKey) {
-    assertThat(measureRepository.getAddedRawMeasures(FILE_1_REF).get(metricKey)).isEmpty();
-    assertThat(measureRepository.getAddedRawMeasures(FILE_2_REF).get(metricKey)).isEmpty();
-    assertThat(measureRepository.getAddedRawMeasures(DIRECTORY_REF).get(metricKey)).isEmpty();
-    assertThat(measureRepository.getAddedRawMeasures(SUB_MODULE_1_REF).get(metricKey)).isEmpty();
-    assertThat(measureRepository.getAddedRawMeasures(MODULE_REF).get(metricKey)).isEmpty();
-    assertThat(measureRepository.getAddedRawMeasures(ROOT_REF).get(metricKey)).isEmpty();
-  }
-
-  private void assertNoRawMeasure(int componentRef, String metricKey) {
-    assertThat(measureRepository.getAddedRawMeasure(componentRef, metricKey)).isAbsent();
-  }
-
-  private void assertRawMeasureValue(int componentRef, String metricKey, int value) {
-    assertThat(measureRepository.getAddedRawMeasure(componentRef, metricKey).get().getIntValue()).isEqualTo(value);
-  }
-
-  private void assertRawMeasureValue(int componentRef, String metricKey, double value) {
-    assertThat(measureRepository.getAddedRawMeasure(componentRef, metricKey).get().getDoubleValue()).isEqualTo(value);
-  }
-
-  private void assertComputedAndAggregatedToZeroInt(String metricKey) {
-    assertRawMeasureValue(FILE_1_REF, metricKey, 0);
-    assertRawMeasureValue(FILE_2_REF, metricKey, 0);
-    assertRawMeasureValue(FILE_3_REF, metricKey, 0);
-    assertRawMeasureValue(FILE_4_REF, metricKey, 0);
-    assertRawMeasureValue(DIRECTORY_REF, metricKey, 0);
-    assertRawMeasureValue(SUB_MODULE_1_REF, metricKey, 0);
-    assertRawMeasureValue(SUB_MODULE_2_REF, metricKey, 0);
-    assertRawMeasureValue(MODULE_REF, metricKey, 0);
-    assertRawMeasureValue(ROOT_REF, metricKey, 0);
-  }
-
-}
index 1e8b50872d492763c6209abb77ec392823a8cf18..1270548b5df1bff49a35ad22f70bc2e96f530674 100644 (file)
@@ -207,63 +207,13 @@ public class ViewsDuplicationMeasuresTest {
     addRawMeasure(PROJECT_VIEW_1_REF, LINES_KEY, 10);
     addRawMeasure(PROJECT_VIEW_2_REF, LINES_KEY, 40);
     addRawMeasure(PROJECT_VIEW_3_REF, LINES_KEY, 70);
-    addRawMeasure(SUB_SUBVIEW_REF, LINES_KEY, 50);
-    addRawMeasure(SUBVIEW_REF, LINES_KEY, 50);
-    addRawMeasure(ROOT_REF, LINES_KEY, 50);
 
     underTest.execute();
 
     assertNoNewRawMeasuresOnProjectViews();
     assertRawMeasureValue(SUB_SUBVIEW_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
     assertRawMeasureValue(SUBVIEW_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
-    assertRawMeasureValue(ROOT_REF, DUPLICATED_LINES_DENSITY_KEY, 18d);
-  }
-
-  @Test
-  public void compute_and_aggregate_duplicated_lines_density_using_nclocs_and_comment_lines() {
-    addRawMeasure(PROJECT_VIEW_1_REF, DUPLICATED_LINES_KEY, 2);
-    addRawMeasure(PROJECT_VIEW_2_REF, DUPLICATED_LINES_KEY, 3);
-    addRawMeasure(PROJECT_VIEW_3_REF, DUPLICATED_LINES_KEY, 4);
-
-    addRawMeasure(PROJECT_VIEW_1_REF, COMMENT_LINES_KEY, 2);
-    addRawMeasure(PROJECT_VIEW_2_REF, COMMENT_LINES_KEY, 10);
-    addRawMeasure(PROJECT_VIEW_3_REF, COMMENT_LINES_KEY, 20);
-    addRawMeasure(SUB_SUBVIEW_REF, COMMENT_LINES_KEY, 12);
-    addRawMeasure(SUBVIEW_REF, COMMENT_LINES_KEY, 12);
-    addRawMeasure(ROOT_REF, COMMENT_LINES_KEY, 12);
-
-    addRawMeasure(PROJECT_VIEW_1_REF, NCLOC_KEY, 8);
-    addRawMeasure(PROJECT_VIEW_2_REF, NCLOC_KEY, 30);
-    addRawMeasure(PROJECT_VIEW_3_REF, NCLOC_KEY, 50);
-    addRawMeasure(SUB_SUBVIEW_REF, NCLOC_KEY, 38);
-    addRawMeasure(SUBVIEW_REF, NCLOC_KEY, 38);
-    addRawMeasure(ROOT_REF, NCLOC_KEY, 38);
-
-    underTest.execute();
-
-    assertNoNewRawMeasuresOnProjectViews();
-    assertRawMeasureValue(SUB_SUBVIEW_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
-    assertRawMeasureValue(SUBVIEW_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
-    assertRawMeasureValue(ROOT_REF, DUPLICATED_LINES_DENSITY_KEY, 18d);
-  }
-
-  @Test
-  public void compute_duplicated_lines_density_using_only_nclocs() {
-    addRawMeasure(PROJECT_VIEW_1_REF, DUPLICATED_LINES_KEY, 2);
-    addRawMeasure(PROJECT_VIEW_2_REF, DUPLICATED_LINES_KEY, 3);
-
-    addRawMeasure(PROJECT_VIEW_1_REF, NCLOC_KEY, 10);
-    addRawMeasure(PROJECT_VIEW_2_REF, NCLOC_KEY, 40);
-    addRawMeasure(SUB_SUBVIEW_REF, NCLOC_KEY, 50);
-    addRawMeasure(SUBVIEW_REF, NCLOC_KEY, 50);
-    addRawMeasure(ROOT_REF, NCLOC_KEY, 50);
-
-    underTest.execute();
-
-    assertNoNewRawMeasuresOnProjectViews();
-    assertRawMeasureValue(SUB_SUBVIEW_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
-    assertRawMeasureValue(SUBVIEW_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
-    assertRawMeasureValue(ROOT_REF, DUPLICATED_LINES_DENSITY_KEY, 10d);
+    assertRawMeasureValue(ROOT_REF, DUPLICATED_LINES_DENSITY_KEY, 7.5d);
   }
 
   @Test
@@ -272,17 +222,8 @@ public class ViewsDuplicationMeasuresTest {
     addRawMeasure(PROJECT_VIEW_2_REF, DUPLICATED_LINES_KEY, 0);
     // no raw measure for PROJECT_VIEW_3_REF
 
-    addRawMeasure(PROJECT_VIEW_1_REF, COMMENT_LINES_KEY, 2);
-    addRawMeasure(PROJECT_VIEW_2_REF, COMMENT_LINES_KEY, 10);
-    addRawMeasure(SUB_SUBVIEW_REF, COMMENT_LINES_KEY, 12);
-    addRawMeasure(SUBVIEW_REF, COMMENT_LINES_KEY, 12);
-    addRawMeasure(ROOT_REF, COMMENT_LINES_KEY, 12);
-
-    addRawMeasure(PROJECT_VIEW_1_REF, NCLOC_KEY, 8);
-    addRawMeasure(PROJECT_VIEW_2_REF, NCLOC_KEY, 30);
-    addRawMeasure(SUB_SUBVIEW_REF, NCLOC_KEY, 38);
-    addRawMeasure(SUBVIEW_REF, NCLOC_KEY, 38);
-    addRawMeasure(ROOT_REF, NCLOC_KEY, 38);
+    addRawMeasure(PROJECT_VIEW_1_REF, LINES_KEY, 10);
+    addRawMeasure(PROJECT_VIEW_2_REF, LINES_KEY, 40);
 
     underTest.execute();
 
index d71c604ac976f75a6be0d2af0e65576b55715cf8..cee907059194205b2bde6d51a85d418577618943 100644 (file)
@@ -92,7 +92,8 @@ public class DuplicationDataMeasuresStepTest extends BaseStepTest {
 
     assertThat(measureRepository.getAddedRawMeasure(FILE_1_REF, DUPLICATIONS_DATA_KEY)).isPresent();
     assertThat(measureRepository.getAddedRawMeasure(FILE_1_REF, DUPLICATIONS_DATA_KEY).get().getData()).isEqualTo(
-      "<duplications><g><b s=\"1\" l=\"5\" r=\"" + FILE_1_KEY + "\"/><b s=\"6\" l=\"5\" r=\"" + FILE_1_KEY + "\"/></g></duplications>");
+      "<duplications><g><b s=\"1\" l=\"5\" t=\"false\" r=\"" + FILE_1_KEY + "\"/><b s=\"6\" l=\"5\" t=\"false\" r=\""
+        + FILE_1_KEY + "\"/></g></duplications>");
     assertThat(measureRepository.getAddedRawMeasure(FILE_2_REF, DUPLICATIONS_DATA_KEY)).isAbsent();
   }
 
@@ -104,7 +105,8 @@ public class DuplicationDataMeasuresStepTest extends BaseStepTest {
 
     assertThat(measureRepository.getAddedRawMeasure(FILE_1_REF, DUPLICATIONS_DATA_KEY)).isPresent();
     assertThat(measureRepository.getAddedRawMeasure(FILE_1_REF, DUPLICATIONS_DATA_KEY).get().getData()).isEqualTo(
-      "<duplications><g><b s=\"1\" l=\"5\" r=\"" + FILE_1_KEY + "\"/><b s=\"6\" l=\"5\" r=\"" + FILE_2_KEY + "\"/></g></duplications>");
+      "<duplications><g><b s=\"1\" l=\"5\" t=\"false\" r=\"" + FILE_1_KEY + "\"/><b s=\"6\" l=\"5\" t=\"false\" r=\""
+        + FILE_2_KEY + "\"/></g></duplications>");
     assertThat(measureRepository.getAddedRawMeasure(FILE_2_REF, DUPLICATIONS_DATA_KEY)).isAbsent();
   }
 
@@ -117,7 +119,8 @@ public class DuplicationDataMeasuresStepTest extends BaseStepTest {
 
     assertThat(measureRepository.getAddedRawMeasure(FILE_1_REF, DUPLICATIONS_DATA_KEY)).isPresent();
     assertThat(measureRepository.getAddedRawMeasure(FILE_1_REF, DUPLICATIONS_DATA_KEY).get().getData()).isEqualTo(
-      "<duplications><g><b s=\"1\" l=\"5\" r=\"" + FILE_1_KEY + "\"/><b s=\"6\" l=\"5\" r=\"" + fileKeyFromOtherProject + "\"/></g></duplications>");
+      "<duplications><g><b s=\"1\" l=\"5\" t=\"false\" r=\"" + FILE_1_KEY + "\"/><b s=\"6\" l=\"5\" t=\"false\" r=\""
+        + fileKeyFromOtherProject + "\"/></g></duplications>");
     assertThat(measureRepository.getAddedRawMeasure(FILE_2_REF, DUPLICATIONS_DATA_KEY)).isAbsent();
   }
 
index cae4d04fb6e8196a7a42e8c053ea02fab1f367db..f9c63bbb61954912537577dcb990815d660943bd 100644 (file)
@@ -23,6 +23,7 @@ import java.util.Arrays;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
+import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
 import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule;
 import org.sonar.ce.task.projectanalysis.component.Component;
 import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
@@ -63,8 +64,11 @@ public class LoadDuplicationsFromReportStepTest {
   public DuplicationRepositoryRule duplicationRepository = DuplicationRepositoryRule.create(treeRootHolder);
   @Rule
   public ExpectedException expectedException = ExpectedException.none();
+  @Rule
+  public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();
 
-  private LoadDuplicationsFromReportStep underTest = new LoadDuplicationsFromReportStep(treeRootHolder, reportReader, duplicationRepository);
+  private LoadDuplicationsFromReportStep underTest = new LoadDuplicationsFromReportStep(treeRootHolder, analysisMetadataHolder,
+    reportReader, duplicationRepository);
 
   @Test
   public void verify_description() {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/Duplication.java b/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/Duplication.java
new file mode 100644 (file)
index 0000000..88c38fd
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * 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.server.duplication.ws;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.sonar.db.component.ComponentDto;
+
+public class Duplication {
+  private final ComponentDto componentDto;
+  private final String componentDbKey;
+  private final Integer from;
+  private final Integer size;
+  private final boolean removed;
+
+  static Duplication newRemovedComponent(String componentDbKey, Integer from, Integer size) {
+    return new Duplication(null, componentDbKey, from, size, true);
+  }
+
+  static Duplication newTextComponent(String componentDbKey, Integer from, Integer size) {
+    return new Duplication(null, componentDbKey, from, size, false);
+  }
+
+  static Duplication newComponent(ComponentDto componentDto, Integer from, Integer size) {
+    return new Duplication(componentDto, componentDto.getDbKey(), from, size, false);
+  }
+
+  private Duplication(@Nullable ComponentDto componentDto, String componentDbKey, Integer from, Integer size, boolean removed) {
+    this.componentDto = componentDto;
+    this.componentDbKey = componentDbKey;
+    this.from = from;
+    this.size = size;
+    this.removed = removed;
+  }
+
+  String componentDbKey() {
+    return componentDbKey;
+  }
+
+  Integer from() {
+    return from;
+  }
+
+  Integer size() {
+    return size;
+  }
+
+  public boolean removed() {
+    return removed;
+  }
+
+  /**
+   * can be null if the file wasn't found in DB. This can happen if the target was removed (cross-project duplications) or
+   * if the target refers to an unchanged file in SLBs/PRs.
+   */
+  @CheckForNull
+  public ComponentDto componentDto() {
+    return componentDto;
+  }
+}
index 8482626bb25503c2d7bd7d84e9d3335c3aa2cc37..f6cc09f3196f7926c3a3fb8c5ad419918839d6a7 100644 (file)
@@ -22,11 +22,11 @@ package org.sonar.server.duplication.ws;
 import com.google.common.annotations.VisibleForTesting;
 import java.io.Serializable;
 import java.io.StringReader;
-import java.util.Collections;
+import java.util.ArrayList;
 import java.util.Comparator;
+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 javax.xml.stream.XMLInputFactory;
@@ -40,12 +40,9 @@ import org.sonar.db.DbSession;
 import org.sonar.db.component.ComponentDao;
 import org.sonar.db.component.ComponentDto;
 
-import static com.google.common.collect.Lists.newArrayList;
-import static com.google.common.collect.Maps.newHashMap;
-
 @ServerSide
 public class DuplicationsParser {
-
+  private static final BlockComparator BLOCK_COMPARATOR = new BlockComparator();
   private final ComponentDao componentDao;
 
   public DuplicationsParser(ComponentDao componentDao) {
@@ -53,53 +50,76 @@ public class DuplicationsParser {
   }
 
   public List<Block> parse(DbSession session, ComponentDto component, @Nullable String branch, @Nullable String pullRequest, @Nullable String duplicationsData) {
-    Map<String, ComponentDto> componentsByKey = newHashMap();
-    List<Block> blocks = newArrayList();
-    if (duplicationsData != null) {
-      try {
-        SMInputFactory inputFactory = initStax();
-        SMHierarchicCursor root = inputFactory.rootElementCursor(new StringReader(duplicationsData));
-        root.advance(); // <duplications>
-        SMInputCursor cursor = root.childElementCursor("g");
-        while (cursor.getNext() != null) {
-          List<Duplication> duplications = newArrayList();
-          SMInputCursor bCursor = cursor.childElementCursor("b");
-          while (bCursor.getNext() != null) {
-            String from = bCursor.getAttrValue("s");
-            String size = bCursor.getAttrValue("l");
-            String componentKey = bCursor.getAttrValue("r");
-            if (from != null && size != null && componentKey != null) {
-              duplications.add(createDuplication(componentsByKey, branch, pullRequest, from, size, componentKey, session));
+    Map<String, ComponentDto> componentsByKey = new HashMap<>();
+    List<Block> blocks = new ArrayList<>();
+    if (duplicationsData == null) {
+      return blocks;
+    }
+
+    DuplicationComparator duplicationComparator = new DuplicationComparator(component.uuid(), component.projectUuid());
+
+    try {
+      SMInputFactory inputFactory = initStax();
+      SMHierarchicCursor root = inputFactory.rootElementCursor(new StringReader(duplicationsData));
+      root.advance(); // <duplications>
+      SMInputCursor cursor = root.childElementCursor("g");
+      while (cursor.getNext() != null) {
+        List<Duplication> duplications = new ArrayList<>();
+        SMInputCursor bCursor = cursor.childElementCursor("b");
+        while (bCursor.getNext() != null) {
+          String from = bCursor.getAttrValue("s");
+          String size = bCursor.getAttrValue("l");
+          boolean onlyText = Boolean.parseBoolean(bCursor.getAttrValue("t"));
+          String componentDbKey = bCursor.getAttrValue("r");
+          if (from != null && size != null && componentDbKey != null) {
+            if (onlyText) {
+              // flag means that the target refers to an unchanged file in SLBs/PRs that doesn't exist in DB.
+              // Display as text without a link or other details.
+              duplications.add(Duplication.newTextComponent(componentDbKey, Integer.valueOf(from), Integer.valueOf(size)));
+            } else {
+              duplications.add(createDuplication(componentsByKey, branch, pullRequest, from, size, componentDbKey, session));
             }
           }
-          Collections.sort(duplications, new DuplicationComparator(component.uuid(), component.projectUuid()));
-          blocks.add(new Block(duplications));
         }
-        Collections.sort(blocks, new BlockComparator());
-      } catch (XMLStreamException e) {
-        throw new IllegalStateException("XML is not valid", e);
+        duplications.sort(duplicationComparator);
+        blocks.add(new Block(duplications));
       }
+      blocks.sort(BLOCK_COMPARATOR);
+      return blocks;
+    } catch (XMLStreamException e) {
+      throw new IllegalStateException("XML is not valid", e);
     }
-    return blocks;
   }
 
-  private Duplication createDuplication(Map<String, ComponentDto> componentsByKey, @Nullable String branch, @Nullable String pullRequest, String from, String size,
-    String componentDbKey, DbSession session) {
+  private Duplication createDuplication(Map<String, ComponentDto> componentsByKey, @Nullable String branch, @Nullable String pullRequest, String from,
+    String size, String componentDbKey, DbSession session) {
     String componentKey = convertToKey(componentDbKey);
-    ComponentDto component = componentsByKey.get(componentKey);
-    if (component == null) {
-      Optional<ComponentDto> componentDtoOptional;
-      if (branch != null) {
-        componentDtoOptional = componentDao.selectByKeyAndBranch(session, componentKey, branch);
-      } else if (pullRequest != null) {
-        componentDtoOptional = componentDao.selectByKeyAndPullRequest(session, componentKey, pullRequest);
-      } else {
-        componentDtoOptional = componentDao.selectByKey(session, componentKey);
-      }
-      component = componentDtoOptional.isPresent() ? componentDtoOptional.get() : null;
+
+    ComponentDto component;
+    if (componentsByKey.containsKey(componentKey)) {
+      component = componentsByKey.get(componentKey);
+    } else {
+      component = loadComponent(session, componentKey, branch, pullRequest);
       componentsByKey.put(componentKey, component);
     }
-    return new Duplication(component, Integer.valueOf(from), Integer.valueOf(size));
+
+    if (component != null) {
+      return Duplication.newComponent(component, Integer.valueOf(from), Integer.valueOf(size));
+    } else {
+      //This can happen if the target was removed (cross-project duplications)
+      return Duplication.newRemovedComponent(componentKey, Integer.valueOf(from), Integer.valueOf(size));
+    }
+  }
+
+  @CheckForNull
+  private ComponentDto loadComponent(DbSession session, String componentKey, @Nullable String branch, @Nullable String pullRequest) {
+    if (branch != null) {
+      return componentDao.selectByKeyAndBranch(session, componentKey, branch).orElse(null);
+    } else if (pullRequest != null) {
+      return componentDao.selectByKeyAndPullRequest(session, componentKey, pullRequest).orElse(null);
+    } else {
+      return componentDao.selectByKey(session, componentKey).orElse(null);
+    }
   }
 
   private static String convertToKey(String dbKey) {
@@ -116,15 +136,21 @@ public class DuplicationsParser {
     return new SMInputFactory(xmlFactory);
   }
 
+  /**
+   * Sorts the duplications with the following criteria:
+   * - Duplications in the same file by starting line
+   * - Duplications in the same project
+   * - Cross project duplications
+   */
   @VisibleForTesting
   static class DuplicationComparator implements Comparator<Duplication>, Serializable {
     private static final long serialVersionUID = 1;
 
-    private final String uuid;
+    private final String fileUuid;
     private final String projectUuid;
 
-    DuplicationComparator(String uuid, String projectUuid) {
-      this.uuid = uuid;
+    DuplicationComparator(String fileUuid, String projectUuid) {
+      this.fileUuid = fileUuid;
       this.projectUuid = projectUuid;
     }
 
@@ -133,34 +159,40 @@ public class DuplicationsParser {
       if (d1 == null || d2 == null) {
         return -1;
       }
-      ComponentDto file1 = d1.file();
-      ComponentDto file2 = d2.file();
+      ComponentDto file1 = d1.componentDto();
+      ComponentDto file2 = d2.componentDto();
 
-      if (file1 == null || file2 == null) {
-        return -1;
-      }
-      if (file1.equals(d2.file())) {
+      if (file1 != null && file1.equals(file2)) {
         // if duplication on same file => order by starting line
         return d1.from().compareTo(d2.from());
       }
-      if (file1.uuid().equals(uuid)) {
+      if (sameFile(file1) && !sameFile(file2)) {
         // the current resource must be displayed first
         return -1;
       }
-      if (file2.uuid().equals(uuid)) {
+      if (sameFile(file2) && !sameFile(file1)) {
         // the current resource must be displayed first
         return 1;
       }
-      if (StringUtils.equals(file1.projectUuid(), projectUuid) && !StringUtils.equals(file2.projectUuid(), projectUuid)) {
+      if (sameProject(file1) && !sameProject(file2)) {
         // if resource is in the same project, this it must be displayed first
         return -1;
       }
-      if (StringUtils.equals(file2.projectUuid(), projectUuid) && !StringUtils.equals(file1.projectUuid(), projectUuid)) {
+      if (sameProject(file2) && !sameProject(file1)) {
         // if resource is in the same project, this it must be displayed first
         return 1;
       }
+
       return d1.from().compareTo(d2.from());
     }
+
+    private boolean sameFile(@Nullable ComponentDto otherDto) {
+      return otherDto != null && StringUtils.equals(otherDto.uuid(), fileUuid);
+    }
+
+    private boolean sameProject(@Nullable ComponentDto otherDto) {
+      return otherDto == null || StringUtils.equals(otherDto.projectUuid(), projectUuid);
+    }
   }
 
   private static class BlockComparator implements Comparator<Block>, Serializable {
@@ -180,34 +212,6 @@ public class DuplicationsParser {
     }
   }
 
-  public static class Duplication {
-    private final ComponentDto file;
-    private final Integer from;
-    private final Integer size;
-
-    Duplication(@Nullable ComponentDto file, Integer from, Integer size) {
-      this.file = file;
-      this.from = from;
-      this.size = size;
-    }
-
-    /**
-     * File can be null when duplication is linked on a removed file
-     */
-    @CheckForNull
-    ComponentDto file() {
-      return file;
-    }
-
-    Integer from() {
-      return from;
-    }
-
-    Integer size() {
-      return size;
-    }
-  }
-
   static class Block {
     private final List<Duplication> duplications;
 
index 7f792ca70d770be59ce5ffad8fad8d8b9c461f99..4212ca7be741a90a9896e2b94e975578fa1b1115 100644 (file)
 package org.sonar.server.duplication.ws;
 
 import com.google.common.annotations.VisibleForTesting;
+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.lang.StringUtils;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.component.ComponentDao;
@@ -32,62 +35,87 @@ import org.sonarqube.ws.Duplications;
 import org.sonarqube.ws.Duplications.Block;
 import org.sonarqube.ws.Duplications.ShowResponse;
 
-import static com.google.common.collect.Maps.newHashMap;
 import static org.sonar.core.util.Protobuf.setNullable;
 
-// TODO Add UT on branch
 public class ShowResponseBuilder {
 
   private final ComponentDao componentDao;
+  private final Map<String, Reference> refByComponentKey = new HashMap<>();
 
   public ShowResponseBuilder(DbClient dbClient) {
     this.componentDao = dbClient.componentDao();
   }
 
-  @VisibleForTesting
-  ShowResponseBuilder(ComponentDao componentDao) {
+  @VisibleForTesting ShowResponseBuilder(ComponentDao componentDao) {
     this.componentDao = componentDao;
   }
 
   ShowResponse build(DbSession session, List<DuplicationsParser.Block> blocks, @Nullable String branch, @Nullable String pullRequest) {
     ShowResponse.Builder response = ShowResponse.newBuilder();
-    Map<String, String> refByComponentKey = newHashMap();
     blocks.stream()
-      .map(block -> toWsDuplication(block, refByComponentKey))
+      .map(this::toWsDuplication)
       .forEach(response::addDuplications);
 
-    writeFiles(session, response, refByComponentKey, branch, pullRequest);
-
+    writeFileRefs(session, response, branch, pullRequest);
     return response.build();
   }
 
-  private static Duplications.Duplication.Builder toWsDuplication(DuplicationsParser.Block block, Map<String, String> refByComponentKey) {
+  private Duplications.Duplication.Builder toWsDuplication(DuplicationsParser.Block block) {
     Duplications.Duplication.Builder wsDuplication = Duplications.Duplication.newBuilder();
     block.getDuplications().stream()
-      .map(d -> toWsBlock(refByComponentKey, d))
+      .map(this::toWsBlock)
       .forEach(wsDuplication::addBlocks);
 
     return wsDuplication;
   }
 
-  private static Block.Builder toWsBlock(Map<String, String> refByComponentKey, DuplicationsParser.Duplication duplication) {
-    String ref = null;
-    ComponentDto componentDto = duplication.file();
-    if (componentDto != null) {
-      String componentKey = componentDto.getDbKey();
-      ref = refByComponentKey.computeIfAbsent(componentKey, k -> Integer.toString(refByComponentKey.size() + 1));
+  private Block.Builder toWsBlock(Duplication duplication) {
+    Block.Builder block = Block.newBuilder();
+
+    if (!duplication.removed()) {
+      Reference ref = refByComponentKey.computeIfAbsent(duplication.componentDbKey(), k -> new Reference(
+        Integer.toString(refByComponentKey.size() + 1),
+        duplication.componentDto(),
+        duplication.componentDbKey()));
+      block.setRef(ref.id);
     }
 
-    Block.Builder block = Block.newBuilder();
     block.setFrom(duplication.from());
     block.setSize(duplication.size());
-    setNullable(ref, block::setRef);
 
     return block;
   }
 
-  private static Duplications.File toWsFile(ComponentDto file, @Nullable ComponentDto project, @Nullable ComponentDto subProject, @Nullable String branch,
-    @Nullable String pullRequest) {
+  private void writeFileRefs(DbSession session, ShowResponse.Builder response, @Nullable String branch, @Nullable String pullRequest) {
+    Map<String, ComponentDto> projectsByUuid = new HashMap<>();
+    Map<String, ComponentDto> parentModulesByUuid = new HashMap<>();
+
+    for (Map.Entry<String, Reference> entry : refByComponentKey.entrySet()) {
+      Reference ref = entry.getValue();
+      ComponentDto file = ref.dto();
+
+      if (file != null) {
+        ComponentDto project = getProject(file.projectUuid(), projectsByUuid, session);
+        ComponentDto parentModule = getParentProject(file.getRootUuid(), parentModulesByUuid, session);
+        response.putFiles(ref.id(), toWsFile(file, project, parentModule, branch, pullRequest));
+      } else {
+        response.putFiles(ref.id(), toWsFile(ref.componentKey(), branch, pullRequest));
+      }
+    }
+  }
+
+  private static Duplications.File toWsFile(String componentKey, @Nullable String branch, @Nullable String pullRequest) {
+    Duplications.File.Builder wsFile = Duplications.File.newBuilder();
+    String keyWithoutBranch = ComponentDto.removeBranchAndPullRequestFromKey(componentKey);
+    wsFile.setKey(keyWithoutBranch);
+    wsFile.setName(StringUtils.substringAfterLast(keyWithoutBranch, ":"));
+    setNullable(branch, wsFile::setBranch);
+    setNullable(pullRequest, wsFile::setPullRequest);
+    return wsFile.build();
+  }
+
+  private static Duplications.File toWsFile(ComponentDto file, @Nullable ComponentDto project, @Nullable ComponentDto subProject,
+    @Nullable String branch, @Nullable String pullRequest) {
     Duplications.File.Builder wsFile = Duplications.File.newBuilder();
     wsFile.setKey(file.getKey());
     wsFile.setUuid(file.uuid());
@@ -110,25 +138,6 @@ public class ShowResponseBuilder {
     return wsFile.build();
   }
 
-  private void writeFiles(DbSession session, ShowResponse.Builder response, Map<String, String> refByComponentKey, @Nullable String branch, @Nullable String pullRequest) {
-    Map<String, ComponentDto> projectsByUuid = newHashMap();
-    Map<String, ComponentDto> parentModulesByUuid = newHashMap();
-    Map<String, Duplications.File> filesByRef = response.getMutableFiles();
-
-    for (Map.Entry<String, String> entry : refByComponentKey.entrySet()) {
-      String componentKey = entry.getKey();
-      String ref = entry.getValue();
-      Optional<ComponentDto> fileOptional = componentDao.selectByKey(session, componentKey);
-      if (fileOptional.isPresent()) {
-        ComponentDto file = fileOptional.get();
-
-        ComponentDto project = getProject(file.projectUuid(), projectsByUuid, session);
-        ComponentDto parentModule = getParentProject(file.getRootUuid(), parentModulesByUuid, session);
-        filesByRef.put(ref, toWsFile(file, project, parentModule, branch, pullRequest));
-      }
-    }
-  }
-
   private ComponentDto getProject(String projectUuid, Map<String, ComponentDto> projectsByUuid, DbSession session) {
     ComponentDto project = projectsByUuid.get(projectUuid);
     if (project == null) {
@@ -153,4 +162,30 @@ public class ShowResponseBuilder {
     return project;
   }
 
+  private static class Reference {
+    private final String id;
+    private final ComponentDto dto;
+    private final String componentKey;
+
+    public Reference(String id, @Nullable ComponentDto dto, String componentKey) {
+      this.id = id;
+      this.dto = dto;
+      this.componentKey = componentKey;
+    }
+
+    public String id() {
+      return id;
+    }
+
+    @CheckForNull
+    public ComponentDto dto() {
+      return dto;
+    }
+
+    public String componentKey() {
+      return componentKey;
+    }
+
+  }
+
 }
index d45ce9934fc0ee42f9db0dc1e93c5af31857d18d..bb13f39ff2dbdc972d391f4d8403f2f538fae253 100644 (file)
@@ -37,7 +37,7 @@ public class DuplicationsParserTest {
   @Rule
   public DbTester db = DbTester.create();
 
-  DuplicationsParser parser = new DuplicationsParser(db.getDbClient().componentDao());
+  private DuplicationsParser parser = new DuplicationsParser(db.getDbClient().componentDao());
 
   @Test
   public void empty_list_when_no_data() {
@@ -60,17 +60,17 @@ public class DuplicationsParserTest {
         "</duplications>", file.getDbKey(), file.getDbKey()));
     assertThat(blocks).hasSize(1);
 
-    List<DuplicationsParser.Duplication> duplications = blocks.get(0).getDuplications();
+    List<Duplication> duplications = blocks.get(0).getDuplications();
     assertThat(duplications).hasSize(2);
 
     // Smallest line comes first
-    DuplicationsParser.Duplication duplication1 = duplications.get(0);
-    assertThat(duplication1.file()).isEqualTo(file);
+    Duplication duplication1 = duplications.get(0);
+    assertThat(duplication1.componentDto()).isEqualTo(file);
     assertThat(duplication1.from()).isEqualTo(20);
     assertThat(duplication1.size()).isEqualTo(5);
 
-    DuplicationsParser.Duplication duplication2 = duplications.get(1);
-    assertThat(duplication2.file()).isEqualTo(file);
+    Duplication duplication2 = duplications.get(1);
+    assertThat(duplication2.componentDto()).isEqualTo(file);
     assertThat(duplication2.from()).isEqualTo(31);
     assertThat(duplication2.size()).isEqualTo(5);
   }
@@ -89,17 +89,17 @@ public class DuplicationsParserTest {
         "</duplications>", file2.getDbKey(), file1.getDbKey()));
     assertThat(blocks).hasSize(1);
 
-    List<DuplicationsParser.Duplication> duplications = blocks.get(0).getDuplications();
+    List<Duplication> duplications = blocks.get(0).getDuplications();
     assertThat(duplications).hasSize(2);
 
     // Current file comes first
-    DuplicationsParser.Duplication duplication1 = duplications.get(0);
-    assertThat(duplication1.file()).isEqualTo(file1);
+    Duplication duplication1 = duplications.get(0);
+    assertThat(duplication1.componentDto()).isEqualTo(file1);
     assertThat(duplication1.from()).isEqualTo(31);
     assertThat(duplication1.size()).isEqualTo(5);
 
-    DuplicationsParser.Duplication duplication2 = duplications.get(1);
-    assertThat(duplication2.file()).isEqualTo(file2);
+    Duplication duplication2 = duplications.get(1);
+    assertThat(duplication2.componentDto()).isEqualTo(file2);
     assertThat(duplication2.from()).isEqualTo(20);
     assertThat(duplication2.size()).isEqualTo(5);
   }
@@ -121,25 +121,25 @@ public class DuplicationsParserTest {
         "</duplications>", file1.getDbKey(), fileOnProject2.getDbKey(), file2.getDbKey()));
     assertThat(blocks).hasSize(1);
 
-    List<DuplicationsParser.Duplication> duplications = blocks.get(0).getDuplications();
+    List<Duplication> duplications = blocks.get(0).getDuplications();
     assertThat(duplications).hasSize(3);
 
     // Current file's project comes first
 
-    DuplicationsParser.Duplication duplication1 = duplications.get(0);
-    assertThat(duplication1.file()).isEqualTo(file1);
+    Duplication duplication1 = duplications.get(0);
+    assertThat(duplication1.componentDto()).isEqualTo(file1);
     assertThat(duplication1.from()).isEqualTo(148);
     assertThat(duplication1.size()).isEqualTo(24);
 
-    DuplicationsParser.Duplication duplication2 = duplications.get(1);
-    assertThat(duplication2.file()).isEqualTo(file2);
+    Duplication duplication2 = duplications.get(1);
+    assertThat(duplication2.componentDto()).isEqualTo(file2);
     assertThat(duplication2.from()).isEqualTo(111);
     assertThat(duplication2.size()).isEqualTo(24);
 
     // Other project comes last
 
-    DuplicationsParser.Duplication duplication3 = duplications.get(2);
-    assertThat(duplication3.file()).isEqualTo(fileOnProject2);
+    Duplication duplication3 = duplications.get(2);
+    assertThat(duplication3.componentDto()).isEqualTo(fileOnProject2);
     assertThat(duplication3.from()).isEqualTo(137);
     assertThat(duplication3.size()).isEqualTo(24);
   }
@@ -189,17 +189,17 @@ public class DuplicationsParserTest {
         "</duplications>", file.getDbKey(), "not_existing"));
     assertThat(blocks).hasSize(1);
 
-    List<DuplicationsParser.Duplication> duplications = blocks.get(0).getDuplications();
+    List<Duplication> duplications = blocks.get(0).getDuplications();
     assertThat(duplications).hasSize(2);
 
     // Duplications on removed file
-    DuplicationsParser.Duplication duplication1 = duplication(duplications, null);
-    assertThat(duplication1.file()).isNull();
+    Duplication duplication1 = duplication(duplications, null);
+    assertThat(duplication1.componentDto()).isNull();
     assertThat(duplication1.from()).isEqualTo(31);
     assertThat(duplication1.size()).isEqualTo(5);
 
-    DuplicationsParser.Duplication duplication2 = duplication(duplications, file.getDbKey());
-    assertThat(duplication2.file()).isEqualTo(file);
+    Duplication duplication2 = duplication(duplications, file.getDbKey());
+    assertThat(duplication2.componentDto()).isEqualTo(file);
     assertThat(duplication2.from()).isEqualTo(20);
     assertThat(duplication2.size()).isEqualTo(5);
   }
@@ -215,26 +215,33 @@ public class DuplicationsParserTest {
     DuplicationsParser.DuplicationComparator comparator = new DuplicationsParser.DuplicationComparator(currentFile.uuid(), currentFile.projectUuid());
 
     // On same file
-    assertThat(comparator.compare(new DuplicationsParser.Duplication(currentFile, 2, 2), new DuplicationsParser.Duplication(currentFile, 5, 2))).isEqualTo(-1);
+    assertThat(comparator.compare(Duplication.newComponent(currentFile, 2, 2),
+      Duplication.newComponent(currentFile, 5, 2))).isEqualTo(-1);
     // Different files on same project
-    assertThat(comparator.compare(new DuplicationsParser.Duplication(currentFile, 2, 2), new DuplicationsParser.Duplication(fileOnSameProject, 5, 2))).isEqualTo(-1);
-    assertThat(comparator.compare(new DuplicationsParser.Duplication(fileOnSameProject, 2, 2), new DuplicationsParser.Duplication(currentFile, 5, 2))).isEqualTo(1);
+    assertThat(comparator.compare(Duplication.newComponent(currentFile, 2, 2),
+      Duplication.newComponent(fileOnSameProject, 5, 2))).isEqualTo(-1);
+    assertThat(comparator.compare(Duplication.newComponent(fileOnSameProject, 2, 2),
+      Duplication.newComponent(currentFile, 5, 2))).isEqualTo(1);
     // Different files on different projects
-    assertThat(comparator.compare(new DuplicationsParser.Duplication(fileOnSameProject, 5, 2), new DuplicationsParser.Duplication(fileOnDifferentProject, 2, 2))).isEqualTo(-1);
-    assertThat(comparator.compare(new DuplicationsParser.Duplication(fileOnDifferentProject, 5, 2), new DuplicationsParser.Duplication(fileOnSameProject, 2, 2))).isEqualTo(1);
+    assertThat(comparator.compare(Duplication.newComponent(fileOnSameProject, 5, 2),
+      Duplication.newComponent(fileOnDifferentProject, 2, 2))).isEqualTo(-1);
+    assertThat(comparator.compare(Duplication.newComponent(fileOnDifferentProject, 5, 2),
+      Duplication.newComponent(fileOnSameProject, 2, 2))).isEqualTo(1);
     // Files on 2 different projects
     ComponentDto project3 = db.components().insertPrivateProject();
-    assertThat(comparator.compare(new DuplicationsParser.Duplication(fileOnDifferentProject, 5, 2),
-      new DuplicationsParser.Duplication(project3, 2, 2))).isEqualTo(1);
+    assertThat(comparator.compare(Duplication.newComponent(fileOnDifferentProject, 5, 2),
+      Duplication.newComponent(project3, 2, 2))).isEqualTo(1);
 
     // With null duplications
-    assertThat(comparator.compare(null, new DuplicationsParser.Duplication(fileOnSameProject, 2, 2))).isEqualTo(-1);
-    assertThat(comparator.compare(new DuplicationsParser.Duplication(fileOnSameProject, 2, 2), null)).isEqualTo(-1);
+    assertThat(comparator.compare(null, Duplication.newComponent(fileOnSameProject, 2, 2))).isEqualTo(-1);
+    assertThat(comparator.compare(Duplication.newComponent(fileOnSameProject, 2, 2), null)).isEqualTo(-1);
     assertThat(comparator.compare(null, null)).isEqualTo(-1);
 
     // On some removed file
-    assertThat(comparator.compare(new DuplicationsParser.Duplication(currentFile, 2, 2), new DuplicationsParser.Duplication(null, 5, 2))).isEqualTo(-1);
-    assertThat(comparator.compare(new DuplicationsParser.Duplication(null, 2, 2), new DuplicationsParser.Duplication(currentFile, 5, 2))).isEqualTo(-1);
+    assertThat(comparator.compare(Duplication.newComponent(currentFile, 2, 2),
+      Duplication.newRemovedComponent("key1", 5, 2))).isEqualTo(-1);
+    assertThat(comparator.compare(Duplication.newRemovedComponent("key2", 2, 2),
+      Duplication.newComponent(currentFile, 5, 2))).isEqualTo(1);
   }
 
   @Test
@@ -252,19 +259,19 @@ public class DuplicationsParserTest {
         "</duplications>", file2.getDbKey(), file1.getDbKey()));
     assertThat(blocks).hasSize(1);
 
-    List<DuplicationsParser.Duplication> duplications = blocks.get(0).getDuplications();
+    List<Duplication> duplications = blocks.get(0).getDuplications();
     assertThat(duplications).hasSize(2);
 
     // Current file comes first
-    DuplicationsParser.Duplication duplication1 = duplications.get(0);
-    assertThat(duplication1.file()).isEqualTo(file1);
-    assertThat(duplication1.file().getKey()).isEqualTo(file1.getKey());
+    Duplication duplication1 = duplications.get(0);
+    assertThat(duplication1.componentDto()).isEqualTo(file1);
+    assertThat(duplication1.componentDto().getKey()).isEqualTo(file1.getKey());
     assertThat(duplication1.from()).isEqualTo(31);
     assertThat(duplication1.size()).isEqualTo(5);
 
-    DuplicationsParser.Duplication duplication2 = duplications.get(1);
-    assertThat(duplication2.file()).isEqualTo(file2);
-    assertThat(duplication2.file().getKey()).isEqualTo(file2.getKey());
+    Duplication duplication2 = duplications.get(1);
+    assertThat(duplication2.componentDto()).isEqualTo(file2);
+    assertThat(duplication2.componentDto().getKey()).isEqualTo(file2.getKey());
     assertThat(duplication2.from()).isEqualTo(20);
     assertThat(duplication2.size()).isEqualTo(5);
   }
@@ -284,26 +291,26 @@ public class DuplicationsParserTest {
         "</duplications>", file2.getDbKey(), file1.getDbKey()));
     assertThat(blocks).hasSize(1);
 
-    List<DuplicationsParser.Duplication> duplications = blocks.get(0).getDuplications();
+    List<Duplication> duplications = blocks.get(0).getDuplications();
     assertThat(duplications).hasSize(2);
 
     // Current file comes first
-    DuplicationsParser.Duplication duplication1 = duplications.get(0);
-    assertThat(duplication1.file()).isEqualTo(file1);
-    assertThat(duplication1.file().getKey()).isEqualTo(file1.getKey());
+    Duplication duplication1 = duplications.get(0);
+    assertThat(duplication1.componentDto()).isEqualTo(file1);
+    assertThat(duplication1.componentDto().getKey()).isEqualTo(file1.getKey());
     assertThat(duplication1.from()).isEqualTo(31);
     assertThat(duplication1.size()).isEqualTo(5);
 
-    DuplicationsParser.Duplication duplication2 = duplications.get(1);
-    assertThat(duplication2.file()).isEqualTo(file2);
-    assertThat(duplication2.file().getKey()).isEqualTo(file2.getKey());
+    Duplication duplication2 = duplications.get(1);
+    assertThat(duplication2.componentDto()).isEqualTo(file2);
+    assertThat(duplication2.componentDto().getKey()).isEqualTo(file2.getKey());
     assertThat(duplication2.from()).isEqualTo(20);
     assertThat(duplication2.size()).isEqualTo(5);
   }
 
-  private static DuplicationsParser.Duplication duplication(List<DuplicationsParser.Duplication> duplications, @Nullable final String componentKey) {
-    return Iterables.find(duplications, input -> input != null && (componentKey == null ? input.file() == null
-      : input.file() != null && componentKey.equals(input.file().getDbKey())));
+  private static Duplication duplication(List<Duplication> duplications, @Nullable final String componentKey) {
+    return Iterables.find(duplications, input -> input != null && (componentKey == null ? input.componentDto() == null
+      : input.componentDto() != null && componentKey.equals(input.componentDto().getDbKey())));
   }
 
 }
index 4042c185b339b69d03ac4947ab8c107e0208be8e..082652ad33bfa4355fbf71058de00b9918bcca92 100644 (file)
@@ -51,8 +51,8 @@ public class ShowResponseBuilderTest {
     ComponentDto file2 = db.components().insertComponent(newFileDto(module));
     List<DuplicationsParser.Block> blocks = newArrayList();
     blocks.add(new DuplicationsParser.Block(newArrayList(
-      new DuplicationsParser.Duplication(file1, 57, 12),
-      new DuplicationsParser.Duplication(file2, 73, 12))));
+      Duplication.newComponent(file1, 57, 12),
+      Duplication.newComponent(file2, 73, 12))));
 
     test(blocks, null, null,
       "{\n" +
@@ -96,8 +96,8 @@ public class ShowResponseBuilderTest {
     ComponentDto file2 = db.components().insertComponent(newFileDto(project));
     List<DuplicationsParser.Block> blocks = newArrayList();
     blocks.add(new DuplicationsParser.Block(newArrayList(
-      new DuplicationsParser.Duplication(file1, 57, 12),
-      new DuplicationsParser.Duplication(file2, 73, 12))));
+      Duplication.newComponent(file1, 57, 12),
+      Duplication.newComponent(file2, 73, 12))));
 
     test(blocks, null, null,
       "{\n" +
@@ -136,9 +136,9 @@ public class ShowResponseBuilderTest {
     ComponentDto file = db.components().insertComponent(newFileDto(project));
     List<DuplicationsParser.Block> blocks = newArrayList();
     blocks.add(new DuplicationsParser.Block(newArrayList(
-      new DuplicationsParser.Duplication(file, 57, 12),
+      Duplication.newComponent(file, 57, 12),
       // Duplication on a removed file
-      new DuplicationsParser.Duplication(null, 73, 12))));
+      Duplication.newRemovedComponent("key", 73, 12))));
 
     test(blocks, null, null,
       "{\n" +
@@ -165,6 +165,45 @@ public class ShowResponseBuilderTest {
         "}");
   }
 
+  @Test
+  public void write_duplications_with_a_component_without_details() {
+    ComponentDto project = db.components().insertPrivateProject();
+    ComponentDto file = db.components().insertComponent(newFileDto(project));
+    List<DuplicationsParser.Block> blocks = newArrayList();
+    blocks.add(new DuplicationsParser.Block(newArrayList(
+      Duplication.newComponent(file, 57, 12),
+      // Duplication on a file without details
+      Duplication.newTextComponent("project:path/to/file", 73, 12))));
+
+    test(blocks, null, null,
+      "{\n" +
+        "  \"duplications\": [\n" +
+        "    {\n" +
+        "      \"blocks\": [\n" +
+        "        {\n" +
+        "          \"from\": 57, \"size\": 12, \"_ref\": \"1\"\n" +
+        "        },\n" +
+        "        {\n" +
+        "          \"from\": 73, \"size\": 12\n" +
+        "        }\n" +
+        "      ]\n" +
+        "    }," +
+        "  ],\n" +
+        "  \"files\": {\n" +
+        "    \"1\": {\n" +
+        "      \"key\": \"" + file.getKey() + "\",\n" +
+        "      \"name\": \"" + file.longName() + "\",\n" +
+        "      \"project\": \"" + project.getKey() + "\",\n" +
+        "      \"projectName\": \"" + project.longName() + "\",\n" +
+        "    }\n" +
+        "    \"2\": {\n" +
+        "      \"key\": \"project:path/to/file\",\n" +
+        "      \"name\": \"path/to/file\",\n" +
+        "    }\n" +
+        "  }" +
+        "}");
+  }
+
   @Test
   public void write_duplications_on_branch() {
     ComponentDto project = db.components().insertMainBranch();
@@ -173,8 +212,8 @@ public class ShowResponseBuilderTest {
     ComponentDto file2 = db.components().insertComponent(newFileDto(branch));
     List<DuplicationsParser.Block> blocks = newArrayList();
     blocks.add(new DuplicationsParser.Block(newArrayList(
-      new DuplicationsParser.Duplication(file1, 57, 12),
-      new DuplicationsParser.Duplication(file2, 73, 12))));
+      Duplication.newComponent(file1, 57, 12),
+      Duplication.newComponent(file2, 73, 12))));
 
     test(blocks, branch.getBranch(), null,
       "{\n" +
@@ -217,8 +256,8 @@ public class ShowResponseBuilderTest {
     ComponentDto file2 = db.components().insertComponent(newFileDto(pullRequest));
     List<DuplicationsParser.Block> blocks = newArrayList();
     blocks.add(new DuplicationsParser.Block(newArrayList(
-      new DuplicationsParser.Duplication(file1, 57, 12),
-      new DuplicationsParser.Duplication(file2, 73, 12))));
+      Duplication.newComponent(file1, 57, 12),
+      Duplication.newComponent(file2, 73, 12))));
 
     test(blocks, null, pullRequest.getPullRequest(),
       "{\n" +
@@ -255,7 +294,7 @@ public class ShowResponseBuilderTest {
 
   @Test
   public void write_nothing_when_no_data() {
-    test(Collections.emptyList(), null, null,"{\"duplications\": [], \"files\": {}}");
+    test(Collections.emptyList(), null, null, "{\"duplications\": [], \"files\": {}}");
   }
 
   private void test(List<DuplicationsParser.Block> blocks, @Nullable String branch, @Nullable String pullRequest, String expected) {
index f0b923791a0595b268bf6a8829698d7d1133bed3..89e2cd0569ca9012978e8a5cf78835e8a9762f7c 100644 (file)
@@ -34,7 +34,6 @@ import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import org.sonar.api.batch.fs.InputComponent;
-import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.internal.DefaultInputComponent;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.utils.log.Logger;
@@ -49,7 +48,6 @@ import org.sonar.scanner.protocol.output.ScannerReport;
 import org.sonar.scanner.protocol.output.ScannerReport.Duplicate;
 import org.sonar.scanner.protocol.output.ScannerReport.Duplication;
 import org.sonar.scanner.report.ReportPublisher;
-import org.sonar.scanner.scan.branch.BranchConfiguration;
 import org.sonar.scanner.scan.filesystem.InputComponentStore;
 import org.sonar.scanner.util.ProgressReport;
 
@@ -68,24 +66,22 @@ public class CpdExecutor {
   private final SonarCpdBlockIndex index;
   private final ReportPublisher publisher;
   private final InputComponentStore componentStore;
-  private final BranchConfiguration branchConfiguration;
   private final ProgressReport progressReport;
   private final CpdSettings settings;
   private final ExecutorService executorService;
   private int count = 0;
   private int total;
 
-  public CpdExecutor(CpdSettings settings, SonarCpdBlockIndex index, ReportPublisher publisher, InputComponentStore inputComponentCache, BranchConfiguration branchConfiguration) {
-    this(settings, index, publisher, inputComponentCache, branchConfiguration, Executors.newSingleThreadExecutor());
+  public CpdExecutor(CpdSettings settings, SonarCpdBlockIndex index, ReportPublisher publisher, InputComponentStore inputComponentCache) {
+    this(settings, index, publisher, inputComponentCache, Executors.newSingleThreadExecutor());
   }
 
   public CpdExecutor(CpdSettings settings, SonarCpdBlockIndex index, ReportPublisher publisher, InputComponentStore inputComponentCache,
-    BranchConfiguration branchConfiguration, ExecutorService executorService) {
+    ExecutorService executorService) {
     this.settings = settings;
     this.index = index;
     this.publisher = publisher;
     this.componentStore = inputComponentCache;
-    this.branchConfiguration = branchConfiguration;
     this.progressReport = new ProgressReport("CPD computation", TimeUnit.SECONDS.toMillis(10));
     this.executorService = executorService;
   }
@@ -102,7 +98,7 @@ public class CpdExecutor {
     while (it.hasNext()) {
       ResourceBlocks resourceBlocks = it.next();
       Optional<FileBlocks> fileBlocks = toFileBlocks(resourceBlocks.resourceId(), resourceBlocks.blocks());
-      if (!fileBlocks.isPresent() || shouldSkip(fileBlocks.get().getInputFile())) {
+      if (!fileBlocks.isPresent()) {
         continue;
       }
       components.add(fileBlocks.get());
@@ -129,10 +125,6 @@ public class CpdExecutor {
     }
   }
 
-  private boolean shouldSkip(DefaultInputFile inputFile) {
-    return branchConfiguration.isShortOrPullRequest() && inputFile.status() == InputFile.Status.SAME;
-  }
-
   private static String pluralize(int files) {
     return files == 1 ? "file" : "files";
   }
index 8acc34a63b3af3fdec3b48f053ed731c60c4e720..30b257eb2f634d572aee25d99dec6eadb133d97a 100644 (file)
@@ -36,7 +36,6 @@ import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.junit.rules.TemporaryFolder;
 import org.mockito.ArgumentMatchers;
-import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.batch.fs.internal.DefaultInputModule;
 import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
@@ -76,13 +75,11 @@ public class CpdExecutorTest {
   private ExecutorService executorService = mock(ExecutorService.class);
   private CpdSettings settings = mock(CpdSettings.class);
   private ReportPublisher publisher = mock(ReportPublisher.class);
-  private BranchConfiguration branchConfiguration = mock(BranchConfiguration.class);
   private SonarCpdBlockIndex index = new SonarCpdBlockIndex(publisher, settings);
   private ScannerReportReader reader;
   private DefaultInputFile batchComponent1;
   private DefaultInputFile batchComponent2;
   private DefaultInputFile batchComponent3;
-  private DefaultInputFile batchComponent4;
   private File baseDir;
   private InputComponentStore componentStore;
 
@@ -94,13 +91,12 @@ public class CpdExecutorTest {
 
     DefaultInputModule inputModule = TestInputFileBuilder.newDefaultInputModule("foo", baseDir);
     componentStore = new InputComponentStore(inputModule, mock(BranchConfiguration.class));
-    executor = new CpdExecutor(settings, index, publisher, componentStore, branchConfiguration, executorService);
+    executor = new CpdExecutor(settings, index, publisher, componentStore, executorService);
     reader = new ScannerReportReader(outputDir);
 
     batchComponent1 = createComponent("src/Foo.php", 5);
     batchComponent2 = createComponent("src/Foo2.php", 5);
     batchComponent3 = createComponent("src/Foo3.php", 5);
-    batchComponent4 = createComponent("src/Foo4.php", 5, f -> f.setStatus(InputFile.Status.SAME));
   }
 
   @Test
@@ -195,21 +191,6 @@ public class CpdExecutorTest {
     assertDuplication(dups[1], 15, 214, batchComponent3.batchId(), 15, 214);
   }
 
-  @Test
-  public void should_ignore_unmodified_files_in_SLB() {
-    when(branchConfiguration.isShortOrPullRequest()).thenReturn(true);
-    Block block = Block.builder()
-      .setBlockHash(new ByteArray("AAAABBBBCCCC"))
-      .setResourceId(batchComponent4.key())
-      .build();
-    index.insert(batchComponent4, Collections.singletonList(block));
-    executor.execute();
-
-    verify(executorService).shutdown();
-    verifyNoMoreInteractions(executorService);
-    readDuplications(batchComponent4, 0);
-  }
-
   @Test
   public void should_ignore_missing_component() {
     Block block = Block.builder()
index ff7a8ff862ed86e34c51043b1331fe6e81ceec5c..0a4522b29ee8ac15b5e04b11c1ba8e5fead66293 100644 (file)
@@ -37,7 +37,6 @@ import org.sonar.api.config.internal.MapSettings;
 import org.sonar.api.internal.SonarRuntimeImpl;
 import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.utils.Version;
-import org.sonar.scanner.scan.branch.BranchConfiguration;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
@@ -58,7 +57,6 @@ public class DefaultSensorContextTest {
   private SensorStorage sensorStorage;
   private AnalysisMode analysisMode;
   private SonarRuntime runtime;
-  private BranchConfiguration branchConfig;
 
   @Before
   public void prepare() throws Exception {
@@ -68,7 +66,6 @@ public class DefaultSensorContextTest {
     when(metricFinder.<Integer>findByKey(CoreMetrics.NCLOC_KEY)).thenReturn(CoreMetrics.NCLOC);
     when(metricFinder.<String>findByKey(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION_KEY)).thenReturn(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION);
     settings = new MapSettings();
-    branchConfig = mock(BranchConfiguration.class);
     sensorStorage = mock(SensorStorage.class);
     analysisMode = mock(AnalysisMode.class);
     runtime = SonarRuntimeImpl.forSonarQube(Version.parse("5.5"), SonarQubeSide.SCANNER);