]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6323 Add some ITs in CrossProjectDuplicationsTest
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Fri, 13 Nov 2015 16:40:58 +0000 (17:40 +0100)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Mon, 16 Nov 2015 07:44:06 +0000 (08:44 +0100)
21 files changed:
it/it-projects/duplications/cross-project/a/pom.xml [deleted file]
it/it-projects/duplications/cross-project/a/src/main/java/MyClass.java [deleted file]
it/it-projects/duplications/cross-project/b/pom.xml [deleted file]
it/it-projects/duplications/cross-project/b/src/main/java/MyClass2.java [deleted file]
it/it-projects/duplications/cross-project/duplicate/sonar-project.properties [new file with mode: 0644]
it/it-projects/duplications/cross-project/duplicate/src/main/xoo/sample/File1.xoo [new file with mode: 0644]
it/it-projects/duplications/cross-project/duplicate/src/main/xoo/sample/File1.xoo.measures [new file with mode: 0644]
it/it-projects/duplications/cross-project/duplicate/src/main/xoo/sample/File2.xoo [new file with mode: 0644]
it/it-projects/duplications/cross-project/duplicate/src/main/xoo/sample/File2.xoo.measures [new file with mode: 0644]
it/it-projects/duplications/cross-project/origin/sonar-project.properties [new file with mode: 0644]
it/it-projects/duplications/cross-project/origin/src/main/xoo/sample/File1.xoo [new file with mode: 0644]
it/it-projects/duplications/cross-project/origin/src/main/xoo/sample/File1.xoo.measures [new file with mode: 0644]
it/it-tests/src/test/java/it/Category4Suite.java
it/it-tests/src/test/java/it/duplication/CrossProjectDuplicationsOnRemoveFileTest.java [new file with mode: 0644]
it/it-tests/src/test/java/it/duplication/CrossProjectDuplicationsTest.java
it/it-tests/src/test/resources/duplication/CrossProjectDuplicationsOnRemoveFileTest/duplications-with-deleted-project.html [new file with mode: 0644]
it/it-tests/src/test/resources/duplication/CrossProjectDuplicationsOnRemoveFileTest/duplications_on_removed_file-expected.json [new file with mode: 0644]
it/it-tests/src/test/resources/duplication/CrossProjectDuplicationsTest/cross-project-duplications-viewer.html [new file with mode: 0644]
it/it-tests/src/test/resources/duplication/CrossProjectDuplicationsTest/duplications_show-expected.json [new file with mode: 0644]
it/it-tests/src/test/resources/duplication/CrossProjectDuplicationsTest/sources_lines_duplication-expected.json [new file with mode: 0644]
it/it-tests/src/test/resources/duplication/xoo-duplication-profile.xml [new file with mode: 0644]

