From: Julien HENRY Date: Tue, 30 Apr 2024 12:59:03 +0000 (+0200) Subject: SCANCLI-145 Update to the scanner library 3.0 X-Git-Tag: 6.0.0.4432~18 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=b04cd6ba08a93a88553c1dfceaa725ced1ba381c;p=sonar-scanner-cli.git SCANCLI-145 Update to the scanner library 3.0 --- diff --git a/pom.xml b/pom.xml index 9d13ffb..a3e3f73 100644 --- a/pom.xml +++ b/pom.xml @@ -64,9 +64,9 @@ - org.sonarsource.scanner.api - sonar-scanner-api - 2.16.3.1081 + org.sonarsource.scanner.lib + sonar-scanner-java-library + 3.0.0.74 com.google.code.findbugs @@ -91,13 +91,13 @@ org.assertj assertj-core - 3.23.1 + 3.24.2 test org.mockito mockito-core - 4.10.0 + 5.10.0 test @@ -126,7 +126,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.5.0 + 3.5.1 package @@ -179,8 +179,8 @@ - 560000 - 600000 + 3000000 + 3100000 ${project.build.directory}/sonar-scanner-${project.version}.zip @@ -213,13 +213,6 @@ - - it - - it - - - dist-linux diff --git a/src/main/java/org/sonarsource/scanner/cli/Cli.java b/src/main/java/org/sonarsource/scanner/cli/Cli.java index aafed18..4de2a36 100644 --- a/src/main/java/org/sonarsource/scanner/cli/Cli.java +++ b/src/main/java/org/sonarsource/scanner/cli/Cli.java @@ -20,7 +20,6 @@ package org.sonarsource.scanner.cli; import java.util.Properties; -import org.sonarsource.scanner.api.ScannerProperties; import static java.util.Arrays.asList; @@ -73,10 +72,7 @@ class Cli { private int processNextArg(String[] args, int pos) { String arg = args[pos]; - if (pos == 0 && arg.charAt(0) != '-') { - props.setProperty(ScannerProperties.TASK, arg); - - } else if (asList("-h", "--help").contains(arg)) { + if (asList("-h", "--help").contains(arg)) { printUsage(); exit.exit(Exit.SUCCESS); diff --git a/src/main/java/org/sonarsource/scanner/cli/Conf.java b/src/main/java/org/sonarsource/scanner/cli/Conf.java index 19b020e..4e9a53a 100644 --- a/src/main/java/org/sonarsource/scanner/cli/Conf.java +++ b/src/main/java/org/sonarsource/scanner/cli/Conf.java @@ -31,7 +31,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; import javax.annotation.Nullable; -import org.sonarsource.scanner.api.Utils; +import org.sonarsource.scanner.lib.EnvironmentConfig; class Conf { private static final String SCANNER_HOME = "scanner.home"; @@ -79,8 +79,8 @@ class Conf { return resolver.resolve(); } - private Properties loadEnvironmentProperties() { - return Utils.loadEnvironmentProperties(env); + private Map loadEnvironmentProperties() { + return EnvironmentConfig.load(logger.getLogOutputAdapter()); } private Properties loadGlobalProperties() { diff --git a/src/main/java/org/sonarsource/scanner/cli/Logs.java b/src/main/java/org/sonarsource/scanner/cli/Logs.java index 038e78f..f8b4ce3 100644 --- a/src/main/java/org/sonarsource/scanner/cli/Logs.java +++ b/src/main/java/org/sonarsource/scanner/cli/Logs.java @@ -22,6 +22,7 @@ package org.sonarsource.scanner.cli; import java.io.PrintStream; import java.time.LocalTime; import java.time.format.DateTimeFormatter; +import org.sonarsource.scanner.lib.LogOutput; public class Logs { private DateTimeFormatter timeFormatter; @@ -77,4 +78,37 @@ public class Logs { stream.println(msg); } } + + /** + * Adapter for the scanner library. + */ + public LogOutput getLogOutputAdapter() { + return new LogOutputAdapter(this); + } + + static class LogOutputAdapter implements LogOutput { + private final Logs logs; + + public LogOutputAdapter(Logs logs) { + this.logs = logs; + } + + @Override + public void log(String formattedMessage, Level level) { + switch (level) { + case TRACE, DEBUG: + logs.debug(formattedMessage); + break; + case ERROR: + logs.error(formattedMessage); + break; + case WARN: + logs.warn(formattedMessage); + break; + case INFO: + default: + logs.info(formattedMessage); + } + } + } } diff --git a/src/main/java/org/sonarsource/scanner/cli/Main.java b/src/main/java/org/sonarsource/scanner/cli/Main.java index ff7f96f..787cc80 100644 --- a/src/main/java/org/sonarsource/scanner/cli/Main.java +++ b/src/main/java/org/sonarsource/scanner/cli/Main.java @@ -19,11 +19,11 @@ */ package org.sonarsource.scanner.cli; -import java.util.Locale; import java.util.Map; import java.util.Properties; -import org.sonarsource.scanner.api.EmbeddedScanner; -import org.sonarsource.scanner.api.ScanProperties; +import org.sonarsource.scanner.lib.ScanProperties; +import org.sonarsource.scanner.lib.ScannerEngineBootstrapper; +import org.sonarsource.scanner.lib.ScannerEngineFacade; /** * Arguments : @@ -42,15 +42,15 @@ public class Main { private final Exit exit; private final Cli cli; private final Conf conf; - private EmbeddedScanner embeddedScanner; - private final ScannerFactory runnerFactory; + private ScannerEngineBootstrapper scannerEngineBootstrapper; + private final ScannerEngineBootstrapperFactory bootstrapperFactory; private final Logs logger; - Main(Exit exit, Cli cli, Conf conf, ScannerFactory runnerFactory, Logs logger) { + Main(Exit exit, Cli cli, Conf conf, ScannerEngineBootstrapperFactory bootstrapperFactory, Logs logger) { this.exit = exit; this.cli = cli; this.conf = conf; - this.runnerFactory = runnerFactory; + this.bootstrapperFactory = bootstrapperFactory; this.logger = logger; } @@ -58,11 +58,11 @@ public class Main { Logs logs = new Logs(System.out, System.err); Exit exit = new Exit(); Cli cli = new Cli(exit, logs).parse(args); - Main main = new Main(exit, cli, new Conf(cli, logs, System.getenv()), new ScannerFactory(logs), logs); - main.execute(); + Main main = new Main(exit, cli, new Conf(cli, logs, System.getenv()), new ScannerEngineBootstrapperFactory(logs), logs); + main.analyze(); } - void execute() { + void analyze() { Stats stats = new Stats(logger).start(); int status = Exit.INTERNAL_ERROR; @@ -71,15 +71,12 @@ public class Main { checkSkip(p); configureLogging(p); init(p); - embeddedScanner.start(); - if (isSonarCloud(p)) { - logger.info("Analyzing on SonarCloud"); - } else { - String serverVersion = embeddedScanner.serverVersion(); - logger.info(String.format("Analyzing on SonarQube server %s", serverVersion)); + try (var engine = scannerEngineBootstrapper.bootstrap()) { + logServerType(engine); + engine.analyze((Map) p); + displayExecutionResult(stats, "SUCCESS"); + status = Exit.SUCCESS; } - execute(stats, p); - status = Exit.SUCCESS; } catch (Throwable e) { displayExecutionResult(stats, "FAILURE"); showError("Error during SonarScanner execution", e, cli.isDebugEnabled()); @@ -89,13 +86,13 @@ public class Main { } } - static boolean isSonarCloud(Properties props) { - String hostUrl = props.getProperty(Conf.PROPERTY_SONAR_HOST_URL); - if (hostUrl != null) { - return hostUrl.toLowerCase(Locale.ENGLISH).contains("sonarcloud"); + private void logServerType(ScannerEngineFacade engine) { + if (engine.isSonarCloud()) { + logger.info("Analyzing on SonarCloud"); + } else { + String serverVersion = engine.getServerVersion(); + logger.info(String.format("Analyzing on SonarQube server %s", serverVersion)); } - - return false; } private void checkSkip(Properties properties) { @@ -111,7 +108,7 @@ public class Main { exit.exit(Exit.SUCCESS); } - embeddedScanner = runnerFactory.create(p, cli.getInvokedFrom()); + scannerEngineBootstrapper = bootstrapperFactory.create(p, cli.getInvokedFrom()); } private void configureLogging(Properties props) { @@ -122,11 +119,6 @@ public class Main { } } - private void execute(Stats stats, Properties p) { - embeddedScanner.execute((Map) p); - displayExecutionResult(stats, "SUCCESS"); - } - private void displayExecutionResult(Stats stats, String resultMsg) { logger.info(SEPARATOR); logger.info("EXECUTION " + resultMsg); diff --git a/src/main/java/org/sonarsource/scanner/cli/ScannerEngineBootstrapperFactory.java b/src/main/java/org/sonarsource/scanner/cli/ScannerEngineBootstrapperFactory.java new file mode 100644 index 0000000..b67254c --- /dev/null +++ b/src/main/java/org/sonarsource/scanner/cli/ScannerEngineBootstrapperFactory.java @@ -0,0 +1,51 @@ +/* + * SonarScanner CLI + * Copyright (C) 2011-2024 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.sonarsource.scanner.cli; + +import java.util.Map; +import java.util.Properties; +import org.sonarsource.scanner.lib.ScannerEngineBootstrapper; + +class ScannerEngineBootstrapperFactory { + + private final Logs logger; + + public ScannerEngineBootstrapperFactory(Logs logger) { + this.logger = logger; + } + + ScannerEngineBootstrapper create(Properties props, String isInvokedFrom) { + String appName = "ScannerCLI"; + String appVersion = ScannerVersion.version(); + if (isInvokedFrom.contains("/")) { + appName = isInvokedFrom.split("/")[0]; + appVersion = isInvokedFrom.split("/")[1]; + } + + return newScannerEngineBootstrapper(appName, appVersion) + .addBootstrapProperties((Map) props); + } + + ScannerEngineBootstrapper newScannerEngineBootstrapper(String appName, String appVersion) { + return new ScannerEngineBootstrapper(appName, appVersion, logger.getLogOutputAdapter()); + } + + +} diff --git a/src/main/java/org/sonarsource/scanner/cli/ScannerFactory.java b/src/main/java/org/sonarsource/scanner/cli/ScannerFactory.java deleted file mode 100644 index d27165e..0000000 --- a/src/main/java/org/sonarsource/scanner/cli/ScannerFactory.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * SonarScanner CLI - * Copyright (C) 2011-2024 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.sonarsource.scanner.cli; - -import java.util.Map; -import java.util.Properties; -import org.sonarsource.scanner.api.EmbeddedScanner; -import org.sonarsource.scanner.api.LogOutput; - -class ScannerFactory { - - private final Logs logger; - - public ScannerFactory(Logs logger) { - this.logger = logger; - } - - EmbeddedScanner create(Properties props, String isInvokedFrom) { - String appName = "ScannerCLI"; - String appVersion = ScannerVersion.version(); - if (!isInvokedFrom.equals("") && isInvokedFrom.contains("/")) { - appName = isInvokedFrom.split("/")[0]; - appVersion = isInvokedFrom.split("/")[1]; - } - - return EmbeddedScanner.create(appName, appVersion, new DefaultLogOutput()) - .addGlobalProperties((Map) props); - } - - class DefaultLogOutput implements LogOutput { - @Override - public void log(String formattedMessage, Level level) { - switch (level) { - case TRACE: - case DEBUG: - logger.debug(formattedMessage); - break; - case ERROR: - logger.error(formattedMessage); - break; - case WARN: - logger.warn(formattedMessage); - break; - case INFO: - default: - logger.info(formattedMessage); - } - } - } -} diff --git a/src/test/java/org/sonarsource/scanner/cli/CliTest.java b/src/test/java/org/sonarsource/scanner/cli/CliTest.java index 961f7d1..ce5b140 100644 --- a/src/test/java/org/sonarsource/scanner/cli/CliTest.java +++ b/src/test/java/org/sonarsource/scanner/cli/CliTest.java @@ -22,11 +22,13 @@ package org.sonarsource.scanner.cli; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.assertj.core.api.Assertions.entry; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; public class CliTest { - private Exit exit = mock(Exit.class); + private final Exit exit = mock(Exit.class); private Logs logs = new Logs(System.out, System.err); private Cli cli = new Cli(exit, logs); @@ -41,17 +43,18 @@ public class CliTest { @Test public void should_extract_properties() { - cli.parse(new String[] {"-D", "foo=bar", "--define", "hello=world", "-Dboolean"}); - assertThat(cli.properties().get("foo")).isEqualTo("bar"); - assertThat(cli.properties().get("hello")).isEqualTo("world"); - assertThat(cli.properties().get("boolean")).isEqualTo("true"); + cli.parse(new String[]{"-D", "foo=bar", "--define", "hello=world", "-Dboolean"}); + assertThat(cli.properties()).contains( + entry("foo", "bar"), + entry("hello", "world"), + entry("boolean", "true")); } @Test public void should_warn_on_duplicate_properties() { logs = mock(Logs.class); cli = new Cli(exit, logs); - cli.parse(new String[] {"-D", "foo=bar", "--define", "foo=baz"}); + cli.parse(new String[]{"-D", "foo=bar", "--define", "foo=baz"}); verify(logs).warn("Property 'foo' with value 'bar' is overridden with value 'baz'"); } @@ -59,75 +62,66 @@ public class CliTest { public void should_fail_on_missing_prop() { logs = mock(Logs.class); cli = new Cli(exit, logs); - cli.parse(new String[] {"-D"}); + cli.parse(new String[]{"-D"}); verify(logs).error("Missing argument for option -D/--define"); verify(exit).exit(Exit.INTERNAL_ERROR); } @Test public void should_not_fail_with_errors_option() { - cli.parse(new String[] {"-e"}); - } - - @Test - public void should_parse_optional_task() { - cli.parse(new String[] {"-D", "foo=bar"}); - assertThat(cli.properties().get("sonar.task")).isNull(); - - cli.parse(new String[] {"views", "-D", "foo=bar"}); - assertThat(cli.properties().get("sonar.task")).isEqualTo("views"); + assertThatNoException().isThrownBy(() -> cli.parse(new String[]{"-e"})); } @Test public void should_enable_debug_mode() { - cli.parse(new String[] {"-X"}); + cli.parse(new String[]{"-X"}); assertThat(cli.isDebugEnabled()).isTrue(); - assertThat(cli.properties().get("sonar.verbose")).isEqualTo("true"); + assertThat(cli.properties()).containsEntry("sonar.verbose", "true"); } @Test public void should_enable_debug_mode_full() { - cli.parse(new String[] {"--debug"}); + cli.parse(new String[]{"--debug"}); assertThat(cli.isDebugEnabled()).isTrue(); - assertThat(cli.properties().get("sonar.verbose")).isEqualTo("true"); + assertThat(cli.properties()).containsEntry("sonar.verbose", "true"); } @Test public void should_show_version() { - cli.parse(new String[] {"-v"}); + cli.parse(new String[]{"-v"}); assertThat(cli.isDisplayVersionOnly()).isTrue(); } @Test public void should_show_version_full() { - cli.parse(new String[] {"--version"}); + cli.parse(new String[]{"--version"}); assertThat(cli.isDisplayVersionOnly()).isTrue(); } @Test public void should_enable_stacktrace_log() { - cli.parse(new String[] {"-e"}); + cli.parse(new String[]{"-e"}); assertThat(cli.isDebugEnabled()).isFalse(); assertThat(cli.properties().get("sonar.verbose")).isNull(); } @Test public void should_enable_stacktrace_log_full() { - cli.parse(new String[] {"--errors"}); + cli.parse(new String[]{"--errors"}); assertThat(cli.isDebugEnabled()).isFalse(); assertThat(cli.properties().get("sonar.verbose")).isNull(); } @Test public void should_parse_from_argument() { - cli.parse(new String[] {"--from=ScannerMSBuild/4.8"}); + cli.parse(new String[]{"--from=ScannerMSBuild/4.8"}); assertThat(cli.getInvokedFrom()).isNotEmpty(); assertThat(cli.getInvokedFrom()).isEqualTo("ScannerMSBuild/4.8"); } @Test public void from_argument_is_only_from_let_value_empty() { - cli.parse(new String[] {"--from="}); + cli.parse(new String[]{"--from="}); assertThat(cli.getInvokedFrom()).isEmpty(); } @@ -142,7 +136,7 @@ public class CliTest { public void should_show_usage() { logs = mock(Logs.class); cli = new Cli(exit, logs); - cli.parse(new String[] {"-h"}); + cli.parse(new String[]{"-h"}); verify(logs).info("usage: sonar-scanner [options]"); verify(exit).exit(Exit.SUCCESS); } @@ -151,7 +145,7 @@ public class CliTest { public void should_show_usage_full() { logs = mock(Logs.class); cli = new Cli(exit, logs); - cli.parse(new String[] {"--help"}); + cli.parse(new String[]{"--help"}); verify(logs).info("usage: sonar-scanner [options]"); verify(exit).exit(Exit.SUCCESS); } @@ -160,7 +154,7 @@ public class CliTest { public void should_show_usage_on_bad_syntax() { logs = mock(Logs.class); cli = new Cli(exit, logs); - cli.parse(new String[] {"-w"}); + cli.parse(new String[]{"-w"}); verify(logs).error("Unrecognized option: -w"); verify(logs).info("usage: sonar-scanner [options]"); verify(exit).exit(Exit.INTERNAL_ERROR); @@ -168,7 +162,7 @@ public class CliTest { @Test public void should_enable_embedded_mode() { - cli.parse(new String[] {"--embedded"}); + cli.parse(new String[]{"--embedded"}); assertThat(cli.isEmbedded()).isTrue(); } } diff --git a/src/test/java/org/sonarsource/scanner/cli/ConfTest.java b/src/test/java/org/sonarsource/scanner/cli/ConfTest.java index 5ff2441..d28754a 100644 --- a/src/test/java/org/sonarsource/scanner/cli/ConfTest.java +++ b/src/test/java/org/sonarsource/scanner/cli/ConfTest.java @@ -32,7 +32,6 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import org.sonarsource.scanner.api.internal.shaded.minimaljson.Json; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; @@ -133,26 +132,6 @@ public class ConfTest { assertThat(properties.getProperty("sonar.projectBaseDir")).isEqualTo(projectHome.toString()); } - @Test - public void shouldLoadEnvironmentProperties() { - env.put("SONARQUBE_SCANNER_PARAMS", "{\"sonar.key1\" : \"v1\", \"sonar.key2\" : \"v2\"}"); - args.put("sonar.key2", "v3"); - - Properties props = conf.properties(); - - assertThat(props.getProperty("sonar.key1")).isEqualTo("v1"); - assertThat(props.getProperty("sonar.key2")).isEqualTo("v3"); - } - - @Test - public void shouldFailWithInvalidEnvironmentProperties() { - env.put("SONARQUBE_SCANNER_PARAMS", "{sonar.key1: \"v1\", \"sonar.key2\" : \"v2\"}"); - - assertThatIllegalStateException() - .isThrownBy(conf::properties) - .withMessage("Failed to parse JSON in SONARQUBE_SCANNER_PARAMS environment variable"); - } - @Test public void shouldSupportDeepModuleConfigurationInRoot() throws Exception { Path projectHome = Paths.get(getClass().getResource("ConfTest/shouldSupportDeepModuleConfigurationInRoot/project").toURI()); @@ -329,23 +308,4 @@ public class ConfTest { assertThat(properties).containsEntry("sonar.prop", "expected"); } - // SQSCANNER-61 - @Test - public void should_load_project_settings_using_env() throws Exception { - Path home = Paths.get(getClass().getResource("ConfTest/shouldOverrideProjectSettingsPath/").toURI()); - args.setProperty("project.home", home.toAbsolutePath().toString()); - - Properties properties = conf.properties(); - assertThat(properties).containsEntry("sonar.prop", "default"); - - String jsonString = Json.object() - .add("project.settings", home.resolve("conf/sq-project.properties").toAbsolutePath().toString()) - .toString(); - - env.put("SONARQUBE_SCANNER_PARAMS", jsonString); - - properties = conf.properties(); - assertThat(properties).containsEntry("sonar.prop", "expected"); - } - } diff --git a/src/test/java/org/sonarsource/scanner/cli/LogsTest.java b/src/test/java/org/sonarsource/scanner/cli/LogsTest.java index e664365..82a26ad 100644 --- a/src/test/java/org/sonarsource/scanner/cli/LogsTest.java +++ b/src/test/java/org/sonarsource/scanner/cli/LogsTest.java @@ -25,7 +25,10 @@ import org.junit.Test; import org.mockito.ArgumentMatchers; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.sonarsource.scanner.lib.LogOutput; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -89,4 +92,37 @@ public class LogsTest { logs.debug("debug"); verifyNoMoreInteractions(stdOut, stdErr); } + + @Test + public void should_forward_logs() { + var mockedLogs = mock(Logs.class); + var logOutput = new Logs.LogOutputAdapter(mockedLogs); + + String msg = "test"; + + logOutput.log(msg, LogOutput.Level.DEBUG); + verify(mockedLogs).debug(msg); + verifyNoMoreInteractions(mockedLogs); + reset(mockedLogs); + + logOutput.log(msg, LogOutput.Level.INFO); + verify(mockedLogs).info(msg); + verifyNoMoreInteractions(mockedLogs); + reset(mockedLogs); + + logOutput.log(msg, LogOutput.Level.ERROR); + verify(mockedLogs).error(msg); + verifyNoMoreInteractions(mockedLogs); + reset(mockedLogs); + + logOutput.log(msg, LogOutput.Level.WARN); + verify(mockedLogs).warn(msg); + verifyNoMoreInteractions(mockedLogs); + reset(mockedLogs); + + logOutput.log(msg, LogOutput.Level.TRACE); + verify(mockedLogs).debug(msg); + verifyNoMoreInteractions(mockedLogs); + reset(mockedLogs); + } } diff --git a/src/test/java/org/sonarsource/scanner/cli/MainTest.java b/src/test/java/org/sonarsource/scanner/cli/MainTest.java index 85f233c..6b8099d 100644 --- a/src/test/java/org/sonarsource/scanner/cli/MainTest.java +++ b/src/test/java/org/sonarsource/scanner/cli/MainTest.java @@ -29,14 +29,14 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.sonar.api.utils.MessageException; -import org.sonarsource.scanner.api.EmbeddedScanner; -import org.sonarsource.scanner.api.ScanProperties; +import org.sonarsource.scanner.lib.ScanProperties; +import org.sonarsource.scanner.lib.ScannerEngineBootstrapper; +import org.sonarsource.scanner.lib.ScannerEngineFacade; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -53,63 +53,62 @@ public class MainTest { @Mock private Properties properties; @Mock - private ScannerFactory scannerFactory; + private ScannerEngineBootstrapperFactory scannerEngineBootstrapperFactory; @Mock - private EmbeddedScanner scanner; + private ScannerEngineBootstrapper bootstrapper; + @Mock + private ScannerEngineFacade engine; @Mock private Logs logs; @Before public void setUp() { MockitoAnnotations.initMocks(this); - when(scannerFactory.create(any(Properties.class), any(String.class))).thenReturn(scanner); + when(scannerEngineBootstrapperFactory.create(any(Properties.class), any(String.class))).thenReturn(bootstrapper); + when(bootstrapper.bootstrap()).thenReturn(engine); when(conf.properties()).thenReturn(properties); } @Test - public void should_execute_runner() { + public void should_execute_scanner_engine() { when(cli.getInvokedFrom()).thenReturn(""); - Main main = new Main(exit, cli, conf, scannerFactory, logs); - main.execute(); + Main main = new Main(exit, cli, conf, scannerEngineBootstrapperFactory, logs); + main.analyze(); verify(exit).exit(Exit.SUCCESS); - verify(scannerFactory).create(properties, ""); + verify(scannerEngineBootstrapperFactory).create(properties, ""); - verify(scanner, times(1)).start(); - verify(scanner, times(1)).execute((Map) properties); + verify(bootstrapper, times(1)).bootstrap(); + verify(engine, times(1)).analyze((Map) properties); } @Test public void should_exit_with_error_on_error_during_analysis() { - EmbeddedScanner runner = mock(EmbeddedScanner.class); Exception e = new NullPointerException("NPE"); e = new IllegalStateException("Error", e); - doThrow(e).when(runner).execute(any()); + doThrow(e).when(engine).analyze(any()); when(cli.getInvokedFrom()).thenReturn(""); - when(scannerFactory.create(any(Properties.class), any(String.class))).thenReturn(runner); when(cli.isDebugEnabled()).thenReturn(true); - Main main = new Main(exit, cli, conf, scannerFactory, logs); - main.execute(); + Main main = new Main(exit, cli, conf, scannerEngineBootstrapperFactory, logs); + main.analyze(); verify(exit).exit(Exit.INTERNAL_ERROR); verify(logs).error("Error during SonarScanner execution", e); } @Test - public void should_exit_with_error_on_error_during_start() { - EmbeddedScanner runner = mock(EmbeddedScanner.class); + public void should_exit_with_error_on_error_during_bootstrap() { Exception e = new NullPointerException("NPE"); e = new IllegalStateException("Error", e); - doThrow(e).when(runner).start(); + doThrow(e).when(bootstrapper).bootstrap(); when(cli.getInvokedFrom()).thenReturn(""); when(cli.isDebugEnabled()).thenReturn(true); - when(scannerFactory.create(any(Properties.class), any(String.class))).thenReturn(runner); - Main main = new Main(exit, cli, conf, scannerFactory, logs); - main.execute(); + Main main = new Main(exit, cli, conf, scannerEngineBootstrapperFactory, logs); + main.analyze(); - verify(runner).start(); - verify(runner, never()).execute(any()); + verify(bootstrapper).bootstrap(); + verify(engine, never()).analyze(any()); verify(exit).exit(Exit.INTERNAL_ERROR); verify(logs).error("Error during SonarScanner execution", e); } @@ -180,13 +179,13 @@ public class MainTest { when(cli.isEmbedded()).thenReturn(isEmbedded); when(cli.getInvokedFrom()).thenReturn(""); - EmbeddedScanner runner = mock(EmbeddedScanner.class); - doThrow(e).when(runner).execute(any()); - when(scannerFactory.create(any(Properties.class), any(String.class))).thenReturn(runner); + doThrow(e).when(engine).analyze(any()); + + when(scannerEngineBootstrapperFactory.create(any(Properties.class), any(String.class))).thenReturn(bootstrapper); - Main main = new Main(exit, cli, conf, scannerFactory, logs); - main.execute(); + Main main = new Main(exit, cli, conf, scannerEngineBootstrapperFactory, logs); + main.analyze(); verify(exit).exit(expectedExitCode); } @@ -209,13 +208,13 @@ public class MainTest { when(cli.getInvokedFrom()).thenReturn(""); when(conf.properties()).thenReturn(p); - Main main = new Main(exit, cli, conf, scannerFactory, logs); - main.execute(); + Main main = new Main(exit, cli, conf, scannerEngineBootstrapperFactory, logs); + main.analyze(); - InOrder inOrder = Mockito.inOrder(exit, scannerFactory); + InOrder inOrder = Mockito.inOrder(exit, scannerEngineBootstrapperFactory); inOrder.verify(exit, times(1)).exit(Exit.SUCCESS); - inOrder.verify(scannerFactory, times(1)).create(p, ""); + inOrder.verify(scannerEngineBootstrapperFactory, times(1)).create(p, ""); inOrder.verify(exit, times(1)).exit(Exit.SUCCESS); } @@ -226,67 +225,43 @@ public class MainTest { when(conf.properties()).thenReturn(p); when(cli.getInvokedFrom()).thenReturn(""); - Main main = new Main(exit, cli, conf, scannerFactory, logs); - main.execute(); + Main main = new Main(exit, cli, conf, scannerEngineBootstrapperFactory, logs); + main.analyze(); verify(logs).info("SonarScanner analysis skipped"); - InOrder inOrder = Mockito.inOrder(exit, scannerFactory); + InOrder inOrder = Mockito.inOrder(exit, scannerEngineBootstrapperFactory); inOrder.verify(exit, times(1)).exit(Exit.SUCCESS); - inOrder.verify(scannerFactory, times(1)).create(p, ""); + inOrder.verify(scannerEngineBootstrapperFactory, times(1)).create(p, ""); inOrder.verify(exit, times(1)).exit(Exit.SUCCESS); } @Test public void shouldLogServerVersion() { - when(scanner.serverVersion()).thenReturn("5.5"); + when(engine.isSonarCloud()).thenReturn(false); + when(engine.getServerVersion()).thenReturn("5.5"); Properties p = new Properties(); when(cli.isDisplayVersionOnly()).thenReturn(true); when(cli.getInvokedFrom()).thenReturn(""); when(conf.properties()).thenReturn(p); - Main main = new Main(exit, cli, conf, scannerFactory, logs); - main.execute(); + Main main = new Main(exit, cli, conf, scannerEngineBootstrapperFactory, logs); + main.analyze(); verify(logs).info("Analyzing on SonarQube server 5.5"); } @Test public void should_log_SonarCloud_server() { + when(engine.isSonarCloud()).thenReturn(true); Properties p = new Properties(); - p.setProperty("sonar.host.url", "https://sonarcloud.io"); when(conf.properties()).thenReturn(p); when(cli.getInvokedFrom()).thenReturn(""); - Main main = new Main(exit, cli, conf, scannerFactory, logs); - main.execute(); + Main main = new Main(exit, cli, conf, scannerEngineBootstrapperFactory, logs); + main.analyze(); verify(logs).info("Analyzing on SonarCloud"); } - // SQSCANNER-57 - @Test - public void should_return_true_is_sonar_cloud() { - - Properties properties = new Properties(); - properties.setProperty("sonar.host.url", "https://sonarcloud.io"); - - assertThat(Main.isSonarCloud(properties)).isTrue(); - } - - // SQSCANNER-57 - @Test - public void should_return_false_is_sonar_cloud() { - Properties properties = new Properties(); - properties.setProperty("sonar.host.url", "https://mysonarqube.com:9000/"); - - assertThat(Main.isSonarCloud(properties)).isFalse(); - } - - // SQSCANNER-57 - @Test - public void should_return_false_is_sonar_cloud_host_is_null() { - assertThat(Main.isSonarCloud(new Properties())).isFalse(); - } - @Test public void should_configure_logging() { Properties analysisProps = testLogging("sonar.verbose", "true"); @@ -327,11 +302,11 @@ public class MainTest { when(conf.properties()).thenReturn(p); when(cli.getInvokedFrom()).thenReturn(""); - Main main = new Main(exit, cli, conf, scannerFactory, logs); - main.execute(); + Main main = new Main(exit, cli, conf, scannerEngineBootstrapperFactory, logs); + main.analyze(); ArgumentCaptor propertiesCapture = ArgumentCaptor.forClass(Properties.class); - verify(scanner).execute((Map) propertiesCapture.capture()); + verify(engine).analyze((Map) propertiesCapture.capture()); return propertiesCapture.getValue(); } diff --git a/src/test/java/org/sonarsource/scanner/cli/ScannerEngineBootstrapperFactoryTest.java b/src/test/java/org/sonarsource/scanner/cli/ScannerEngineBootstrapperFactoryTest.java new file mode 100644 index 0000000..f3ae408 --- /dev/null +++ b/src/test/java/org/sonarsource/scanner/cli/ScannerEngineBootstrapperFactoryTest.java @@ -0,0 +1,84 @@ +/* + * SonarScanner CLI + * Copyright (C) 2011-2024 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.sonarsource.scanner.cli; + +import java.util.Properties; +import org.junit.Test; +import org.sonarsource.scanner.lib.ScannerEngineBootstrapper; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.notNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class ScannerEngineBootstrapperFactoryTest { + + private final Properties props = new Properties(); + private final Logs logs = mock(Logs.class); + private ScannerEngineBootstrapperFactory underTest = new ScannerEngineBootstrapperFactory(logs); + + @Test + public void should_create_engine_bootstrapper_and_pass_app_and_properties() { + props.setProperty("foo", "bar"); + var spy = spy(underTest); + var mockedBootstrapper = mock(ScannerEngineBootstrapper.class); + when(mockedBootstrapper.addBootstrapProperties(any())).thenReturn(mockedBootstrapper); + when(spy.newScannerEngineBootstrapper(any(), any())).thenReturn(mockedBootstrapper); + + var bootstrapper = spy.create(props, ""); + + assertThat(bootstrapper).isNotNull(); + verify(spy).newScannerEngineBootstrapper(eq("ScannerCLI"), notNull()); + verify(mockedBootstrapper).addBootstrapProperties(argThat(props::equals)); + } + + @Test + public void should_create_engine_bootstrapper_with_app_from_argument() { + var spy = spy(underTest); + var mockedBootstrapper = mock(ScannerEngineBootstrapper.class); + when(mockedBootstrapper.addBootstrapProperties(any())).thenReturn(mockedBootstrapper); + when(spy.newScannerEngineBootstrapper(any(), any())).thenReturn(mockedBootstrapper); + + var bootstrapper = spy.create(props, "ScannerMSBuild/4.8.0"); + + assertThat(bootstrapper).isNotNull(); + verify(spy).newScannerEngineBootstrapper("ScannerMSBuild", "4.8.0"); + } + + @Test + public void if_from_argument_is_not_regex_compliant_revert_to_default_scanner_name() { + var spy = spy(underTest); + var mockedBootstrapper = mock(ScannerEngineBootstrapper.class); + when(mockedBootstrapper.addBootstrapProperties(any())).thenReturn(mockedBootstrapper); + when(spy.newScannerEngineBootstrapper(any(), any())).thenReturn(mockedBootstrapper); + + var bootstrapper = spy.create(props, "ScannerMSBuild4.8.0WithoutSlash"); + + assertThat(bootstrapper).isNotNull(); + verify(spy).newScannerEngineBootstrapper(eq("ScannerCLI"), notNull()); + } + + +} diff --git a/src/test/java/org/sonarsource/scanner/cli/ScannerFactoryTest.java b/src/test/java/org/sonarsource/scanner/cli/ScannerFactoryTest.java deleted file mode 100644 index 289da0a..0000000 --- a/src/test/java/org/sonarsource/scanner/cli/ScannerFactoryTest.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * SonarScanner CLI - * Copyright (C) 2011-2024 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.sonarsource.scanner.cli; - -import java.util.Properties; -import org.junit.Test; -import org.sonarsource.scanner.api.EmbeddedScanner; -import org.sonarsource.scanner.api.LogOutput; -import org.sonarsource.scanner.api.LogOutput.Level; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; - -public class ScannerFactoryTest { - - private final Properties props = new Properties(); - private final Logs logs = mock(Logs.class); - - @Test - public void should_create_embedded_runner() { - props.setProperty("foo", "bar"); - EmbeddedScanner runner = new ScannerFactory(logs).create(props, ""); - - assertThat(runner).isInstanceOf(EmbeddedScanner.class); - assertThat(runner.globalProperties()).containsEntry("foo", "bar"); - assertThat(runner.app()).isEqualTo("ScannerCLI"); - assertThat(runner.appVersion()).isNotNull(); - } - - @Test - public void should_create_embedded_runner_with_scannername_from_argument() { - props.setProperty("foo", "bar"); - EmbeddedScanner runner = new ScannerFactory(logs).create(props, "ScannerMSBuild/4.8.0"); - - assertThat(runner).isInstanceOf(EmbeddedScanner.class); - assertThat(runner.globalProperties()).containsEntry("foo", "bar"); - assertThat(runner.app()).isEqualTo("ScannerMSBuild"); - assertThat(runner.appVersion()).isEqualTo("4.8.0"); - assertThat(runner.appVersion()).isNotNull(); - } - - @Test - public void should_create_embedded_runner_from_argument_is_not_regex_compliant_revert_to_default_scanner_name() { - props.setProperty("foo", "bar"); - EmbeddedScanner runner = new ScannerFactory(logs).create(props, "ScannerMSBuild4.8.0"); - - assertThat(runner).isInstanceOf(EmbeddedScanner.class); - assertThat(runner.globalProperties()).containsEntry("foo", "bar"); - assertThat(runner.app()).isEqualTo("ScannerCLI"); - assertThat(runner.appVersion()).isNotNull(); - } - - @Test - public void should_fwd_logs() { - LogOutput logOutput = new ScannerFactory(logs).new DefaultLogOutput(); - - String msg = "test"; - - logOutput.log(msg, Level.DEBUG); - verify(logs).debug(msg); - verifyNoMoreInteractions(logs); - reset(logs); - - logOutput.log(msg, Level.INFO); - verify(logs).info(msg); - verifyNoMoreInteractions(logs); - reset(logs); - - logOutput.log(msg, Level.ERROR); - verify(logs).error(msg); - verifyNoMoreInteractions(logs); - reset(logs); - - logOutput.log(msg, Level.WARN); - verify(logs).warn(msg); - verifyNoMoreInteractions(logs); - reset(logs); - - logOutput.log(msg, Level.TRACE); - verify(logs).debug(msg); - verifyNoMoreInteractions(logs); - reset(logs); - } - -}