import java.util.Properties;
import org.sonarsource.scanner.api.ScannerProperties;
+import static java.util.Arrays.asList;
+
class Cli {
private boolean debugEnabled = false;
Cli parse(String[] args) {
reset();
props.putAll(System.getProperties());
- for (int i = 0; i < args.length; i++) {
- String arg = args[i];
- if (i == 0 && arg.charAt(0) != '-') {
- props.setProperty(ScannerProperties.TASK, arg);
-
- } else if ("-h".equals(arg) || "--help".equals(arg)) {
- printUsage();
- exit.exit(Exit.SUCCESS);
-
- } else if ("-v".equals(arg) || "--version".equals(arg)) {
- displayVersionOnly = true;
-
- } else if ("-e".equals(arg) || "--errors".equals(arg)) {
- logger.info("Option -e/--errors is no longer supported and will be ignored");
-
- } else if ("-X".equals(arg) || "--debug".equals(arg)) {
- props.setProperty("sonar.verbose", "true");
- debugEnabled = true;
- logger.setDebugEnabled(true);
-
- } else if ("-D".equals(arg) || "--define".equals(arg)) {
- i++;
- if (i >= args.length) {
- printError("Missing argument for option --define");
- }
- arg = args[i];
- appendPropertyTo(arg, props);
-
- } else if (arg.startsWith("-D")) {
- arg = arg.substring(2);
- appendPropertyTo(arg, props);
-
- } else {
- printError("Unrecognized option: " + arg);
- }
+ if (args.length > 0) {
+ int pos = 0;
+ do {
+ pos = processNextArg(args, pos);
+ } while (pos < args.length);
}
-
return this;
}
+ 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)) {
+ printUsage();
+ exit.exit(Exit.SUCCESS);
+
+ } else if (asList("-v", "--version").contains(arg)) {
+ displayVersionOnly = true;
+
+ } else if (asList("-e", "--errors").contains(arg)) {
+ logger.info("Option -e/--errors is no longer supported and will be ignored");
+
+ } else if (asList("-X", "--debug").contains(arg)) {
+ props.setProperty("sonar.verbose", "true");
+ debugEnabled = true;
+ logger.setDebugEnabled(true);
+
+ } else if (asList("-D", "--define").contains(arg)) {
+ return processProp(args, pos);
+
+ } else if (arg.startsWith("-D")) {
+ arg = arg.substring(2);
+ appendPropertyTo(arg, props);
+
+ } else {
+ printErrorAndExit("Unrecognized option: " + arg);
+ }
+ return pos + 1;
+ }
+
+ private int processProp(String[] args, int pos) {
+ int valuePos = pos + 1;
+ if (valuePos >= args.length) {
+ printErrorAndExit("Missing argument for option -D/--define");
+ } else {
+ appendPropertyTo(args[valuePos], props);
+ }
+ return valuePos + 1;
+ }
+
private void reset() {
props.clear();
debugEnabled = false;
props.setProperty(key, value);
}
- private void printError(String message) {
+ private void printErrorAndExit(String message) {
logger.error(message);
printUsage();
exit.exit(Exit.ERROR);
package org.sonar.api.utils;
public class MessageException extends RuntimeException {
- public MessageException(String msg) {
- super(msg);
+ public MessageException(String msg, Throwable cause) {
+ super(msg, cause);
}
}
assertThat(cli.properties().get("boolean")).isEqualTo("true");
}
+ @Test
+ public void should_fail_on_missing_prop() {
+ logs = mock(Logs.class);
+ cli = new Cli(exit, logs);
+ cli.parse(new String[] {"-D"});
+ verify(logs).error("Missing argument for option -D/--define");
+ verify(exit).exit(Exit.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.verbose")).isEqualTo("true");
}
+ @Test
+ public void should_enable_debug_mode_full() {
+ cli.parse(new String[] {"--debug"});
+ assertThat(cli.isDebugEnabled()).isTrue();
+ assertThat(cli.properties().get("sonar.verbose")).isEqualTo("true");
+ }
+
+ @Test
+ public void should_show_version() {
+ cli.parse(new String[] {"-v"});
+ assertThat(cli.isDisplayVersionOnly()).isTrue();
+ }
+
+ @Test
+ public void should_show_version_full() {
+ cli.parse(new String[] {"--version"});
+ assertThat(cli.isDisplayVersionOnly()).isTrue();
+ }
+
@Test
public void should_enable_stacktrace_log() {
cli.parse(new String[] {"-e"});
assertThat(cli.properties().get("sonar.verbose")).isNull();
}
+ @Test
+ public void should_enable_stacktrace_log_full() {
+ cli.parse(new String[] {"--errors"});
+ assertThat(cli.isDebugEnabled()).isFalse();
+ assertThat(cli.properties().get("sonar.verbose")).isNull();
+ }
+
@Test
public void should_disable_debug_mode_and_stacktrace_log_by_default() {
cli.parse(new String[0]);
verify(exit).exit(Exit.SUCCESS);
}
+ @Test
+ public void should_show_usage_full() {
+ logs = mock(Logs.class);
+ cli = new Cli(exit, logs);
+ cli.parse(new String[] {"--help"});
+ verify(logs).info("usage: sonar-scanner [options]");
+ verify(exit).exit(Exit.SUCCESS);
+ }
+
@Test
public void should_show_usage_on_bad_syntax() {
logs = mock(Logs.class);
}
@Test
- public void show_error() {
+ public void should_exit_on_error() {
+ EmbeddedScanner runner = mock(EmbeddedScanner.class);
+ Exception e = new NullPointerException("NPE");
+ e = new IllegalStateException("Error", e);
+ doThrow(e).when(runner).stop();
+ when(runnerFactory.create(any(Properties.class))).thenReturn(runner);
+
+ Main main = new Main(exit, cli, conf, runnerFactory, logs);
+ main.execute();
+
+ verify(runner).stop();
+ verify(exit).exit(Exit.ERROR);
+ verify(logs).error("Unable to properly stop the scanner", e);
+ }
+
+ @Test
+ public void show_error_with_stacktrace() {
Exception e = createException(false);
testException(e, false);
testException(e, false);
verify(logs).error("Error during SonarQube Scanner execution");
+ verify(logs).error("Caused by: NPE");
verify(logs).error("Re-run SonarQube Scanner using the -X switch to enable full debug logging.");
}
private Exception createException(boolean messageException) {
Exception e;
if (messageException) {
- e = new MessageException("my message");
+ e = new MessageException("my message", new NullPointerException("NPE"));
} else {
e = new IllegalStateException("Error", new NullPointerException("NPE"));
}