diff --git a/it/it-projects/duplications/cross-project/a/pom.xml b/it/it-projects/duplications/cross-project/a/pom.xml
deleted file mode 100644 (file)
index fc279f2..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-
-  <groupId>com.sonarsource.it.samples.duplications</groupId>
-  <artifactId>a</artifactId>
-  <version>1.0-SNAPSHOT</version>
-
-</project>
diff --git a/it/it-projects/duplications/cross-project/a/src/main/java/MyClass.java b/it/it-projects/duplications/cross-project/a/src/main/java/MyClass.java
deleted file mode 100644 (file)
index 2dca0f2..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-public class MyClass {
-  public void sayHello() {
-    int d1;
-    int d2;
-    int d3;
-    int d4;
-    int d5;
-    int d6;
-    int d7;
-    int d8;
-    int d9;
-    int d10;
-  }
-}
diff --git a/it/it-projects/duplications/cross-project/b/pom.xml b/it/it-projects/duplications/cross-project/b/pom.xml
deleted file mode 100644 (file)
index e3db8fc..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-
-  <groupId>com.sonarsource.it.samples.duplications</groupId>
-  <artifactId>b</artifactId>
-  <version>1.0-SNAPSHOT</version>
-
-</project>
diff --git a/it/it-projects/duplications/cross-project/b/src/main/java/MyClass2.java b/it/it-projects/duplications/cross-project/b/src/main/java/MyClass2.java
deleted file mode 100644 (file)
index b8a203b..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-public class MyClass2 {
-  public void sayHello2() {
-    int d1;
-    int d2;
-    int d3;
-    int d4;
-    int d5;
-    int d6;
-    int d7;
-    int d8;
-    int d9;
-    int d10;
-  }
-}
diff --git a/it/it-projects/duplications/cross-project/duplicate/sonar-project.properties b/it/it-projects/duplications/cross-project/duplicate/sonar-project.properties
new file mode 100644 (file)
index 0000000..cc02038
--- /dev/null
@@ -0,0 +1,5 @@
+sonar.projectKey=cross-project
+sonar.projectName=Cross project
+sonar.projectVersion=1.0-SNAPSHOT
+sonar.sources=src/main/xoo
+sonar.language=xoo
diff --git a/it/it-projects/duplications/cross-project/duplicate/src/main/xoo/sample/File1.xoo b/it/it-projects/duplications/cross-project/duplicate/src/main/xoo/sample/File1.xoo
new file mode 100644 (file)
index 0000000..5e494b1
--- /dev/null
@@ -0,0 +1,35 @@
+package sample;
+
+public class File1 {
+
+  public File1() {
+  }
+
+  public void test() {
+    char[] charList = new char[30];
+    for (int i = 0; i < 10; i++) {
+      charList[i] = 'a';
+    }
+    for (int i = 0; i < 10; i++) {
+      charList[i] = 'a';
+    }
+     int intergerToBeIncremented = 0;
+    while (intergerToBeIncremented < 100) {
+      intergerToBeIncremented++;
+    }
+    int intergerToBeIncremented2 = 0;
+    while (intergerToBeIncremented2 < 100) {
+      intergerToBeIncremented2++;
+    }
+    String temp = "";
+    for (int i=0; i<10; i++){
+      temp += "say something"+i;
+    }
+    for (int i=0; i<20; i++){
+      temp += "say nothing"+i;
+    }
+    for (int i=0; i<30; i++){
+      temp += "always say nothing"+i;
+    }
+  }
+}
diff --git a/it/it-projects/duplications/cross-project/duplicate/src/main/xoo/sample/File1.xoo.measures b/it/it-projects/duplications/cross-project/duplicate/src/main/xoo/sample/File1.xoo.measures
new file mode 100644 (file)
index 0000000..5a79b0b
--- /dev/null
@@ -0,0 +1 @@
+ncloc:36
diff --git a/it/it-projects/duplications/cross-project/duplicate/src/main/xoo/sample/File2.xoo b/it/it-projects/duplications/cross-project/duplicate/src/main/xoo/sample/File2.xoo
new file mode 100644 (file)
index 0000000..00b502d
--- /dev/null
@@ -0,0 +1,23 @@
+package sample;
+
+public class File1 {
+
+  public File1() {
+  }
+
+  public void otherMethod() {
+    String temp = "";
+    for (int i=0; i<10; i++){
+      temp += "say something"+i;
+      int nothing = 0;
+    }
+    for (int i=0; i<20; i++){
+      temp += "say nothing"+i;
+      int nothing = 1;
+    }
+    for (int i=0; i<30; i++){
+      temp += "always say nothing"+i;
+      int nothing = 2;
+    }
+  }
+}
diff --git a/it/it-projects/duplications/cross-project/duplicate/src/main/xoo/sample/File2.xoo.measures b/it/it-projects/duplications/cross-project/duplicate/src/main/xoo/sample/File2.xoo.measures
new file mode 100644 (file)
index 0000000..d90983a
--- /dev/null
@@ -0,0 +1 @@
+ncloc:24
diff --git a/it/it-projects/duplications/cross-project/origin/sonar-project.properties b/it/it-projects/duplications/cross-project/origin/sonar-project.properties
new file mode 100644 (file)
index 0000000..cc02038
--- /dev/null
@@ -0,0 +1,5 @@
+sonar.projectKey=cross-project
+sonar.projectName=Cross project
+sonar.projectVersion=1.0-SNAPSHOT
+sonar.sources=src/main/xoo
+sonar.language=xoo
diff --git a/it/it-projects/duplications/cross-project/origin/src/main/xoo/sample/File1.xoo b/it/it-projects/duplications/cross-project/origin/src/main/xoo/sample/File1.xoo
new file mode 100644 (file)
index 0000000..cc0b661
--- /dev/null
@@ -0,0 +1,35 @@
+package sample;
+
+public class File1 {
+
+  public File1() {
+  }
+
+  public void test2() {
+    char[] charList = new char[30];
+    for (int i = 0; i < 10; i++) {
+      charList[i] = 'a';
+    }
+    for (int i = 0; i < 10; i++) {
+      charList[i] = 'a';
+    }
+     int intergerToBeIncremented = 0;
+    while (intergerToBeIncremented < 100) {
+      intergerToBeIncremented++;
+    }
+    int intergerToBeIncremented2 = 0;
+    while (intergerToBeIncremented2 < 100) {
+      intergerToBeIncremented2++;
+    }
+    String temp = "";
+    for (int i=0; i<10; i++){
+      temp += "say something"+i;
+    }
+    for (int i=0; i<20; i++){
+      temp += "say nothing"+i;
+    }
+    for (int i=0; i<30; i++){
+      temp += "always say nothing"+i;
+    }
+  }
+}
diff --git a/it/it-projects/duplications/cross-project/origin/src/main/xoo/sample/File1.xoo.measures b/it/it-projects/duplications/cross-project/origin/src/main/xoo/sample/File1.xoo.measures
new file mode 100644 (file)
index 0000000..5a79b0b
--- /dev/null
@@ -0,0 +1 @@
+ncloc:36
index 3c43fec6f8be2c68cf360ef951ca95dbb081ec60..9a566c5741cf9e945d80430f1eeeaeb0cd5ccf89 100644 (file)
@@ -24,6 +24,7 @@ import it.analysisExclusion.FileExclusionsTest;
 import it.analysisExclusion.IssueExclusionsTest;
 import it.componentSearch.ProjectSearchTest;
 import it.dbCleaner.PurgeTest;
