Browse Source

SCANCLI-145 Update to the scanner library 3.0

tags/6.0.0.4432
Julien HENRY 1 month ago
parent
commit
b04cd6ba08

+ 8
- 15
pom.xml View File



<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.sonarsource.scanner.api</groupId>
<artifactId>sonar-scanner-api</artifactId>
<version>2.16.3.1081</version>
<groupId>org.sonarsource.scanner.lib</groupId>
<artifactId>sonar-scanner-java-library</artifactId>
<version>3.0.0.74</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.google.code.findbugs</groupId> <groupId>com.google.code.findbugs</groupId>
<dependency> <dependency>
<groupId>org.assertj</groupId> <groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId> <artifactId>assertj-core</artifactId>
<version>3.23.1</version>
<version>3.24.2</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.mockito</groupId> <groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId> <artifactId>mockito-core</artifactId>
<version>4.10.0</version>
<version>5.10.0</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-shade-plugin</artifactId>
<version>3.5.0</version>
<version>3.5.1</version>
<executions> <executions>
<execution> <execution>
<phase>package</phase> <phase>package</phase>
<configuration> <configuration>
<rules> <rules>
<requireFilesSize> <requireFilesSize>
<minsize>560000</minsize>
<maxsize>600000</maxsize>
<minsize>3000000</minsize>
<maxsize>3100000</maxsize>
<files> <files>
<file>${project.build.directory}/sonar-scanner-${project.version}.zip</file> <file>${project.build.directory}/sonar-scanner-${project.version}.zip</file>
</files> </files>
</build> </build>


<profiles> <profiles>
<profile>
<id>it</id>
<modules>
<module>it</module>
</modules>
</profile>

<profile> <profile>
<id>dist-linux</id> <id>dist-linux</id>
<build> <build>

+ 1
- 5
src/main/java/org/sonarsource/scanner/cli/Cli.java View File

package org.sonarsource.scanner.cli; package org.sonarsource.scanner.cli;


import java.util.Properties; import java.util.Properties;
import org.sonarsource.scanner.api.ScannerProperties;


import static java.util.Arrays.asList; import static java.util.Arrays.asList;




private int processNextArg(String[] args, int pos) { private int processNextArg(String[] args, int pos) {
String arg = args[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(); printUsage();
exit.exit(Exit.SUCCESS); exit.exit(Exit.SUCCESS);



+ 3
- 3
src/main/java/org/sonarsource/scanner/cli/Conf.java View File

import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.sonarsource.scanner.api.Utils;
import org.sonarsource.scanner.lib.EnvironmentConfig;


class Conf { class Conf {
private static final String SCANNER_HOME = "scanner.home"; private static final String SCANNER_HOME = "scanner.home";
return resolver.resolve(); return resolver.resolve();
} }


private Properties loadEnvironmentProperties() {
return Utils.loadEnvironmentProperties(env);
private Map<String, String> loadEnvironmentProperties() {
return EnvironmentConfig.load(logger.getLogOutputAdapter());
} }


private Properties loadGlobalProperties() { private Properties loadGlobalProperties() {

+ 34
- 0
src/main/java/org/sonarsource/scanner/cli/Logs.java View File

import java.io.PrintStream; import java.io.PrintStream;
import java.time.LocalTime; import java.time.LocalTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import org.sonarsource.scanner.lib.LogOutput;


public class Logs { public class Logs {
private DateTimeFormatter timeFormatter; private DateTimeFormatter timeFormatter;
stream.println(msg); 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);
}
}
}
} }

+ 22
- 30
src/main/java/org/sonarsource/scanner/cli/Main.java View File

*/ */
package org.sonarsource.scanner.cli; package org.sonarsource.scanner.cli;


import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Properties; 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 : * Arguments :
private final Exit exit; private final Exit exit;
private final Cli cli; private final Cli cli;
private final Conf conf; private final Conf conf;
private EmbeddedScanner embeddedScanner;
private final ScannerFactory runnerFactory;
private ScannerEngineBootstrapper scannerEngineBootstrapper;
private final ScannerEngineBootstrapperFactory bootstrapperFactory;
private final Logs logger; 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.exit = exit;
this.cli = cli; this.cli = cli;
this.conf = conf; this.conf = conf;
this.runnerFactory = runnerFactory;
this.bootstrapperFactory = bootstrapperFactory;
this.logger = logger; this.logger = logger;
} }


