]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6989 Feed the file duplication index in the batch report
authorJulien HENRY <julien.henry@sonarsource.com>
Wed, 4 Nov 2015 16:43:02 +0000 (17:43 +0100)
committerJulien HENRY <julien.henry@sonarsource.com>
Mon, 9 Nov 2015 07:41:26 +0000 (08:41 +0100)
25 files changed:
server/sonar-server/src/main/java/org/sonar/server/computation/source/DuplicationLineReader.java
server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistDuplicationsStep.java
server/sonar-server/src/test/java/org/sonar/server/computation/source/DuplicationLineReaderTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistDuplicationsStepTest.java
sonar-batch-protocol/src/main/protobuf/batch_report.proto
sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/output/BatchReportReaderTest.java
sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/output/BatchReportWriterTest.java
sonar-batch/src/main/java/org/sonar/batch/analysis/DefaultAnalysisMode.java
sonar-batch/src/main/java/org/sonar/batch/cpd/CpdComponents.java
sonar-batch/src/main/java/org/sonar/batch/cpd/DefaultCpdEngine.java
sonar-batch/src/main/java/org/sonar/batch/cpd/JavaCpdEngine.java
sonar-batch/src/main/java/org/sonar/batch/cpd/index/IndexFactory.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/cpd/index/SonarDuplicationsIndex.java
sonar-batch/src/main/java/org/sonar/batch/mediumtest/TaskResult.java
sonar-batch/src/main/java/org/sonar/batch/report/DuplicationsPublisher.java
sonar-batch/src/main/java/org/sonar/batch/report/MetadataPublisher.java
sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorStorage.java
sonar-batch/src/test/java/org/sonar/batch/cpd/CpdSensorTest.java
sonar-batch/src/test/java/org/sonar/batch/cpd/DefaultCpdEngineTest.java
sonar-batch/src/test/java/org/sonar/batch/cpd/index/IndexFactoryTest.java [deleted file]
sonar-batch/src/test/java/org/sonar/batch/mediumtest/cpd/CpdMediumTest.java
sonar-batch/src/test/java/org/sonar/batch/report/DuplicationsPublisherTest.java
sonar-batch/src/test/java/org/sonar/batch/report/MetadataPublisherTest.java
sonar-core/src/main/java/org/sonar/core/config/CorePropertyDefinitions.java
sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java

index 863bdc4c4b02b3e9c171ccc71897a0b764e3dafa..3c1773d1c8649c18601c514a0fe60599d28526a8 100644 (file)
 
 package org.sonar.server.computation.source;
 
-import org.sonar.batch.protocol.output.BatchReport;
-import org.sonar.db.protobuf.DbFileSources;
-
 import java.io.Serializable;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import org.sonar.batch.protocol.output.BatchReport;
+import org.sonar.db.protobuf.DbFileSources;
 
 import static com.google.common.collect.Lists.newArrayList;
 import static com.google.common.collect.Maps.newHashMap;