+import it.duplication.CrossProjectDuplicationsOnRemoveFileTest;
 import it.duplication.CrossProjectDuplicationsTest;
 import it.duplication.DuplicationsTest;
 import it.serverSystem.DevModeTest;
@@ -58,6 +59,7 @@ import static util.ItUtils.xooPlugin;
   IssueExclusionsTest.class,
   // duplication
   CrossProjectDuplicationsTest.class,
+  CrossProjectDuplicationsOnRemoveFileTest.class,
   DuplicationsTest.class,
   // db cleaner
   PurgeTest.class
diff --git a/it/it-tests/src/test/java/it/duplication/CrossProjectDuplicationsOnRemoveFileTest.java b/it/it-tests/src/test/java/it/duplication/CrossProjectDuplicationsOnRemoveFileTest.java
new file mode 100644 (file)
index 0000000..f90de75
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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 it.duplication;
+
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.locator.FileLocation;
+import com.sonar.orchestrator.selenium.Selenese;
+import it.Category4Suite;
+import org.apache.commons.io.IOUtils;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.sonar.wsclient.services.ResourceQuery;
+import util.selenium.SeleneseTest;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.skyscreamer.jsonassert.JSONAssert.assertEquals;
+import static util.ItUtils.runProjectAnalysis;
+
+public class CrossProjectDuplicationsOnRemoveFileTest {
+
+  static final String ORIGIN_PROJECT = "origin-project";
+  static final String DUPLICATE_PROJECT = "duplicate-project";
+  static final String DUPLICATE_FILE = DUPLICATE_PROJECT + ":src/main/xoo/sample/File1.xoo";
+
+  @ClassRule
+  public static Orchestrator orchestrator = Category4Suite.ORCHESTRATOR;
+
+  @BeforeClass
+  public static void analyzeProjects() {
+    orchestrator.resetData();
+    orchestrator.getServer().restoreProfile(FileLocation.ofClasspath("/duplication/xoo-duplication-profile.xml"));
+
+    analyzeProject(ORIGIN_PROJECT, "duplications/cross-project/origin");
+    analyzeProject(DUPLICATE_PROJECT, "duplications/cross-project/duplicate");
+
+    // Remove origin project
+    orchestrator.getServer().adminWsClient().post("api/projects/bulk_delete", "keys", ORIGIN_PROJECT);
+    assertThat(orchestrator.getServer().getAdminWsClient().find(ResourceQuery.create(ORIGIN_PROJECT))).isNull();
+  }
+
+  @Test
+  public void duplications_show_ws_does_not_contain_key_of_deleted_file() throws Exception {
+    String duplication = orchestrator.getServer().adminWsClient().get("api/duplications/show", "key", DUPLICATE_FILE);
+
+    assertEquals(IOUtils.toString(CrossProjectDuplicationsTest.class.getResourceAsStream(
+      "/duplication/CrossProjectDuplicationsOnRemoveFileTest/duplications_on_removed_file-expected.json"), "UTF-8"),
+      duplication, false);
+
+    // Only one file should be reference, so the reference 2 on origin-project must not exist
+    assertThat(duplication).doesNotContain("\"2\"");
+    assertThat(duplication).doesNotContain("origin-project");
+  }
+
+  /**
+   * SONAR-3277
+   */
+  @Test
+  public void display_message_in_viewer_when_duplications_with_deleted_files_are_found() throws Exception {
+    // TODO stas, please replace this IT by a medium test
+    new SeleneseTest(
+      Selenese.builder().setHtmlTestsInClasspath("duplications-on-deleted-project",
+        "/duplication/CrossProjectDuplicationsOnRemoveFileTest/duplications-with-deleted-project.html")
+        .build())
+      .runOn(orchestrator);
+  }
+
+  private static void analyzeProject(String projectKey, String path) {
+    orchestrator.getServer().provisionProject(projectKey, projectKey);
+    orchestrator.getServer().associateProjectToQualityProfile(projectKey, "xoo", "xoo-duplication-profile");
+
+    runProjectAnalysis(orchestrator, path,
+      "sonar.cpd.cross_project", "true",
+      "sonar.projectKey", projectKey,
+      "sonar.projectName", projectKey);
+  }
+
+}
index 3bb705cc73b04a6e6ab295f8eef03f29fcf8b1f7..83864d4a7ca6d53a3047185481892821d6db71b4 100644 (file)
 /*
- * Copyright (C) 2009-2014 SonarSource SA
- * All rights reserved
+ * 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 it.duplication;
 
 import com.sonar.orchestrator.Orchestrator;
-import com.sonar.orchestrator.build.MavenBuild;
+import com.sonar.orchestrator.locator.FileLocation;
+import com.sonar.orchestrator.selenium.Selenese;
 import it.Category4Suite;
-import org.junit.Before;
+import java.util.List;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.ArrayUtils;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.ClassRule;
 import org.junit.Test;
+import org.sonar.wsclient.issue.Issue;
+import org.sonar.wsclient.issue.IssueQuery;
 import org.sonar.wsclient.services.Resource;
 import org.sonar.wsclient.services.ResourceQuery;
-import util.ItUtils;
+import util.selenium.SeleneseTest;
 
-import static org.hamcrest.Matchers.notNullValue;
-import static org.hamcrest.core.Is.is;
-import static org.junit.Assert.assertThat;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.skyscreamer.jsonassert.JSONAssert.assertEquals;
+import static util.ItUtils.runProjectAnalysis;
+import static util.ItUtils.setServerProperty;
 
 public class CrossProjectDuplicationsTest {
 
+  static final String ORIGIN_PROJECT = "origin-project";
+  static final String DUPLICATE_PROJECT = "duplicate-project";
+  static final String PROJECT_WITH_EXCLUSION = "project-with-exclusion";
+  static final String PROJECT_WITHOUT_ENOUGH_TOKENS = "project_without_enough_tokens";
+
+  static final String DUPLICATE_FILE = DUPLICATE_PROJECT + ":src/main/xoo/sample/File1.xoo";
+  static final String BRANCH = "with-branch";
+
+  static final String ORIGIN_PATH = "duplications/cross-project/origin";
+  static final String DUPLICATE_PATH = "duplications/cross-project/duplicate";
+
   @ClassRule
   public static Orchestrator orchestrator = Category4Suite.ORCHESTRATOR;
 
-  @Before
-  public void analyzeProjects() {
+  @BeforeClass
+  public static void analyzeProjects() {
     orchestrator.resetData();
+    orchestrator.getServer().restoreProfile(FileLocation.ofClasspath("/duplication/xoo-duplication-profile.xml"));
+
+    analyzeProject(ORIGIN_PROJECT, ORIGIN_PATH);
+    analyzeProject(DUPLICATE_PROJECT, DUPLICATE_PATH);
+    analyzeProjectWithBranch(DUPLICATE_PROJECT, DUPLICATE_PATH, BRANCH);
+    analyzeProject(PROJECT_WITH_EXCLUSION, DUPLICATE_PATH, "sonar.cpd.exclusions", "**/File*");
+
+    // Set minimum tokens to a big value in order to not get duplications
+    setServerProperty(orchestrator, "sonar.cpd.xoo.minimumTokens", "1000");
+    analyzeProject(PROJECT_WITHOUT_ENOUGH_TOKENS, DUPLICATE_PATH);
+  }
+
+  @AfterClass
+  public static void resetServerProperties() throws Exception {
+    setServerProperty(orchestrator, "sonar.cpd.xoo.minimumTokens", null);
+  }
+
+  @Test
+  public void origin_project_has_no_duplication_as_it_has_not_been_analyzed_twice() throws Exception {
+    assertProjectHasNoDuplication(ORIGIN_PROJECT);
+  }
+
+  @Test
+  public void duplicate_project_has_duplication_as_it_has_been_analyzed_twice() throws Exception {
+    assertThat(getMeasure(DUPLICATE_PROJECT, "duplicated_lines")).isEqualTo(27);
+    assertThat(getMeasure(DUPLICATE_PROJECT, "duplicated_blocks")).isEqualTo(1);
+    assertThat(getMeasure(DUPLICATE_PROJECT, "duplicated_files")).isEqualTo(1);
+    assertThat(getComponent(DUPLICATE_PROJECT, "duplicated_lines_density").getMeasureValue("duplicated_lines_density")).isEqualTo(45d);
+  }
+
+  @Test
+  public void issue_on_duplicated_blocks_is_generated_on_file() throws Exception {
+    List<Issue> issues = orchestrator.getServer().wsClient().issueClient()
+      .find(IssueQuery.create()
+        .components(DUPLICATE_FILE)
+        .rules("common-xoo:DuplicatedBlocks"))
+      .list();
+    assertThat(issues).hasSize(1);
+  }
+
+  @Test
+  public void verify_sources_lines_ws_duplication_information() throws Exception {
+    verifyWsResultOnDuplicateFile("api/sources/lines", "sources_lines_duplication-expected.json");
+  }
 