Logs logs = new Logs(System.out, System.err); Logs logs = new Logs(System.out, System.err);
Exit exit = new Exit(); Exit exit = new Exit();
Cli cli = new Cli(exit, logs).parse(args); 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(); Stats stats = new Stats(logger).start();


int status = Exit.INTERNAL_ERROR; int status = Exit.INTERNAL_ERROR;
checkSkip(p); checkSkip(p);
configureLogging(p); configureLogging(p);
init(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) { } catch (Throwable e) {
displayExecutionResult(stats, "FAILURE"); displayExecutionResult(stats, "FAILURE");
showError("Error during SonarScanner execution", e, cli.isDebugEnabled()); showError("Error during SonarScanner execution", e, cli.isDebugEnabled());
} }
} }


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) { private void checkSkip(Properties properties) {
exit.exit(Exit.SUCCESS); exit.exit(Exit.SUCCESS);
} }


embeddedScanner = runnerFactory.create(p, cli.getInvokedFrom());
scannerEngineBootstrapper = bootstrapperFactory.create(p, cli.getInvokedFrom());
} }


private void configureLogging(Properties props) { private void configureLogging(Properties props) {
} }
} }


private void execute(Stats stats, Properties p) {
embeddedScanner.execute((Map) p);
displayExecutionResult(stats, "SUCCESS");
}

