aboutsummaryrefslogtreecommitdiffstats
path: root/it/it-tests/src/test/java/batch/suite/IssuesModeTest.java
diff options
context:
space:
mode:
Diffstat (limited to 'it/it-tests/src/test/java/batch/suite/IssuesModeTest.java')
-rw-r--r--it/it-tests/src/test/java/batch/suite/IssuesModeTest.java388
1 files changed, 388 insertions, 0 deletions
diff --git a/it/it-tests/src/test/java/batch/suite/IssuesModeTest.java b/it/it-tests/src/test/java/batch/suite/IssuesModeTest.java
new file mode 100644
index 00000000000..6b448214c7b
--- /dev/null
+++ b/it/it-tests/src/test/java/batch/suite/IssuesModeTest.java
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2009-2014 SonarSource SA
+ * All rights reserved
+ * mailto:contact AT sonarsource DOT com
+ */
+package batch.suite;
+
+import com.google.common.collect.Maps;
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.build.BuildFailureException;
+import com.sonar.orchestrator.build.BuildResult;
+import com.sonar.orchestrator.build.SonarRunner;
+import com.sonar.orchestrator.build.SonarRunnerInstaller;
+import com.sonar.orchestrator.config.FileSystem;
+import com.sonar.orchestrator.locator.FileLocation;
+import com.sonar.orchestrator.version.Version;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import org.apache.commons.lang.ObjectUtils;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.wsclient.SonarClient;
+import org.sonar.wsclient.issue.Issue;
+import org.sonar.wsclient.issue.IssueQuery;
+import org.sonar.wsclient.issue.Issues;
+import org.sonar.wsclient.services.Resource;
+import org.sonar.wsclient.services.ResourceQuery;
+import org.sonar.wsclient.user.UserParameters;
+import util.ItUtils;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+
+public class IssuesModeTest {
+
+ @ClassRule
+ public static Orchestrator orchestrator = BatchTestSuite.ORCHESTRATOR;
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Before
+ public void deleteData() throws IOException {
+ orchestrator.resetData();
+ }
+
+ @Test
+ public void issues_analysis_on_new_project() throws IOException {
+ restoreProfile("one-issue-per-line.xml");
+ orchestrator.getServer().provisionProject("sample", "xoo-sample");
+ orchestrator.getServer().associateProjectToQualityProfile("sample", "xoo", "one-issue-per-line");
+ SonarRunner runner = configureRunnerIssues("shared/xoo-sample", "sonar.verbose", "true");
+ BuildResult result = orchestrator.executeBuild(runner);
+ assertThat(ItUtils.countIssuesInJsonReport(result, true)).isEqualTo(17);
+ }
+
+ @Test
+ public void invalid_incremental_mode() throws IOException {
+ restoreProfile("one-issue-per-line.xml");
+ orchestrator.getServer().provisionProject("sample", "xoo-sample");
+ orchestrator.getServer().associateProjectToQualityProfile("sample", "xoo", "one-issue-per-line");
+ SonarRunner runner = configureRunner("shared/xoo-sample");
+ runner.setProperty("sonar.analysis.mode", "incremental");
+
+ thrown.expect(BuildFailureException.class);
+ BuildResult res = orchestrator.executeBuild(runner);
+
+ assertThat(res.getLogs()).contains("Invalid analysis mode: incremental. This mode was removed in SonarQube 5.2");
+ }
+
+ @Test
+ public void non_associated_mode() throws IOException {
+ restoreProfile("one-issue-per-line.xml");
+ setDefaultQualityProfile("xoo", "one-issue-per-line");
+ SonarRunner runner = configureRunnerIssues("shared/xoo-sample-non-associated");
+ BuildResult result = orchestrator.executeBuild(runner);
+
+ assertThat(result.getLogs()).contains("Local analysis");
+ assertThat(result.getLogs()).contains("Cache not found, synchronizing data");
+ assertThat(ItUtils.countIssuesInJsonReport(result, true)).isEqualTo(17);
+
+ result = orchestrator.executeBuild(runner);
+ assertThat(ItUtils.countIssuesInJsonReport(result, true)).isEqualTo(17);
+ assertThat(result.getLogs()).contains("Found cache");
+ }
+
+ // SONAR-5715
+ @Test
+ public void test_issues_mode_on_project_with_space_in_filename() throws IOException {
+ restoreProfile("with-many-rules.xml");
+ orchestrator.getServer().provisionProject("sample", "xoo-sample-with-spaces");
+ orchestrator.getServer().associateProjectToQualityProfile("sample", "xoo", "with-many-rules");
+
+ SonarRunner runner = configureRunner("batch/xoo-sample-with-spaces/v2");
+ BuildResult result = orchestrator.executeBuild(runner);
+ assertThat(getResource("sample:my sources/main/xoo/sample/My Sample.xoo")).isNotNull();
+
+ runner = configureRunnerIssues("batch/xoo-sample-with-spaces/v2");
+ result = orchestrator.executeBuild(runner);
+ // Analysis is not persisted in database
+ Resource project = getResource("com.sonarsource.it.samples:simple-sample");
+ assertThat(project).isNull();
+ assertThat(result.getLogs()).contains("Issues");
+ assertThat(result.getLogs()).contains("ANALYSIS SUCCESSFUL");
+ }
+
+ @Test
+ public void should_not_fail_on_resources_that_have_existed_before() throws IOException {
+ restoreProfile("with-many-rules.xml");
+ orchestrator.getServer().provisionProject("sample", "xoo-history");
+ orchestrator.getServer().associateProjectToQualityProfile("sample", "xoo", "with-many-rules");
+
+ // First real scan with source
+ SonarRunner runner = configureRunner("shared/xoo-history-v2");
+ BuildResult result = orchestrator.executeBuild(runner);
+ assertThat(getResource("sample:src/main/xoo/sample/ClassAdded.xoo")).isNotNull();
+
+ // Second scan should remove ClassAdded.xoo
+ runner = configureRunner("shared/xoo-history-v1");
+ result = orchestrator.executeBuild(runner);
+ assertThat(getResource("sample:src/main/xoo/sample/ClassAdded.xoo")).isNull();
+
+ // Re-add ClassAdded.xoo in local workspace
+ runner = configureRunnerIssues("shared/xoo-history-v2");
+ result = orchestrator.executeBuild(runner);
+
+ assertThat(getResource("sample:src/main/xoo/sample/ClassAdded.xoo")).isNull();
+ assertThat(result.getLogs()).contains("Issues");
+ assertThat(result.getLogs()).contains("ANALYSIS SUCCESSFUL");
+ }
+
+ @Test
+ public void should_fail_if_plugin_access_secured_properties() throws IOException {
+ // Test access from task (ie BatchSettings)
+ SonarRunner runner = configureRunnerIssues("shared/xoo-sample",
+ "accessSecuredFromTask", "true");
+ BuildResult result = orchestrator.executeBuildQuietly(runner);
+
+ assertThat(result.getLogs()).contains("Access to the secured property 'foo.bar.secured' is not possible in issues mode. "
+ + "The SonarQube plugin which requires this property must be deactivated in issues mode.");
+
+ // Test access from sensor (ie ModuleSettings)
+ runner = configureRunnerIssues("shared/xoo-sample",
+ "accessSecuredFromSensor", "true");
+ result = orchestrator.executeBuildQuietly(runner);
+
+ assertThat(result.getLogs()).contains("Access to the secured property 'foo.bar.secured' is not possible in issues mode. "
+ + "The SonarQube plugin which requires this property must be deactivated in issues mode.");
+ }
+
+ // SONAR-4602
+ @Test
+ public void no_issues_mode_cache_after_new_analysis() throws Exception {
+ restoreProfile("one-issue-per-line.xml");
+ restoreProfile("empty.xml");
+ orchestrator.getServer().provisionProject("sample", "xoo-sample");
+
+ // First run (publish mode)
+ orchestrator.getServer().associateProjectToQualityProfile("sample", "xoo", "empty");
+ SonarRunner runner = configureRunner("shared/xoo-sample");
+ orchestrator.executeBuild(runner);
+
+ // First run issues mode
+ orchestrator.getServer().associateProjectToQualityProfile("sample", "xoo", "one-issue-per-line");
+ runner = configureRunnerIssues("shared/xoo-sample");
+ BuildResult result = orchestrator.executeBuild(runner);
+
+ // As many new issue as lines
+ assertThat(ItUtils.countIssuesInJsonReport(result, true)).isEqualTo(17);
+
+ // Second run (publish mode) should invalidate cache
+ runner = configureRunner("shared/xoo-sample");
+ orchestrator.executeBuild(runner);
+
+ // Second run issues mode
+ runner = configureRunnerIssues("shared/xoo-sample", "sonar.report.export.path", "sonar-report.json");
+ result = orchestrator.executeBuild(runner);
+
+ // No new issue this time
+ assertThat(ItUtils.countIssuesInJsonReport(result, true)).isEqualTo(0);
+ }
+
+ // SONAR-4602
+ @Test
+ public void no_issues_mode_cache_after_profile_change() throws Exception {
+ restoreProfile("one-issue-per-line-empty.xml");
+ orchestrator.getServer().provisionProject("sample", "xoo-sample");
+ orchestrator.getServer().associateProjectToQualityProfile("sample", "xoo", "one-issue-per-line");
+
+ // First run (publish mode)
+ SonarRunner runner = configureRunner("shared/xoo-sample");
+ orchestrator.executeBuild(runner);
+
+ // First issues mode
+ runner = configureRunnerIssues("shared/xoo-sample");
+ BuildResult result = orchestrator.executeBuild(runner);
+
+ // No new issues
+ assertThat(ItUtils.countIssuesInJsonReport(result, true)).isEqualTo(0);
+
+ // Modification of QP should invalidate cache
+ restoreProfile("/one-issue-per-line.xml");
+
+ // Second issues mode
+ runner = configureRunnerIssues("shared/xoo-sample", "sonar.report.export.path", "sonar-report.json");
+ result = orchestrator.executeBuild(runner);
+
+ // As many new issue as lines
+ assertThat(ItUtils.countIssuesInJsonReport(result, true)).isEqualTo(17);
+ }
+
+ // SONAR-4602
+ @Test
+ public void no_issues_mode_cache_after_issue_change() throws Exception {
+ restoreProfile("one-issue-per-line.xml");
+ orchestrator.getServer().provisionProject("sample", "xoo-sample");
+ orchestrator.getServer().associateProjectToQualityProfile("sample", "xoo", "one-issue-per-line");
+
+ // First run (publish mode)
+ SonarRunner runner = configureRunner("shared/xoo-sample");
+ orchestrator.executeBuild(runner);
+
+ // First issues mode
+ runner = configureRunnerIssues("shared/xoo-sample");
+ BuildResult result = orchestrator.executeBuild(runner);
+
+ // 17 issues
+ assertThat(ItUtils.countIssuesInJsonReport(result, false)).isEqualTo(17);
+
+ // Flag one issue as false positive
+ JSONObject obj = ItUtils.getJSONReport(result);
+ String key = ((JSONObject) ((JSONArray) obj.get("issues")).get(0)).get("key").toString();
+ orchestrator.getServer().adminWsClient().issueClient().doTransition(key, "falsepositive");
+
+ // Second issues mode
+ runner = configureRunnerIssues("shared/xoo-sample");
+ result = orchestrator.executeBuild(runner);
+
+ // False positive is not returned
+ assertThat(ItUtils.countIssuesInJsonReport(result, false)).isEqualTo(16);
+ }
+
+ // SONAR-6522
+ @Test
+ public void load_user_name_in_json_report() throws Exception {
+ restoreProfile("one-issue-per-line.xml");
+ orchestrator.getServer().provisionProject("sample", "xoo-sample");
+ orchestrator.getServer().associateProjectToQualityProfile("sample", "xoo", "one-issue-per-line");
+
+ // First run (publish mode)
+ SonarRunner runner = configureRunner("shared/xoo-sample");
+ orchestrator.executeBuild(runner);
+
+ SonarClient client = orchestrator.getServer().adminWsClient();
+
+ Issues issues = client.issueClient().find(IssueQuery.create());
+ Issue issue = issues.list().get(0);
+
+ UserParameters creationParameters = UserParameters.create().login("julien").name("Julien H")
+ .password("password").passwordConfirmation("password");
+ client.userClient().create(creationParameters);
+
+ // Assign issue
+ client.issueClient().assign(issue.key(), "julien");
+
+ // Issues
+ runner = configureRunnerIssues("shared/xoo-sample");
+ BuildResult result = orchestrator.executeBuild(runner);
+
+ JSONObject obj = ItUtils.getJSONReport(result);
+
+ Map<String, String> userNameByLogin = Maps.newHashMap();
+ final JSONArray users = (JSONArray) obj.get("users");
+ if (users != null) {
+ for (Object user : users) {
+ String login = ObjectUtils.toString(((JSONObject) user).get("login"));
+ String name = ObjectUtils.toString(((JSONObject) user).get("name"));
+ userNameByLogin.put(login, name);
+ }
+ }
+ assertThat(userNameByLogin.get("julien")).isEqualTo("Julien H");
+
+ for (Object issueJson : (JSONArray) obj.get("issues")) {
+ JSONObject jsonObject = (JSONObject) issueJson;
+ if (issue.key().equals(jsonObject.get("key"))) {
+ assertThat(jsonObject.get("assignee")).isEqualTo("julien");
+ return;
+ }
+ }
+ fail("Issue not found");
+ }
+
+ @Test
+ public void concurrent_issue_mode_on_existing_project() throws Exception {
+ restoreProfile("one-issue-per-line.xml");
+ orchestrator.getServer().provisionProject("sample", "xoo-sample");
+ orchestrator.getServer().associateProjectToQualityProfile("sample", "xoo", "one-issue-per-line");
+
+ SonarRunner runner = configureRunner("shared/xoo-sample");
+ orchestrator.executeBuild(runner);
+
+ runConcurrentIssues();
+ }
+
+ private void runConcurrentIssues() throws InterruptedException, ExecutionException {
+ // Install sonar-runner in advance to avoid concurrent unzip issues
+ FileSystem fileSystem = orchestrator.getConfiguration().fileSystem();
+ new SonarRunnerInstaller(fileSystem).install(Version.create(SonarRunner.DEFAULT_RUNNER_VERSION), fileSystem.workspace());
+ final int nThreads = 3;
+ ExecutorService executorService = Executors.newFixedThreadPool(nThreads);
+ List<Callable<BuildResult>> tasks = new ArrayList<>();
+ for (int i = 0; i < nThreads; i++) {
+ tasks.add(new Callable<BuildResult>() {
+
+ public BuildResult call() throws Exception {
+ SonarRunner runner = configureRunnerIssues("shared/xoo-sample");
+ return orchestrator.executeBuild(runner);
+ }
+ });
+ }
+
+ boolean expectedError = false;
+ for (Future<BuildResult> result : executorService.invokeAll(tasks)) {
+ try {
+ result.get();
+ } catch (ExecutionException e) {
+ if (e.getCause() instanceof BuildFailureException) {
+ BuildFailureException bfe = (BuildFailureException) e.getCause();
+ assertThat(bfe.getResult().getLogs()).contains("Another SonarQube analysis is already in progress for this project");
+ expectedError = true;
+ }
+ }
+ }
+ if (!expectedError) {
+ fail("At least one of the threads should have failed");
+ }
+ }
+
+ private void restoreProfile(String fileName) {
+ orchestrator.getServer().restoreProfile(FileLocation.ofClasspath("/batch/IssuesModeTest/" + fileName));
+ }
+
+ private Resource getResource(String key) {
+ return orchestrator.getServer().getWsClient().find(ResourceQuery.createForMetrics(key, "lines"));
+ }
+
+ private SonarRunner configureRunnerIssues(String projectDir, String... props) throws IOException {
+ SonarRunner runner = SonarRunner.create(ItUtils.projectDir(projectDir),
+ "sonar.working.directory", temp.newFolder().getAbsolutePath(),
+ "sonar.analysis.mode", "issues",
+ "sonar.report.export.path", "sonar-report.json",
+ "sonar.userHome", temp.newFolder().getAbsolutePath());
+ runner.setProperties(props);
+ return runner;
+ }
+
+ private SonarRunner configureRunner(String projectDir, String... props) throws IOException {
+ SonarRunner runner = SonarRunner.create(ItUtils.projectDir(projectDir),
+ "sonar.working.directory", temp.newFolder().getAbsolutePath(),
+ "sonar.report.export.path", "sonar-report.json",
+ "sonar.userHome", temp.newFolder().getAbsolutePath());
+ runner.setProperties(props);
+ return runner;
+ }
+
+ private void setDefaultQualityProfile(String languageKey, String profileName) {
+ orchestrator.getServer().adminWsClient().post("api/qualityprofiles/set_default",
+ "language", languageKey,
+ "profileName", profileName);
+ }
+
+}