-    MavenBuild build = MavenBuild.create(ItUtils.projectPom("duplications/cross-project/a"))
-      .setCleanSonarGoals()
-      .setProperty("sonar.cpd.cross_project", "true")
-      .setProperty("sonar.dynamicAnalysis", "false");
-    orchestrator.executeBuild(build);
+  @Test
+  public void verify_duplications_show_ws() throws Exception {
+    verifyWsResultOnDuplicateFile("api/duplications/show", "duplications_show-expected.json");
+  }
+
+  @Test
+  public void project_with_branch_has_no_duplication() throws Exception {
+    assertProjectHasNoDuplication(DUPLICATE_PROJECT + ":" + BRANCH);
+  }
 
-    build = MavenBuild.create(ItUtils.projectPom("duplications/cross-project/b"))
-      .setCleanSonarGoals()
-      .setProperty("sonar.cpd.cross_project", "true")
-      .setProperty("sonar.dynamicAnalysis", "false");
-    orchestrator.executeBuild(build);
+  @Test
+  public void project_with_exclusion_has_no_duplication() throws Exception {
+    assertProjectHasNoDuplication(PROJECT_WITH_EXCLUSION);
+  }
 
-    build = MavenBuild.create(ItUtils.projectPom("duplications/cross-project/b"))
-      .setCleanSonarGoals()
-      .setProperty("sonar.cpd.cross_project", "true")
-      .setProperty("sonar.branch", "branch")
-      .setProperty("sonar.dynamicAnalysis", "false");
-    orchestrator.executeBuild(build);
+  @Test
+  public void project_without_enough_tokens_has_duplication() throws Exception {
+    assertProjectHasNoDuplication(PROJECT_WITHOUT_ENOUGH_TOKENS);
   }
 
   @Test