private void displayExecutionResult(Stats stats, String resultMsg) { private void displayExecutionResult(Stats stats, String resultMsg) {
logger.info(SEPARATOR); logger.info(SEPARATOR);
logger.info("EXECUTION " + resultMsg); logger.info("EXECUTION " + resultMsg);

src/main/java/org/sonarsource/scanner/cli/ScannerFactory.java → src/main/java/org/sonarsource/scanner/cli/ScannerEngineBootstrapperFactory.java View File



import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import org.sonarsource.scanner.api.EmbeddedScanner;
import org.sonarsource.scanner.api.LogOutput;
import org.sonarsource.scanner.lib.ScannerEngineBootstrapper;


class ScannerFactory {
class ScannerEngineBootstrapperFactory {


private final Logs logger; private final Logs logger;


public ScannerFactory(Logs logger) {
public ScannerEngineBootstrapperFactory(Logs logger) {
this.logger = logger; this.logger = logger;
} }


EmbeddedScanner create(Properties props, String isInvokedFrom) {
ScannerEngineBootstrapper create(Properties props, String isInvokedFrom) {
String appName = "ScannerCLI"; String appName = "ScannerCLI";
String appVersion = ScannerVersion.version(); String appVersion = ScannerVersion.version();
if (!isInvokedFrom.equals("") && isInvokedFrom.contains("/")) {
if (isInvokedFrom.contains("/")) {
appName = isInvokedFrom.split("/")[0]; appName = isInvokedFrom.split("/")[0];
appVersion = isInvokedFrom.split("/")[1]; appVersion = isInvokedFrom.split("/")[1];
} }


return EmbeddedScanner.create(appName, appVersion, new DefaultLogOutput())
.addGlobalProperties((Map) props);
return newScannerEngineBootstrapper(appName, appVersion)
.addBootstrapProperties((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);
}
}
ScannerEngineBootstrapper newScannerEngineBootstrapper(String appName, String appVersion) {
return new ScannerEngineBootstrapper(appName, appVersion, logger.getLogOutputAdapter());
} }


} }

+ 25
- 31
src/test/java/org/sonarsource/scanner/cli/CliTest.java View File

import org.junit.Test; import org.junit.Test;


import static org.assertj.core.api.Assertions.assertThat; 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.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;


public class CliTest { 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 Logs logs = new Logs(System.out, System.err);
private Cli cli = new Cli(exit, logs); private Cli cli = new Cli(exit, logs);




@Test @Test
public void should_extract_properties() { 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 @Test
public void should_warn_on_duplicate_properties() { public void should_warn_on_duplicate_properties() {
logs = mock(Logs.class); logs = mock(Logs.class);
cli = new Cli(exit, logs); 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'"); verify(logs).warn("Property 'foo' with value 'bar' is overridden with value 'baz'");
} }


public void should_fail_on_missing_prop() { public void should_fail_on_missing_prop() {
logs = mock(Logs.class); logs = mock(Logs.class);
cli = new Cli(exit, logs); 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(logs).error("Missing argument for option -D/--define");
verify(exit).exit(Exit.INTERNAL_ERROR); verify(exit).exit(Exit.INTERNAL_ERROR);
} }


@Test @Test
public void should_not_fail_with_errors_option() { 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 @Test
public void should_enable_debug_mode() { public void should_enable_debug_mode() {
cli.parse(new String[] {"-X"});
cli.parse(new String[]{"-X"});
assertThat(cli.isDebugEnabled()).isTrue(); assertThat(cli.isDebugEnabled()).isTrue();
assertThat(cli.properties().get("sonar.verbose")).isEqualTo("true");
assertThat(cli.properties()).containsEntry("sonar.verbose", "true");
} }


@Test @Test
public void should_enable_debug_mode_full() { public void should_enable_debug_mode_full() {
cli.parse(new String[] {"--debug"});
cli.parse(new String[]{"--debug"});
assertThat(cli.isDebugEnabled()).isTrue(); assertThat(cli.isDebugEnabled()).isTrue();
assertThat(cli.properties().get("sonar.verbose")).isEqualTo("true");
assertThat(cli.properties()).containsEntry("sonar.verbose", "true");
} }


@Test @Test
public void should_show_version() { public void should_show_version() {
cli.parse(new String[] {"-v"});
cli.parse(new String[]{"-v"});
assertThat(cli.isDisplayVersionOnly()).isTrue(); assertThat(cli.isDisplayVersionOnly()).isTrue();
} }


@Test @Test
public void should_show_version_full() { public void should_show_version_full() {
cli.parse(new String[] {"--version"});
cli.parse(new String[]{"--version"});
assertThat(cli.isDisplayVersionOnly()).isTrue(); assertThat(cli.isDisplayVersionOnly()).isTrue();
} }


@Test @Test
public void should_enable_stacktrace_log() { public void should_enable_stacktrace_log() {
cli.parse(new String[] {"-e"});
cli.parse(new String[]{"-e"});
assertThat(cli.isDebugEnabled()).isFalse(); assertThat(cli.isDebugEnabled()).isFalse();
assertThat(cli.properties().get("sonar.verbose")).isNull(); assertThat(cli.properties().get("sonar.verbose")).isNull();
} }


@Test @Test
public void should_enable_stacktrace_log_full() { public void should_enable_stacktrace_log_full() {
cli.parse(new String[] {"--errors"});
cli.parse(new String[]{"--errors"});
assertThat(cli.isDebugEnabled()).isFalse(); assertThat(cli.isDebugEnabled()).isFalse();
assertThat(cli.properties().get("sonar.verbose")).isNull(); assertThat(cli.properties().get("sonar.verbose")).isNull();
} }


@Test @Test
public void should_parse_from_argument() { 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()).isNotEmpty();
assertThat(cli.getInvokedFrom()).isEqualTo("ScannerMSBuild/4.8"); assertThat(cli.getInvokedFrom()).isEqualTo("ScannerMSBuild/4.8");
} }


@Test @Test
public void from_argument_is_only_from_let_value_empty() { public void from_argument_is_only_from_let_value_empty() {
cli.parse(new String[] {"--from="});
cli.parse(new String[]{"--from="});
assertThat(cli.getInvokedFrom()).isEmpty(); assertThat(cli.getInvokedFrom()).isEmpty();
} }


public void should_show_usage() { public void should_show_usage() {
logs = mock(Logs.class); logs = mock(Logs.class);
cli = new Cli(exit, logs); cli = new Cli(exit, logs);
cli.parse(new String[] {"-h"});
cli.parse(new String[]{"-h"});
verify(logs).info("usage: sonar-scanner [options]"); verify(logs).info("usage: sonar-scanner [options]");
verify(exit).exit(Exit.SUCCESS); verify(exit).exit(Exit.SUCCESS);
} }
public void should_show_usage_full() { public void should_show_usage_full() {
logs = mock(Logs.class); logs = mock(Logs.class);
cli = new Cli(exit, logs); cli = new Cli(exit, logs);
cli.parse(new String[] {"--help"});
cli.parse(new String[]{"--help"});
verify(logs).info("usage: sonar-scanner [options]"); verify(logs).info("usage: sonar-scanner [options]");
verify(exit).exit(Exit.SUCCESS); verify(exit).exit(Exit.SUCCESS);
} }
public void should_show_usage_on_bad_syntax() { public void should_show_usage_on_bad_syntax() {
logs = mock(Logs.class); logs = mock(Logs.class);
cli = new Cli(exit, logs); cli = new Cli(exit, logs);
cli.parse(new String[] {"-w"});
cli.parse(new String[]{"-w"});
verify(logs).error("Unrecognized option: -w"); verify(logs).error("Unrecognized option: -w");
verify(logs).info("usage: sonar-scanner [options]"); verify(logs).info("usage: sonar-scanner [options]");
verify(exit).exit(Exit.INTERNAL_ERROR); verify(exit).exit(Exit.INTERNAL_ERROR);


@Test @Test
public void should_enable_embedded_mode() { public void should_enable_embedded_mode() {
cli.parse(new String[] {"--embedded"});
cli.parse(new String[]{"--embedded"});
assertThat(cli.isEmbedded()).isTrue(); assertThat(cli.isEmbedded()).isTrue();
} }
} }

+ 0
- 40
src/test/java/org/sonarsource/scanner/cli/ConfTest.java View File

import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.TemporaryFolder; 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.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatCode;
assertThat(properties.getProperty("sonar.projectBaseDir")).isEqualTo(projectHome.toString()); 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 @Test
public void shouldSupportDeepModuleConfigurationInRoot() throws Exception { public void shouldSupportDeepModuleConfigurationInRoot() throws Exception {
Path projectHome = Paths.get(getClass().getResource("ConfTest/shouldSupportDeepModuleConfigurationInRoot/project").toURI()); Path projectHome = Paths.get(getClass().getResource("ConfTest/shouldSupportDeepModuleConfigurationInRoot/project").toURI());
assertThat(properties).containsEntry("sonar.prop", "expected"); 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");
}

} }

+ 36
- 0
src/test/java/org/sonarsource/scanner/cli/LogsTest.java View File

import org.mockito.ArgumentMatchers; import org.mockito.ArgumentMatchers;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; 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.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions;


logs.debug("debug"); logs.debug("debug");
verifyNoMoreInteractions(stdOut, stdErr); 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);
}
} }

