]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6060 Move console issues report to the batch
authorJulien HENRY <julien.henry@sonarsource.com>
Mon, 19 Jan 2015 15:52:44 +0000 (16:52 +0100)
committerJulien HENRY <julien.henry@sonarsource.com>
Tue, 20 Jan 2015 08:34:15 +0000 (09:34 +0100)
19 files changed:
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapContainer.java
sonar-batch/src/main/java/org/sonar/batch/phases/DefaultPhaseExecutor.java
sonar-batch/src/main/java/org/sonar/batch/phases/PreviewPhaseExecutor.java
sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
sonar-batch/src/main/java/org/sonar/batch/scan/report/ConsoleReport.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/scan/report/IssuesReports.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/scan/report/JSONReport.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/scan/report/JsonReport.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/scan/report/Reporter.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/user/User.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/user/UserRepository.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/user/package-info.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/ReportsMediumTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/scan/report/JSONReportTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/scan/report/JsonReportTest.java [deleted file]
sonar-batch/src/test/java/org/sonar/batch/user/UserRepositoryTest.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/user/UserFinder.java

index a531afc577a9a8eeac8212034d1e7e72964f3b94..42c78ea50ba63805cc42adb2d4ebaf7c51af3f89 100644 (file)
@@ -30,6 +30,7 @@ import org.sonar.batch.maven.DefaultMavenPluginExecutor;
 import org.sonar.batch.maven.MavenProjectBootstrapper;
 import org.sonar.batch.maven.MavenProjectBuilder;
 import org.sonar.batch.maven.MavenProjectConverter;
+import org.sonar.batch.scan.report.ConsoleReport;
 import org.sonar.batch.scm.ScmConfiguration;
 import org.sonar.batch.scm.ScmSensor;
 import org.sonar.batch.source.LinesSensor;
@@ -64,6 +65,9 @@ public class BatchComponents {
 
       LinesSensor.class,
 
+      // Reports
+      ConsoleReport.class,
+
       // dbcleaner
       DefaultPeriodCleaner.class,
       DefaultPurgeTask.class
index ca174d3a80a41676f18a7cd78a81e1d456ca2a36..75071b09ef14d5e1fc91239ccf843fc07d224bb3 100644 (file)
@@ -40,6 +40,7 @@ import org.sonar.batch.referential.DefaultProjectReferentialsLoader;
 import org.sonar.batch.referential.GlobalReferentialsLoader;
 import org.sonar.batch.referential.GlobalReferentialsProvider;
 import org.sonar.batch.referential.ProjectReferentialsLoader;
+import org.sonar.batch.user.UserRepository;
 import org.sonar.core.cluster.NullQueue;
 import org.sonar.core.config.Logback;
 import org.sonar.core.i18n.DefaultI18n;
@@ -104,7 +105,8 @@ public class BootstrapContainer extends ComponentContainer {
       UriReader.class,
       new FileCacheProvider(),
       System2.INSTANCE,
-      new GlobalReferentialsProvider());
+      new GlobalReferentialsProvider(),
+      UserRepository.class);
     if (getComponentByType(PluginsReferential.class) == null) {
       add(DefaultPluginsReferential.class);
     }
index f57d09158cd83790cac57d5aef3552bd3635ab7b..8e1ad8ad1c3fc7e41c34044653a50874f65bf256 100644 (file)
@@ -36,7 +36,7 @@ import org.sonar.batch.rule.QProfileVerifier;
 import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
 import org.sonar.batch.scan.filesystem.FileSystemLogger;
 import org.sonar.batch.scan.maven.MavenPluginsConfigurator;