-  public void testMeasures() throws Exception {
-    Resource project = getResource("com.sonarsource.it.samples.duplications:a");
-    assertThat(project, notNullValue());
-    assertThat(project.getMeasureIntValue("duplicated_lines"), is(0));
+  public void verify_viewer() {
+    new SeleneseTest(
+      Selenese.builder().setHtmlTestsInClasspath("duplications-viewer",
+        "/duplication/CrossProjectDuplicationsTest/cross-project-duplications-viewer.html")
+        .build())
+      .runOn(orchestrator);
+  }
+
+  private static void analyzeProject(String projectKey, String path, String... additionalProperties) {
+    initProject(projectKey);
+    executeAnalysis(projectKey, path, additionalProperties);
+  }
+
+  private static void analyzeProjectWithBranch(String projectKey, String path, String branch) {
+    initProject(projectKey + ":" + branch);
+    executeAnalysis(projectKey, path, "sonar.branch", branch);
+  }
+
+  private static void initProject(String effectiveProjectKey) {
+    orchestrator.getServer().provisionProject(effectiveProjectKey, effectiveProjectKey);
+    orchestrator.getServer().associateProjectToQualityProfile(effectiveProjectKey, "xoo", "xoo-duplication-profile");
+  }
 
-    project = getResource("com.sonarsource.it.samples.duplications:b");
-    assertThat(project, notNullValue());
-    assertThat(project.getMeasureIntValue("duplicated_lines"), is(10));
+  private static void executeAnalysis(String projectKey, String path, String... additionalProperties) {
+    runProjectAnalysis(orchestrator, path,
+      ArrayUtils.addAll(
+        new String[] {
+          "sonar.cpd.cross_project", "true",
+          "sonar.projectKey", projectKey,
+          "sonar.projectName", projectKey
+        },
+        additionalProperties));
+  }
+
+  private static int getMeasure(String projectKey, String metricKey) {
+    Integer intMeasure = getComponent(projectKey, metricKey).getMeasureIntValue(metricKey);
+    assertThat(intMeasure).isNotNull();
+    return intMeasure;
+  }
+
+  private static Resource getComponent(String projectKey, String metricKey) {
+    return orchestrator.getServer().getWsClient().find(ResourceQuery.createForMetrics(projectKey, metricKey));
+  }
 
-    project = getResource("com.sonarsource.it.samples.duplications:b:branch");
-    assertThat(project, notNullValue());
-    assertThat(project.getMeasureIntValue("duplicated_lines"), is(0));
+  private static void assertProjectHasNoDuplication(String projectKey) {
+    assertThat(getMeasure(projectKey, "duplicated_lines")).isZero();
   }
 
-  private Resource getResource(String key) {
-    return orchestrator.getServer().getWsClient().find(ResourceQuery.createForMetrics(key, "duplicated_lines"));
+  private static void verifyWsResultOnDuplicateFile(String ws, String expectedFilePath) throws Exception {
+    String duplication = orchestrator.getServer().adminWsClient().get(ws, "key", DUPLICATE_FILE);
+    assertEquals(IOUtils.toString(CrossProjectDuplicationsTest.class.getResourceAsStream("/duplication/CrossProjectDuplicationsTest/" + expectedFilePath), "UTF-8"), duplication,
+      false);
   }
 
 }
diff --git a/it/it-tests/src/test/resources/duplication/CrossProjectDuplicationsOnRemoveFileTest/duplications-with-deleted-project.html b/it/it-tests/src/test/resources/duplication/CrossProjectDuplicationsOnRemoveFileTest/duplications-with-deleted-project.html
new file mode 100644 (file)
index 0000000..f819db4
--- /dev/null
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+  <title>duplications-with-deleted-project</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <thead>
+  <tr>
+    <td rowspan="1" colspan="3">duplications-with-deleted-project</td>
+  </tr>
+  </thead>
+  <tbody>
+  <tr>
+    <td>open</td>
+    <td>/sonar/component/index?id=duplicate-project%3Asrc%2Fmain%2Fxoo%2Fsample%2FFile1.xoo</td>
+    <td></td>
+  </tr>
+  <tr>
+    <td>waitForText</td>
+    <td>css=.source-viewer</td>
+    <td>*src/main/xoo/sample/File1.xoo*</td>
+  </tr>
+  <tr>
+    <td>waitForText</td>
+    <td>css=.source-table</td>
+    <td>*File1*</td>
+  </tr>
+  <tr>
+    <td>click</td>
+    <td>css=.source-line-duplicated</td>
+    <td></td>
+  </tr>
+  <tr>
+    <td>waitForElementPresent</td>
+    <td>css=.bubble-popup</td>
+    <td></td>
+  </tr>
+  <tr>
+    <td>assertElementPresent</td>
+    <td>css=.bubble-popup .alert</td>
+    <td></td>
+  </tr>
+  </tbody>
+</table>
+</body>
+</html>
diff --git a/it/it-tests/src/test/resources/duplication/CrossProjectDuplicationsOnRemoveFileTest/duplications_on_removed_file-expected.json b/it/it-tests/src/test/resources/duplication/CrossProjectDuplicationsOnRemoveFileTest/duplications_on_removed_file-expected.json
new file mode 100644 (file)
index 0000000..37f0e40
--- /dev/null
@@ -0,0 +1,25 @@
+{
+  "duplications": [
+    {
+      "blocks": [
+        {
+          "from": 9,
+          "size": 27
+        },
+        {
+          "from": 9,
+          "size": 27,
+          "_ref": "1"
+        }
+      ]
+    }
+  ],
+  "files": {
+    "1": {
+      "key": "duplicate-project:src/main/xoo/sample/File1.xoo",
+      "name": "src/main/xoo/sample/File1.xoo",
+      "project": "duplicate-project",
+      "projectName": "duplicate-project"
+    }
+  }
+}
diff --git a/it/it-tests/src/test/resources/duplication/CrossProjectDuplicationsTest/cross-project-duplications-viewer.html b/it/it-tests/src/test/resources/duplication/CrossProjectDuplicationsTest/cross-project-duplications-viewer.html
new file mode 100644 (file)
index 0000000..221fee9
--- /dev/null
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+  <title>cross-project-duplications-viewer</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+  <thead>
+  <tr>
+    <td rowspan="1" colspan="3">cross-project-duplications-viewer</td>
+  </tr>
+  </thead>
+  <tbody>
+  <tr>
+    <td>open</td>
+    <td>/sonar/component/index?id=duplicate-project%3Asrc%2Fmain%2Fxoo%2Fsample%2FFile1.xoo</td>
+    <td></td>
+  </tr>
+  <tr>
+    <td>waitForElementPresent</td>
+    <td>css=.source-line</td>
+    <td></td>
+  </tr>
+  <tr>
+    <td>assertText</td>
+    <td>css=.source-viewer-header</td>
+    <td>*75.0%*</td>
+  </tr>
+  <tr>
+    <td>waitForElementPresent</td>
+    <td>css=.source-line-duplicated</td>
+    <td></td>
+  </tr>
+  <tr>
+    <td>assertText</td>
+    <td>css=[data-line-number='3']</td>
+    <td>*File1*</td>
+  </tr>
+  <tr>
+    <td>click</td>
+    <td>css=.source-line-duplicated</td>
+    <td></td>
+  </tr>
+  <tr>
+    <td>waitForElementPresent</td>
+    <td>css=.bubble-popup</td>
+    <td></td>
+  </tr>
+  <tr>
+    <td>assertText</td>
+    <td>css=.bubble-popup</td>
+    <td>glob:*origin-project*File1.xoo*</td>
+  </tr>
+  </tbody>
+</table>
+</body>
+</html>
diff --git a/it/it-tests/src/test/resources/duplication/CrossProjectDuplicationsTest/duplications_show-expected.json b/it/it-tests/src/test/resources/duplication/CrossProjectDuplicationsTest/duplications_show-expected.json
new file mode 100644 (file)
index 0000000..a9b0c62
--- /dev/null
@@ -0,0 +1,32 @@
+{
+  "duplications": [
+    {
+      "blocks": [
+        {
+          "from": 9,
+          "size": 27,
+          "_ref": "1"
+        },
+        {
+          "from": 9,
+          "size": 27,
+          "_ref": "2"
+        }
+      ]
+    }
+  ],
+  "files": {
+    "2": {
+      "key": "origin-project:src/main/xoo/sample/File1.xoo",
+      "name": "src/main/xoo/sample/File1.xoo",
+      "project": "origin-project",
+      "projectName": "origin-project"
+    },
+    "1": {
+      "key": "duplicate-project:src/main/xoo/sample/File1.xoo",
+      "name": "src/main/xoo/sample/File1.xoo",
+      "project": "duplicate-project",
+      "projectName": "duplicate-project"
+    }
+  }
+}
diff --git a/it/it-tests/src/test/resources/duplication/CrossProjectDuplicationsTest/sources_lines_duplication-expected.json b/it/it-tests/src/test/resources/duplication/CrossProjectDuplicationsTest/sources_lines_duplication-expected.json
new file mode 100644 (file)
index 0000000..0ce4c68
--- /dev/null
@@ -0,0 +1,184 @@
+{
+  "sources": [
+    {
+      "line": 1,
+      "code": "package sample;",
+      "duplicated": false
+    },
+    {
+      "line": 2,
+      "code": "",
+      "duplicated": false
+    },
+    {
+      "line": 3,
+      "code": "public class File1 {",
+      "duplicated": false
+    },
+    {
+      "line": 4,
+      "code": "",
+      "duplicated": false
+    },
+    {
+      "line": 5,
+      "code": "  public File1() {",
+      "duplicated": false
+    },
+    {
+      "line": 6,
+      "code": "  }",
+      "duplicated": false
+    },
+    {
+      "line": 7,
+      "code": "",
+      "duplicated": false
+    },
+    {
+      "line": 8,
+      "code": "  public void test() {",
+      "duplicated": false
+    },
+    {
+      "line": 9,
+      "code": "    char[] charList = new char[30];",
+      "duplicated": true
+    },
+    {
+      "line": 10,
+      "code": "    for (int i = 0; i &lt; 10; i++) {",
+      "duplicated": true
+    },
+    {
+      "line": 11,
+      "code": "      charList[i] = 'a';",
+      "duplicated": true
+    },
+    {
+      "line": 12,
+      "code": "    }",
+      "duplicated": true
+    },
+    {
+      "line": 13,
+      "code": "    for (int i = 0; i &lt; 10; i++) {",
+      "duplicated": true
+    },
+    {
+      "line": 14,
+      "code": "      charList[i] = 'a';",
+      "duplicated": true
+    },
+    {
+      "line": 15,
+      "code": "    }",
+      "duplicated": true
+    },
+    {
+      "line": 16,
+      "code": "     int intergerToBeIncremented = 0;",
+      "duplicated": true
+    },
+    {
+      "line": 17,
+      "code": "    while (intergerToBeIncremented &lt; 100) {",
+      "duplicated": true
+    },
+    {
+      "line": 18,
+      "code": "      intergerToBeIncremented++;",
+      "duplicated": true
+    },
+    {
+      "line": 19,
+      "code": "    }",
+      "duplicated": true
+    },
+    {
+      "line": 20,
+      "code": "    int intergerToBeIncremented2 = 0;",
+      "duplicated": true
+    },
+    {
+      "line": 21,
+      "code": "    while (intergerToBeIncremented2 &lt; 100) {",
+      "duplicated": true
+    },
+    {
+      "line": 22,
+      "code": "      intergerToBeIncremented2++;",
+      "duplicated": true
+    },
+    {
+      "line": 23,
+      "code": "    }",
+      "duplicated": true
+    },
+    {
+      "line": 24,
+      "code": "    String temp = \"\";",
+      "duplicated": true
+    },
+    {
+      "line": 25,
+      "code": "    for (int i=0; i&lt;10; i++){",
+      "duplicated": true
+    },
+    {
+      "line": 26,
+      "code": "      temp += \"say something\"+i;",
+      "duplicated": true
+    },
+    {
+      "line": 27,
+      "code": "    }",
+      "duplicated": true
+    },
+    {
+      "line": 28,
+      "code": "    for (int i=0; i&lt;20; i++){",
+      "duplicated": true
+    },
+    {
+      "line": 29,
+      "code": "      temp += \"say nothing\"+i;",
+      "duplicated": true
+    },
+    {
+      "line": 30,
+      "code": "    }",
+      "duplicated": true
+    },
+    {
+      "line": 31,
+      "code": "    for (int i=0; i&lt;30; i++){",
+      "duplicated": true
+    },
+    {
+      "line": 32,
+      "code": "      temp += \"always say nothing\"+i;",
+      "duplicated": true
+    },
+    {
+      "line": 33,
+      "code": "    }",
+      "duplicated": true
+    },
+    {
+      "line": 34,
+      "code": "  }",
+      "duplicated": true
+    },
+    {
+      "line": 35,
+      "code": "}",
+      "duplicated": true
+    },
+    {
+      "line": 36,
+      "code": "",
+      "duplicated": false
+    }
+  ]
+}
diff --git a/it/it-tests/src/test/resources/duplication/xoo-duplication-profile.xml b/it/it-tests/src/test/resources/duplication/xoo-duplication-profile.xml
new file mode 100644 (file)
index 0000000..999b64a
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0"?><!-- Generated by Sonar -->
+<profile>
+  <name>xoo-duplication-profile</name>
+  <language>xoo</language>
+  <rules>
+    <rule>
+      <repositoryKey>common-xoo</repositoryKey>
+      <key>DuplicatedBlocks</key>
+      <priority>CRITICAL</priority>
+    </rule>
+  </rules>
+</profile>