From: Julien HENRY Date: Mon, 19 Jan 2015 15:52:44 +0000 (+0100) Subject: SONAR-6060 Move console issues report to the batch X-Git-Tag: latest-silver-master-#65~173 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=143fa804c8bc20968026caa376f3922f795cb6c7;p=sonarqube.git SONAR-6060 Move console issues report to the batch --- diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java index a531afc577a..42c78ea50ba 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java @@ -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 diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapContainer.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapContainer.java index ca174d3a80a..75071b09ef1 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapContainer.java @@ -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); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/DefaultPhaseExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/phases/DefaultPhaseExecutor.java index f57d09158cd..8e1ad8ad1c3 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/phases/DefaultPhaseExecutor.java +++ b/sonar-batch/src/main/java/org/sonar/batch/phases/DefaultPhaseExecutor.java @@ -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; diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/PreviewPhaseExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/phases/PreviewPhaseExecutor.java index 3ef7161945f..de4bd65a3b4 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/phases/PreviewPhaseExecutor.java +++ b/sonar-batch/src/main/java/org/sonar/batch/phases/PreviewPhaseExecutor.java @@ -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)); diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java index a2d9f3a165f..59dc45098d5 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java @@ -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, diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java index 397e074d546..1a2ad21958e 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java @@ -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 index 00000000000..4e50af72e52 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/report/ConsoleReport.java @@ -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 index 00000000000..803871d2bc0 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/report/IssuesReports.java @@ -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 index 00000000000..78688636482 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/report/JSONReport.java @@ -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 ruleKeys = newHashSet(); + Set userLogins = newHashSet(); + writeJsonIssues(json, ruleKeys, userLogins); + writeJsonComponents(json); + writeJsonRules(json, ruleKeys); + Collection users = userRepository.loadFromWs(new ArrayList(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 ruleKeys, Set 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 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 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 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 index c094a13c9a4..00000000000 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/report/JsonReport.java +++ /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 ruleKeys = newHashSet(); - Set userLogins = newHashSet(); - writeJsonIssues(json, ruleKeys, userLogins); - writeJsonComponents(json); - writeJsonRules(json, ruleKeys); - List users = userFinder != null ? userFinder.findByLogins(new ArrayList(userLogins)) : Collections.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 ruleKeys, Set 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 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 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 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 index 00000000000..f2c84e963f7 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/report/Reporter.java @@ -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 index 00000000000..bd96d273f8c --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/user/User.java @@ -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 index 00000000000..b82325cf0bf --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/user/UserRepository.java @@ -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 users = new ArrayList<>(); + + public List getUsers() { + return users; + } + } + + public Collection loadFromWs(List 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 index 00000000000..6415ba162e8 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/user/package-info.java @@ -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 index 00000000000..23f6b90f2c4 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/ReportsMediumTest.java @@ -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 index 00000000000..4eee91caad3 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/report/JSONReportTest.java @@ -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.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.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.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.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 index 50fdd74d274..00000000000 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/report/JsonReportTest.java +++ /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.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.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.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.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 index 00000000000..7191d185fa3 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/user/UserRepositoryTest.java @@ -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")); + } +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/user/UserFinder.java b/sonar-plugin-api/src/main/java/org/sonar/api/user/UserFinder.java index 115e6a5af5a..84018f8759a 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/user/UserFinder.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/user/UserFinder.java @@ -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);