@@ -72,7 +71,7 @@ public class DuplicationLineReader implements LineReader {
   }
 
   private static boolean isDuplicationOnSameFile(BatchReport.Duplicate duplicate) {
-    return !duplicate.hasOtherFileKey() && !duplicate.hasOtherFileRef();
+    return !duplicate.hasOtherFileRef();
   }
 
   private static boolean matchLine(BatchReport.TextRange range, int line) {
index 8f0daa93d6d1a26eb043384b9e8f091d2c34d29d..1eae2e88f526ef05adb913cb8cd55bf89b404052 100644 (file)
@@ -121,18 +121,12 @@ public class PersistDuplicationsStep implements ComputationStep {
     }
 
     private void processDuplicationBlock(StringBuilder xml, BatchReport.Duplicate duplicate, String componentKey) {
-      if (duplicate.hasOtherFileKey()) {
-        // componentKey is only set for cross project duplications
-        String crossProjectComponentKey = duplicate.getOtherFileKey();
-        appendDuplication(xml, crossProjectComponentKey, duplicate);
+      if (duplicate.hasOtherFileRef()) {
+        // Duplication is on a different file
+        appendDuplication(xml, treeRootHolder.getComponentByRef(duplicate.getOtherFileRef()).getKey(), duplicate);
       } else {
-        if (duplicate.hasOtherFileRef()) {
-          // Duplication is on a different file
-          appendDuplication(xml, treeRootHolder.getComponentByRef(duplicate.getOtherFileRef()).getKey(), duplicate);
-        } else {
-          // Duplication is on a the same file
-          appendDuplication(xml, componentKey, duplicate);
-        }
+        // Duplication is on a the same file
+        appendDuplication(xml, componentKey, duplicate);
       }
     }
 
index c7bf7c127fe45ad989310f667fac8916e1536e79..44289f855ea4aa56019933aa3a8b5239b84e1a82 100644 (file)
@@ -100,34 +100,6 @@ public class DuplicationLineReaderTest {
     assertThat(line4.getDuplicationList()).isEmpty();
   }
 
-  @Test
-  public void read_duplication_with_duplicates_on_other_file_from_other_project() {
-    DuplicationLineReader reader = new DuplicationLineReader(Iterators.forArray(
-      BatchReport.Duplication.newBuilder()
-        .setOriginPosition(BatchReport.TextRange.newBuilder()
-          .setStartLine(1)
-          .setEndLine(2)
-          .build())
-        .addDuplicate(BatchReport.Duplicate.newBuilder()
-          .setOtherFileKey("other-component-key-from-another-project")
-          .setRange(BatchReport.TextRange.newBuilder()
-            .setStartLine(3)
-            .setEndLine(4)
-            .build())
-          .build())
-        .build()));
-
-    reader.read(line1);
-    reader.read(line2);
-    reader.read(line3);
-    reader.read(line4);
-
-    assertThat(line1.getDuplicationList()).containsExactly(1);
-    assertThat(line2.getDuplicationList()).containsExactly(1);
-    assertThat(line3.getDuplicationList()).isEmpty();
-    assertThat(line4.getDuplicationList()).isEmpty();
-  }
-
   @Test
   public void read_many_duplications() {
     DuplicationLineReader reader = new DuplicationLineReader(Iterators.forArray(
index 2a729e87cc81d783f5d038c246ea488dd1b43258..40611e6367a74045d3d8276ccc7ea1e91b30fab7 100644 (file)
@@ -279,35 +279,6 @@ public class PersistDuplicationsStepTest extends BaseStepTest {
     assertThat(dto.get("textValue")).isEqualTo("<duplications><g><b s=\"1\" l=\"5\" r=\"PROJECT_KEY:file\"/><b s=\"6\" l=\"5\" r=\"PROJECT_KEY:file2\"/></g></duplications>");
   }
 
-  @Test
-  public void persist_duplications_on_different_projects() {
-    saveDuplicationMetric();
-    initReportWithProjectAndFile();
-
-    BatchReport.Duplication duplication = BatchReport.Duplication.newBuilder()
-      .setOriginPosition(BatchReport.TextRange.newBuilder()
-        .setStartLine(1)
-        .setEndLine(5)
-        .build())
-      .addDuplicate(BatchReport.Duplicate.newBuilder()
-        .setOtherFileKey("PROJECT2_KEY:file2")
-        .setRange(BatchReport.TextRange.newBuilder()
-          .setStartLine(6)
-          .setEndLine(10)
-          .build())
-        .build())
-      .build();
-    reportReader.putDuplications(2, newArrayList(duplication));
-
-    underTest.execute();
-
-    assertThat(dbTester.countRowsOfTable("project_measures")).isEqualTo(1);
-
-    Map<String, Object> dto = dbTester.selectFirst("select snapshot_id as \"snapshotId\", text_value as \"textValue\" from project_measures");
-    assertThat(dto.get("snapshotId")).isEqualTo(11L);
-    assertThat(dto.get("textValue")).isEqualTo("<duplications><g><b s=\"1\" l=\"5\" r=\"PROJECT_KEY:file\"/><b s=\"6\" l=\"5\" r=\"PROJECT2_KEY:file2\"/></g></duplications>");
-  }
-
   private void initReportWithProjectAndFile() {
     Component file = ReportComponent.builder(Component.Type.FILE, 2).setUuid("BCDE").setKey("PROJECT_KEY:file").build();
     Component project = ReportComponent.builder(Component.Type.PROJECT, 1).setUuid("ABCD").setKey(PROJECT_KEY).addChildren(file).build();
index bd5b87278216f4f9921e068d7ce68cfe6ce451a1..7937353fe9fbd9e9b6c414e9018e6be05e80b8bf 100644 (file)
@@ -134,9 +134,6 @@ message Duplicate {
   // Will be null when duplicate is in the same file
   optional int32 other_file_ref = 1;
   optional TextRange range = 2;
-
-  // temporary field during development of computation stack for cross project duplications
-  optional string other_file_key = 3;
 }
 
 message Duplication {
@@ -147,12 +144,11 @@ message Duplication {
 
 // Used for cross project duplication
 message DuplicationBlock {
-  repeated int32 hash = 1;
-  optional int32 index_in_file = 2;
-  optional int32 start_line = 3;
-  optional int32 end_line = 4;
-  optional int32 start_token_index = 5;
-  optional int32 end_token_index = 6;
+  repeated int32 hash = 1 [packed = true];
+  optional int32 start_line = 2;
+  optional int32 end_line = 3;
+  optional int32 start_token_index = 4;
+  optional int32 end_token_index = 5;
 }
 
 // Lines start at 1 and line offsets start at 0
index 50dfd49d6c348ed38b6791dcef65c848006ef805..3c2c5ac5da98794b458e2f0cc6e30a0153962290 100644 (file)
@@ -154,7 +154,6 @@ public class BatchReportReaderTest {
         .setEndLine(5)
         .build())
       .addDuplicate(BatchReport.Duplicate.newBuilder()
-        .setOtherFileKey("COMPONENT_A")
         .setOtherFileRef(2)
         .setRange(BatchReport.TextRange.newBuilder()
           .setStartLine(6)
@@ -182,7 +181,6 @@ public class BatchReportReaderTest {
       .setRef(1).build());
 
     BatchReport.DuplicationBlock duplicationBlock = BatchReport.DuplicationBlock.newBuilder()
-      .setIndexInFile(1)
       .addAllHash(asList(1, 2, 3, 5, 7))
       .setStartLine(1)
       .setEndLine(2)
index eda7000899b324ff3188d4c4e816af5efd2292ff..fdd1ba2b67b9d06215bf643aa9fb93c0c9725fcd 100644 (file)
@@ -170,7 +170,6 @@ public class BatchReportWriterTest {
         .setEndLine(5)
         .build())
       .addDuplicate(BatchReport.Duplicate.newBuilder()
-        .setOtherFileKey("COMPONENT_A")
         .setOtherFileRef(2)
         .setRange(BatchReport.TextRange.newBuilder()
           .setStartLine(6)
@@ -195,7 +194,6 @@ public class BatchReportWriterTest {
     assertThat(underTest.hasComponentData(FileStructure.Domain.DUPLICATION_BLOCKS, 1)).isFalse();
 
     BatchReport.DuplicationBlock duplicationBlock = BatchReport.DuplicationBlock.newBuilder()
-      .setIndexInFile(1)
       .addAllHash(asList(1, 2, 3, 5, 7))
       .setStartLine(1)
       .setEndLine(2)
@@ -209,7 +207,6 @@ public class BatchReportWriterTest {
     assertThat(file).exists().isFile();
     try (CloseableIterator<BatchReport.DuplicationBlock> duplicationBlocks = Protobuf.readStream(file, BatchReport.DuplicationBlock.parser())) {
       BatchReport.DuplicationBlock duplicationBlockResult = duplicationBlocks.next();
-      assertThat(duplicationBlockResult.getIndexInFile()).isEqualTo(1);
       assertThat(duplicationBlockResult.getHashList()).containsOnly(1, 2, 3, 5, 7);
       assertThat(duplicationBlockResult.getStartLine()).isEqualTo(1);
       assertThat(duplicationBlockResult.getEndLine()).isEqualTo(2);
index 31784aa1e806ec3f8d993035e40fbc8b648950ab..34df18afaa84a172e5028f28f78fdeba82dee2f0 100644 (file)
  */
 package org.sonar.batch.analysis;
 
-import org.sonar.batch.bootstrap.AbstractAnalysisMode;
-
-import org.sonar.batch.mediumtest.FakePluginInstaller;
-import org.sonar.batch.bootstrap.GlobalProperties;
+import java.util.Map;
+import javax.annotation.CheckForNull;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.AnalysisMode;
-
-import java.util.Map;
+import org.sonar.batch.bootstrap.AbstractAnalysisMode;
+import org.sonar.batch.bootstrap.GlobalProperties;
+import org.sonar.batch.mediumtest.FakePluginInstaller;
 
 /**
  * @since 4.0
@@ -99,6 +98,7 @@ public class DefaultAnalysisMode extends AbstractAnalysisMode implements Analysi
     }
   }
 
+  @CheckForNull
   private static String getPropertyWithFallback(Map<String, String> props1, Map<String, String> props2, String key) {
     if (props1.containsKey(key)) {
       return props1.get(key);
index e2c5555d4e20b85183317873f0b7ef02520e83da..ffac2cba012f641e081b71d846db4372903e3aec 100644 (file)
 package org.sonar.batch.cpd;
 
 import com.google.common.collect.ImmutableList;
-
 import java.util.List;
 
-import org.sonar.batch.cpd.index.IndexFactory;
-
 public final class CpdComponents {
 
   private CpdComponents() {
@@ -34,7 +31,6 @@ public final class CpdComponents {
     return ImmutableList.of(
       CpdSensor.class,
       CpdMappings.class,
-      IndexFactory.class,
       JavaCpdEngine.class,
       DefaultCpdEngine.class);
   }
index 93fc1726b46cab5c7046e61bebb783b2a709fbdc..a63b04f00065e5393f7e5d48fe27873d71332663 100644 (file)
@@ -23,6 +23,13 @@ import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.CoreProperties;
@@ -33,24 +40,13 @@ import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.batch.sensor.SensorContext;
 import org.sonar.api.config.Settings;
-import org.sonar.api.resources.Project;
-import org.sonar.api.utils.SonarException;
-import org.sonar.batch.cpd.index.IndexFactory;
 import org.sonar.batch.cpd.index.SonarDuplicationsIndex;
+import org.sonar.batch.index.BatchComponentCache;
+import org.sonar.batch.report.ReportPublisher;
 import org.sonar.duplications.block.Block;
 import org.sonar.duplications.index.CloneGroup;
 import org.sonar.duplications.internal.pmd.TokenizerBridge;
 
-import javax.annotation.Nullable;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
 public class DefaultCpdEngine extends CpdEngine {
 
   private static final Logger LOG = LoggerFactory.getLogger(DefaultCpdEngine.class);
@@ -60,22 +56,18 @@ public class DefaultCpdEngine extends CpdEngine {
    */
   private static final int TIMEOUT = 5 * 60;
 
-  private final IndexFactory indexFactory;
   private final CpdMappings mappings;
   private final FileSystem fs;
   private final Settings settings;
-  private final Project project;
+  private final ReportPublisher publisher;
+  private final BatchComponentCache batchComponentCache;
 
-  public DefaultCpdEngine(@Nullable Project project, IndexFactory indexFactory, CpdMappings mappings, FileSystem fs, Settings settings) {
-    this.project = project;
-    this.indexFactory = indexFactory;
+  public DefaultCpdEngine(CpdMappings mappings, FileSystem fs, Settings settings, ReportPublisher publisher, BatchComponentCache batchComponentCache) {
     this.mappings = mappings;
     this.fs = fs;
     this.settings = settings;
-  }
-
-  public DefaultCpdEngine(IndexFactory indexFactory, CpdMappings mappings, FileSystem fs, Settings settings) {
-    this(null, indexFactory, mappings, fs, settings);
+    this.publisher = publisher;
+    this.batchComponentCache = batchComponentCache;
   }
 
   @Override
@@ -97,14 +89,13 @@ public class DefaultCpdEngine extends CpdEngine {
     List<InputFile> sourceFiles = Lists.newArrayList(fs.inputFiles(p.and(
       p.hasType(InputFile.Type.MAIN),
       p.hasLanguage(languageKey),
-      p.doesNotMatchPathPatterns(cpdExclusions)
-      )));
+      p.doesNotMatchPathPatterns(cpdExclusions))));
     if (sourceFiles.isEmpty()) {
       return;
     }
 
     // Create index
-    SonarDuplicationsIndex index = indexFactory.create(project, languageKey);
+    SonarDuplicationsIndex index = new SonarDuplicationsIndex(publisher, batchComponentCache, settings);
     populateIndex(languageKey, sourceFiles, mapping, index);
 
     // Detect
@@ -129,7 +120,7 @@ public class DefaultCpdEngine extends CpdEngine {
           filtered = null;
           LOG.warn("Timeout during detection of duplications for " + inputFile, e);
         } catch (InterruptedException | ExecutionException e) {
-          throw new SonarException("Fail during detection of duplication for " + inputFile, e);
+          throw new IllegalStateException("Fail during detection of duplication for " + inputFile, e);
         }
 
         JavaCpdEngine.save(context, inputFile, filtered);
@@ -144,8 +135,8 @@ public class DefaultCpdEngine extends CpdEngine {
     for (InputFile inputFile : sourceFiles) {
       LOG.debug("Populating index from {}", inputFile);
       String resourceEffectiveKey = ((DefaultInputFile) inputFile).key();
-      List<Block> blocks2 = bridge.chunk(resourceEffectiveKey, inputFile.file());
-      index.insert(inputFile, blocks2);
+      List<Block> blocks = bridge.chunk(resourceEffectiveKey, inputFile.file());
+      index.insert(inputFile, blocks);
     }
   }
 
@@ -162,7 +153,7 @@ public class DefaultCpdEngine extends CpdEngine {
   static int getDefaultBlockSize(String languageKey) {
     if ("cobol".equals(languageKey)) {
       return 30;
-    } else if ("abap".equals(languageKey) || "natur".equals(languageKey)) {
+    } else if ("abap".equals(languageKey)) {
       return 20;
     } else {
       return 10;
@@ -172,9 +163,6 @@ public class DefaultCpdEngine extends CpdEngine {
   @VisibleForTesting
   int getMinimumTokens(String languageKey) {
     int minimumTokens = settings.getInt("sonar.cpd." + languageKey + ".minimumTokens");
-    if (minimumTokens == 0) {
-      minimumTokens = settings.getInt(CoreProperties.CPD_MINIMUM_TOKENS_PROPERTY);
-    }
     if (minimumTokens == 0) {
       minimumTokens = 100;
     }
index 97059241af5d5427531472182620a743e65c221a..1adca4f11cace27e3aa4310679e5f3ce08a450f9 100644 (file)
@@ -22,6 +22,21 @@ package org.sonar.batch.cpd;
 
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import javax.annotation.Nullable;
 import org.apache.commons.io.IOUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -36,10 +51,9 @@ import org.sonar.api.batch.sensor.duplication.internal.DefaultDuplication;
 import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
 import org.sonar.api.config.Settings;
 import org.sonar.api.measures.CoreMetrics;
-import org.sonar.api.resources.Project;
-import org.sonar.api.utils.SonarException;
-import org.sonar.batch.cpd.index.IndexFactory;
 import org.sonar.batch.cpd.index.SonarDuplicationsIndex;
+import org.sonar.batch.index.BatchComponentCache;
+import org.sonar.batch.report.ReportPublisher;
 import org.sonar.duplications.block.Block;
 import org.sonar.duplications.block.BlockChunker;
 import org.sonar.duplications.detector.suffixtree.SuffixTreeCloneDetectionAlgorithm;
@@ -52,23 +66,6 @@ import org.sonar.duplications.statement.Statement;
 import org.sonar.duplications.statement.StatementChunker;
 import org.sonar.duplications.token.TokenChunker;
 
-import javax.annotation.Nullable;
-
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
 public class JavaCpdEngine extends CpdEngine {
 
   private static final Logger LOG = LoggerFactory.getLogger(JavaCpdEngine.class);
@@ -83,20 +80,16 @@ public class JavaCpdEngine extends CpdEngine {
   private static final int MAX_CLONE_GROUP_PER_FILE = 100;
   private static final int MAX_CLONE_PART_PER_GROUP = 100;
 
-  private final IndexFactory indexFactory;
   private final FileSystem fs;
   private final Settings settings;
-  private final Project project;
+  private final ReportPublisher publisher;
+  private final BatchComponentCache batchComponentCache;
 
-  public JavaCpdEngine(@Nullable Project project, IndexFactory indexFactory, FileSystem fs, Settings settings) {
-    this.project = project;
-    this.indexFactory = indexFactory;
+  public JavaCpdEngine(FileSystem fs, Settings settings, ReportPublisher publisher, BatchComponentCache batchComponentCache) {
     this.fs = fs;
     this.settings = settings;
-  }
-
-  public JavaCpdEngine(IndexFactory indexFactory, FileSystem fs, Settings settings) {
-    this(null, indexFactory, fs, settings);
+    this.publisher = publisher;
+    this.batchComponentCache = batchComponentCache;
   }
 
   @Override
@@ -112,17 +105,16 @@ public class JavaCpdEngine extends CpdEngine {
     List<InputFile> sourceFiles = Lists.newArrayList(fs.inputFiles(p.and(
       p.hasType(InputFile.Type.MAIN),
       p.hasLanguage(languageKey),
-      p.doesNotMatchPathPatterns(cpdExclusions)
-      )));
+      p.doesNotMatchPathPatterns(cpdExclusions))));
     if (sourceFiles.isEmpty()) {
       return;
     }
-    SonarDuplicationsIndex index = createIndex(project, languageKey, sourceFiles);
+    SonarDuplicationsIndex index = createIndex(sourceFiles);
     detect(index, context, sourceFiles);
   }
 
-  private SonarDuplicationsIndex createIndex(@Nullable Project project, String language, Iterable<InputFile> sourceFiles) {
-    final SonarDuplicationsIndex index = indexFactory.create(project, language);
+  private SonarDuplicationsIndex createIndex(Iterable<InputFile> sourceFiles) {
+    final SonarDuplicationsIndex index = new SonarDuplicationsIndex(publisher, batchComponentCache, settings);
 
     TokenChunker tokenChunker = JavaTokenProducer.build();
     StatementChunker statementChunker = JavaStatementBuilder.build();
@@ -139,7 +131,7 @@ public class JavaCpdEngine extends CpdEngine {
         reader = new InputStreamReader(new FileInputStream(inputFile.file()), fs.encoding());
         statements = statementChunker.chunk(tokenChunker.chunk(reader));
       } catch (FileNotFoundException e) {
-        throw new SonarException("Cannot find file " + inputFile.file(), e);
+        throw new IllegalStateException("Cannot find file " + inputFile.file(), e);
       } finally {
         IOUtils.closeQuietly(reader);
       }
@@ -166,10 +158,8 @@ public class JavaCpdEngine extends CpdEngine {
         } catch (TimeoutException e) {
           clones = null;
           LOG.warn("Timeout during detection of duplications for " + inputFile, e);
-        } catch (InterruptedException e) {
-          throw new SonarException("Fail during detection of duplication for " + inputFile, e);
-        } catch (ExecutionException e) {
-          throw new SonarException("Fail during detection of duplication for " + inputFile, e);
+        } catch (InterruptedException | ExecutionException e) {
+          throw new IllegalStateException("Fail during detection of duplication for " + inputFile, e);
         }
 
         save(context, inputFile, clones);
@@ -209,20 +199,20 @@ public class JavaCpdEngine extends CpdEngine {
       .forMetric(CoreMetrics.DUPLICATED_FILES)
       .on(inputFile)
       .withValue(1))
-      .setFromCore()
-      .save();
+        .setFromCore()
+        .save();
     ((DefaultMeasure<Integer>) context.<Integer>newMeasure()
       .forMetric(CoreMetrics.DUPLICATED_LINES)
       .on(inputFile)
       .withValue(duplicatedLines))
-      .setFromCore()
-      .save();
+        .setFromCore()
+        .save();
     ((DefaultMeasure<Integer>) context.<Integer>newMeasure()
       .forMetric(CoreMetrics.DUPLICATED_BLOCKS)
       .on(inputFile)
       .withValue(duplicatedBlocks))
-      .setFromCore()
-      .save();
+        .setFromCore()
+        .save();
   }
 
   private static void saveDuplications(org.sonar.api.batch.sensor.SensorContext context, InputFile inputFile, Iterable<CloneGroup> duplications) {
diff --git a/sonar-batch/src/main/java/org/sonar/batch/cpd/index/IndexFactory.java b/sonar-batch/src/main/java/org/sonar/batch/cpd/index/IndexFactory.java
deleted file mode 100644 (file)
index 31a4b03..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.batch.cpd.index;
-
-import org.sonar.batch.analysis.DefaultAnalysisMode;
-
-import com.google.common.annotations.VisibleForTesting;
-
-import javax.annotation.Nullable;
-
-import org.apache.commons.lang.StringUtils;
-import org.slf4j.Logger;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.batch.BatchSide;
-import org.sonar.api.config.Settings;
-import org.sonar.api.resources.Project;
-
-@BatchSide
-public class IndexFactory {
-
-  private final Settings settings;
-  private final DefaultAnalysisMode mode;
-
-  public IndexFactory(DefaultAnalysisMode mode, Settings settings) {
-    this.mode = mode;
-    this.settings = settings;
-  }
-
-  public SonarDuplicationsIndex create(@Nullable Project project, String languageKey) {
-    return new SonarDuplicationsIndex();
-  }
-
-  @VisibleForTesting
-  boolean verifyCrossProject(@Nullable Project project, Logger logger) {
-    boolean crossProject = false;
-
-    if (settings.getBoolean(CoreProperties.CPD_CROSS_PROJECT)) {
-      if (mode.isIssues()) {
-        logger.info("Cross-project analysis disabled. Not supported in issues mode.");
-      } else if (StringUtils.isNotBlank(settings.getString(CoreProperties.PROJECT_BRANCH_PROPERTY))) {
-        logger.info("Cross-project analysis disabled. Not supported on project branches.");
-      } else if (project == null) {
-        // New sensor mode
-        logger.info("Cross-project analysis disabled. Not supported in new sensor mode.");
-      } else {
-        logger.info("Cross-project analysis enabled");
-        crossProject = true;
-      }
-    } else {
-      logger.info("Cross-project analysis disabled");
-    }
-    return crossProject;
-  }
-}
index ab583d672ddf1b9efecb8b06efe5a30b16232af4..51a094a869e4efa80be5449fcd895304b21f5a27 100644 (file)
  */
 package org.sonar.batch.cpd.index;
 
+import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
 import java.util.Collection;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.config.Settings;
+import org.sonar.batch.index.BatchComponentCache;
+import org.sonar.batch.protocol.output.BatchReport;
+import org.sonar.batch.protocol.output.BatchReport.DuplicationBlock;
+import org.sonar.batch.report.ReportPublisher;
 import org.sonar.duplications.block.Block;
 import org.sonar.duplications.block.ByteArray;
 import org.sonar.duplications.index.AbstractCloneIndex;
@@ -30,13 +39,46 @@ import org.sonar.duplications.index.PackedMemoryCloneIndex;
 public class SonarDuplicationsIndex extends AbstractCloneIndex {
 
   private final CloneIndex mem = new PackedMemoryCloneIndex();
+  private final ReportPublisher publisher;
+  private final BatchComponentCache batchComponentCache;
+  private final Settings settings;
+
+  public SonarDuplicationsIndex(ReportPublisher publisher, BatchComponentCache batchComponentCache, Settings settings) {
+    this.publisher = publisher;
+    this.batchComponentCache = batchComponentCache;
+    this.settings = settings;
+  }
 
   public void insert(InputFile inputFile, Collection<Block> blocks) {
+    if (isCrossProjectDuplicationEnabled(settings)) {
+      int id = batchComponentCache.get(inputFile).batchId();
+      final BatchReport.DuplicationBlock.Builder builder = BatchReport.DuplicationBlock.newBuilder();
+      publisher.getWriter().writeDuplicationBlocks(id, Iterables.transform(blocks, new Function<Block, BatchReport.DuplicationBlock>() {
+        @Override
+        public DuplicationBlock apply(Block input) {
+          builder.clear();
+          builder.setStartLine(input.getStartLine());
+          builder.setEndLine(input.getEndLine());
+          builder.setStartTokenIndex(input.getStartUnit());
+          builder.setEndTokenIndex(input.getEndUnit());
+          for (int i : input.getBlockHash().toIntArray()) {
+            builder.addHash(i);
+          }
+          return builder.build();
+        }
+      }));
+    }
     for (Block block : blocks) {
       mem.insert(block);
     }
   }
 
+  public static boolean isCrossProjectDuplicationEnabled(Settings settings) {
+    return settings.getBoolean(CoreProperties.CPD_CROSS_PROJECT)
+      // No cross project duplication for branches
+      && StringUtils.isBlank(settings.getString(CoreProperties.PROJECT_BRANCH_PROPERTY));
+  }
+
   public Collection<Block> getByInputFile(InputFile inputFile, String resourceKey) {
     return mem.getByResourceId(resourceKey);
   }
index 04b5724b2c39d717aad86cc8ea53b81deebdf2aa..49c7f95736b8adf66e0397ee8e571207ee66fee6 100644 (file)
@@ -41,10 +41,8 @@ import org.sonar.api.batch.fs.TextPointer;
 import org.sonar.api.batch.fs.TextRange;
 import org.sonar.api.batch.fs.internal.DefaultInputDir;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.batch.sensor.duplication.Duplication;
 import org.sonar.api.batch.sensor.highlighting.TypeOfText;
 import org.sonar.api.issue.Issue;
-import org.sonar.batch.duplication.DuplicationCache;
 import org.sonar.batch.issue.IssueCache;
 import org.sonar.batch.protocol.output.BatchReport;
 import org.sonar.batch.protocol.output.BatchReport.Component;
@@ -63,7 +61,6 @@ public class TaskResult implements org.sonar.batch.mediumtest.ScanTaskObserver {
   private static final Logger LOG = LoggerFactory.getLogger(TaskResult.class);
 
   private List<Issue> issues = new ArrayList<>();
-  private Map<String, List<Duplication>> duplications = new HashMap<>();
   private Map<String, InputFile> inputFiles = new HashMap<>();
   private Map<String, Component> reportComponents = new HashMap<>();
   private Map<String, InputDir> inputDirs = new HashMap<>();
@@ -86,7 +83,6 @@ public class TaskResult implements org.sonar.batch.mediumtest.ScanTaskObserver {
 
     storeFs(container);
 
-    storeDuplication(container);
   }
 
   private void storeReportComponents(int componentRef, String parentModuleKey, @Nullable String branch) {
@@ -106,13 +102,6 @@ public class TaskResult implements org.sonar.batch.mediumtest.ScanTaskObserver {
     return reader;
   }
 
-  private void storeDuplication(ProjectScanContainer container) {
-    DuplicationCache duplicationCache = container.getComponentByType(DuplicationCache.class);
-    for (String effectiveKey : duplicationCache.componentKeys()) {
-      duplications.put(effectiveKey, Lists.<Duplication>newArrayList(duplicationCache.byComponent(effectiveKey)));
-    }
-  }
-
   private void storeFs(ProjectScanContainer container) {
     InputPathCache inputFileCache = container.getComponentByType(InputPathCache.class);
     for (InputFile inputPath : inputFileCache.allFiles()) {
@@ -127,6 +116,10 @@ public class TaskResult implements org.sonar.batch.mediumtest.ScanTaskObserver {
     return issues;
   }
 
+  public Component getReportComponent(String key) {
+    return reportComponents.get(key);
+  }
+
   public List<BatchReport.Issue> issuesFor(InputPath inputPath) {
     List<BatchReport.Issue> result = Lists.newArrayList();
     int ref = reportComponents.get(key(inputPath)).getRef();
@@ -162,10 +155,6 @@ public class TaskResult implements org.sonar.batch.mediumtest.ScanTaskObserver {
     return inputDirs.get(relativePath);
   }
 
-  public List<Duplication> duplicationsFor(InputFile inputFile) {
-    return duplications.get(((DefaultInputFile) inputFile).key());
-  }
-
   public Map<String, List<BatchReport.Measure>> allMeasures() {
     Map<String, List<BatchReport.Measure>> result = new HashMap<>();
     for (Map.Entry<String, Component> component : reportComponents.entrySet()) {
@@ -226,6 +215,32 @@ public class TaskResult implements org.sonar.batch.mediumtest.ScanTaskObserver {
     return Collections.emptyList();
   }
 
+  public List<BatchReport.Duplication> duplicationsFor(InputFile file) {
+    List<BatchReport.Duplication> result = new ArrayList<>();
+    int ref = reportComponents.get(((DefaultInputFile) file).key()).getRef();
+    try (CloseableIterator<BatchReport.Duplication> it = getReportReader().readComponentDuplications(ref)) {
+      while (it.hasNext()) {
+        result.add(it.next());
+      }
+    } catch (Exception e) {
+      throw new IllegalStateException(e);
+    }
+    return result;
+  }
+
+  public List<BatchReport.DuplicationBlock> duplicationBlocksFor(InputFile file) {
+    List<BatchReport.DuplicationBlock> result = new ArrayList<>();
+    int ref = reportComponents.get(((DefaultInputFile) file).key()).getRef();
+    try (CloseableIterator<BatchReport.DuplicationBlock> it = getReportReader().readComponentDuplicationBlocks(ref)) {
+      while (it.hasNext()) {
+        result.add(it.next());
+      }
+    } catch (Exception e) {
+      throw new IllegalStateException(e);
+    }
+    return result;
+  }
+
   @CheckForNull
   public BatchReport.Coverage coverageFor(InputFile file, int line) {
     int ref = reportComponents.get(((DefaultInputFile) file).key()).getRef();
index 6cf132a9f6ee7e955a346f1c31be5a91e773a639..068cb1c10e46674e20e8b3fd980fa85eb0c5c928 100644 (file)
@@ -81,7 +81,8 @@ public class DuplicationsPublisher implements ReportPublisherStep {
         if (sameProjectComponent != null) {
           blockBuilder.setOtherFileRef(sameProjectComponent.batchId());
         } else {
-          blockBuilder.setOtherFileKey(componentKey);
+          // Should never happens
+          throw new IllegalStateException("No cross project duplication supported on batch side: " + componentKey);
         }
       }
       dupBuilder.addDuplicate(blockBuilder
index 52e98b53d81f31d91275026fb9e65566991ada8a..9c1d2faca6d3801660b25b3918b01b032faeca91 100644 (file)
@@ -21,7 +21,9 @@ package org.sonar.batch.report;
 
 import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.config.Settings;
 import org.sonar.api.resources.Project;
+import org.sonar.batch.cpd.index.SonarDuplicationsIndex;
 import org.sonar.batch.index.BatchComponent;
 import org.sonar.batch.index.BatchComponentCache;
 import org.sonar.batch.protocol.output.BatchReport;
@@ -32,10 +34,12 @@ public class MetadataPublisher implements ReportPublisherStep {
 
   private final BatchComponentCache componentCache;
   private final ImmutableProjectReactor reactor;
+  private final Settings settings;
 
-  public MetadataPublisher(BatchComponentCache componentCache, ImmutableProjectReactor reactor) {
+  public MetadataPublisher(BatchComponentCache componentCache, ImmutableProjectReactor reactor, Settings settings) {
     this.componentCache = componentCache;
     this.reactor = reactor;
+    this.settings = settings;
   }
 
   @Override
@@ -46,6 +50,7 @@ public class MetadataPublisher implements ReportPublisherStep {
       .setAnalysisDate(((Project) rootProject.resource()).getAnalysisDate().getTime())
       // Here we want key without branch
       .setProjectKey(root.getKey())
+      .setCrossProjectDuplicationActivated(SonarDuplicationsIndex.isCrossProjectDuplicationEnabled(settings))
       .setRootComponentRef(rootProject.batchId());
     String branch = root.properties().get(CoreProperties.PROJECT_BRANCH_PROPERTY);
     if (branch != null) {
index 0c6a9d67fa23d4ef8f9d19dea358ce93d58ffb2e..bcb38b4958aa8e8cbee5d48dde1100a3c7a5b221 100644 (file)
@@ -95,18 +95,18 @@ public class DefaultSensorStorage implements SensorStorage {
   private final ModuleIssues moduleIssues;
   private final CoverageExclusions coverageExclusions;
   private final DuplicationCache duplicationCache;
-  private final BatchComponentCache resourceCache;
+  private final BatchComponentCache componentCache;
   private final ReportPublisher reportPublisher;
   private final MeasureCache measureCache;
 
   public DefaultSensorStorage(MetricFinder metricFinder, ModuleIssues moduleIssues,
     Settings settings, FileSystem fs, ActiveRules activeRules, DuplicationCache duplicationCache,
-    CoverageExclusions coverageExclusions, BatchComponentCache resourceCache, ReportPublisher reportPublisher, MeasureCache measureCache) {
+    CoverageExclusions coverageExclusions, BatchComponentCache componentCache, ReportPublisher reportPublisher, MeasureCache measureCache) {
     this.metricFinder = metricFinder;
     this.moduleIssues = moduleIssues;
     this.coverageExclusions = coverageExclusions;
     this.duplicationCache = duplicationCache;
-    this.resourceCache = resourceCache;
+    this.componentCache = componentCache;
     this.reportPublisher = reportPublisher;
     this.measureCache = measureCache;
   }
@@ -127,7 +127,7 @@ public class DefaultSensorStorage implements SensorStorage {
     setValueAccordingToMetricType(newMeasure, m, measureToSave);
     measureToSave.setFromCore(measure.isFromCore());
     InputComponent inputComponent = newMeasure.inputComponent();
-    Resource resource = resourceCache.get(inputComponent).resource();
+    Resource resource = componentCache.get(inputComponent).resource();
     if (coverageExclusions.accept(resource, measureToSave)) {
       saveMeasure(resource, measureToSave);
     }
@@ -183,7 +183,7 @@ public class DefaultSensorStorage implements SensorStorage {
   }
 
   private File getFile(InputFile file) {
-    BatchComponent r = resourceCache.get(file);
+    BatchComponent r = componentCache.get(file);
     if (r == null) {
       throw new IllegalStateException("Provided input file is not indexed");
     }
@@ -199,13 +199,13 @@ public class DefaultSensorStorage implements SensorStorage {
   public void store(DefaultHighlighting highlighting) {
     BatchReportWriter writer = reportPublisher.getWriter();
     DefaultInputFile inputFile = (DefaultInputFile) highlighting.inputFile();
-    writer.writeComponentSyntaxHighlighting(resourceCache.get(inputFile).batchId(),
+    writer.writeComponentSyntaxHighlighting(componentCache.get(inputFile).batchId(),
       Iterables.transform(highlighting.getSyntaxHighlightingRuleSet(), new BuildSyntaxHighlighting()));
   }
 
   public void store(DefaultInputFile inputFile, Map<Symbol, Set<TextRange>> referencesBySymbol) {
     BatchReportWriter writer = reportPublisher.getWriter();
-    writer.writeComponentSymbols(resourceCache.get(inputFile).batchId(),
+    writer.writeComponentSymbols(componentCache.get(inputFile).batchId(),
       Iterables.transform(referencesBySymbol.entrySet(), new Function<Map.Entry<Symbol, Set<TextRange>>, BatchReport.Symbol>() {
         private BatchReport.Symbol.Builder builder = BatchReport.Symbol.newBuilder();
         private BatchReport.TextRange.Builder rangeBuilder = BatchReport.TextRange.newBuilder();
index a99d356c87b07c500abc8c064043014f2c652bf8..5c5adb3a4c204f52dd10431545f783e8de438cf7 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.batch.cpd;
 
+import java.io.IOException;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -27,12 +28,8 @@ import org.sonar.api.batch.fs.internal.DefaultFileSystem;
 import org.sonar.api.config.PropertyDefinitions;
 import org.sonar.api.config.Settings;
 import org.sonar.api.resources.Java;
-import org.sonar.batch.cpd.index.IndexFactory;
-
-import java.io.IOException;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
 
 public class CpdSensorTest {
 
@@ -46,9 +43,8 @@ public class CpdSensorTest {
 
   @Before
   public void setUp() throws IOException {
-    IndexFactory indexFactory = mock(IndexFactory.class);
-    sonarEngine = new JavaCpdEngine(indexFactory, null, null);
-    sonarBridgeEngine = new DefaultCpdEngine(indexFactory, new CpdMappings(), null, null);
+    sonarEngine = new JavaCpdEngine(null, null, null, null);
+    sonarBridgeEngine = new DefaultCpdEngine(new CpdMappings(), null, null, null, null);
     settings = new Settings(new PropertyDefinitions(CpdComponents.class));
 
     DefaultFileSystem fs = new DefaultFileSystem(temp.newFolder().toPath());
index fb2a632e9c923ae3bb170184adef336b5dfd2315..427c62a1e4cd1c7ab46eaa762c5a67c34886425f 100644 (file)
@@ -23,7 +23,6 @@ import org.junit.Before;
 import org.junit.Test;
 import org.slf4j.Logger;
 import org.sonar.api.config.Settings;
-import org.sonar.api.resources.Project;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Matchers.anyString;
@@ -40,7 +39,7 @@ public class DefaultCpdEngineTest {
   @Before
   public void init() {
     settings = new Settings();
-    engine = new DefaultCpdEngine(null, null, null, settings);
+    engine = new DefaultCpdEngine(null, null, settings, null, null);
   }
 
   @Test
@@ -61,7 +60,6 @@ public class DefaultCpdEngineTest {
   @Test
   public void shouldReturnDefaultBlockSize() {
     assertThat(DefaultCpdEngine.getDefaultBlockSize("cobol")).isEqualTo(30);
-    assertThat(DefaultCpdEngine.getDefaultBlockSize("natur")).isEqualTo(20);
     assertThat(DefaultCpdEngine.getDefaultBlockSize("abap")).isEqualTo(20);
     assertThat(DefaultCpdEngine.getDefaultBlockSize("other")).isEqualTo(10);
   }
@@ -84,13 +82,6 @@ public class DefaultCpdEngineTest {
     assertThat(engine.getMinimumTokens("java")).isEqualTo(100);
   }
 
-  @Test
-  public void generalMinimumTokens() {
-    settings.setProperty("sonar.cpd.minimumTokens", 33);
-
-    assertThat(engine.getMinimumTokens("java")).isEqualTo(33);
-  }
-
   @Test
   public void minimumTokensByLanguage() {
     settings.setProperty("sonar.cpd.java.minimumTokens", "42");
@@ -102,7 +93,4 @@ public class DefaultCpdEngineTest {
     assertThat(engine.getMinimumTokens("php")).isEqualTo(33);
   }
 
-  private static Project newProject(String key) {
-    return new Project(key).setAnalysisType(Project.AnalysisType.DYNAMIC);
-  }
 }
diff --git a/sonar-batch/src/test/java/org/sonar/batch/cpd/index/IndexFactoryTest.java b/sonar-batch/src/test/java/org/sonar/batch/cpd/index/IndexFactoryTest.java
deleted file mode 100644 (file)
index f33f40b..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.batch.cpd.index;
-
-import org.sonar.batch.analysis.DefaultAnalysisMode;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.slf4j.Logger;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.config.Settings;
-import org.sonar.api.resources.Project;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class IndexFactoryTest {
-
-  Project project;
-  Settings settings;
-  IndexFactory factory;
-  Logger logger;
-  private DefaultAnalysisMode analysisMode;
-
-  @Before
-  public void setUp() {
-    project = new Project("foo");
-    settings = new Settings();
-    analysisMode = mock(DefaultAnalysisMode.class);
-    factory = new IndexFactory(analysisMode, settings);
-    logger = mock(Logger.class);
-  }
-
-  @Test
-  public void crossProjectEnabled() {
-    settings.setProperty(CoreProperties.CPD_CROSS_PROJECT, "true");
-    assertThat(factory.verifyCrossProject(project, logger)).isTrue();
-    verify(logger).info("Cross-project analysis enabled");
-  }
-
-  @Test
-  public void noCrossProjectWithBranch() {
-    settings.setProperty(CoreProperties.CPD_CROSS_PROJECT, "true");
-    settings.setProperty(CoreProperties.PROJECT_BRANCH_PROPERTY, "branch");
-    assertThat(factory.verifyCrossProject(project, logger)).isFalse();
-    verify(logger).info("Cross-project analysis disabled. Not supported on project branches.");
-  }
-
-  @Test
-  public void cross_project_should_be_disabled_on_issues_mode() {
-    when(analysisMode.isIssues()).thenReturn(true);
-    settings.setProperty(CoreProperties.CPD_CROSS_PROJECT, "true");
-    assertThat(factory.verifyCrossProject(project, logger)).isFalse();
-    verify(logger).info("Cross-project analysis disabled. Not supported in issues mode.");
-  }
-
-  @Test
-  public void crossProjectDisabled() {
-    settings.setProperty(CoreProperties.CPD_CROSS_PROJECT, "false");
-    assertThat(factory.verifyCrossProject(project, logger)).isFalse();
-    verify(logger).info("Cross-project analysis disabled");
-  }
-
-}
index 03b171ae31eb0a37fd7013dbc7c44677effdbbde..7a0c260e4f72b99d002fb087ad2dabdb8d124dba 100644 (file)
@@ -34,10 +34,10 @@ import org.junit.rules.ExpectedException;
 import org.junit.rules.TemporaryFolder;
 import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.batch.sensor.duplication.Duplication;
 import org.sonar.api.measures.CoreMetrics;
 import org.sonar.batch.mediumtest.BatchMediumTester;
 import org.sonar.batch.mediumtest.TaskResult;
+import org.sonar.batch.protocol.output.BatchReport.DuplicationBlock;
 import org.sonar.batch.protocol.output.BatchReport.Measure;
 import org.sonar.xoo.XooPlugin;
 
@@ -112,28 +112,70 @@ public class CpdMediumTest {
 
     InputFile inputFile1 = result.inputFile("src/sample1.xoo");
     InputFile inputFile2 = result.inputFile("src/sample2.xoo");
+
     // One clone group on each file
-    List<Duplication> duplicationGroupsFile1 = result.duplicationsFor(inputFile1);
+    List<org.sonar.batch.protocol.output.BatchReport.Duplication> duplicationGroupsFile1 = result.duplicationsFor(inputFile1);
     assertThat(duplicationGroupsFile1).hasSize(1);
 
-    Duplication cloneGroupFile1 = duplicationGroupsFile1.get(0);
-    assertThat(cloneGroupFile1.duplicates()).hasSize(1);
-    assertThat(cloneGroupFile1.originBlock().startLine()).isEqualTo(1);
-    assertThat(cloneGroupFile1.originBlock().length()).isEqualTo(17);
-    assertThat(cloneGroupFile1.originBlock().resourceKey()).isEqualTo(((DefaultInputFile) inputFile1).key());
-    assertThat(cloneGroupFile1.duplicates()).hasSize(1);
-    assertThat(cloneGroupFile1.duplicates().get(0).resourceKey()).isEqualTo(((DefaultInputFile) inputFile2).key());
+    org.sonar.batch.protocol.output.BatchReport.Duplication cloneGroupFile1 = duplicationGroupsFile1.get(0);
+    assertThat(cloneGroupFile1.getOriginPosition().getStartLine()).isEqualTo(1);
+    assertThat(cloneGroupFile1.getOriginPosition().getEndLine()).isEqualTo(17);
+    assertThat(cloneGroupFile1.getDuplicateList()).hasSize(1);
+    assertThat(cloneGroupFile1.getDuplicate(0).getOtherFileRef()).isEqualTo(result.getReportComponent(((DefaultInputFile) inputFile2).key()).getRef());
 
-    List<Duplication> duplicationGroupsFile2 = result.duplicationsFor(inputFile2);
+    List<org.sonar.batch.protocol.output.BatchReport.Duplication> duplicationGroupsFile2 = result.duplicationsFor(inputFile2);
     assertThat(duplicationGroupsFile2).hasSize(1);
 
-    Duplication cloneGroupFile2 = duplicationGroupsFile2.get(0);
-    assertThat(cloneGroupFile2.duplicates()).hasSize(1);
-    assertThat(cloneGroupFile2.originBlock().startLine()).isEqualTo(1);
-    assertThat(cloneGroupFile2.originBlock().length()).isEqualTo(17);
-    assertThat(cloneGroupFile2.originBlock().resourceKey()).isEqualTo(((DefaultInputFile) inputFile2).key());
-    assertThat(cloneGroupFile2.duplicates()).hasSize(1);
-    assertThat(cloneGroupFile2.duplicates().get(0).resourceKey()).isEqualTo(((DefaultInputFile) inputFile1).key());
+    org.sonar.batch.protocol.output.BatchReport.Duplication cloneGroupFile2 = duplicationGroupsFile2.get(0);
+    assertThat(cloneGroupFile2.getOriginPosition().getStartLine()).isEqualTo(1);
+    assertThat(cloneGroupFile2.getOriginPosition().getEndLine()).isEqualTo(17);
+    assertThat(cloneGroupFile2.getDuplicateList()).hasSize(1);
+    assertThat(cloneGroupFile2.getDuplicate(0).getOtherFileRef()).isEqualTo(result.getReportComponent(((DefaultInputFile) inputFile1).key()).getRef());
+
+    assertThat(result.duplicationBlocksFor(inputFile1)).isEmpty();
+  }
+
+  @Test
+  public void enableCrossProjectDuplication() throws IOException {
+    File srcDir = new File(baseDir, "src");
+    srcDir.mkdir();
+
+    String duplicatedStuff = "Sample xoo\ncontent\nfoo\nbar\ntoto\ntiti\nfoo";
+
+    File xooFile1 = new File(srcDir, "sample1.xoo");
+    FileUtils.write(xooFile1, duplicatedStuff);
+
+    TaskResult result = tester.newTask()
+      .properties(builder
+        .put("sonar.sources", "src")
+        .put("sonar.cpd.xoo.minimumTokens", "1")
+        .put("sonar.cpd.xoo.minimumLines", "5")
+        .put("sonar.verbose", "true")
+        .put("sonar.cpd.cross_project", "true")
+        .build())
+      .start();
+
+    InputFile inputFile1 = result.inputFile("src/sample1.xoo");
+
+    List<DuplicationBlock> duplicationBlocks = result.duplicationBlocksFor(inputFile1);
+    assertThat(duplicationBlocks).hasSize(3);
+    assertThat(duplicationBlocks.get(0).getStartLine()).isEqualTo(1);
+    assertThat(duplicationBlocks.get(0).getEndLine()).isEqualTo(5);
+    assertThat(duplicationBlocks.get(0).getStartTokenIndex()).isEqualTo(1);
+    assertThat(duplicationBlocks.get(0).getEndTokenIndex()).isEqualTo(6);
+    assertThat(duplicationBlocks.get(0).getHashList()).isNotEmpty();
+
+    assertThat(duplicationBlocks.get(1).getStartLine()).isEqualTo(2);
+    assertThat(duplicationBlocks.get(1).getEndLine()).isEqualTo(6);
+    assertThat(duplicationBlocks.get(1).getStartTokenIndex()).isEqualTo(3);
+    assertThat(duplicationBlocks.get(1).getEndTokenIndex()).isEqualTo(7);
+    assertThat(duplicationBlocks.get(0).getHashList()).isNotEmpty();
+
+    assertThat(duplicationBlocks.get(2).getStartLine()).isEqualTo(3);
+    assertThat(duplicationBlocks.get(2).getEndLine()).isEqualTo(7);
+    assertThat(duplicationBlocks.get(2).getStartTokenIndex()).isEqualTo(4);
+    assertThat(duplicationBlocks.get(2).getEndTokenIndex()).isEqualTo(8);
+    assertThat(duplicationBlocks.get(0).getHashList()).isNotEmpty();
   }
 
   // SONAR-6000
@@ -171,11 +213,11 @@ public class CpdMediumTest {
       Tuple.tuple(CoreMetrics.DUPLICATED_BLOCKS_KEY, blockCount),
       Tuple.tuple(CoreMetrics.DUPLICATED_LINES_KEY, blockCount));
 
-    List<Duplication> duplicationGroups = result.duplicationsFor(result.inputFile("src/sample.xoo"));
+    List<org.sonar.batch.protocol.output.BatchReport.Duplication> duplicationGroups = result.duplicationsFor(result.inputFile("src/sample.xoo"));
     assertThat(duplicationGroups).hasSize(1);
 
-    Duplication cloneGroup = duplicationGroups.get(0);
-    assertThat(cloneGroup.duplicates()).hasSize(100);
+    org.sonar.batch.protocol.output.BatchReport.Duplication cloneGroup = duplicationGroups.get(0);
+    assertThat(cloneGroup.getDuplicateList()).hasSize(100);
   }
 
   @Test
@@ -205,16 +247,15 @@ public class CpdMediumTest {
 
     InputFile inputFile = result.inputFile("src/sample.xoo");
     // One clone group
-    List<Duplication> duplicationGroups = result.duplicationsFor(inputFile);
+    List<org.sonar.batch.protocol.output.BatchReport.Duplication> duplicationGroups = result.duplicationsFor(inputFile);
     assertThat(duplicationGroups).hasSize(1);
 
-    Duplication cloneGroup = duplicationGroups.get(0);
-    assertThat(cloneGroup.duplicates()).hasSize(1);
-    assertThat(cloneGroup.originBlock().startLine()).isEqualTo(1);
-    assertThat(cloneGroup.originBlock().length()).isEqualTo(2);
-    assertThat(cloneGroup.duplicates()).hasSize(1);
-    assertThat(cloneGroup.duplicates().get(0).startLine()).isEqualTo(5);
-    assertThat(cloneGroup.duplicates().get(0).length()).isEqualTo(2);
+    org.sonar.batch.protocol.output.BatchReport.Duplication cloneGroup = duplicationGroups.get(0);
+    assertThat(cloneGroup.getOriginPosition().getStartLine()).isEqualTo(1);
+    assertThat(cloneGroup.getOriginPosition().getEndLine()).isEqualTo(2);
+    assertThat(cloneGroup.getDuplicateList()).hasSize(1);
+    assertThat(cloneGroup.getDuplicate(0).getRange().getStartLine()).isEqualTo(5);
+    assertThat(cloneGroup.getDuplicate(0).getRange().getEndLine()).isEqualTo(6);
   }
 
 }
index 731bad05db4cfabec75477c26e787ef729748dab..b57c18df8e2c74e48586a6225b71d10434aea840 100644 (file)
@@ -72,12 +72,9 @@ public class DuplicationsPublisherTest {
       .setOriginBlock(new Duplication.Block("foo:src/Foo.php", 1, 10))
       .isDuplicatedBy("foo:src/Foo.php", 20, 50);
     DefaultDuplication dup2 = new DefaultDuplication()
-      .setOriginBlock(new Duplication.Block("foo:src/Foo.php", 11, 10))
-      .isDuplicatedBy("another", 20, 50);
-    DefaultDuplication dup3 = new DefaultDuplication()
       .setOriginBlock(new Duplication.Block("foo:src/Foo.php", 11, 10))
       .isDuplicatedBy("foo:src/Foo2.php", 20, 50);
-    when(duplicationCache.byComponent("foo:src/Foo.php")).thenReturn(Arrays.asList(dup1, dup2, dup3));
+    when(duplicationCache.byComponent("foo:src/Foo.php")).thenReturn(Arrays.asList(dup1, dup2));
 
     File outputDir = temp.newFolder();
     BatchReportWriter writer = new BatchReportWriter(outputDir);
@@ -91,7 +88,6 @@ public class DuplicationsPublisherTest {
       org.sonar.batch.protocol.output.BatchReport.Duplication savedDup1 = componentDuplications.next();
       assertThat(savedDup1.getOriginPosition().getStartLine()).isEqualTo(1);
       assertThat(savedDup1.getOriginPosition().getEndLine()).isEqualTo(10);
-      assertThat(savedDup1.getDuplicate(0).hasOtherFileKey()).isFalse();
       assertThat(savedDup1.getDuplicate(0).hasOtherFileRef()).isFalse();
       assertThat(savedDup1.getDuplicate(0).getRange().getStartLine()).isEqualTo(20);
       assertThat(savedDup1.getDuplicate(0).getRange().getEndLine()).isEqualTo(50);
@@ -99,19 +95,10 @@ public class DuplicationsPublisherTest {
       org.sonar.batch.protocol.output.BatchReport.Duplication savedDup2 = componentDuplications.next();
       assertThat(savedDup2.getOriginPosition().getStartLine()).isEqualTo(11);
       assertThat(savedDup2.getOriginPosition().getEndLine()).isEqualTo(20);
-      assertThat(savedDup2.getDuplicate(0).getOtherFileKey()).isEqualTo("another");
-      assertThat(savedDup2.getDuplicate(0).hasOtherFileRef()).isFalse();
+      assertThat(savedDup2.getDuplicate(0).getOtherFileRef()).isEqualTo(3);
       assertThat(savedDup2.getDuplicate(0).getRange().getStartLine()).isEqualTo(20);
       assertThat(savedDup2.getDuplicate(0).getRange().getEndLine()).isEqualTo(50);
 
-      org.sonar.batch.protocol.output.BatchReport.Duplication savedDup3 = componentDuplications.next();
-      assertThat(savedDup3.getOriginPosition().getStartLine()).isEqualTo(11);
-      assertThat(savedDup3.getOriginPosition().getEndLine()).isEqualTo(20);
-      assertThat(savedDup3.getDuplicate(0).hasOtherFileKey()).isFalse();
-      assertThat(savedDup3.getDuplicate(0).getOtherFileRef()).isEqualTo(3);
-      assertThat(savedDup3.getDuplicate(0).getRange().getStartLine()).isEqualTo(20);
-      assertThat(savedDup3.getDuplicate(0).getRange().getEndLine()).isEqualTo(50);
-
       assertThat(componentDuplications.hasNext()).isFalse();
     }
 
index eab392c374fbf977f0129af70a67cc0f9ecbbfbc..eb93a5661c50d800a5a094437a112028a56b15fe 100644 (file)
@@ -27,6 +27,7 @@ import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.config.Settings;
 import org.sonar.api.resources.Project;
 import org.sonar.batch.index.BatchComponentCache;
 import org.sonar.batch.protocol.output.BatchReport;
@@ -41,10 +42,10 @@ public class MetadataPublisherTest {
   @Rule
   public TemporaryFolder temp = new TemporaryFolder();
 
-  ProjectDefinition projectDef;
-  Project project;
-
-  MetadataPublisher underTest;
+  private ProjectDefinition projectDef;
+  private Project project;
+  private MetadataPublisher underTest;
+  private Settings settings;
 
   @Before
   public void prepare() {
@@ -54,11 +55,13 @@ public class MetadataPublisherTest {
     org.sonar.api.resources.Resource sampleFile = org.sonar.api.resources.File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php");
     componentCache.add(project, null);
     componentCache.add(sampleFile, project);
-    underTest = new MetadataPublisher(componentCache, new ImmutableProjectReactor(projectDef));
+    settings = new Settings();
+    underTest = new MetadataPublisher(componentCache, new ImmutableProjectReactor(projectDef), settings);
   }
 
   @Test
   public void write_metadata() throws Exception {
+    settings.setProperty(CoreProperties.CPD_CROSS_PROJECT, "true");
     File outputDir = temp.newFolder();
     BatchReportWriter writer = new BatchReportWriter(outputDir);
 
@@ -68,10 +71,14 @@ public class MetadataPublisherTest {
     BatchReport.Metadata metadata = reader.readMetadata();
     assertThat(metadata.getAnalysisDate()).isEqualTo(1234567L);
     assertThat(metadata.getProjectKey()).isEqualTo("foo");
+    assertThat(metadata.getProjectKey()).isEqualTo("foo");
+    assertThat(metadata.getCrossProjectDuplicationActivated()).isTrue();
   }
 
   @Test
   public void write_project_branch() throws Exception {
+    settings.setProperty(CoreProperties.CPD_CROSS_PROJECT, "true");
+    settings.setProperty(CoreProperties.PROJECT_BRANCH_PROPERTY, "myBranch");
     projectDef.properties().put(CoreProperties.PROJECT_BRANCH_PROPERTY, "myBranch");
     project.setKey("foo:myBranch");
     project.setEffectiveKey("foo:myBranch");
@@ -86,6 +93,8 @@ public class MetadataPublisherTest {
     assertThat(metadata.getAnalysisDate()).isEqualTo(1234567L);
     assertThat(metadata.getProjectKey()).isEqualTo("foo");
     assertThat(metadata.getBranch()).isEqualTo("myBranch");
+    // Cross project duplication disabled on branches
+    assertThat(metadata.getCrossProjectDuplicationActivated()).isFalse();
   }
 
 }
index 554ca4b07eeb39d4d29ddb061fd0558a0790c9de..30f06ad2e7c9707896444436d33069ca7c254c46 100644 (file)
@@ -276,42 +276,31 @@ public class CorePropertyDefinitions {
         .onlyOnQualifiers(Qualifiers.PROJECT)
         .category(CoreProperties.CATEGORY_GENERAL)
         .subCategory(CoreProperties.SUBCATEGORY_DIFFERENTIAL_VIEWS)
-        .build()));
-
+        .build(),
 
       // CPD
-      // CPD properties disabled in 5.2, will be enabled back in 5.3 (see SONAR-6323)
-//      PropertyDefinition.builder(CoreProperties.CPD_CROSS_PROJECT)
-//        .defaultValue(Boolean.toString(CoreProperties.CPD_CROSS_RPOJECT_DEFAULT_VALUE))
-//        .name("Cross project duplication detection")
-//        .description("By default, SonarQube detects duplications at sub-project level. This means that a block "
-//          + "duplicated on two sub-projects of the same project won't be reported. Setting this parameter to \"true\" "
-//          + "allows to detect duplicates across sub-projects and more generally across projects. Note that activating "
-//          + "this property will slightly increase each SonarQube analysis time.")
-//        .onQualifiers(Qualifiers.PROJECT, Qualifiers.MODULE)
-//        .category(CoreProperties.CATEGORY_GENERAL)
-//        .subCategory(CoreProperties.SUBCATEGORY_DUPLICATIONS)
-//        .type(PropertyType.BOOLEAN)
-//        .build(),
-//      PropertyDefinition.builder(CoreProperties.CPD_SKIP_PROPERTY)
-//        .defaultValue(String.valueOf(false))
-//        .name("Skip")
-//        .description("Disable detection of duplications")
-//        .hidden()
-//        .category(CoreProperties.CATEGORY_GENERAL)
-//        .subCategory(CoreProperties.SUBCATEGORY_DUPLICATIONS)
-//        .type(PropertyType.BOOLEAN)
-//        .build(),
-//      PropertyDefinition.builder(CoreProperties.CPD_EXCLUSIONS)
-//        .defaultValue("")
-//        .name("Duplication Exclusions")
-//        .description("Patterns used to exclude some source files from the duplication detection mechanism. " +
-//          "See below to know how to use wildcards to specify this property.")
-//        .onQualifiers(Qualifiers.PROJECT, Qualifiers.MODULE)
-//        .category(CoreProperties.CATEGORY_EXCLUSIONS)
-//        .subCategory(CoreProperties.SUBCATEGORY_DUPLICATIONS_EXCLUSIONS)
-//        .multiValues(true)
-//        .build()));
+      PropertyDefinition.builder(CoreProperties.CPD_CROSS_PROJECT)
+        .defaultValue(Boolean.toString(CoreProperties.CPD_CROSS_PROJECT_DEFAULT_VALUE))
+        .name("Cross project duplication detection")
+        .description("By default, SonarQube detects duplications at sub-project level. This means that a block "
+          + "duplicated on two sub-projects of the same project won't be reported. Setting this parameter to \"true\" "
+          + "allows to detect duplicates across sub-projects and more generally across projects. Note that activating "
+          + "this property will slightly increase each SonarQube analysis time.")
+        .onQualifiers(Qualifiers.PROJECT)
+        .category(CoreProperties.CATEGORY_GENERAL)
+        .subCategory(CoreProperties.SUBCATEGORY_DUPLICATIONS)
+        .type(PropertyType.BOOLEAN)
+        .build(),
+      PropertyDefinition.builder(CoreProperties.CPD_EXCLUSIONS)
+        .defaultValue("")
+        .name("Duplication Exclusions")
+        .description("Patterns used to exclude some source files from the duplication detection mechanism. " +
+          "See below to know how to use wildcards to specify this property.")
+        .onQualifiers(Qualifiers.PROJECT, Qualifiers.MODULE)
+        .category(CoreProperties.CATEGORY_EXCLUSIONS)
+        .subCategory(CoreProperties.SUBCATEGORY_DUPLICATIONS_EXCLUSIONS)
+        .multiValues(true)
+        .build()));
     return defs;
   }
 }
index a5f974c81017f6b1ce976dc28c0dc9ddae98323d..9e4a625b626eac1a531bef2247dca1b2d20f2150 100644 (file)
@@ -265,12 +265,6 @@ public interface CoreProperties {
   /* CPD */
   String CPD_PLUGIN = "cpd";
 
-  /**
-   * @deprecated in 3.1
-   */
-  @Deprecated
-  String CPD_MINIMUM_TOKENS_PROPERTY = "sonar.cpd.minimumTokens";
-
   /**
    * @deprecated in 5.0
    * @see <a href="https://jira.sonarsource.com/browse/SONAR-5339">SONAR-5339</a>
@@ -287,7 +281,7 @@ public interface CoreProperties {
    * @see #CPD_CROSS_PROJECT
    * @since 2.11
    */
-  boolean CPD_CROSS_RPOJECT_DEFAULT_VALUE = false;
+  boolean CPD_CROSS_PROJECT_DEFAULT_VALUE = false;
 
   /**
    * @since 3.5
@@ -434,7 +428,7 @@ public interface CoreProperties {
    * @since 5.2
    */
   String ANALYSIS_MODE_ISSUES = "issues";
-  
+
   /**
    * @since 5.2
    */