+ 47
- 72
src/test/java/org/sonarsource/scanner/cli/MainTest.java View File

import org.mockito.Mockito; import org.mockito.Mockito;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.sonar.api.utils.MessageException; 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.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@Mock @Mock
private Properties properties; private Properties properties;
@Mock @Mock
private ScannerFactory scannerFactory;
private ScannerEngineBootstrapperFactory scannerEngineBootstrapperFactory;
@Mock @Mock
private EmbeddedScanner scanner;
private ScannerEngineBootstrapper bootstrapper;
@Mock
private ScannerEngineFacade engine;
@Mock @Mock
private Logs logs; private Logs logs;


@Before @Before
public void setUp() { public void setUp() {
MockitoAnnotations.initMocks(this); 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); when(conf.properties()).thenReturn(properties);
} }


@Test @Test
public void should_execute_runner() {
public void should_execute_scanner_engine() {
when(cli.getInvokedFrom()).thenReturn(""); 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(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 @Test
public void should_exit_with_error_on_error_during_analysis() { public void should_exit_with_error_on_error_during_analysis() {
EmbeddedScanner runner = mock(EmbeddedScanner.class);
Exception e = new NullPointerException("NPE"); Exception e = new NullPointerException("NPE");
e = new IllegalStateException("Error", e); e = new IllegalStateException("Error", e);
doThrow(e).when(runner).execute(any());
doThrow(e).when(engine).analyze(any());
when(cli.getInvokedFrom()).thenReturn(""); when(cli.getInvokedFrom()).thenReturn("");
when(scannerFactory.create(any(Properties.class), any(String.class))).thenReturn(runner);
when(cli.isDebugEnabled()).thenReturn(true); 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(exit).exit(Exit.INTERNAL_ERROR);
verify(logs).error("Error during SonarScanner execution", e); verify(logs).error("Error during SonarScanner execution", e);
} }


@Test @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"); Exception e = new NullPointerException("NPE");
e = new IllegalStateException("Error", e); e = new IllegalStateException("Error", e);
doThrow(e).when(runner).start();
doThrow(e).when(bootstrapper).bootstrap();
when(cli.getInvokedFrom()).thenReturn(""); when(cli.getInvokedFrom()).thenReturn("");
when(cli.isDebugEnabled()).thenReturn(true); 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(exit).exit(Exit.INTERNAL_ERROR);
verify(logs).error("Error during SonarScanner execution", e); verify(logs).error("Error during SonarScanner execution", e);
} }
when(cli.isEmbedded()).thenReturn(isEmbedded); when(cli.isEmbedded()).thenReturn(isEmbedded);
when(cli.getInvokedFrom()).thenReturn(""); 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); verify(exit).exit(expectedExitCode);
} }
when(cli.getInvokedFrom()).thenReturn(""); when(cli.getInvokedFrom()).thenReturn("");
when(conf.properties()).thenReturn(p); 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(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); inOrder.verify(exit, times(1)).exit(Exit.SUCCESS);
} }