-import org.sonar.batch.scan.report.JsonReport;
+import org.sonar.batch.scan.report.IssuesReports;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -61,7 +61,7 @@ public final class DefaultPhaseExecutor implements PhaseExecutor {
   private final ProjectInitializer pi;
   private final ScanPersister[] persisters;
   private final FileSystemLogger fsLogger;
-  private final JsonReport jsonReport;
+  private final IssuesReports jsonReport;
   private final DefaultModuleFileSystem fs;
   private final QProfileVerifier profileVerifier;
   private final IssueExclusionsLoader issueExclusionsLoader;
@@ -74,7 +74,7 @@ public final class DefaultPhaseExecutor implements PhaseExecutor {
     PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor,
     SensorContext sensorContext, DefaultIndex index,
     EventBus eventBus, PublishReportJob publishReportJob, ProjectInitializer pi,
-    ScanPersister[] persisters, FileSystemLogger fsLogger, JsonReport jsonReport, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier,
+    ScanPersister[] persisters, FileSystemLogger fsLogger, IssuesReports jsonReport, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier,
     IssueExclusionsLoader issueExclusionsLoader, AnalysisMode analysisMode, DatabaseSession session, ResourcePersister resourcePersister) {
     this.phases = phases;
     this.decoratorsExecutor = decoratorsExecutor;
index 3ef7161945f56b628bcb85a8674b67e00117d23c..de4bd65a3b4e5e667717e56066c7e339eb818fc9 100644 (file)
@@ -29,7 +29,7 @@ import org.sonar.batch.rule.QProfileVerifier;
 import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
 import org.sonar.batch.scan.filesystem.FileSystemLogger;
 import org.sonar.batch.scan.maven.MavenPluginsConfigurator;
-import org.sonar.batch.scan.report.JsonReport;
+import org.sonar.batch.scan.report.IssuesReports;
 
 public final class PreviewPhaseExecutor implements PhaseExecutor {
 
@@ -45,13 +45,13 @@ public final class PreviewPhaseExecutor implements PhaseExecutor {
   private final DefaultModuleFileSystem fs;
   private final QProfileVerifier profileVerifier;
   private final IssueExclusionsLoader issueExclusionsLoader;
-  private final JsonReport jsonReport;
+  private final IssuesReports issuesReport;
 
   public PreviewPhaseExecutor(Phases phases,
     MavenPluginsConfigurator mavenPluginsConfigurator, InitializersExecutor initializersExecutor,
     SensorsExecutor sensorsExecutor,
     SensorContext sensorContext, DefaultIndex index,
-    EventBus eventBus, ProjectInitializer pi, FileSystemLogger fsLogger, JsonReport jsonReport, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier,
+    EventBus eventBus, ProjectInitializer pi, FileSystemLogger fsLogger, IssuesReports jsonReport, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier,
     IssueExclusionsLoader issueExclusionsLoader) {
     this.phases = phases;
     this.mavenPluginsConfigurator = mavenPluginsConfigurator;
@@ -62,7 +62,7 @@ public final class PreviewPhaseExecutor implements PhaseExecutor {
     this.eventBus = eventBus;
     this.pi = pi;
     this.fsLogger = fsLogger;
-    this.jsonReport = jsonReport;
+    this.issuesReport = jsonReport;
     this.fs = fs;
     this.profileVerifier = profileVerifier;
     this.issueExclusionsLoader = issueExclusionsLoader;
@@ -95,13 +95,20 @@ public final class PreviewPhaseExecutor implements PhaseExecutor {
     }
 
     if (module.isRoot()) {
-      jsonReport.execute();
+      issuesReport();
     }
 
     cleanMemory();
     eventBus.fireEvent(new ProjectAnalysisEvent(module, false));
   }
 
+  private void issuesReport() {
+    String stepName = "Issues Reports";
+    eventBus.fireEvent(new BatchStepEvent(stepName, true));
+    issuesReport.execute();
+    eventBus.fireEvent(new BatchStepEvent(stepName, false));
+  }
+
   private void initIssueExclusions() {
     String stepName = "Init issue exclusions";
     eventBus.fireEvent(new BatchStepEvent(stepName, true));
index a2d9f3a165f70b4955d95d2ef0df75fc1568b878..59dc45098d513a1871983d40c44eb5b5075c403d 100644 (file)
@@ -96,7 +96,7 @@ import org.sonar.batch.scan.filesystem.ModuleInputFileCache;
 import org.sonar.batch.scan.filesystem.ProjectFileSystemAdapter;
 import org.sonar.batch.scan.filesystem.StatusDetectionFactory;
 import org.sonar.batch.scan.maven.MavenPluginsConfigurator;
-import org.sonar.batch.scan.report.JsonReport;
+import org.sonar.batch.scan.report.IssuesReports;
 import org.sonar.batch.sensor.DefaultSensorContext;
 import org.sonar.batch.sensor.DefaultSensorStorage;
 import org.sonar.batch.sensor.coverage.CoverageExclusions;
@@ -192,7 +192,7 @@ public class ModuleScanContainer extends ComponentContainer {
       CheckFactory.class,
 
       // report
-      JsonReport.class,
+      IssuesReports.class,
 
       // issues
       IssuableFactory.class,
index 397e074d54624220c2cada7278e3cb61df41b565..1a2ad21958e152ff8add3cb8966eb496c038fdc8 100644 (file)
@@ -83,7 +83,6 @@ import org.sonar.core.test.TestPlanBuilder;
 import org.sonar.core.test.TestPlanPerspectiveLoader;
 import org.sonar.core.test.TestableBuilder;
 import org.sonar.core.test.TestablePerspectiveLoader;
-import org.sonar.core.user.DefaultUserFinder;
 
 public class ProjectScanContainer extends ComponentContainer {
   private boolean sensorMode;
@@ -199,8 +198,6 @@ public class ProjectScanContainer extends ComponentContainer {
       SourcePersister.class,
       ResourceKeyMigration.class,
 
-      DefaultUserFinder.class,
-
       // Rules
       new RulesProvider(),
       new DebtModelProvider(),
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/report/ConsoleReport.java b/sonar-batch/src/main/java/org/sonar/batch/scan/report/ConsoleReport.java
new file mode 100644 (file)
index 0000000..4e50af7
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.scan.report;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.Properties;
+import org.sonar.api.Property;
+import org.sonar.api.PropertyType;
+import org.sonar.api.config.Settings;
+import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.api.rule.Severity;
+import org.sonar.batch.issue.IssueCache;
+
+@Properties({
+  @Property(key = ConsoleReport.CONSOLE_REPORT_ENABLED_KEY, name = "Enable console report", description = "Set this to true to generate a report in console output",
+    type = PropertyType.BOOLEAN, defaultValue = "false")})
+public class ConsoleReport implements Reporter {
+  private static final Logger LOG = LoggerFactory.getLogger(ConsoleReport.class);
+
+  public static final String CONSOLE_REPORT_ENABLED_KEY = "sonar.issuesReport.console.enable";
+  private static final int LEFT_PAD = 10;
+
+  private Settings settings;
+  private Logger logger;
+
+  private IssueCache issueCache;
+
+  public ConsoleReport(Settings settings, IssueCache issueCache) {
+    this(settings, issueCache, LOG);
+  }
+
+  @VisibleForTesting
+  public ConsoleReport(Settings settings, IssueCache issueCache, Logger logger) {
+    this.settings = settings;
+    this.issueCache = issueCache;
+    this.logger = logger;
+  }
+
+  private static class Report {
+    int totalNewIssues = 0;
+    int newBlockerIssues = 0;
+    int newCriticalIssues = 0;
+    int newMajorIssues = 0;
+    int newMinorIssues = 0;
+    int newInfoIssues = 0;
+
+    public void process(DefaultIssue issue) {
+      if (issue.isNew()) {
+        totalNewIssues++;
+        switch (issue.severity()) {
+          case Severity.BLOCKER:
+            newBlockerIssues++;
+            break;
+          case Severity.CRITICAL:
+            newCriticalIssues++;
+            break;
+          case Severity.MAJOR:
+            newMajorIssues++;
+            break;
+          case Severity.MINOR:
+            newMinorIssues++;
+            break;
+          case Severity.INFO:
+            newInfoIssues++;
+            break;
+          default:
+            throw new IllegalStateException("Unknow severity: " + issue.severity());
+        }
+      }
+    }
+  }
+
+  @Override
+  public void execute() {
+    if (settings.getBoolean(CONSOLE_REPORT_ENABLED_KEY)) {
+      Report r = new Report();
+      for (DefaultIssue issue : issueCache.all()) {
+        r.process(issue);
+      }
+      printNewIssues(r);
+    }
+  }
+
+  public void printNewIssues(Report r) {
+    StringBuilder sb = new StringBuilder();
+
+    int newIssues = r.totalNewIssues;
+    sb.append("\n\n-------------  Issues Report  -------------\n\n");
+    if (newIssues > 0) {
+      sb.append(StringUtils.leftPad("+" + newIssues, LEFT_PAD)).append(" issue" + (newIssues > 1 ? "s" : "")).append("\n\n");
+      printNewIssues(sb, r.newBlockerIssues, Severity.BLOCKER, "blocking");
+      printNewIssues(sb, r.newCriticalIssues, Severity.CRITICAL, "critical");
+      printNewIssues(sb, r.newMajorIssues, Severity.MAJOR, "major");
+      printNewIssues(sb, r.newMinorIssues, Severity.MINOR, "minor");
+      printNewIssues(sb, r.newInfoIssues, Severity.INFO, "info");
+    } else {
+      sb.append("  No new issue").append("\n");
+    }
+    sb.append("\n-------------------------------------------\n\n");
+
+    logger.info(sb.toString());
+  }
+
+  private void printNewIssues(StringBuilder sb, int issueCount, String severity, String severityLabel) {
+    if (issueCount > 0) {
+      sb.append(StringUtils.leftPad("+" + issueCount, LEFT_PAD)).append(" ").append(severityLabel).append("\n");
+    }
+  }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/report/IssuesReports.java b/sonar-batch/src/main/java/org/sonar/batch/scan/report/IssuesReports.java
new file mode 100644 (file)
index 0000000..803871d
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.scan.report;
+
+import org.sonar.api.BatchComponent;
+import org.sonar.batch.bootstrap.AnalysisMode;
+
+public class IssuesReports implements BatchComponent {
+
+  private final AnalysisMode analysisMode;
+  private final Reporter[] reporters;
+
+  public IssuesReports(AnalysisMode analysisMode, Reporter... reporters) {
+    this.reporters = reporters;
+    this.analysisMode = analysisMode;
+  }
+
+  public void execute() {
+    if (analysisMode.isPreview() || analysisMode.isSensorMode()) {
+      for (Reporter reporter : reporters) {
+        reporter.execute();
+      }
+    }
+  }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/report/JSONReport.java b/sonar-batch/src/main/java/org/sonar/batch/scan/report/JSONReport.java
new file mode 100644 (file)
index 0000000..7868863
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.scan.report;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Charsets;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.fs.InputDir;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.InputPath;
+import org.sonar.api.batch.fs.internal.DefaultInputDir;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.rule.ActiveRules;
+import org.sonar.api.batch.rule.internal.DefaultActiveRule;
+import org.sonar.api.config.Settings;
+import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.api.platform.Server;
+import org.sonar.api.resources.Project;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.utils.SonarException;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.batch.issue.IssueCache;
+import org.sonar.batch.scan.filesystem.InputPathCache;
+import org.sonar.batch.user.User;
+import org.sonar.batch.user.UserRepository;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Set;
+
+import static com.google.common.collect.Sets.newHashSet;
+
+public class JSONReport implements Reporter {
+
+  private static final Logger LOG = LoggerFactory.getLogger(JSONReport.class);
+  private final Settings settings;
+  private final FileSystem fileSystem;
+  private final Server server;
+  private final ActiveRules activeRules;
+  private final IssueCache issueCache;
+  private final InputPathCache fileCache;
+  private final Project rootModule;
+  private final UserRepository userRepository;
+
+  public JSONReport(Settings settings, FileSystem fileSystem, Server server, ActiveRules activeRules, IssueCache issueCache,
+    Project rootModule, InputPathCache fileCache, UserRepository userRepository) {
+    this.settings = settings;
+    this.fileSystem = fileSystem;
+    this.server = server;
+    this.activeRules = activeRules;
+    this.issueCache = issueCache;
+    this.rootModule = rootModule;
+    this.fileCache = fileCache;
+    this.userRepository = userRepository;
+  }
+
+  @Override
+  public void execute() {
+    String exportPath = settings.getString("sonar.report.export.path");
+    if (exportPath != null) {
+      exportResults(exportPath);
+    }
+  }
+
+  private void exportResults(String exportPath) {
+    File exportFile = new File(fileSystem.workDir(), exportPath);
+
+    LOG.info("Export issues to " + exportFile.getAbsolutePath());
+    try (Writer output = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(exportFile), Charsets.UTF_8))) {
+      writeJson(output);
+
+    } catch (IOException e) {
+      throw new IllegalStateException("Unable to write report results in file " + exportFile.getAbsolutePath(), e);
+    }
+  }
+
+  @VisibleForTesting
+  void writeJson(Writer writer) {
+    try {
+      JsonWriter json = JsonWriter.of(writer);
+      json.beginObject();
+      json.prop("version", server.getVersion());
+
+      Set<RuleKey> ruleKeys = newHashSet();
+      Set<String> userLogins = newHashSet();
+      writeJsonIssues(json, ruleKeys, userLogins);
+      writeJsonComponents(json);
+      writeJsonRules(json, ruleKeys);
+      Collection<User> users = userRepository.loadFromWs(new ArrayList<String>(userLogins));
+      writeUsers(json, users);
+      json.endObject().close();
+
+    } catch (IOException e) {
+      throw new SonarException("Unable to write JSON report", e);
+    }
+  }
+
+  private void writeJsonIssues(JsonWriter json, Set<RuleKey> ruleKeys, Set<String> logins) throws IOException {
+    json.name("issues").beginArray();
+    for (DefaultIssue issue : getIssues()) {
+      if (issue.resolution() == null) {
+        json
+          .beginObject()
+          .prop("key", issue.key())
+          .prop("component", issue.componentKey())
+          .prop("line", issue.line())
+          .prop("message", issue.message())
+          .prop("severity", issue.severity())
+          .prop("rule", issue.ruleKey().toString())
+          .prop("status", issue.status())
+          .prop("resolution", issue.resolution())
+          .prop("isNew", issue.isNew())
+          .prop("reporter", issue.reporter())
+          .prop("assignee", issue.assignee())
+          .prop("effortToFix", issue.effortToFix())
+          .propDateTime("creationDate", issue.creationDate())
+          .propDateTime("updateDate", issue.updateDate())
+          .propDateTime("closeDate", issue.closeDate());
+        if (issue.reporter() != null) {
+          logins.add(issue.reporter());
+        }
+        if (issue.assignee() != null) {
+          logins.add(issue.assignee());
+        }
+        json.endObject();
+        ruleKeys.add(issue.ruleKey());
+      }
+    }
+    json.endArray();
+  }
+
+  private void writeJsonComponents(JsonWriter json) throws IOException {
+    json.name("components").beginArray();
+    // Dump modules
+    writeJsonModuleComponents(json, rootModule);
+    for (InputPath inputPath : fileCache.all()) {
+      if (inputPath instanceof InputFile) {
+        InputFile inputFile = (InputFile) inputPath;
+        String key = ((DefaultInputFile) inputFile).key();
+        json
+          .beginObject()
+          .prop("key", key)
+          .prop("path", inputFile.relativePath())
+          .prop("moduleKey", StringUtils.substringBeforeLast(key, ":"))
+          .prop("status", inputFile.status().name())
+          .endObject();
+      } else {
+        InputDir inputDir = (InputDir) inputPath;
+        String key = ((DefaultInputDir) inputDir).key();
+        json
+          .beginObject()
+          .prop("key", key)
+          .prop("path", inputDir.relativePath())
+          .prop("moduleKey", StringUtils.substringBeforeLast(key, ":"))
+          .endObject();
+      }
+
+    }
+    json.endArray();
+  }
+
+  private void writeJsonModuleComponents(JsonWriter json, Project module) {
+    json
+      .beginObject()
+      .prop("key", module.getEffectiveKey())
+      .prop("path", module.getPath())
+      .endObject();
+    for (Project subModule : module.getModules()) {
+      writeJsonModuleComponents(json, subModule);
+    }
+  }
+
+  private void writeJsonRules(JsonWriter json, Set<RuleKey> ruleKeys) throws IOException {
+    json.name("rules").beginArray();
+    for (RuleKey ruleKey : ruleKeys) {
+      json
+        .beginObject()
+        .prop("key", ruleKey.toString())
+        .prop("rule", ruleKey.rule())
+        .prop("repository", ruleKey.repository())
+        .prop("name", getRuleName(ruleKey))
+        .endObject();
+    }
+    json.endArray();
+  }
+
+  private void writeUsers(JsonWriter json, Collection<User> users) throws IOException {
+    json.name("users").beginArray();
+    for (User user : users) {
+      json
+        .beginObject()
+        .prop("login", user.login())
+        .prop("name", user.name())
+        .endObject();
+    }
+    json.endArray();
+  }
+
+  private String getRuleName(RuleKey ruleKey) {
+    DefaultActiveRule rule = (DefaultActiveRule) activeRules.find(ruleKey);
+    return rule != null ? rule.name() : null;
+  }
+
+  @VisibleForTesting
+  Iterable<DefaultIssue> getIssues() {
+    return issueCache.all();
+  }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/report/JsonReport.java b/sonar-batch/src/main/java/org/sonar/batch/scan/report/JsonReport.java
deleted file mode 100644 (file)
index c094a13..0000000
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.batch.scan.report;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Charsets;
-import org.apache.commons.lang.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.sonar.api.BatchComponent;
-import org.sonar.api.batch.fs.FileSystem;
-import org.sonar.api.batch.fs.InputDir;
-import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.fs.InputPath;
-import org.sonar.api.batch.fs.internal.DefaultInputDir;
-import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.batch.rule.ActiveRules;
-import org.sonar.api.batch.rule.internal.DefaultActiveRule;
-import org.sonar.api.config.Settings;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.platform.Server;
-import org.sonar.api.resources.Project;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.api.user.User;
-import org.sonar.api.user.UserFinder;
-import org.sonar.api.utils.SonarException;
-import org.sonar.api.utils.text.JsonWriter;
-import org.sonar.batch.bootstrap.AnalysisMode;
-import org.sonar.batch.events.BatchStepEvent;
-import org.sonar.batch.events.EventBus;
-import org.sonar.batch.issue.IssueCache;
-import org.sonar.batch.scan.filesystem.InputPathCache;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-
-import static com.google.common.collect.Sets.newHashSet;
-
-/**
- * @since 3.6
- */
-
-public class JsonReport implements BatchComponent {
-
-  private static final Logger LOG = LoggerFactory.getLogger(JsonReport.class);
-  private final Settings settings;
-  private final FileSystem fileSystem;
-  private final Server server;
-  private final ActiveRules activeRules;
-  private final IssueCache issueCache;
-  private final EventBus eventBus;
-  private final AnalysisMode analysisMode;
-  private final UserFinder userFinder;
-  private final InputPathCache fileCache;
-  private final Project rootModule;
-
-  public JsonReport(Settings settings, FileSystem fileSystem, Server server, ActiveRules activeRules, IssueCache issueCache,
-    EventBus eventBus, AnalysisMode analysisMode, UserFinder userFinder, Project rootModule, InputPathCache fileCache) {
-    this.settings = settings;
-    this.fileSystem = fileSystem;
-    this.server = server;
-    this.activeRules = activeRules;
-    this.issueCache = issueCache;
-    this.eventBus = eventBus;
-    this.analysisMode = analysisMode;
-    this.userFinder = userFinder;
-    this.rootModule = rootModule;
-    this.fileCache = fileCache;
-  }
-
-  public JsonReport(Settings settings, FileSystem fileSystem, Server server, ActiveRules activeRules, IssueCache issueCache,
-    EventBus eventBus, AnalysisMode analysisMode, Project rootModule, InputPathCache fileCache) {
-    this.settings = settings;
-    this.fileSystem = fileSystem;
-    this.server = server;
-    this.activeRules = activeRules;
-    this.issueCache = issueCache;
-    this.eventBus = eventBus;
-    this.analysisMode = analysisMode;
-    this.userFinder = null;
-    this.rootModule = rootModule;
-    this.fileCache = fileCache;
-  }
-
-  public void execute() {
-    String exportPath = settings.getString("sonar.report.export.path");
-    if (exportPath != null && (analysisMode.isPreview() || analysisMode.isSensorMode())) {
-      eventBus.fireEvent(new BatchStepEvent("JSON report", true));
-      exportResults(exportPath);
-      eventBus.fireEvent(new BatchStepEvent("JSON report", false));
-    }
-  }
-
-  private void exportResults(String exportPath) {
-    File exportFile = new File(fileSystem.workDir(), exportPath);
-
-    LOG.info("Export results to " + exportFile.getAbsolutePath());
-    try (Writer output = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(exportFile), Charsets.UTF_8))) {
-      writeJson(output);
-
-    } catch (IOException e) {
-      throw new IllegalStateException("Unable to write report results in file " + exportFile.getAbsolutePath(), e);
-    }
-  }
-
-  @VisibleForTesting
-  void writeJson(Writer writer) {
-    try {
-      JsonWriter json = JsonWriter.of(writer);
-      json.beginObject();
-      json.prop("version", server.getVersion());
-
-      Set<RuleKey> ruleKeys = newHashSet();
-      Set<String> userLogins = newHashSet();
-      writeJsonIssues(json, ruleKeys, userLogins);
-      writeJsonComponents(json);
-      writeJsonRules(json, ruleKeys);
-      List<User> users = userFinder != null ? userFinder.findByLogins(new ArrayList<String>(userLogins)) : Collections.<User>emptyList();
-      writeUsers(json, users);
-      json.endObject().close();
-
-    } catch (IOException e) {
-      throw new SonarException("Unable to write JSON report", e);
-    }
-  }
-
-  private void writeJsonIssues(JsonWriter json, Set<RuleKey> ruleKeys, Set<String> logins) throws IOException {
-    json.name("issues").beginArray();
-    for (DefaultIssue issue : getIssues()) {
-      if (issue.resolution() == null) {
-        json
-          .beginObject()
-          .prop("key", issue.key())
-          .prop("component", issue.componentKey())
-          .prop("line", issue.line())
-          .prop("message", issue.message())
-          .prop("severity", issue.severity())
-          .prop("rule", issue.ruleKey().toString())
-          .prop("status", issue.status())
-          .prop("resolution", issue.resolution())
-          .prop("isNew", issue.isNew())
-          .prop("reporter", issue.reporter())
-          .prop("assignee", issue.assignee())
-          .prop("effortToFix", issue.effortToFix())
-          .propDateTime("creationDate", issue.creationDate())
-          .propDateTime("updateDate", issue.updateDate())
-          .propDateTime("closeDate", issue.closeDate());
-        if (issue.reporter() != null) {
-          logins.add(issue.reporter());
-        }
-        if (issue.assignee() != null) {
-          logins.add(issue.assignee());
-        }
-        json.endObject();
-        ruleKeys.add(issue.ruleKey());
-      }
-    }
-    json.endArray();
-  }
-
-  private void writeJsonComponents(JsonWriter json) throws IOException {
-    json.name("components").beginArray();
-    // Dump modules
-    writeJsonModuleComponents(json, rootModule);
-    for (InputPath inputPath : fileCache.all()) {
-      if (inputPath instanceof InputFile) {
-        InputFile inputFile = (InputFile) inputPath;
-        String key = ((DefaultInputFile) inputFile).key();
-        json
-          .beginObject()
-          .prop("key", key)
-          .prop("path", inputFile.relativePath())
-          .prop("moduleKey", StringUtils.substringBeforeLast(key, ":"))
-          .prop("status", inputFile.status().name())
-          .endObject();
-      } else {
-        InputDir inputDir = (InputDir) inputPath;
-        String key = ((DefaultInputDir) inputDir).key();
-        json
-          .beginObject()
-          .prop("key", key)
-          .prop("path", inputDir.relativePath())
-          .prop("moduleKey", StringUtils.substringBeforeLast(key, ":"))
-          .endObject();
-      }
-
-    }
-    json.endArray();
-  }
-
-  private void writeJsonModuleComponents(JsonWriter json, Project module) {
-    json
-      .beginObject()
-      .prop("key", module.getEffectiveKey())
-      .prop("path", module.getPath())
-      .endObject();
-    for (Project subModule : module.getModules()) {
-      writeJsonModuleComponents(json, subModule);
-    }
-  }
-
-  private void writeJsonRules(JsonWriter json, Set<RuleKey> ruleKeys) throws IOException {
-    json.name("rules").beginArray();
-    for (RuleKey ruleKey : ruleKeys) {
-      json
-        .beginObject()
-        .prop("key", ruleKey.toString())
-        .prop("rule", ruleKey.rule())
-        .prop("repository", ruleKey.repository())
-        .prop("name", getRuleName(ruleKey))
-        .endObject();
-    }
-    json.endArray();
-  }
-
-  private void writeUsers(JsonWriter json, List<User> users) throws IOException {
-    json.name("users").beginArray();
-    for (User user : users) {
-      json
-        .beginObject()
-        .prop("login", user.login())
-        .prop("name", user.name())
-        .endObject();
-    }
-    json.endArray();
-  }
-
-  private String getRuleName(RuleKey ruleKey) {
-    DefaultActiveRule rule = (DefaultActiveRule) activeRules.find(ruleKey);
-    return rule != null ? rule.name() : null;
-  }
-
-  @VisibleForTesting
-  Iterable<DefaultIssue> getIssues() {
-    return issueCache.all();
-  }
-}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/report/Reporter.java b/sonar-batch/src/main/java/org/sonar/batch/scan/report/Reporter.java
new file mode 100644 (file)
index 0000000..f2c84e9
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.scan.report;
+
+import org.sonar.api.BatchComponent;
+
+public interface Reporter extends BatchComponent {
+
+  public void execute();
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/user/User.java b/sonar-batch/src/main/java/org/sonar/batch/user/User.java
new file mode 100644 (file)
index 0000000..bd96d27
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.user;
+
+import org.apache.commons.lang.builder.EqualsBuilder;
+
+public class User {
+
+  private final String login;
+  private final String name;
+
+  public User(String login, String name) {
+    this.login = login;
+    this.name = name;
+  }
+
+  public String login() {
+    return login;
+  }
+
+  public String name() {
+    return name;
+  }
+
+  // For testing
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null) {
+      return false;
+    }
+    if (obj == this) {
+      return true;
+    }
+    if (obj.getClass() != getClass()) {
+      return false;
+    }
+    User rhs = (User) obj;
+    return new EqualsBuilder()
+      .append(login, rhs.login)
+      .append(name, rhs.name)
+      .isEquals();
+  }
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/user/UserRepository.java b/sonar-batch/src/main/java/org/sonar/batch/user/UserRepository.java
new file mode 100644 (file)
index 0000000..b82325c
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.user;
+
+import com.google.common.base.Joiner;
+import org.sonar.batch.bootstrap.ServerClient;
+import org.sonar.batch.protocol.GsonHelper;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class UserRepository {
+
+  private ServerClient serverClient;
+
+  public UserRepository(ServerClient serverClient) {
+    this.serverClient = serverClient;
+  }
+
+  private static class Users {
+
+    private List<User> users = new ArrayList<>();
+
+    public List<User> getUsers() {
+      return users;
+    }
+  }
+
+  public Collection<User> loadFromWs(List<String> userLogins) {
+    String url = "/api/users/search?format=json&includeDeactivated=true&logins=" + Joiner.on(',').join(userLogins);
+    String json = serverClient.request(url);
+    Users users = GsonHelper.create().fromJson(json, Users.class);
+    return users.getUsers();
+  }
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/user/package-info.java b/sonar-batch/src/main/java/org/sonar/batch/user/package-info.java
new file mode 100644 (file)
index 0000000..6415ba1
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.batch.user;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/ReportsMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/ReportsMediumTest.java
new file mode 100644 (file)
index 0000000..23f6b90
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.mediumtest.issues;
+
+import com.google.common.collect.ImmutableMap;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.batch.mediumtest.TaskResult;
+import org.sonar.batch.protocol.input.ActiveRule;
+import org.sonar.xoo.XooPlugin;
+
+import java.io.File;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ReportsMediumTest {
+
+  @org.junit.Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
+  public BatchMediumTester tester = BatchMediumTester.builder()
+    .registerPlugin("xoo", new XooPlugin())
+    .addDefaultQProfile("xoo", "Sonar Way")
+    .activateRule(new ActiveRule("xoo", "OneIssuePerLine", "One issue per line", "MAJOR", "OneIssuePerLine.internal", "xoo"))
+    .bootstrapProperties(ImmutableMap.of("sonar.analysis.mode", "sensor"))
+    .build();
+
+  @Before
+  public void prepare() {
+    tester.start();
+  }
+
+  @After
+  public void stop() {
+    tester.stop();
+  }
+
+  @Test
+  public void testConsoleReport() throws Exception {
+    File projectDir = new File(ReportsMediumTest.class.getResource("/mediumtest/xoo/sample").toURI());
+
+    TaskResult result = tester
+      .newScanTask(new File(projectDir, "sonar-project.properties"))
+      .property("sonar.issuesReport.console.enable", "true")
+      .start();
+
+    assertThat(result.issues()).hasSize(26);
+  }
+
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/report/JSONReportTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/report/JSONReportTest.java
new file mode 100644 (file)
index 0000000..4eee91c
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.scan.report;
+
+import com.google.common.collect.Lists;
+import com.google.common.io.Resources;
+import org.apache.commons.codec.Charsets;
+import org.json.JSONException;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.skyscreamer.jsonassert.JSONAssert;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.InputPath;
+import org.sonar.api.batch.fs.internal.DefaultFileSystem;
+import org.sonar.api.batch.fs.internal.DefaultInputDir;
+import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile;
+import org.sonar.api.batch.rule.ActiveRules;
+import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
+import org.sonar.api.config.Settings;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.api.platform.Server;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.batch.issue.IssueCache;
+import org.sonar.batch.scan.filesystem.InputPathCache;
+import org.sonar.batch.user.User;
+import org.sonar.batch.user.UserRepository;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.TimeZone;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.anyListOf;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class JSONReportTest {
+
+  private SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
+
+  @org.junit.Rule
+  public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+  JSONReport jsonReport;
+  Resource resource = mock(Resource.class);
+  DefaultFileSystem fs = new DefaultFileSystem();
+  Server server = mock(Server.class);
+  ActiveRules activeRules = mock(ActiveRules.class);
+  Settings settings = new Settings();
+  IssueCache issueCache = mock(IssueCache.class);
+  private UserRepository userRepository;
+
+  @Before
+  public void before() {
+    SIMPLE_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT+02:00"));
+    when(resource.getEffectiveKey()).thenReturn("Action.java");
+    when(server.getVersion()).thenReturn("3.6");
+    userRepository = mock(UserRepository.class);
+    DefaultInputDir inputDir = new DefaultInputDir("struts", "src/main/java/org/apache/struts");
+    DeprecatedDefaultInputFile inputFile = new DeprecatedDefaultInputFile("struts", "src/main/java/org/apache/struts/Action.java");
+    inputFile.setStatus(InputFile.Status.CHANGED);
+    InputPathCache fileCache = mock(InputPathCache.class);
+    when(fileCache.all()).thenReturn(Arrays.<InputPath>asList(inputDir, inputFile));
+    Project rootModule = new Project("struts");
+    Project moduleA = new Project("struts-core");
+    moduleA.setParent(rootModule).setPath("core");
+    Project moduleB = new Project("struts-ui");
+    moduleB.setParent(rootModule).setPath("ui");
+    activeRules = new ActiveRulesBuilder()
+      .create(RuleKey.of("squid", "AvoidCycles")).setName("Avoid Cycles").activate()
+      .build();
+    jsonReport = new JSONReport(settings, fs, server, activeRules, issueCache, rootModule, fileCache, userRepository);
+  }
+
+  @Test
+  public void should_write_json() throws Exception {
+    DefaultIssue issue = new DefaultIssue()
+      .setKey("200")
+      .setComponentKey("struts:src/main/java/org/apache/struts/Action.java")
+      .setRuleKey(RuleKey.of("squid", "AvoidCycles"))
+      .setMessage("There are 2 cycles")
+      .setSeverity("MINOR")
+      .setStatus(Issue.STATUS_OPEN)
+      .setResolution(null)
+      .setLine(1)
+      .setEffortToFix(3.14)
+      .setReporter("julien")
+      .setAssignee("simon")
+      .setCreationDate(SIMPLE_DATE_FORMAT.parse("2013-04-24"))
+      .setUpdateDate(SIMPLE_DATE_FORMAT.parse("2013-04-25"))
+      .setNew(false);
+    when(jsonReport.getIssues()).thenReturn(Lists.newArrayList(issue));
+    User user1 = new User("julien", "Julien");
+    User user2 = new User("simon", "Simon");
+    when(userRepository.loadFromWs(anyListOf(String.class))).thenReturn(Lists.<User>newArrayList(user1, user2));
+
+    StringWriter writer = new StringWriter();
+    jsonReport.writeJson(writer);
+
+    String expected = Resources.toString(Resources.getResource("org/sonar/batch/scan/report/JsonReportTest/report.json"), Charsets.UTF_8);
+    JSONAssert.assertEquals(expected, writer.toString(), false);
+  }
+
+  @Test
+  public void should_exclude_resolved_issues() throws Exception {
+    RuleKey ruleKey = RuleKey.of("squid", "AvoidCycles");
+    DefaultIssue issue = new DefaultIssue()
+      .setKey("200")
+      .setComponentKey("struts:src/main/java/org/apache/struts/Action.java")
+      .setRuleKey(ruleKey)
+      .setStatus(Issue.STATUS_CLOSED)
+      .setResolution(Issue.RESOLUTION_FIXED)
+      .setCreationDate(SIMPLE_DATE_FORMAT.parse("2013-04-24"))
+      .setUpdateDate(SIMPLE_DATE_FORMAT.parse("2013-04-25"))
+      .setCloseDate(SIMPLE_DATE_FORMAT.parse("2013-04-26"))
+      .setNew(false);
+    when(jsonReport.getIssues()).thenReturn(Lists.newArrayList(issue));
+
+    StringWriter writer = new StringWriter();
+    jsonReport.writeJson(writer);
+
+    String expected = Resources.toString(Resources.getResource(
+      "org/sonar/batch/scan/report/JsonReportTest/report-without-resolved-issues.json"), Charsets.UTF_8);
+    JSONAssert.assertEquals(expected, writer.toString(), false);
+  }
+
+  @Test
+  public void should_ignore_components_without_issue() throws JSONException {
+    when(jsonReport.getIssues()).thenReturn(Collections.<DefaultIssue>emptyList());
+
+    StringWriter writer = new StringWriter();
+    jsonReport.writeJson(writer);
+
+    JSONAssert.assertEquals("{\"version\":\"3.6\"}", writer.toString(), false);
+  }
+
+  @Test
+  public void should_export_issues_to_file() throws IOException {
+    File workDir = temporaryFolder.newFolder("sonar");
+    fs.setWorkDir(workDir);
+
+    when(jsonReport.getIssues()).thenReturn(Collections.<DefaultIssue>emptyList());
+
+    settings.setProperty("sonar.report.export.path", "output.json");
+
+    jsonReport.execute();
+
+    assertThat(new File(workDir, "output.json")).exists();
+  }
+
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/report/JsonReportTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/report/JsonReportTest.java
deleted file mode 100644 (file)
index 50fdd74..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.batch.scan.report;
-
-import com.google.common.collect.Lists;
-import com.google.common.io.Resources;
-import org.apache.commons.codec.Charsets;
-import org.json.JSONException;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.skyscreamer.jsonassert.JSONAssert;
-import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.fs.InputPath;
-import org.sonar.api.batch.fs.internal.DefaultFileSystem;
-import org.sonar.api.batch.fs.internal.DefaultInputDir;
-import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile;
-import org.sonar.api.batch.rule.ActiveRules;
-import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
-import org.sonar.api.config.Settings;
-import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.platform.Server;
-import org.sonar.api.resources.Project;
-import org.sonar.api.resources.Resource;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.api.user.User;
-import org.sonar.api.user.UserFinder;
-import org.sonar.batch.bootstrap.AnalysisMode;
-import org.sonar.batch.events.EventBus;
-import org.sonar.batch.issue.IssueCache;
-import org.sonar.batch.scan.filesystem.InputPathCache;
-import org.sonar.core.user.DefaultUser;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.text.SimpleDateFormat;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.TimeZone;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Matchers.anyListOf;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class JsonReportTest {
-
-  private SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
-
-  @org.junit.Rule
-  public TemporaryFolder temporaryFolder = new TemporaryFolder();
-
-  JsonReport jsonReport;
-  Resource resource = mock(Resource.class);
-  DefaultFileSystem fs = new DefaultFileSystem();
-  Server server = mock(Server.class);
-  ActiveRules activeRules = mock(ActiveRules.class);
-  Settings settings = new Settings();
-  IssueCache issueCache = mock(IssueCache.class);
-  private UserFinder userFinder;
-
-  @Before
-  public void before() {
-    SIMPLE_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT+02:00"));
-    when(resource.getEffectiveKey()).thenReturn("Action.java");
-    when(server.getVersion()).thenReturn("3.6");
-    AnalysisMode mode = mock(AnalysisMode.class);
-    when(mode.isPreview()).thenReturn(true);
-    userFinder = mock(UserFinder.class);
-    DefaultInputDir inputDir = new DefaultInputDir("struts", "src/main/java/org/apache/struts");
-    DeprecatedDefaultInputFile inputFile = new DeprecatedDefaultInputFile("struts", "src/main/java/org/apache/struts/Action.java");
-    inputFile.setStatus(InputFile.Status.CHANGED);
-    InputPathCache fileCache = mock(InputPathCache.class);
-    when(fileCache.all()).thenReturn(Arrays.<InputPath>asList(inputDir, inputFile));
-    Project rootModule = new Project("struts");
-    Project moduleA = new Project("struts-core");
-    moduleA.setParent(rootModule).setPath("core");
-    Project moduleB = new Project("struts-ui");
-    moduleB.setParent(rootModule).setPath("ui");
-    activeRules = new ActiveRulesBuilder()
-      .create(RuleKey.of("squid", "AvoidCycles")).setName("Avoid Cycles").activate()
-      .build();
-    jsonReport = new JsonReport(settings, fs, server, activeRules, issueCache, mock(EventBus.class),
-      mode, userFinder, rootModule, fileCache);
-  }
-
-  @Test
-  public void should_write_json() throws Exception {
-    DefaultIssue issue = new DefaultIssue()
-      .setKey("200")
-      .setComponentKey("struts:src/main/java/org/apache/struts/Action.java")
-      .setRuleKey(RuleKey.of("squid", "AvoidCycles"))
-      .setMessage("There are 2 cycles")
-      .setSeverity("MINOR")
-      .setStatus(Issue.STATUS_OPEN)
-      .setResolution(null)
-      .setLine(1)
-      .setEffortToFix(3.14)
-      .setReporter("julien")
-      .setAssignee("simon")
-      .setCreationDate(SIMPLE_DATE_FORMAT.parse("2013-04-24"))
-      .setUpdateDate(SIMPLE_DATE_FORMAT.parse("2013-04-25"))
-      .setNew(false);
-    when(jsonReport.getIssues()).thenReturn(Lists.newArrayList(issue));
-    DefaultUser user1 = new DefaultUser().setLogin("julien").setName("Julien");
-    DefaultUser user2 = new DefaultUser().setLogin("simon").setName("Simon");
-    when(userFinder.findByLogins(anyListOf(String.class))).thenReturn(Lists.<User>newArrayList(user1, user2));
-
-    StringWriter writer = new StringWriter();
-    jsonReport.writeJson(writer);
-
-    String expected = Resources.toString(Resources.getResource("org/sonar/batch/scan/report/JsonReportTest/report.json"), Charsets.UTF_8);
-    JSONAssert.assertEquals(expected, writer.toString(), false);
-  }
-
-  @Test
-  public void should_exclude_resolved_issues() throws Exception {
-    RuleKey ruleKey = RuleKey.of("squid", "AvoidCycles");
-    DefaultIssue issue = new DefaultIssue()
-      .setKey("200")
-      .setComponentKey("struts:src/main/java/org/apache/struts/Action.java")
-      .setRuleKey(ruleKey)
-      .setStatus(Issue.STATUS_CLOSED)
-      .setResolution(Issue.RESOLUTION_FIXED)
-      .setCreationDate(SIMPLE_DATE_FORMAT.parse("2013-04-24"))
-      .setUpdateDate(SIMPLE_DATE_FORMAT.parse("2013-04-25"))
-      .setCloseDate(SIMPLE_DATE_FORMAT.parse("2013-04-26"))
-      .setNew(false);
-    when(jsonReport.getIssues()).thenReturn(Lists.newArrayList(issue));
-
-    StringWriter writer = new StringWriter();
-    jsonReport.writeJson(writer);
-
-    String expected = Resources.toString(Resources.getResource(
-      "org/sonar/batch/scan/report/JsonReportTest/report-without-resolved-issues.json"), Charsets.UTF_8);
-    JSONAssert.assertEquals(expected, writer.toString(), false);
-  }
-
-  @Test
-  public void should_ignore_components_without_issue() throws JSONException {
-    when(jsonReport.getIssues()).thenReturn(Collections.<DefaultIssue>emptyList());
-
-    StringWriter writer = new StringWriter();
-    jsonReport.writeJson(writer);
-
-    JSONAssert.assertEquals("{\"version\":\"3.6\"}", writer.toString(), false);
-  }
-
-  @Test
-  public void should_export_issues_to_file() throws IOException {
-    File workDir = temporaryFolder.newFolder("sonar");
-    fs.setWorkDir(workDir);
-
-    when(jsonReport.getIssues()).thenReturn(Collections.<DefaultIssue>emptyList());
-
-    settings.setProperty("sonar.report.export.path", "output.json");
-
-    jsonReport.execute();
-
-    assertThat(new File(workDir, "output.json")).exists();
-  }
-
-}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/user/UserRepositoryTest.java b/sonar-batch/src/test/java/org/sonar/batch/user/UserRepositoryTest.java
new file mode 100644 (file)
index 0000000..7191d18
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.user;
+
+import org.junit.Test;
+import org.sonar.batch.bootstrap.ServerClient;
+
+import java.util.Arrays;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class UserRepositoryTest {
+
+  @Test
+  public void testLoad() {
+    ServerClient serverClient = mock(ServerClient.class);
+    UserRepository userRepo = new UserRepository(serverClient);
+
+    when(serverClient.request("/api/users/search?format=json&includeDeactivated=true&logins=fmallet,sbrandhof"))
+      .thenReturn(
+        "{ \"users\": [ { \"login\": \"fmallet\", \"name\": \"Freddy Mallet\", \"active\": true, \"email\": \"f@m.com\" }, { \"login\": \"sbrandhof\", \"name\": \"Simon\", \"active\": true } ] }");
+
+    assertThat(userRepo.loadFromWs(Arrays.asList("fmallet", "sbrandhof"))).containsOnly(new User("fmallet", "Freddy Mallet"), new User("sbrandhof", "Simon"));
+  }
+}
index 115e6a5af5ab602f4298f64f878cad923004c291..84018f8759a17c232caa95f34682b441433ea63a 100644 (file)
@@ -19,7 +19,6 @@
  */
 package org.sonar.api.user;
 
-import org.sonar.api.BatchComponent;
 import org.sonar.api.ServerComponent;
 
 import javax.annotation.CheckForNull;
@@ -29,7 +28,7 @@ import java.util.List;
 /**
  * @since 3.6
  */
-public interface UserFinder extends ServerComponent, BatchComponent {
+public interface UserFinder extends ServerComponent {
 
   @CheckForNull
   User findByLogin(String login);