From 27d2f9f67e536f4186d3b763aa8c88e147a31eb0 Mon Sep 17 00:00:00 2001 From: Jacek Date: Thu, 31 Oct 2019 10:36:24 +0100 Subject: [PATCH] SONAR-12630 reorganize log results --- .../sonar/api/impl/utils/JUnitTempFolder.java | 12 +- .../analysis/AnalysisResultReporter.java | 54 +++++++++ .../scanner/qualitygate/QualityGateCheck.java | 35 +++--- .../report/CeTaskReportDataHolder.java | 62 +++++++++++ .../sonar/scanner/report/ReportPublisher.java | 56 +++++----- .../scanner/scan/ProjectScanContainer.java | 4 + .../analysis/AnalysisResultReporterTest.java | 85 ++++++++++++++ .../mediumtest/ScannerMediumTester.java | 3 + .../report/CeTaskReportDataHolderTest.java | 63 +++++++++++ .../scanner/report/ReportPublisherTest.java | 105 ++++++++---------- 10 files changed, 366 insertions(+), 113 deletions(-) create mode 100644 sonar-scanner-engine/src/main/java/org/sonar/scanner/analysis/AnalysisResultReporter.java create mode 100644 sonar-scanner-engine/src/main/java/org/sonar/scanner/report/CeTaskReportDataHolder.java create mode 100644 sonar-scanner-engine/src/test/java/org/sonar/scanner/analysis/AnalysisResultReporterTest.java create mode 100644 sonar-scanner-engine/src/test/java/org/sonar/scanner/report/CeTaskReportDataHolderTest.java diff --git a/sonar-plugin-api-impl/src/main/java/org/sonar/api/impl/utils/JUnitTempFolder.java b/sonar-plugin-api-impl/src/main/java/org/sonar/api/impl/utils/JUnitTempFolder.java index a52da93ca7c..08eb33266b1 100644 --- a/sonar-plugin-api-impl/src/main/java/org/sonar/api/impl/utils/JUnitTempFolder.java +++ b/sonar-plugin-api-impl/src/main/java/org/sonar/api/impl/utils/JUnitTempFolder.java @@ -19,6 +19,9 @@ */ package org.sonar.api.impl.utils; +import java.io.File; +import java.io.IOException; +import javax.annotation.Nullable; import org.apache.commons.lang.StringUtils; import org.junit.rules.ExternalResource; import org.junit.rules.TemporaryFolder; @@ -26,11 +29,6 @@ import org.junit.runner.Description; import org.junit.runners.model.Statement; import org.sonar.api.utils.TempFolder; -import javax.annotation.Nullable; - -import java.io.File; -import java.io.IOException; - /** * Implementation of {@link org.sonar.api.utils.TempFolder} to be used * only in JUnit tests. It wraps {@link org.junit.rules.TemporaryFolder}. @@ -105,4 +103,8 @@ public class JUnitTempFolder extends ExternalResource implements TempFolder { throw new IllegalStateException("Fail to create temp file", e); } } + + public File getRoot() { + return junit.getRoot(); + } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/analysis/AnalysisResultReporter.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/analysis/AnalysisResultReporter.java new file mode 100644 index 00000000000..20c32d0b7c6 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/analysis/AnalysisResultReporter.java @@ -0,0 +1,54 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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.scanner.analysis; + +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.scanner.bootstrap.GlobalAnalysisMode; +import org.sonar.scanner.report.CeTaskReportDataHolder; +import org.sonar.scanner.scan.ScanProperties; + +public class AnalysisResultReporter { + + private static final Logger LOG = Loggers.get(AnalysisResultReporter.class); + + private final CeTaskReportDataHolder ceTaskReportDataHolder; + private final GlobalAnalysisMode analysisMode; + private final ScanProperties scanProperties; + + public AnalysisResultReporter(GlobalAnalysisMode analysisMode, CeTaskReportDataHolder ceTaskReportDataHolder, + ScanProperties scanProperties) { + this.analysisMode = analysisMode; + this.ceTaskReportDataHolder = ceTaskReportDataHolder; + this.scanProperties = scanProperties; + } + + public void report() { + if (analysisMode.isMediumTest()) { + LOG.info("ANALYSIS SUCCESSFUL"); + } else { + LOG.info("ANALYSIS SUCCESSFUL, you can browse {}", ceTaskReportDataHolder.getDashboardUrl()); + if (!scanProperties.shouldWaitForQualityGate()) { + LOG.info("Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report"); + } + LOG.info("More about the report processing at {}", ceTaskReportDataHolder.getCeTaskUrl()); + } + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/qualitygate/QualityGateCheck.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/qualitygate/QualityGateCheck.java index bae3d39b634..871ba195b74 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/qualitygate/QualityGateCheck.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/qualitygate/QualityGateCheck.java @@ -19,21 +19,17 @@ */ package org.sonar.scanner.qualitygate; -import java.io.IOException; import java.io.InputStream; -import java.io.Reader; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; import java.time.Duration; import java.time.temporal.ChronoUnit; import java.util.EnumSet; -import java.util.Properties; import org.picocontainer.Startable; import org.sonar.api.utils.MessageException; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.scanner.bootstrap.DefaultScannerWsClient; +import org.sonar.scanner.bootstrap.GlobalAnalysisMode; +import org.sonar.scanner.report.CeTaskReportDataHolder; import org.sonar.scanner.scan.ScanProperties; import org.sonarqube.ws.Ce; import org.sonarqube.ws.Ce.TaskStatus; @@ -51,14 +47,19 @@ public class QualityGateCheck implements Startable { private static final int POLLING_INTERVAL_IN_MS = 5000; private final DefaultScannerWsClient wsClient; + private final GlobalAnalysisMode analysisMode; + private final CeTaskReportDataHolder reportMetadataHolder; private final ScanProperties properties; private long qualityGateTimeoutInMs; private boolean enabled; - public QualityGateCheck(ScanProperties properties, DefaultScannerWsClient wsClient) { + public QualityGateCheck(DefaultScannerWsClient wsClient, GlobalAnalysisMode analysisMode, CeTaskReportDataHolder reportMetadataHolder, + ScanProperties properties) { this.wsClient = wsClient; this.properties = properties; + this.reportMetadataHolder = reportMetadataHolder; + this.analysisMode = analysisMode; } @Override @@ -73,12 +74,16 @@ public class QualityGateCheck implements Startable { } public void await() { + if (!analysisMode.isMediumTest()) { + throw new IllegalStateException("Quality Gate check not available in medium test mode"); + } + if (!enabled) { - LOG.debug("Quality Gate Check disabled - skipping"); + LOG.debug("Quality Gate check disabled - skipping"); return; } - String taskId = readTaskIdFromMetaDataFile(); + String taskId = reportMetadataHolder.getCeTaskId(); Ce.Task task = waitForCeTaskToFinish(taskId); @@ -93,22 +98,10 @@ public class QualityGateCheck implements Startable { } else if (Status.NONE.equals(qualityGateStatus)) { LOG.info("No Quality Gate is associated with the analysis - skipping"); } else { - LOG.info("Quality Gate - FAILED"); throw MessageException.of("Quality Gate - FAILED"); } } - private String readTaskIdFromMetaDataFile() { - Path file = properties.metadataFilePath(); - try (Reader reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) { - Properties metadata = new Properties(); - metadata.load(reader); - return metadata.getProperty("ceTaskId"); - } catch (IOException e) { - throw new IllegalStateException("Unable to read metadata file: " + file, e); - } - } - private Ce.Task waitForCeTaskToFinish(String taskId) { GetRequest getTaskResultReq = new GetRequest("api/ce/task") .setMediaType(MediaTypes.PROTOBUF) diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/CeTaskReportDataHolder.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/CeTaskReportDataHolder.java new file mode 100644 index 00000000000..a64bf79a855 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/CeTaskReportDataHolder.java @@ -0,0 +1,62 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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.scanner.report; + +import static java.util.Objects.requireNonNull; + +public class CeTaskReportDataHolder { + private boolean initialized = false; + private String ceTaskId; + private String ceTaskUrl; + private String dashboardUrl; + + public void init(String ceTaskId, String ceTaskUrl, String dashboardUrl) { + requireNonNull(ceTaskId, "CE task id must not be null"); + requireNonNull(ceTaskUrl, "CE task url must not be null"); + requireNonNull(dashboardUrl, "Dashboard url map must not be null"); + + this.ceTaskId = ceTaskId; + this.ceTaskUrl = ceTaskUrl; + this.dashboardUrl = dashboardUrl; + + this.initialized = true; + } + + private void verifyInitialized() { + if (!initialized) { + throw new IllegalStateException("Scan report hasn't been published yet"); + } + } + + public String getCeTaskId() { + verifyInitialized(); + return ceTaskId; + } + + public String getDashboardUrl() { + verifyInitialized(); + return dashboardUrl; + } + + public String getCeTaskUrl() { + verifyInitialized(); + return ceTaskUrl; + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java index ca0c8f5c1cf..1c6ce3987b1 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java @@ -40,8 +40,8 @@ import org.sonar.api.utils.TempFolder; import org.sonar.api.utils.ZipUtils; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; -import org.sonar.scanner.bootstrap.GlobalAnalysisMode; import org.sonar.scanner.bootstrap.DefaultScannerWsClient; +import org.sonar.scanner.bootstrap.GlobalAnalysisMode; import org.sonar.scanner.fs.InputModuleHierarchy; import org.sonar.scanner.protocol.output.ScannerReportReader; import org.sonar.scanner.protocol.output.ScannerReportWriter; @@ -79,13 +79,15 @@ public class ReportPublisher implements Startable { private final Server server; private final BranchConfiguration branchConfiguration; private final ScanProperties properties; + private final CeTaskReportDataHolder ceTaskReportDataHolder; private Path reportDir; private ScannerReportWriter writer; private ScannerReportReader reader; public ReportPublisher(ScanProperties properties, DefaultScannerWsClient wsClient, Server server, AnalysisContextReportPublisher contextPublisher, - InputModuleHierarchy moduleHierarchy, GlobalAnalysisMode analysisMode, TempFolder temp, ReportPublisherStep[] publishers, BranchConfiguration branchConfiguration) { + InputModuleHierarchy moduleHierarchy, GlobalAnalysisMode analysisMode, TempFolder temp, ReportPublisherStep[] publishers, BranchConfiguration branchConfiguration, + CeTaskReportDataHolder ceTaskReportDataHolder) { this.wsClient = wsClient; this.server = server; this.contextPublisher = contextPublisher; @@ -95,6 +97,7 @@ public class ReportPublisher implements Startable { this.publishers = publishers; this.branchConfiguration = branchConfiguration; this.properties = properties; + this.ceTaskReportDataHolder = ceTaskReportDataHolder; } @Override @@ -132,15 +135,14 @@ public class ReportPublisher implements Startable { } public void execute() { - String taskId = null; File report = generateReportFile(); if (properties.shouldKeepReport()) { LOG.info("Analysis report generated in " + reportDir); } if (!analysisMode.isMediumTest()) { - taskId = upload(report); + String taskId = upload(report); + prepareAndDumpMetadata(taskId); } - logSuccess(taskId); } private File generateReportFile() { @@ -204,36 +206,28 @@ public class ReportPublisher implements Startable { } } - void logSuccess(@Nullable String taskId) { - if (taskId == null) { - LOG.info("ANALYSIS SUCCESSFUL"); - } else { + void prepareAndDumpMetadata(String taskId) { + Map metadata = new LinkedHashMap<>(); - Map metadata = new LinkedHashMap<>(); + properties.organizationKey().ifPresent(org -> metadata.put("organization", org)); + metadata.put("projectKey", moduleHierarchy.root().key()); + metadata.put("serverUrl", server.getPublicRootUrl()); + metadata.put("serverVersion", server.getVersion()); + properties.branch().ifPresent(branch -> metadata.put("branch", branch)); - properties.organizationKey().ifPresent(org -> metadata.put("organization", org)); - metadata.put("projectKey", moduleHierarchy.root().key()); - metadata.put("serverUrl", server.getPublicRootUrl()); - metadata.put("serverVersion", server.getVersion()); - properties.branch().ifPresent(branch -> metadata.put("branch", branch)); + URL dashboardUrl = buildDashboardUrl(server.getPublicRootUrl(), moduleHierarchy.root().key()); + metadata.put("dashboardUrl", dashboardUrl.toExternalForm()); - URL dashboardUrl = buildDashboardUrl(server.getPublicRootUrl(), moduleHierarchy.root().key()); - metadata.put("dashboardUrl", dashboardUrl.toExternalForm()); + URL taskUrl = HttpUrl.parse(server.getPublicRootUrl()).newBuilder() + .addPathSegment("api").addPathSegment("ce").addPathSegment("task") + .addQueryParameter(ID, taskId) + .build() + .url(); + metadata.put("ceTaskId", taskId); + metadata.put("ceTaskUrl", taskUrl.toExternalForm()); - URL taskUrl = HttpUrl.parse(server.getPublicRootUrl()).newBuilder() - .addPathSegment("api").addPathSegment("ce").addPathSegment("task") - .addQueryParameter(ID, taskId) - .build() - .url(); - metadata.put("ceTaskId", taskId); - metadata.put("ceTaskUrl", taskUrl.toExternalForm()); - - LOG.info("ANALYSIS SUCCESSFUL, you can browse {}", dashboardUrl); - LOG.info("Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report"); - LOG.info("More about the report processing at {}", taskUrl); - - dumpMetadata(metadata); - } + ceTaskReportDataHolder.init(taskId, taskUrl.toExternalForm(), dashboardUrl.toExternalForm()); + dumpMetadata(metadata); } private URL buildDashboardUrl(String publicUrl, String effectiveKey) { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java index 5ab47b36755..45b63ec6cc1 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java @@ -37,6 +37,7 @@ import org.sonar.core.metric.ScannerMetrics; import org.sonar.core.platform.ComponentContainer; import org.sonar.scanner.DefaultFileLinesContextFactory; import org.sonar.scanner.ProjectInfo; +import org.sonar.scanner.analysis.AnalysisResultReporter; import org.sonar.scanner.analysis.AnalysisTempFolderProvider; import org.sonar.scanner.analysis.DefaultAnalysisMode; import org.sonar.scanner.bootstrap.ExtensionInstaller; @@ -248,6 +249,7 @@ public class ProjectScanContainer extends ComponentContainer { TestExecutionPublisher.class, SourcePublisher.class, ChangedLinesPublisher.class, + AnalysisResultReporter.class, //QualityGate check QualityGateCheck.class, @@ -360,6 +362,8 @@ public class ProjectScanContainer extends ComponentContainer { getComponentByType(QualityGateCheck.class).await(); } + getComponentByType(AnalysisResultReporter.class).report(); + getComponentByType(PostJobsExecutor.class).execute(); if (analysisMode.isMediumTest()) { diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/analysis/AnalysisResultReporterTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/analysis/AnalysisResultReporterTest.java new file mode 100644 index 00000000000..f5c9d89b60e --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/analysis/AnalysisResultReporterTest.java @@ -0,0 +1,85 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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.scanner.analysis; + +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.utils.log.LogTester; +import org.sonar.api.utils.log.LoggerLevel; +import org.sonar.scanner.bootstrap.GlobalAnalysisMode; +import org.sonar.scanner.report.CeTaskReportDataHolder; +import org.sonar.scanner.scan.ScanProperties; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class AnalysisResultReporterTest { + @Rule + public LogTester logTester = new LogTester(); + + private CeTaskReportDataHolder ceTaskReportDataHolder = mock(CeTaskReportDataHolder.class); + private GlobalAnalysisMode analysisMode = mock(GlobalAnalysisMode.class); + private ScanProperties scanProperties = mock(ScanProperties.class); + + private AnalysisResultReporter underTest = new AnalysisResultReporter(analysisMode, ceTaskReportDataHolder, scanProperties); + + @Test + public void should_log_simple_success_message() { + when(analysisMode.isMediumTest()).thenReturn(true); + underTest.report(); + + assertThat(logTester.logs(LoggerLevel.INFO)) + .containsOnly("ANALYSIS SUCCESSFUL"); + } + + @Test + public void should_log_success_message_with_urls() { + String ceTaskUrl = "http://sonarqube/taskurl"; + String dashboardUrl = "http://sonarqube/dashboardurl"; + when(ceTaskReportDataHolder.getCeTaskUrl()).thenReturn(ceTaskUrl); + when(ceTaskReportDataHolder.getDashboardUrl()).thenReturn(dashboardUrl); + + underTest.report(); + + assertThat(logTester.logs(LoggerLevel.INFO)) + .containsExactly( + "ANALYSIS SUCCESSFUL, you can browse " + dashboardUrl, + "Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report", + "More about the report processing at " + ceTaskUrl); + } + + @Test + public void should_log_short_success_message_with_urls_if_quality_gate_wait_enabled() { + String ceTaskUrl = "http://sonarqube/taskurl"; + String dashboardUrl = "http://sonarqube/dashboardurl"; + when(ceTaskReportDataHolder.getCeTaskUrl()).thenReturn(ceTaskUrl); + when(ceTaskReportDataHolder.getDashboardUrl()).thenReturn(dashboardUrl); + + when(scanProperties.shouldWaitForQualityGate()).thenReturn(true); + + underTest.report(); + + assertThat(logTester.logs(LoggerLevel.INFO)) + .containsExactly( + "ANALYSIS SUCCESSFUL, you can browse " + dashboardUrl, + "More about the report processing at " + ceTaskUrl); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java index a8769acd7a5..be6be13b727 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java @@ -57,6 +57,7 @@ import org.sonar.batch.bootstrapper.Batch; import org.sonar.batch.bootstrapper.EnvironmentInformation; import org.sonar.batch.bootstrapper.LogOutput; import org.sonar.scanner.bootstrap.GlobalAnalysisMode; +import org.sonar.scanner.report.CeTaskReportDataHolder; import org.sonar.scanner.repository.FileData; import org.sonar.scanner.repository.MetricsRepository; import org.sonar.scanner.repository.MetricsRepositoryLoader; @@ -95,6 +96,7 @@ public class ScannerMediumTester extends ExternalResource { private final FakeQualityProfileLoader qualityProfiles = new FakeQualityProfileLoader(); private final FakeActiveRulesLoader activeRules = new FakeActiveRulesLoader(); private final FakeSonarRuntime sonarRuntime = new FakeSonarRuntime(); + private final CeTaskReportDataHolder reportMetadataHolder = new CeTaskReportDataHolder(); private LogOutput logOutput = null; private static void createWorkingDirs() throws IOException { @@ -294,6 +296,7 @@ public class ScannerMediumTester extends ExternalResource { tester.globalSettingsLoader, tester.projectSettingsLoader, tester.sonarRuntime, + tester.reportMetadataHolder, result) .setLogOutput(tester.logOutput) .build().execute(); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/CeTaskReportDataHolderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/CeTaskReportDataHolderTest.java new file mode 100644 index 00000000000..4d184299729 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/CeTaskReportDataHolderTest.java @@ -0,0 +1,63 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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.scanner.report; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import static org.assertj.core.api.Assertions.assertThat; + +public class CeTaskReportDataHolderTest { + + @Rule + public ExpectedException exception = ExpectedException.none(); + + CeTaskReportDataHolder underTest = new CeTaskReportDataHolder(); + + @Test + public void should_initialize_field() { + String ceTaskId = "ceTaskId"; + String ceTaskUrl = "ceTaskUrl"; + String dashboardUrl = "dashboardUrl"; + underTest.init(ceTaskId, ceTaskUrl, dashboardUrl); + assertThat(underTest.getCeTaskId()).isEqualTo(ceTaskId); + assertThat(underTest.getCeTaskUrl()).isEqualTo(ceTaskUrl); + assertThat(underTest.getDashboardUrl()).isEqualTo(dashboardUrl); + } + + @Test + public void getCeTaskId_should_fail_if_not_initialized() { + exception.expect(IllegalStateException.class); + underTest.getCeTaskId(); + } + + @Test + public void getCeTaskUrl_should_fail_if_not_initialized() { + exception.expect(IllegalStateException.class); + underTest.getCeTaskUrl(); + } + + @Test + public void getDashboardUrl_should_fail_if_not_initialized() { + exception.expect(IllegalStateException.class); + underTest.getDashboardUrl(); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ReportPublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ReportPublisherTest.java index 11781cc6b25..3fc00a31110 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ReportPublisherTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ReportPublisherTest.java @@ -30,23 +30,24 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import org.junit.rules.TemporaryFolder; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.impl.utils.JUnitTempFolder; import org.sonar.api.platform.Server; import org.sonar.api.utils.MessageException; import org.sonar.api.utils.TempFolder; import org.sonar.api.utils.log.LogTester; import org.sonar.api.utils.log.LoggerLevel; -import org.sonar.scanner.bootstrap.GlobalAnalysisMode; import org.sonar.scanner.bootstrap.DefaultScannerWsClient; -import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.scanner.bootstrap.GlobalAnalysisMode; import org.sonar.scanner.fs.InputModuleHierarchy; import org.sonar.scanner.scan.ScanProperties; import org.sonar.scanner.scan.branch.BranchConfiguration; import org.sonarqube.ws.Ce; import org.sonarqube.ws.client.HttpException; +import org.sonarqube.ws.client.MockWsResponse; import org.sonarqube.ws.client.WsRequest; import org.sonarqube.ws.client.WsResponse; @@ -66,7 +67,7 @@ public class ReportPublisherTest { public LogTester logTester = new LogTester(); @Rule - public TemporaryFolder temp = new TemporaryFolder(); + public JUnitTempFolder reportTempFolder = new JUnitTempFolder(); @Rule public ExpectedException exception = ExpectedException.none(); @@ -79,29 +80,26 @@ public class ReportPublisherTest { DefaultInputModule root; AnalysisContextReportPublisher contextPublisher = mock(AnalysisContextReportPublisher.class); BranchConfiguration branchConfiguration = mock(BranchConfiguration.class); - ReportPublisher underTest = new ReportPublisher(properties, wsClient, server, contextPublisher, moduleHierarchy, mode, mock(TempFolder.class), - new ReportPublisherStep[0], branchConfiguration); + CeTaskReportDataHolder reportMetadataHolder = mock(CeTaskReportDataHolder.class); + ReportPublisher underTest = new ReportPublisher(properties, wsClient, server, contextPublisher, moduleHierarchy, mode, reportTempFolder, + new ReportPublisherStep[0], branchConfiguration, reportMetadataHolder); @Before - public void setUp() throws IOException { - root = new DefaultInputModule(ProjectDefinition.create().setKey("org.sonarsource.sonarqube:sonarqube").setBaseDir(temp.newFolder()).setWorkDir(temp.getRoot())); + public void setUp() { + root = new DefaultInputModule( + ProjectDefinition.create().setKey("org.sonarsource.sonarqube:sonarqube").setBaseDir(reportTempFolder.newDir()).setWorkDir(reportTempFolder.getRoot())); when(moduleHierarchy.root()).thenReturn(root); when(server.getPublicRootUrl()).thenReturn("https://localhost"); when(server.getVersion()).thenReturn("6.4"); - when(properties.metadataFilePath()).thenReturn(temp.newFolder().toPath() + when(properties.metadataFilePath()).thenReturn(reportTempFolder.newDir().toPath() .resolve("folder") .resolve("report-task.txt")); } @Test - public void log_and_dump_information_about_report_uploading() throws IOException { + public void dump_information_about_report_uploading() throws IOException { when(properties.organizationKey()).thenReturn(Optional.of("MyOrg")); - underTest.logSuccess("TASK-123"); - - assertThat(logTester.logs(LoggerLevel.INFO)) - .contains("ANALYSIS SUCCESSFUL, you can browse https://localhost/dashboard?id=org.sonarsource.sonarqube%3Asonarqube") - .contains("Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report") - .contains("More about the report processing at https://localhost/api/ce/task?id=TASK-123"); + underTest.prepareAndDumpMetadata("TASK-123"); assertThat(readFileToString(properties.metadataFilePath().toFile(), StandardCharsets.UTF_8)).isEqualTo( "organization=MyOrg\n" + @@ -114,7 +112,7 @@ public class ReportPublisherTest { } @Test - public void parse_upload_error_message() throws IOException { + public void parse_upload_error_message() { HttpException ex = new HttpException("url", 404, "{\"errors\":[{\"msg\":\"Organization with key 'MyOrg' does not exist\"}]}"); WsResponse response = mock(WsResponse.class); when(response.failIfNotSuccessful()).thenThrow(ex); @@ -122,18 +120,14 @@ public class ReportPublisherTest { exception.expect(MessageException.class); exception.expectMessage("Failed to upload report - Organization with key 'MyOrg' does not exist"); - underTest.upload(temp.newFile()); + underTest.upload(reportTempFolder.newFile()); } @Test - public void log_public_url_if_defined_for_main_branch() throws IOException { + public void dump_public_url_if_defined_for_main_branch() throws IOException { when(server.getPublicRootUrl()).thenReturn("https://publicserver/sonarqube"); - underTest.logSuccess("TASK-123"); - - assertThat(logTester.logs(LoggerLevel.INFO)) - .contains("ANALYSIS SUCCESSFUL, you can browse https://publicserver/sonarqube/dashboard?id=org.sonarsource.sonarqube%3Asonarqube") - .contains("More about the report processing at https://publicserver/sonarqube/api/ce/task?id=TASK-123"); + underTest.prepareAndDumpMetadata("TASK-123"); assertThat(readFileToString(properties.metadataFilePath().toFile(), StandardCharsets.UTF_8)).isEqualTo( "projectKey=org.sonarsource.sonarqube:sonarqube\n" + @@ -145,17 +139,14 @@ public class ReportPublisherTest { } @Test - public void log_public_url_if_defined_for_long_living_branches() throws IOException { + public void dump_public_url_if_defined_for_long_living_branches() throws IOException { when(server.getPublicRootUrl()).thenReturn("https://publicserver/sonarqube"); when(branchConfiguration.branchType()).thenReturn(LONG); when(branchConfiguration.branchName()).thenReturn("branch-6.7"); ReportPublisher underTest = new ReportPublisher(properties, wsClient, server, contextPublisher, moduleHierarchy, mode, mock(TempFolder.class), - new ReportPublisherStep[0], branchConfiguration); + new ReportPublisherStep[0], branchConfiguration, reportMetadataHolder); - underTest.logSuccess("TASK-123"); - assertThat(logTester.logs(LoggerLevel.INFO)) - .contains("ANALYSIS SUCCESSFUL, you can browse https://publicserver/sonarqube/dashboard?id=org.sonarsource.sonarqube%3Asonarqube&branch=branch-6.7") - .contains("More about the report processing at https://publicserver/sonarqube/api/ce/task?id=TASK-123"); + underTest.prepareAndDumpMetadata("TASK-123"); assertThat(readFileToString(properties.metadataFilePath().toFile(), StandardCharsets.UTF_8)).isEqualTo( "projectKey=org.sonarsource.sonarqube:sonarqube\n" + @@ -167,17 +158,14 @@ public class ReportPublisherTest { } @Test - public void log_public_url_if_defined_for_short_living_branches() throws IOException { + public void dump_public_url_if_defined_for_short_living_branches() throws IOException { when(server.getPublicRootUrl()).thenReturn("https://publicserver/sonarqube"); when(branchConfiguration.branchType()).thenReturn(SHORT); when(branchConfiguration.branchName()).thenReturn("branch-6.7"); ReportPublisher underTest = new ReportPublisher(properties, wsClient, server, contextPublisher, moduleHierarchy, mode, mock(TempFolder.class), - new ReportPublisherStep[0], branchConfiguration); + new ReportPublisherStep[0], branchConfiguration, reportMetadataHolder); - underTest.logSuccess("TASK-123"); - assertThat(logTester.logs(LoggerLevel.INFO)) - .contains("ANALYSIS SUCCESSFUL, you can browse https://publicserver/sonarqube/dashboard?id=org.sonarsource.sonarqube%3Asonarqube&branch=branch-6.7&resolved=false") - .contains("More about the report processing at https://publicserver/sonarqube/api/ce/task?id=TASK-123"); + underTest.prepareAndDumpMetadata("TASK-123"); assertThat(readFileToString(properties.metadataFilePath().toFile(), StandardCharsets.UTF_8)).isEqualTo( "projectKey=org.sonarsource.sonarqube:sonarqube\n" + @@ -189,20 +177,16 @@ public class ReportPublisherTest { } @Test - public void log_public_url_if_defined_for_pull_request() throws IOException { + public void dump_public_url_if_defined_for_pull_request() throws IOException { when(server.getPublicRootUrl()).thenReturn("https://publicserver/sonarqube"); when(branchConfiguration.branchName()).thenReturn("Bitbucket cloud Widget"); when(branchConfiguration.branchType()).thenReturn(PULL_REQUEST); when(branchConfiguration.pullRequestKey()).thenReturn("105"); ReportPublisher underTest = new ReportPublisher(properties, wsClient, server, contextPublisher, moduleHierarchy, mode, mock(TempFolder.class), - new ReportPublisherStep[0], branchConfiguration); + new ReportPublisherStep[0], branchConfiguration, reportMetadataHolder); - underTest.logSuccess("TASK-123"); - - assertThat(logTester.logs(LoggerLevel.INFO)) - .contains("ANALYSIS SUCCESSFUL, you can browse https://publicserver/sonarqube/dashboard?id=org.sonarsource.sonarqube%3Asonarqube&pullRequest=105") - .contains("More about the report processing at https://publicserver/sonarqube/api/ce/task?id=TASK-123"); + underTest.prepareAndDumpMetadata("TASK-123"); assertThat(readFileToString(properties.metadataFilePath().toFile(), StandardCharsets.UTF_8)).isEqualTo( "projectKey=org.sonarsource.sonarqube:sonarqube\n" + @@ -223,19 +207,28 @@ public class ReportPublisherTest { } @Test - public void log_but_not_dump_information_when_report_is_not_uploaded() throws IOException { - underTest.logSuccess(/* report not uploaded, no server task */null); + public void should_not_dump_information_when_medium_test_enabled() { + when(mode.isMediumTest()).thenReturn(true); + underTest.start(); + underTest.execute(); + assertThat(properties.metadataFilePath()).doesNotExist(); + } - assertThat(logTester.logs(LoggerLevel.INFO)) - .contains("ANALYSIS SUCCESSFUL") - .doesNotContain("dashboard/index"); + @Test + public void should_upload_and_dump_information() { + MockWsResponse submitMockResponse = new MockWsResponse(); + submitMockResponse.setContent(Ce.SubmitResponse.newBuilder().setTaskId("task-1234").build().toByteArray()); + when(wsClient.call(any())).thenReturn(submitMockResponse); + underTest.start(); + underTest.execute(); - assertThat(properties.metadataFilePath()).doesNotExist(); + assertThat(properties.metadataFilePath()).exists(); + assertThat(logTester.logs(LoggerLevel.DEBUG)).contains("Report metadata written to " + properties.metadataFilePath()); } @Test - public void log_and_dump_information_to_custom_path() throws IOException { - underTest.logSuccess("TASK-123"); + public void dump_information_to_custom_path() { + underTest.prepareAndDumpMetadata("TASK-123"); assertThat(properties.metadataFilePath()).exists(); assertThat(logTester.logs(LoggerLevel.DEBUG)).contains("Report metadata written to " + properties.metadataFilePath()); @@ -244,7 +237,7 @@ public class ReportPublisherTest { @Test public void should_not_delete_report_if_property_is_set() throws IOException { when(properties.shouldKeepReport()).thenReturn(true); - Path reportDir = temp.getRoot().toPath().resolve("scanner-report"); + Path reportDir = reportTempFolder.getRoot().toPath().resolve("scanner-report"); Files.createDirectory(reportDir); underTest.start(); @@ -254,7 +247,7 @@ public class ReportPublisherTest { @Test public void should_delete_report_by_default() throws IOException { - Path reportDir = temp.getRoot().toPath().resolve("scanner-report"); + Path reportDir = reportTempFolder.getRoot().toPath().resolve("scanner-report"); Files.createDirectory(reportDir); underTest.start(); @@ -277,7 +270,7 @@ public class ReportPublisherTest { when(response.contentStream()).thenReturn(in); when(wsClient.call(any(WsRequest.class))).thenReturn(response); - underTest.upload(temp.newFile()); + underTest.upload(reportTempFolder.newFile()); ArgumentCaptor capture = ArgumentCaptor.forClass(WsRequest.class); verify(wsClient).call(capture.capture()); @@ -308,7 +301,7 @@ public class ReportPublisherTest { when(response.contentStream()).thenReturn(in); when(wsClient.call(any(WsRequest.class))).thenReturn(response); - underTest.upload(temp.newFile()); + underTest.upload(reportTempFolder.newFile()); ArgumentCaptor capture = ArgumentCaptor.forClass(WsRequest.class); verify(wsClient).call(capture.capture()); @@ -343,7 +336,7 @@ public class ReportPublisherTest { when(response.contentStream()).thenReturn(in); when(wsClient.call(any(WsRequest.class))).thenReturn(response); - underTest.upload(temp.newFile()); + underTest.upload(reportTempFolder.newFile()); ArgumentCaptor capture = ArgumentCaptor.forClass(WsRequest.class); verify(wsClient).call(capture.capture()); -- 2.39.5