when(conf.properties()).thenReturn(p); when(conf.properties()).thenReturn(p);
when(cli.getInvokedFrom()).thenReturn(""); 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"); 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(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); inOrder.verify(exit, times(1)).exit(Exit.SUCCESS);
} }


@Test @Test
public void shouldLogServerVersion() { public void shouldLogServerVersion() {
when(scanner.serverVersion()).thenReturn("5.5");
when(engine.isSonarCloud()).thenReturn(false);
when(engine.getServerVersion()).thenReturn("5.5");
Properties p = new Properties(); Properties p = new Properties();
when(cli.isDisplayVersionOnly()).thenReturn(true); when(cli.isDisplayVersionOnly()).thenReturn(true);
when(cli.getInvokedFrom()).thenReturn(""); when(cli.getInvokedFrom()).thenReturn("");
when(conf.properties()).thenReturn(p); 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"); verify(logs).info("Analyzing on SonarQube server 5.5");
} }


@Test @Test
public void should_log_SonarCloud_server() { public void should_log_SonarCloud_server() {
when(engine.isSonarCloud()).thenReturn(true);
Properties p = new Properties(); Properties p = new Properties();
p.setProperty("sonar.host.url", "https://sonarcloud.io");
when(conf.properties()).thenReturn(p); when(conf.properties()).thenReturn(p);
when(cli.getInvokedFrom()).thenReturn(""); 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"); 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 @Test
public void should_configure_logging() { public void should_configure_logging() {
Properties analysisProps = testLogging("sonar.verbose", "true"); Properties analysisProps = testLogging("sonar.verbose", "true");
when(conf.properties()).thenReturn(p); when(conf.properties()).thenReturn(p);
when(cli.getInvokedFrom()).thenReturn(""); 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<Properties> propertiesCapture = ArgumentCaptor.forClass(Properties.class); ArgumentCaptor<Properties> propertiesCapture = ArgumentCaptor.forClass(Properties.class);
verify(scanner).execute((Map) propertiesCapture.capture());
verify(engine).analyze((Map) propertiesCapture.capture());


return propertiesCapture.getValue(); return propertiesCapture.getValue();
} }

+ 84
- 0
src/test/java/org/sonarsource/scanner/cli/ScannerEngineBootstrapperFactoryTest.java View File

/*
* 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());
}


}

+ 0
- 105
src/test/java/org/sonarsource/scanner/cli/ScannerFactoryTest.java View File

/*
* 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);
}

}

Loading…
Cancel
Save