]> source.dussan.org Git - sonarqube.git/commitdiff
add ProjectAnalysisRule + use it in issues package
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Wed, 21 Oct 2015 16:00:29 +0000 (18:00 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Fri, 23 Oct 2015 08:44:16 +0000 (10:44 +0200)
rule avoid code factoring by static code + remove code duplication + add auto reset features
also fixed licence headers in issues package

15 files changed:
it/it-tests/src/test/java/issue/suite/CommonRulesTest.java
it/it-tests/src/test/java/issue/suite/CustomRulesTest.java
it/it-tests/src/test/java/issue/suite/IssueActionTest.java
it/it-tests/src/test/java/issue/suite/IssueBulkChangeTest.java
it/it-tests/src/test/java/issue/suite/IssueChangelogTest.java
it/it-tests/src/test/java/issue/suite/IssueFilterExtensionTest.java
it/it-tests/src/test/java/issue/suite/IssuePurgeTest.java
it/it-tests/src/test/java/issue/suite/IssueWorkflowTest.java
it/it-tests/src/test/java/issue/suite/ManualRulesTest.java
it/it-tests/src/test/java/util/LoadedProfiles.java [new file with mode: 0644]
it/it-tests/src/test/java/util/LoadedProjects.java [new file with mode: 0644]
it/it-tests/src/test/java/util/Profile.java [new file with mode: 0644]
it/it-tests/src/test/java/util/ProjectAnalysis.java [new file with mode: 0644]
it/it-tests/src/test/java/util/ProjectAnalysisRule.java [new file with mode: 0644]
it/it-tests/src/test/java/util/ProjectState.java [new file with mode: 0644]

index 8805d2bfdba34213591572094b2271b76894fd3d..ba6d871245d656bef1f670965cc1840a2076397b 100644 (file)
@@ -1,7 +1,21 @@
 /*
- * 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 issue.suite;
 
@@ -33,8 +47,8 @@ public class CommonRulesTest {
     orchestrator.getServer().provisionProject("common-rules-project", "Sample");
     orchestrator.getServer().associateProjectToQualityProfile("common-rules-project", "xoo", "xoo-common-rules");
     runProjectAnalysis(orchestrator, "issue/common-rules",
-      "sonar.cpd.xoo.minimumTokens", "2",
-      "sonar.cpd.xoo.minimumLines", "2");
+        "sonar.cpd.xoo.minimumTokens", "2",
+        "sonar.cpd.xoo.minimumLines", "2");
   }
 
   @Test
index 2271dc3f951f01869f3b5b72bdd36a6c33e1f604..66671a40912ac772eb8c4b62c9da44834a0ce1b3 100644 (file)
@@ -1,35 +1,56 @@
 /*
- * 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 issue.suite;
 
 import com.sonar.orchestrator.Orchestrator;
-import com.sonar.orchestrator.locator.FileLocation;
 import java.util.List;
 import org.junit.Before;
 import org.junit.ClassRule;
+import org.junit.Rule;
 import org.junit.Test;
 import org.sonar.wsclient.issue.Issue;
+import util.ProjectAnalysis;
+import util.ProjectAnalysisRule;
 
 import static issue.suite.IssueTestSuite.searchIssues;
 import static org.assertj.core.api.Assertions.assertThat;
-import static util.ItUtils.runProjectAnalysis;
 
 public class CustomRulesTest {
 
   @ClassRule
   public static Orchestrator orchestrator = IssueTestSuite.ORCHESTRATOR;
+  @Rule
+  public final ProjectAnalysisRule projectAnalysisRule = ProjectAnalysisRule.from(orchestrator);
+
+  private ProjectAnalysis xooSampleAnalysis;
 
   @Before
-  public void deleteData() {
-    orchestrator.resetData();
+  public void setup() {
+    String profileKey = projectAnalysisRule.registerProfile("/issue/suite/CustomRulesTest/custom.xml");
+    String projectKey = projectAnalysisRule.registerProject("shared/xoo-sample");
+    this.xooSampleAnalysis = projectAnalysisRule.newProjectAnalysis(projectKey)
+      .withQualityProfile(profileKey);
   }
 
   @Test
   public void analyzeProjectWithCustomRules() throws Exception {
-
     orchestrator.getServer().adminWsClient().post("api/rules/create",
       "template_key", "xoo:TemplateRule",
       "custom_key", "MyCustomRule",
@@ -38,12 +59,7 @@ public class CustomRulesTest {
       "severity", "BLOCKER",
       "params", "line=2");
 
-    orchestrator.getServer().restoreProfile(FileLocation.ofClasspath("/issue/suite/CustomRulesTest/custom.xml"));
-
-    orchestrator.getServer().provisionProject("sample", "Sample");
-    orchestrator.getServer().associateProjectToQualityProfile("sample", "xoo", "Custom");
-
-    runProjectAnalysis(orchestrator, "shared/xoo-sample");
+    xooSampleAnalysis.run();
 
     List<Issue> issues = searchIssues();
     assertThat(issues).hasSize(1);
index 8ea2c10b601b31fc577bede76561208714d79e34..cb0de90b12c38b67cb1c4f5178b3bd1c50f63d72 100644 (file)
 package issue.suite;
 
 import com.sonar.orchestrator.Orchestrator;
-import com.sonar.orchestrator.build.SonarRunner;
-import com.sonar.orchestrator.locator.FileLocation;
 import java.util.List;
 import org.junit.Before;
 import org.junit.ClassRule;
+import org.junit.Rule;
 import org.junit.Test;
 import org.sonar.wsclient.base.HttpException;
 import org.sonar.wsclient.issue.ActionPlan;
@@ -34,6 +33,8 @@ import org.sonar.wsclient.issue.IssueComment;
 import org.sonar.wsclient.issue.IssueQuery;
 import org.sonar.wsclient.issue.Issues;
 import org.sonar.wsclient.issue.NewActionPlan;
+import util.ProjectAnalysis;
+import util.ProjectAnalysisRule;
 
 import static issue.suite.IssueTestSuite.ORCHESTRATOR;
 import static issue.suite.IssueTestSuite.adminIssueClient;
@@ -43,7 +44,6 @@ import static issue.suite.IssueTestSuite.searchIssues;
 import static issue.suite.IssueTestSuite.searchRandomIssue;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.Assert.fail;
-import static util.ItUtils.runProjectAnalysis;
 import static util.ItUtils.toDate;
 import static util.ItUtils.verifyHttpException;
 
@@ -51,27 +51,20 @@ public class IssueActionTest {
 
   @ClassRule
   public static Orchestrator orchestrator = ORCHESTRATOR;
+  @Rule
+  public final ProjectAnalysisRule projectAnalysisRule = ProjectAnalysisRule.from(orchestrator);
 
   Issue issue;
-  SonarRunner scan;
-
-  private static List<Issue> searchIssuesBySeverities(String componentKey, String... severities) {
-    return searchIssues(IssueQuery.create().componentRoots(componentKey).severities(severities));
-  }
-
-  private static ActionPlanClient adminActionPlanClient() {
-    return orchestrator.getServer().adminWsClient().actionPlanClient();
-  }
+  ProjectAnalysis projectAnalysis;
 
   @Before
-  public void resetData() {
-    orchestrator.resetData();
-    orchestrator.getServer().restoreProfile(FileLocation.ofClasspath("/issue/suite/IssueActionTest/xoo-one-issue-per-line-profile.xml"));
-    orchestrator.getServer().provisionProject("sample", "Sample");
-    orchestrator.getServer().associateProjectToQualityProfile("sample", "xoo", "xoo-one-issue-per-line-profile");
-
-    scan = runProjectAnalysis(orchestrator, "shared/xoo-sample");
-    issue = searchRandomIssue();
+  public void setup() {
+    String qualityProfileKey = projectAnalysisRule.registerProfile("/issue/suite/IssueActionTest/xoo-one-issue-per-line-profile.xml");
+    String projectKey = projectAnalysisRule.registerProject("shared/xoo-sample");
+
+    this.projectAnalysis = projectAnalysisRule.newProjectAnalysis(projectKey).withQualityProfile(qualityProfileKey);
+    this.projectAnalysis.run();
+    this.issue = searchRandomIssue();
   }
 
   @Test
@@ -127,7 +120,7 @@ public class IssueActionTest {
 
     assertThat(searchIssuesBySeverities(componentKey, "BLOCKER")).hasSize(1);
 
-    orchestrator.executeBuild(scan);
+    projectAnalysis.run();
     Issue reloaded = searchIssueByKey(issue.key());
     assertThat(reloaded.severity()).isEqualTo("BLOCKER");
     assertThat(reloaded.status()).isEqualTo("OPEN");
@@ -148,7 +141,7 @@ public class IssueActionTest {
     adminIssueClient().assign(issue.key(), "admin");
     assertThat(searchIssues(IssueQuery.create().assignees("admin"))).hasSize(1);
 
-    orchestrator.executeBuild(scan);
+    projectAnalysis.run();
     Issue reloaded = searchIssueByKey(issue.key());
     assertThat(reloaded.assignee()).isEqualTo("admin");
     assertThat(reloaded.creationDate()).isEqualTo(issue.creationDate());
@@ -192,7 +185,7 @@ public class IssueActionTest {
     adminIssueClient().plan(issue.key(), newActionPlan.key());
     assertThat(search(IssueQuery.create().actionPlans(newActionPlan.key())).list()).hasSize(1);
 
-    orchestrator.executeBuild(scan);
+    projectAnalysis.run();
     Issue reloaded = searchIssueByKey(issue.key());
     assertThat(reloaded.actionPlan()).isEqualTo(newActionPlan.key());
     assertThat(reloaded.creationDate()).isEqualTo(issue.creationDate());
@@ -227,7 +220,7 @@ public class IssueActionTest {
     adminIssueClient().plan(issue.key(), null);
     assertThat(search(IssueQuery.create().actionPlans(newActionPlan.key())).list()).hasSize(0);
 
-    orchestrator.executeBuild(scan);
+    projectAnalysis.run();
     Issue reloaded = searchIssueByKey(issue.key());
     assertThat(reloaded.actionPlan()).isNull();
     assertThat(reloaded.creationDate()).isEqualTo(issue.creationDate());
@@ -264,10 +257,18 @@ public class IssueActionTest {
     adminIssueClient().doAction(issue.key(), "fake");
     assertThat(adminIssueClient().actions(issue.key())).doesNotContain("fake");
 
-    orchestrator.executeBuild(scan);
+    projectAnalysis.run();
 
     // Fake action is no more available if the issue attribute is still there
     assertThat(adminIssueClient().actions(issue.key())).doesNotContain("fake");
   }
 
+  private static List<Issue> searchIssuesBySeverities(String componentKey, String... severities) {
+    return searchIssues(IssueQuery.create().componentRoots(componentKey).severities(severities));
+  }
+
+  private static ActionPlanClient adminActionPlanClient() {
+    return orchestrator.getServer().adminWsClient().actionPlanClient();
+  }
+
 }
index b1d4ca7177b4c1464ee7e62e1340259aa541a26d..f5b438a7ef456b53e497e6f8a8fc32921732d6ca 100644 (file)
@@ -22,10 +22,10 @@ package issue.suite;
 import com.google.common.base.Function;
 import com.google.common.collect.FluentIterable;
 import com.sonar.orchestrator.Orchestrator;
-import com.sonar.orchestrator.locator.FileLocation;
 import java.util.List;
 import org.junit.Before;
 import org.junit.ClassRule;
+import org.junit.Rule;
 import org.junit.Test;
 import org.sonar.wsclient.base.HttpException;
 import org.sonar.wsclient.issue.ActionPlan;
@@ -35,12 +35,13 @@ import org.sonar.wsclient.issue.BulkChangeQuery;
 import org.sonar.wsclient.issue.Issue;
 import org.sonar.wsclient.issue.NewActionPlan;
 import util.ItUtils;
+import util.ProjectAnalysis;
+import util.ProjectAnalysisRule;
 
 import static issue.suite.IssueTestSuite.ORCHESTRATOR;
 import static issue.suite.IssueTestSuite.adminIssueClient;
 import static issue.suite.IssueTestSuite.issueClient;
 import static org.assertj.core.api.Assertions.assertThat;
-import static util.ItUtils.runProjectAnalysis;
 
 /**
  * SONAR-4421
@@ -53,15 +54,22 @@ public class IssueBulkChangeTest {
 
   @ClassRule
   public static Orchestrator orchestrator = ORCHESTRATOR;
+  @Rule
+  public final ProjectAnalysisRule projectAnalysisRule = ProjectAnalysisRule.from(orchestrator);
+
+  private ProjectAnalysis xooSampleLittleIssuesAnalysis;
 
   @Before
-  public void resetData() {
-    orchestrator.resetData();
+  public void setUp() throws Exception {
+    String qualityProfileKey = projectAnalysisRule.registerProfile("/issue/suite/IssueBulkChangeTest/one-issue-per-line-profile.xml");
+    String projectKey = projectAnalysisRule.registerProject("shared/xoo-sample");
+    this.xooSampleLittleIssuesAnalysis = projectAnalysisRule.newProjectAnalysis(projectKey)
+      .withQualityProfile(qualityProfileKey);
   }
 
   @Test
   public void should_change_severity() {
-    analyzeSampleProjectWillSmallNumberOfIssues();
+    xooSampleLittleIssuesAnalysis.run();
 
     String newSeverity = "BLOCKER";
     String[] issueKeys = searchIssueKeys(BULK_EDITED_ISSUE_COUNT);
@@ -73,7 +81,7 @@ public class IssueBulkChangeTest {
 
   @Test
   public void should_do_transition() {
-    analyzeSampleProjectWillSmallNumberOfIssues();
+    xooSampleLittleIssuesAnalysis.run();
     String[] issueKeys = searchIssueKeys(BULK_EDITED_ISSUE_COUNT);
     BulkChange bulkChange = bulkTransitionStatusOfIssues(issueKeys, "confirm");
 
@@ -83,7 +91,7 @@ public class IssueBulkChangeTest {
 
   @Test
   public void should_assign() {
-    analyzeSampleProjectWillSmallNumberOfIssues();
+    xooSampleLittleIssuesAnalysis.run();
 
     String[] issueKeys = searchIssueKeys(BULK_EDITED_ISSUE_COUNT);
     BulkChange bulkChange = buldChangeAssigneeOfIssues(issueKeys, "admin");
@@ -96,7 +104,7 @@ public class IssueBulkChangeTest {
 
   @Test
   public void should_plan() {
-    analyzeSampleProjectWillSmallNumberOfIssues();
+    xooSampleLittleIssuesAnalysis.run();
 
     // Create action plan
     ActionPlan newActionPlan = adminActionPlanClient().create(
@@ -118,7 +126,7 @@ public class IssueBulkChangeTest {
 
   @Test
   public void should_setSeverity_add_comment_in_single_WS_call() {
-    analyzeSampleProjectWillSmallNumberOfIssues();
+    xooSampleLittleIssuesAnalysis.run();
 
     String newSeverity = "BLOCKER";
     String[] issueKeys = searchIssueKeys(BULK_EDITED_ISSUE_COUNT);
@@ -140,7 +148,7 @@ public class IssueBulkChangeTest {
 
   @Test
   public void should_apply_bulk_change_on_many_actions() {
-    analyzeSampleProjectWillSmallNumberOfIssues();
+    xooSampleLittleIssuesAnalysis.run();
 
     String newSeverity = "BLOCKER";
     String[] issueKeys = searchIssueKeys(BULK_EDITED_ISSUE_COUNT);
@@ -167,7 +175,7 @@ public class IssueBulkChangeTest {
 
   @Test
   public void should_not_apply_bulk_change_if_not_logged() {
-    analyzeSampleProjectWillSmallNumberOfIssues();
+    xooSampleLittleIssuesAnalysis.run();
 
     String newSeverity = "BLOCKER";
     String[] issueKeys = searchIssueKeys(BULK_EDITED_ISSUE_COUNT);
@@ -181,7 +189,7 @@ public class IssueBulkChangeTest {
 
   @Test
   public void should_not_apply_bulk_change_if_no_change_to_do() {
-    analyzeSampleProjectWillSmallNumberOfIssues();
+    xooSampleLittleIssuesAnalysis.run();
 
     String newSeverity = "BLOCKER";
     String[] issueKeys = searchIssueKeys(BULK_EDITED_ISSUE_COUNT);
@@ -207,7 +215,7 @@ public class IssueBulkChangeTest {
 
   @Test
   public void should_not_apply_bulk_change_if_action_is_invalid() {
-    analyzeSampleProjectWillSmallNumberOfIssues();
+    xooSampleLittleIssuesAnalysis.run();
 
     int limit = BULK_EDITED_ISSUE_COUNT;
     String[] issueKeys = searchIssueKeys(limit);
@@ -222,7 +230,7 @@ public class IssueBulkChangeTest {
 
   @Test
   public void should_add_comment_only_on_issues_that_will_be_changed() {
-    analyzeSampleProjectWillSmallNumberOfIssues();
+    xooSampleLittleIssuesAnalysis.run();
     int nbIssues = BULK_EDITED_ISSUE_COUNT;
     String[] issueKeys = searchIssueKeys(nbIssues);
 
@@ -249,13 +257,6 @@ public class IssueBulkChangeTest {
     assertThat(nbIssuesWithComment).isEqualTo(1);
   }
 
-  private void analyzeSampleProjectWillSmallNumberOfIssues() {
-    orchestrator.getServer().restoreProfile(FileLocation.ofClasspath("/issue/suite/IssueBulkChangeTest/one-issue-per-line-profile.xml"));
-    orchestrator.getServer().provisionProject("sample", "Sample");
-    orchestrator.getServer().associateProjectToQualityProfile("sample", "xoo", "one-issue-per-line");
-    runProjectAnalysis(orchestrator, "shared/xoo-sample");
-  }
-
   private static void assertIssueSeverity(String[] issueKeys, String expectedSeverity) {
     for (Issue issue : IssueTestSuite.searchIssues(issueKeys)) {
       assertThat(issue.severity()).isEqualTo(expectedSeverity);
index a8b20a5f90f46537251df7a1c35a81834fe2c586..e141e2af2bf3e851c83efcc7101a49f9c7266759 100644 (file)
 package issue.suite;
 
 import com.sonar.orchestrator.Orchestrator;
-import com.sonar.orchestrator.build.SonarRunner;
-import com.sonar.orchestrator.locator.FileLocation;
 import java.util.List;
 import org.junit.Before;
 import org.junit.ClassRule;
+import org.junit.Rule;
 import org.junit.Test;
 import org.sonar.wsclient.issue.Issue;
 import org.sonar.wsclient.issue.IssueChange;
 import org.sonar.wsclient.issue.IssueChangeDiff;
 import org.sonar.wsclient.issue.IssueQuery;
+import util.ProjectAnalysis;
+import util.ProjectAnalysisRule;
 
 import static issue.suite.IssueTestSuite.ORCHESTRATOR;
 import static issue.suite.IssueTestSuite.adminIssueClient;
 import static issue.suite.IssueTestSuite.issueClient;
 import static org.assertj.core.api.Assertions.assertThat;
-import static util.ItUtils.runProjectAnalysis;
 
 public class IssueChangelogTest {
 
   @ClassRule
   public static Orchestrator orchestrator = ORCHESTRATOR;
+  @Rule
+  public final ProjectAnalysisRule projectAnalysisRule = ProjectAnalysisRule.from(orchestrator);
 
   Issue issue;
-  SonarRunner scan;
+  ProjectAnalysis xooSampleAnalysis;
 
   @Before
   public void resetData() {
-    orchestrator.resetData();
-    orchestrator.getServer().restoreProfile(FileLocation.ofClasspath("/issue/suite/IssueChangelogTest/one-issue-per-line-profile.xml"));
-    orchestrator.getServer().provisionProject("sample", "Sample");
-    orchestrator.getServer().associateProjectToQualityProfile("sample", "xoo", "one-issue-per-line");
-    scan = runProjectAnalysis(orchestrator, "shared/xoo-sample");
+    xooSampleAnalysis = projectAnalysisRule
+      .newProjectAnalysis(projectAnalysisRule.registerProject("shared/xoo-sample"))
+      .withQualityProfile(projectAnalysisRule.registerProfile("/issue/suite/IssueChangelogTest/one-issue-per-line-profile.xml"));
+    xooSampleAnalysis.run();
     issue = searchRandomIssue();
   }
 
@@ -79,7 +80,7 @@ public class IssueChangelogTest {
 
     // re analyse the project after resolving an issue in order to reopen it
     adminIssueClient().doTransition(issue.key(), "resolve");
-    orchestrator.executeBuild(scan);
+    xooSampleAnalysis.run();
 
     List<IssueChange> changes = retrieveChangeForIssue(issue.key());
     assertThat(changes).hasSize(2);
index 1104aa27b57364e4fb13c67fe7bbaec4aa1c02f7..3e00bdfb6826a4eb3ed10854f2b9d9ae6f802a6b 100644 (file)
 package issue.suite;
 
 import com.sonar.orchestrator.Orchestrator;
-import com.sonar.orchestrator.locator.FileLocation;
 import java.util.List;
-import org.junit.Before;
 import org.junit.ClassRule;
+import org.junit.Rule;
 import org.junit.Test;
 import org.sonar.wsclient.issue.Issue;
 import org.sonar.wsclient.issue.IssueQuery;
 import org.sonar.wsclient.services.Measure;
 import org.sonar.wsclient.services.Resource;
 import org.sonar.wsclient.services.ResourceQuery;
+import util.ProjectAnalysis;
+import util.ProjectAnalysisRule;
 
 import static issue.suite.IssueTestSuite.ORCHESTRATOR;
 import static issue.suite.IssueTestSuite.searchIssues;
 import static org.assertj.core.api.Assertions.assertThat;
-import static util.ItUtils.runProjectAnalysis;
 
 /**
  * Tests the extension point IssueFilter
  */
 public class IssueFilterExtensionTest {
 
-  private static final String MULTI_MODULES_SAMPLE_PROJECT_NAME = "com.sonarsource.it.samples:multi-modules-sample";
-
   @ClassRule
   public static Orchestrator orchestrator = ORCHESTRATOR;
+  @Rule
+  public final ProjectAnalysisRule projectAnalysisRule = ProjectAnalysisRule.from(orchestrator);
 
-  @Before
-  public void resetData() {
-    orchestrator.resetData();
-    orchestrator.getServer().restoreProfile(FileLocation.ofClasspath("/issue/suite/IssueFilterExtensionTest/xoo-with-many-rules.xml"));
-  }
+  private final String manyRuleProfileKey = projectAnalysisRule.registerProfile("/issue/suite/IssueFilterExtensionTest/xoo-with-many-rules.xml");
+  private final String xooMultiModuleProjectKey = projectAnalysisRule.registerProject("shared/xoo-multi-modules-sample");
+  private final ProjectAnalysis analysis = projectAnalysisRule.newProjectAnalysis(xooMultiModuleProjectKey)
+    .withQualityProfile(manyRuleProfileKey);
 
   @Test
   public void should_filter_files() throws Exception {
-    orchestrator.getServer().provisionProject(MULTI_MODULES_SAMPLE_PROJECT_NAME, "Sonar :: Integration Tests :: Multi-modules Sample");
-    orchestrator.getServer().associateProjectToQualityProfile(MULTI_MODULES_SAMPLE_PROJECT_NAME, "xoo", "with-many-rules");
-    runProjectAnalysis(orchestrator, "shared/xoo-multi-modules-sample",
-      "sonar.exclusions", "**/HelloA1.xoo");
+    analysis.withProperties("sonar.exclusions", "**/HelloA1.xoo").run();
 
     List<Issue> issues = searchIssues();
     assertThat(issues).isNotEmpty();
@@ -66,30 +62,27 @@ public class IssueFilterExtensionTest {
       assertThat(issue.componentKey()).doesNotContain("HelloA1");
     }
 
-    assertThat(getMeasure(MULTI_MODULES_SAMPLE_PROJECT_NAME, "violations").getIntValue()).isEqualTo(issues.size());
+    assertThat(getMeasure(xooMultiModuleProjectKey, "violations").getIntValue()).isEqualTo(issues.size());
   }
 
   @Test
   public void should_filter_issues() {
-    // first analysis without isssue-filter
-    orchestrator.getServer().provisionProject(MULTI_MODULES_SAMPLE_PROJECT_NAME, "Sonar :: Integration Tests :: Multi-modules Sample");
-    orchestrator.getServer().associateProjectToQualityProfile(MULTI_MODULES_SAMPLE_PROJECT_NAME, "xoo", "with-many-rules");
-    runProjectAnalysis(orchestrator, "shared/xoo-multi-modules-sample");
+    // first analysis without issue-filter
+    analysis.run();
 
     // Issue filter removes issues on lines < 5
     // Deprecated violation filter removes issues detected by PMD
-    List<Issue> unresolvedIssues = searchResolvedIssues(MULTI_MODULES_SAMPLE_PROJECT_NAME);
+    List<Issue> unresolvedIssues = searchResolvedIssues(xooMultiModuleProjectKey);
     int issuesBeforeLine5 = countIssuesBeforeLine5(unresolvedIssues);
     int pmdIssues = countModuleIssues(unresolvedIssues);
     assertThat(issuesBeforeLine5).isGreaterThan(0);
     assertThat(pmdIssues).isGreaterThan(0);
 
     // Enable issue filters
-    runProjectAnalysis(orchestrator, "shared/xoo-multi-modules-sample",
-      "enableIssueFilters", "true");
+    analysis.withProperties("enableIssueFilters", "true").run();
 
-    unresolvedIssues = searchResolvedIssues(MULTI_MODULES_SAMPLE_PROJECT_NAME);
-    List<Issue> resolvedIssues = searchUnresolvedIssues(MULTI_MODULES_SAMPLE_PROJECT_NAME);
+    unresolvedIssues = searchResolvedIssues(xooMultiModuleProjectKey);
+    List<Issue> resolvedIssues = searchUnresolvedIssues(xooMultiModuleProjectKey);
     assertThat(countIssuesBeforeLine5(unresolvedIssues)).isZero();
     assertThat(countModuleIssues(unresolvedIssues)).isZero();
     assertThat(countModuleIssues(resolvedIssues)).isGreaterThan(0);
index f7a89303d536b04974493db76a387adb50bdf36e..7dfb307d3fc8a82cba2b380df8b4b4140c9e99d6 100644 (file)
 package issue.suite;
 
 import com.sonar.orchestrator.Orchestrator;
-import com.sonar.orchestrator.locator.FileLocation;
 import java.util.List;
 import org.junit.Before;
 import org.junit.ClassRule;
+import org.junit.Rule;
 import org.junit.Test;
 import org.sonar.wsclient.issue.Issue;
 import org.sonar.wsclient.issue.IssueQuery;
+import util.ProjectAnalysis;
+import util.ProjectAnalysisRule;
 
 import static issue.suite.IssueTestSuite.ORCHESTRATOR;
 import static issue.suite.IssueTestSuite.searchIssues;
 import static org.assertj.core.api.Assertions.assertThat;
-import static util.ItUtils.runProjectAnalysis;
-import static util.ItUtils.setServerProperty;
 
 public class IssuePurgeTest {
 
   @ClassRule
   public static Orchestrator orchestrator = ORCHESTRATOR;
+  @Rule
+  public final ProjectAnalysisRule projectAnalysisRule = ProjectAnalysisRule.from(orchestrator);
+
+  private ProjectAnalysis xooSampleAnalysis;
+  private ProjectAnalysis xooMultiModuleAnalysis;
 
   @Before
-  public void deleteAnalysisData() {
-    orchestrator.resetData();
-    // reset settings before test
-    setServerProperty(orchestrator, "sonar.dbcleaner.daysBeforeDeletingClosedIssues", null);
-    orchestrator.getServer().restoreProfile(FileLocation.ofClasspath("/issue/suite/IssuePurgeTest/with-many-rules.xml"));
+  public void setUp() throws Exception {
+    String manyRulesProfile = projectAnalysisRule.registerProfile("/issue/suite/IssuePurgeTest/with-many-rules.xml");
+    String xooSampleProjectKey = projectAnalysisRule.registerProject("shared/xoo-sample");
+    this.xooSampleAnalysis = projectAnalysisRule.newProjectAnalysis(xooSampleProjectKey)
+      .withQualityProfile(manyRulesProfile);
+    String xooMultiModuleProjectKey = projectAnalysisRule.registerProject("shared/xoo-multi-modules-sample");
+    this.xooMultiModuleAnalysis = projectAnalysisRule.newProjectAnalysis(xooMultiModuleProjectKey)
+      .withQualityProfile(manyRulesProfile);
   }
 
   /**
@@ -52,14 +60,13 @@ public class IssuePurgeTest {
    */
   @Test
   public void purge_old_closed_issues() throws Exception {
-    setServerProperty(orchestrator, "sonar.dbcleaner.daysBeforeDeletingClosedIssues", "5000");
+    projectAnalysisRule.setServerProperty("sonar.dbcleaner.daysBeforeDeletingClosedIssues", "5000");
 
     // Generate some issues
-    orchestrator.getServer().provisionProject("sample", "Sample");
-    orchestrator.getServer().associateProjectToQualityProfile("sample", "xoo", "with-many-rules");
-    runProjectAnalysis(orchestrator, "shared/xoo-sample",
+    xooSampleAnalysis.withProperties(
       "sonar.dynamicAnalysis", "false",
-      "sonar.projectDate", "2014-10-01");
+      "sonar.projectDate", "2014-10-01")
+      .run();
 
     // All the issues are open
     List<Issue> issues = searchIssues();
@@ -69,10 +76,12 @@ public class IssuePurgeTest {
 
     // Second scan with empty profile -> all issues are resolved and closed
     // -> Not deleted because less than 5000 days long
-    orchestrator.getServer().associateProjectToQualityProfile("sample", "xoo", "empty");
-    runProjectAnalysis(orchestrator, "shared/xoo-sample",
-      "sonar.dynamicAnalysis", "false",
-      "sonar.projectDate", "2014-10-15");
+    xooSampleAnalysis
+      .withXooEmptyProfile()
+      .withProperties(
+        "sonar.dynamicAnalysis", "false",
+        "sonar.projectDate", "2014-10-15")
+      .run();
     issues = searchIssues();
     assertThat(issues).isNotEmpty();
     for (Issue issue : issues) {
@@ -81,11 +90,13 @@ public class IssuePurgeTest {
     }
 
     // Third scan -> closed issues are deleted
-    setServerProperty(orchestrator, "sonar.dbcleaner.daysBeforeDeletingClosedIssues", "1");
+    projectAnalysisRule.setServerProperty("sonar.dbcleaner.daysBeforeDeletingClosedIssues", "1");
 
-    runProjectAnalysis(orchestrator, "shared/xoo-sample",
-      "sonar.dynamicAnalysis", "false",
-      "sonar.projectDate", "2014-10-20");
+    xooSampleAnalysis.withXooEmptyProfile()
+      .withProperties(
+        "sonar.dynamicAnalysis", "false",
+        "sonar.projectDate", "2014-10-20")
+      .run();
     assertThat(searchIssues(IssueQuery.create())).isEmpty();
   }
 
@@ -94,12 +105,10 @@ public class IssuePurgeTest {
    */
   @Test
   public void resolve_issues_when_removing_module() throws Exception {
-    orchestrator.getServer().provisionProject("com.sonarsource.it.samples:multi-modules-sample", "Sonar :: Integration Tests :: Multi-modules Sample");
-    orchestrator.getServer().associateProjectToQualityProfile("com.sonarsource.it.samples:multi-modules-sample", "xoo", "with-many-rules");
-
     // Generate some issues
-    runProjectAnalysis(orchestrator, "shared/xoo-multi-modules-sample",
-      "sonar.dynamicAnalysis", "false");
+    xooMultiModuleAnalysis
+      .withProperties("sonar.dynamicAnalysis", "false")
+      .run();
 
     // All the issues are open
     List<Issue> issues = searchIssues();
@@ -112,9 +121,11 @@ public class IssuePurgeTest {
     assertThat(issuesOnModuleB).isEqualTo(28);
 
     // Second scan without module B -> issues on module B are resolved as removed and closed
-    runProjectAnalysis(orchestrator, "shared/xoo-multi-modules-sample",
-      "sonar.dynamicAnalysis", "false",
-      "sonar.modules", "module_a");
+    xooMultiModuleAnalysis
+      .withProperties(
+        "sonar.dynamicAnalysis", "false",
+        "sonar.modules", "module_a")
+      .run();
 
     // Resolved should should all be mark as REMOVED and affect to module b
     List<Issue> reloadedIssues = searchIssues(IssueQuery.create().resolved(true));
index 7070e1d74dcdfe4616d72f22d6a61549c331c1da..2e0f567c35bfd74dc41e78f19617bfdf6ea541fa 100644 (file)
@@ -1,40 +1,60 @@
+/*
+ * 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 issue.suite;
 
 import com.sonar.orchestrator.Orchestrator;
-import com.sonar.orchestrator.build.SonarRunner;
-import com.sonar.orchestrator.locator.FileLocation;
 import java.util.List;
 import org.junit.Before;
 import org.junit.ClassRule;
+import org.junit.Rule;
 import org.junit.Test;
 import org.sonar.wsclient.issue.Issue;
 import org.sonar.wsclient.issue.IssueQuery;
+import util.ProjectAnalysis;
+import util.ProjectAnalysisRule;
 
 import static issue.suite.IssueTestSuite.adminIssueClient;
 import static issue.suite.IssueTestSuite.searchIssueByKey;
 import static issue.suite.IssueTestSuite.searchIssues;
 import static issue.suite.IssueTestSuite.searchRandomIssue;
 import static org.assertj.core.api.Assertions.assertThat;
-import static util.ItUtils.projectDir;
 
 public class IssueWorkflowTest {
 
   @ClassRule
   public static Orchestrator orchestrator = IssueTestSuite.ORCHESTRATOR;
 
-  Issue issue;
-  SonarRunner scan;
+  @Rule
+  public final ProjectAnalysisRule projectAnalysisRule = ProjectAnalysisRule.from(orchestrator);
 
-  @Before
-  public void resetData() {
-    orchestrator.resetData();
-
-    orchestrator.getServer().restoreProfile(FileLocation.ofClasspath("/issue/suite/IssueWorkflowTest/xoo-one-issue-per-line-profile.xml"));
-    orchestrator.getServer().provisionProject("workflow", "Workflow");
-    orchestrator.getServer().associateProjectToQualityProfile("workflow", "xoo", "xoo-one-issue-per-line-profile");
+  private ProjectAnalysis analysisWithIssues;
+  private ProjectAnalysis analysisWithoutIssues;
+  private Issue issue;
 
-    scan = SonarRunner.create(projectDir("issue/workflow"));
-    orchestrator.executeBuild(scan);
+  @Before
+  public void before() {
+    String oneIssuePerFileProfileKey = projectAnalysisRule.registerProfile("/issue/suite/IssueWorkflowTest/xoo-one-issue-per-line-profile.xml");
+    String analyzedProjectKey = projectAnalysisRule.registerProject("issue/workflow");
+    analysisWithIssues = projectAnalysisRule.newProjectAnalysis(analyzedProjectKey).withQualityProfile(oneIssuePerFileProfileKey);
+    analysisWithoutIssues = analysisWithIssues.withXooEmptyProfile();
+    analysisWithIssues.run();
 
     issue = searchRandomIssue();
   }
@@ -49,8 +69,7 @@ public class IssueWorkflowTest {
     assertThat(issues).isNotEmpty();
 
     // re-analyze with profile "empty". The rule is disabled so the issues must be closed
-    orchestrator.getServer().associateProjectToQualityProfile("workflow", "xoo", "empty");
-    orchestrator.executeBuild(scan);
+    analysisWithoutIssues.run();
     issues = searchIssues(IssueQuery.create().rules("xoo:OneIssuePerLine"));
     assertThat(issues).isNotEmpty();
     for (Issue issue : issues) {
@@ -118,8 +137,7 @@ public class IssueWorkflowTest {
     assertThat(falsePositive.creationDate()).isEqualTo(issue.creationDate());
 
     // scan without any rules -> confirmed is closed
-    orchestrator.getServer().associateProjectToQualityProfile("workflow", "xoo", "empty");
-    orchestrator.executeBuild(scan);
+    analysisWithoutIssues.run();
     Issue closed = searchIssueByKey(issue.key());
     assertThat(closed.status()).isEqualTo("CLOSED");
     assertThat(closed.resolution()).isEqualTo("REMOVED");
@@ -141,7 +159,7 @@ public class IssueWorkflowTest {
     assertThat(resolvedIssue.updateDate().before(issue.updateDate())).isFalse();
 
     // re-execute scan, with the same Q profile -> the issue has not been fixed
-    orchestrator.executeBuild(scan);
+    analysisWithIssues.run();
 
     // reload issue
     Issue reopenedIssue = searchIssueByKey(issue.key());
@@ -167,8 +185,7 @@ public class IssueWorkflowTest {
     assertThat(resolvedIssue.closeDate()).isNull();
 
     // re-execute scan without rules -> the issue is removed with resolution "REMOVED"
-    orchestrator.getServer().associateProjectToQualityProfile("workflow", "xoo", "empty");
-    orchestrator.executeBuild(scan);
+    analysisWithoutIssues.run();
 
     // reload issue
     Issue closedIssue = searchIssueByKey(issue.key());
@@ -217,7 +234,7 @@ public class IssueWorkflowTest {
     assertThat(falsePositive.creationDate()).isEqualTo(issue.creationDate());
 
     // re-execute the same scan
-    orchestrator.executeBuild(scan);
+    analysisWithIssues.run();
 
     // refresh
     Issue reloaded = searchIssueByKey(falsePositive.key());
@@ -240,8 +257,7 @@ public class IssueWorkflowTest {
     assertThat(falsePositive.creationDate()).isEqualTo(issue.creationDate());
 
     // scan without any rules -> false-positive is closed
-    orchestrator.getServer().associateProjectToQualityProfile("workflow", "xoo", "empty");
-    orchestrator.executeBuild(scan);
+    analysisWithoutIssues.run();
     Issue closed = searchIssueByKey(issue.key());
     assertThat(closed.status()).isEqualTo("CLOSED");
     assertThat(closed.resolution()).isEqualTo("REMOVED");
@@ -276,8 +292,7 @@ public class IssueWorkflowTest {
     adminIssueClient().doTransition(issue.key(), "resolve");
 
     // re-execute scan without rules -> the issue is closed
-    orchestrator.getServer().associateProjectToQualityProfile("workflow", "xoo", "empty");
-    orchestrator.executeBuild(scan);
+    analysisWithoutIssues.run();
 
     // user try to reopen the issue
     assertThat(adminIssueClient().transitions(issue.key())).isEmpty();
index aa986112501c7ae20ba2cc35af2f5c3f19abb97a..7efb925dcea01ddce3f0a798e407f6127ceadd2a 100644 (file)
@@ -1,7 +1,21 @@
 /*
- * 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 issue.suite;
 
diff --git a/it/it-tests/src/test/java/util/LoadedProfiles.java b/it/it-tests/src/test/java/util/LoadedProfiles.java
new file mode 100644 (file)
index 0000000..3f79564
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * 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 util;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+final class LoadedProfiles {
+  private final Map<String, Profile> profileStatesPerProfileKey = new HashMap<>();
+
+  public LoadedProfiles() {
+    init();
+  }
+
+  public String loadProfile(String relativePathToProfile) {
+    try {
+      URL resource = getClass().getResource(relativePathToProfile);
+      Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(resource.openStream());
+
+      String profileKey = null;
+      String languageKey = null;
+
+      Element documentElement = document.getDocumentElement();
+      checkArgument("profile".equals(documentElement.getNodeName()), "%s is not a quality profile file. Root node is not %s", resource.toURI().toString());
+      NodeList childNodes = documentElement.getChildNodes();
+      for (int i = 0; i < childNodes.getLength(); i++) {
+        Node childNode = childNodes.item(i);
+        if ("name".equals(childNode.getNodeName())) {
+          profileKey = childNode.getChildNodes().item(0).getNodeValue();
+        } else if ("language".equals(childNode.getNodeName())) {
+          languageKey = childNode.getChildNodes().item(0).getNodeValue();
+        }
+      }
+      checkArgument(profileKey != null, "Quality profile file %s is missing profile key", resource.toURI().toString());
+      checkArgument(languageKey != null, "Quality profile file %s is missing language key", resource.toURI().toString());
+      this.profileStatesPerProfileKey.put(profileKey, new Profile(profileKey, languageKey, relativePathToProfile));
+
+      return profileKey;
+    } catch (URISyntaxException | SAXException | IOException | ParserConfigurationException e) {
+      throw new RuntimeException("Can not load quality profile " + relativePathToProfile, e);
+    }
+  }
+
+  public Profile getState(String qualityProfileKey) {
+    Profile profile = this.profileStatesPerProfileKey.get(qualityProfileKey);
+    checkArgument(profile != null, "Quality Profile with key %s is unknown to %s", qualityProfileKey, ProjectAnalysisRule.class.getSimpleName());
+    return profile;
+  }
+
+  public void reset() {
+    this.profileStatesPerProfileKey.clear();
+    init();
+  }
+
+  private void init() {
+    this.profileStatesPerProfileKey.put(Profile.XOO_EMPTY_PROFILE.getProfileKey(), Profile.XOO_EMPTY_PROFILE);
+  }
+}
diff --git a/it/it-tests/src/test/java/util/LoadedProjects.java b/it/it-tests/src/test/java/util/LoadedProjects.java
new file mode 100644 (file)
index 0000000..c40082d
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * 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 util;
+
+import com.google.common.base.Throwables;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+
+final class LoadedProjects {
+
+  static final String SONAR_PROJECT_PROPERTIES_FILE_NAME = "sonar-project.properties";
+
+  private final Map<String, ProjectState> projectStatePerProjectKey = new HashMap<>();
+  private final Set<String> knownProjects = new HashSet<>();
+
+  public void reset() {
+    this.projectStatePerProjectKey.clear();
+    this.knownProjects.clear();
+  }
+
+  public String load(String projectRelativePath) {
+    checkState(!knownProjects.contains(projectRelativePath), "Project at location %s already loaded", projectRelativePath);
+
+    File projectDir = ItUtils.projectDir(projectRelativePath);
+    Properties sonarProjectProperties = loadProjectProperties(projectDir);
+    ProjectState projectState = new ProjectState(projectDir, sonarProjectProperties);
+
+    register(projectRelativePath, projectState);
+
+    return projectState.getProjectKey();
+  }
+
+  public ProjectState getProjectState(String projectKey) {
+    ProjectState projectState = this.projectStatePerProjectKey.get(projectKey);
+    checkArgument(projectState != null, "Project with key %s is unknown to %s", projectKey, ProjectAnalysisRule.class.getSimpleName());
+    return projectState;
+  }
+
+  private void register(String projectRelativePath, ProjectState projectState) {
+    this.projectStatePerProjectKey.put(projectState.getProjectKey(), projectState);
+    this.knownProjects.add(projectRelativePath);
+  }
+
+  private static Properties loadProjectProperties(File projectDir) {
+    File sonarPropertiesFile = new File(projectDir, SONAR_PROJECT_PROPERTIES_FILE_NAME);
+    checkArgument(sonarPropertiesFile.exists(), "Can not locate %s in project %s", SONAR_PROJECT_PROPERTIES_FILE_NAME, projectDir.getAbsolutePath());
+
+    Properties properties = new Properties();
+    try {
+      properties.load(new FileReader(sonarPropertiesFile));
+    } catch (IOException e) {
+      Throwables.propagate(e);
+    }
+    return properties;
+  }
+}
diff --git a/it/it-tests/src/test/java/util/Profile.java b/it/it-tests/src/test/java/util/Profile.java
new file mode 100644 (file)
index 0000000..414acac
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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 util;
+
+import javax.annotation.concurrent.Immutable;
+
+@Immutable
+final class Profile {
+  static final Profile XOO_EMPTY_PROFILE = new Profile("empty", "xoo", "n/a");
+
+  private final String profileKey;
+  private final String languageKey;
+  private final String relativePath;
+
+  Profile(String profileKey, String languageKey, String relativePath) {
+    this.profileKey = profileKey;
+    this.languageKey = languageKey;
+    this.relativePath = relativePath;
+  }
+
+  public String getProfileKey() {
+    return profileKey;
+  }
+
+  public String getLanguageKey() {
+    return languageKey;
+  }
+
+  public String getRelativePath() {
+    return relativePath;
+  }
+}
diff --git a/it/it-tests/src/test/java/util/ProjectAnalysis.java b/it/it-tests/src/test/java/util/ProjectAnalysis.java
new file mode 100644 (file)
index 0000000..dbf3eee
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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 util;
+
+public interface ProjectAnalysis {
+  /**
+   * Creates a new ProjectAnalysis which will use the specified quality profile.
+   *
+   * @throws IllegalArgumentException if the quality profile with the specified key has not been loaded into the Rule
+   * @see {@link ProjectAnalysisRule#registerProfile(String)}
+   */
+  ProjectAnalysis withQualityProfile(String qualityProfileKey);
+
+  /**
+   * Creates a new ProjectAnalysis which will use the built-in Xoo empty profile.
+   */
+  ProjectAnalysis withXooEmptyProfile();
+
+  /**
+   * Creates a new ProjectAnalysis which will have debug logs enabled (or not).
+   */
+  ProjectAnalysis withDebugLogs(boolean enabled);
+
+  /**
+   * Creates a new ProjectAnalysis which will have the specified properties.
+   */
+  ProjectAnalysis withProperties(String... properties);
+
+  /**
+   * Execute the current ProjectAnalysis.
+   * This method can be called any number of time and will run the same analysis again and again.
+   */
+  void run();
+}
diff --git a/it/it-tests/src/test/java/util/ProjectAnalysisRule.java b/it/it-tests/src/test/java/util/ProjectAnalysisRule.java
new file mode 100644 (file)
index 0000000..17710c4
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * 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 util;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableMap;
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.build.SonarRunner;
+import com.sonar.orchestrator.locator.FileLocation;
+import java.util.HashSet;
+import java.util.Set;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+import org.junit.rules.ExternalResource;
+import org.sonar.wsclient.services.PropertyDeleteQuery;
+import org.sonar.wsclient.services.PropertyUpdateQuery;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Rule wrapping around an {@link Orchestrator} instance which handle:
+ * <ul>
+ *   <li>automatic reset of Orchestrator data after each method when used as a {@link org.junit.Rule}, 
+ *      after each class when used as a {@link org.junit.ClassRule}</li>
+ *   <li>automatic reset of server properties after each method when used as a {@link org.junit.Rule}, 
+ *      after each class when used as a {@link org.junit.ClassRule}</li>
+ *   <li>associating project with a specific Quality Profile before running an analysis</li>
+ *   <li>provisioning a project before its first analysis so that a Quality Profile can be associated to it</li>
+ *   <li>"restoring" a Quality Profile before an analysis with a specific Quality Profile</li>
+ * </ul>
+ *
+ * This Rule has preparatory methods ({@link #registerProfile(String)} and {@link #registerProject(String)}) which
+ * will allow consequent calls to the rule methods to be based solely on Quality Profile and Project keys. In addition,
+ * these methods returns the Quality Profile and Project key to avoid information duplication (the only magic string
+ * the IT developer has to know is the relative path of the Project or the Quality Profile).
+ *
+ * To run an analysis, use method {@link #newProjectAnalysis(String)} to create a {@link ProjectAnalysis}
+ * object. This object has a {@link ProjectAnalysis#run()} method which will start the analysis.
+ * {@link ProjectAnalysis} can safely be reused to run the same analysis multiple times. In addition, these objects are
+ * immutable. Any call to one of their method which would modify their state will create a new instance which can also
+ * be reused at will.
+ */
+public class ProjectAnalysisRule extends ExternalResource {
+
+  private final Orchestrator orchestrator;
+  private final LoadedProfiles loadedProfiles = new LoadedProfiles();
+  private final LoadedProjects loadedProjects = new LoadedProjects();
+  private final Set<String> serverProperties = new HashSet<>();
+
+  private ProjectAnalysisRule(Orchestrator orchestrator) {
+    this.orchestrator = orchestrator;
+  }
+
+  public static ProjectAnalysisRule from(Orchestrator orchestrator) {
+    return new ProjectAnalysisRule(requireNonNull(orchestrator, "Orchestrator instance can not be null"));
+  }
+
+  /**
+   * @param relativePathToProfile eg.: "/issue/suite/IssueFilterExtensionTest/xoo-with-many-rules.xml"
+   *
+   * @return the quality profile key
+   */
+  public String registerProfile(String relativePathToProfile) {
+    return this.loadedProfiles.loadProfile(relativePathToProfile);
+  }
+
+  /**
+   * @param projectRelativePath path relative to it/it-projects, eg. "shared/xoo-multi-modules-sample"
+   *
+   * @return the project key
+   */
+  public String registerProject(String projectRelativePath) {
+    return this.loadedProjects.load(projectRelativePath);
+  }
+
+  public ProjectAnalysis newProjectAnalysis(String projectKey) {
+    ProjectState projectState = this.loadedProjects.getProjectState(projectKey);
+
+    return new ProjectAnalysisImpl(projectState, null, false);
+  }
+
+  @Override
+  protected void before() throws Throwable {
+    orchestrator.resetData();
+  }
+
+  @Override
+  protected void after() {
+    resetServerProperties();
+    resetRuleState();
+  }
+
+  private void resetServerProperties() {
+    for (String serverProperty : serverProperties) {
+      setServerPropertyImpl(serverProperty, null);
+    }
+  }
+
+  public void setServerPropertyImpl(String key, @Nullable String value) {
+    if (value == null) {
+      orchestrator.getServer().getAdminWsClient().delete(new PropertyDeleteQuery(key));
+    } else {
+      orchestrator.getServer().getAdminWsClient().update(new PropertyUpdateQuery().setKey(key).setValue(value));
+    }
+  }
+
+  public ProjectAnalysisRule setServerProperty(String key, String value) {
+    setServerPropertyImpl(key, value);
+    this.serverProperties.add(key);
+    return this;
+  }
+
+  @Immutable
+  private final class ProjectAnalysisImpl implements ProjectAnalysis {
+    private final ProjectState projectState;
+    @CheckForNull
+    private final Profile qualityProfile;
+    private final boolean debugLogs;
+    @CheckForNull
+    private final String[] properties;
+
+    private ProjectAnalysisImpl(ProjectState projectState, @Nullable Profile qualityProfile, boolean debugLogs, String... properties) {
+      this.projectState = projectState;
+      this.qualityProfile = qualityProfile;
+      this.debugLogs = debugLogs;
+      this.properties = properties;
+    }
+
+    @Override
+    public ProjectAnalysis withQualityProfile(String qualityProfileKey) {
+      checkNotNull(qualityProfileKey, "Specified Quality Profile Key can not be null");
+      if (this.qualityProfile != null && this.qualityProfile.getProfileKey().equals(qualityProfileKey)) {
+        return this;
+      }
+
+      return new ProjectAnalysisImpl(this.projectState, loadedProfiles.getState(qualityProfileKey), this.debugLogs, this.properties);
+    }
+
+    @Override
+    public ProjectAnalysis withXooEmptyProfile() {
+      if (this.qualityProfile == Profile.XOO_EMPTY_PROFILE) {
+        return this;
+      }
+      return new ProjectAnalysisImpl(this.projectState, Profile.XOO_EMPTY_PROFILE, this.debugLogs, this.properties);
+    }
+
+    @Override
+    public ProjectAnalysis withDebugLogs(boolean enabled) {
+      if (this.debugLogs == enabled) {
+        return this;
+      }
+      return new ProjectAnalysisImpl(this.projectState, this.qualityProfile, enabled, this.properties);
+    }
+
+    @Override
+    public ProjectAnalysis withProperties(String... properties) {
+      checkArgument(
+        properties == null || properties.length % 2 == 0,
+        "there must be an even number of String parameters (got %s): key/value pairs must be complete", properties == null ? 0 : properties.length);
+      return new ProjectAnalysisImpl(this.projectState, this.qualityProfile, this.debugLogs, properties);
+    }
+
+    @Override
+    public void run() {
+      provisionIfNecessary();
+      setQualityProfileIfNecessary();
+      runAnalysis();
+    }
+
+    private void setQualityProfileIfNecessary() {
+      if (this.qualityProfile != null) {
+        if (this.qualityProfile != Profile.XOO_EMPTY_PROFILE) {
+          orchestrator.getServer().restoreProfile(FileLocation.ofClasspath(this.qualityProfile.getRelativePath()));
+        }
+        orchestrator.getServer().associateProjectToQualityProfile(
+          this.projectState.getProjectKey(),
+          this.qualityProfile.getLanguageKey(),
+          this.qualityProfile.getProfileKey());
+      }
+    }
+
+    private void provisionIfNecessary() {
+      if (this.qualityProfile != null && !projectState.isProvisioned()) {
+        String projectKey = projectState.getProjectKey();
+        orchestrator.getServer().provisionProject(projectKey, MoreObjects.firstNonNull(projectState.getProjectName(), projectKey));
+        projectState.setProvisioned(true);
+      }
+    }
+
+    private SonarRunner runAnalysis() {
+      SonarRunner sonarRunner = SonarRunner.create(projectState.getProjectDir());
+      ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
+      for (int i = 0; i < this.properties.length; i += 2) {
+        builder.put(this.properties[i], this.properties[i + 1]);
+      }
+      SonarRunner scan = sonarRunner.setDebugLogs(this.debugLogs).setProperties(builder.build());
+      orchestrator.executeBuild(scan);
+      return scan;
+    }
+  }
+
+  private void resetRuleState() {
+    this.loadedProjects.reset();
+    this.loadedProfiles.reset();
+    this.serverProperties.clear();
+  }
+
+}
diff --git a/it/it-tests/src/test/java/util/ProjectState.java b/it/it-tests/src/test/java/util/ProjectState.java
new file mode 100644 (file)
index 0000000..7448a66
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * 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 util;
+
+import java.io.File;
+import java.util.Properties;
+
+import static com.google.common.base.Preconditions.checkState;
+import static util.LoadedProjects.SONAR_PROJECT_PROPERTIES_FILE_NAME;
+
+final class ProjectState {
+  private static final String SONAR_PROJECT_KEY_PROPERTY_NAME = "sonar.projectKey";
+  private static final String SONAR_PROJECT_NAME_PROPERTY_NAME = "sonar.projectName";
+
+  private final File projectDir;
+  private final Properties properties;
+  private boolean provisioned = false;
+
+  ProjectState(File projectDir, Properties properties) {
+    this.projectDir = projectDir;
+    this.properties = properties;
+  }
+
+  public File getProjectDir() {
+    return projectDir;
+  }
+
+  public Properties getProperties() {
+    return properties;
+  }
+
+  public String getProjectKey() {
+    return getProperty(SONAR_PROJECT_KEY_PROPERTY_NAME);
+  }
+
+  public String getProjectName() {
+    return getProperty(SONAR_PROJECT_NAME_PROPERTY_NAME);
+  }
+
+  private String getProperty(String propertyName) {
+    String value = this.properties.getProperty(propertyName);
+    checkState(value != null, "Property %s is missing in %s file in project directory %s",
+      propertyName, SONAR_PROJECT_PROPERTIES_FILE_NAME, projectDir.getAbsolutePath());
+    return value;
+  }
+
+  public boolean isProvisioned() {
+    return provisioned;
+  }
+
+  public void setProvisioned(boolean provisioned) {
+    this.provisioned = provisioned;
+  }
+}