import java.util.Properties; | import java.util.Properties; | ||||
import org.sonarsource.scanner.api.ScannerProperties; | import org.sonarsource.scanner.api.ScannerProperties; | ||||
import static java.util.Arrays.asList; | |||||
class Cli { | class Cli { | ||||
private boolean debugEnabled = false; | private boolean debugEnabled = false; | ||||
Cli parse(String[] args) { | Cli parse(String[] args) { | ||||
reset(); | reset(); | ||||
props.putAll(System.getProperties()); | 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; | 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() { | private void reset() { | ||||
props.clear(); | props.clear(); | ||||
debugEnabled = false; | debugEnabled = false; | ||||
props.setProperty(key, value); | props.setProperty(key, value); | ||||
} | } | ||||
private void printError(String message) { | |||||
private void printErrorAndExit(String message) { | |||||
logger.error(message); | logger.error(message); | ||||
printUsage(); | printUsage(); | ||||
exit.exit(Exit.ERROR); | exit.exit(Exit.ERROR); |
package org.sonar.api.utils; | package org.sonar.api.utils; | ||||
public class MessageException extends RuntimeException { | 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"); | 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 | @Test | ||||
public void should_not_fail_with_errors_option() { | public void should_not_fail_with_errors_option() { | ||||
cli.parse(new String[] {"-e"}); | cli.parse(new String[] {"-e"}); | ||||
} | } | ||||
@Test | @Test | ||||
public void should_parse_optional_task() { | public void should_parse_optional_task() { | ||||
cli.parse(new String[] {"-D", "foo=bar"}); | cli.parse(new String[] {"-D", "foo=bar"}); | ||||
assertThat(cli.properties().get("sonar.verbose")).isEqualTo("true"); | 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 | @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.properties().get("sonar.verbose")).isNull(); | 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 | @Test | ||||
public void should_disable_debug_mode_and_stacktrace_log_by_default() { | public void should_disable_debug_mode_and_stacktrace_log_by_default() { | ||||
cli.parse(new String[0]); | cli.parse(new String[0]); | ||||
verify(exit).exit(Exit.SUCCESS); | 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 | @Test | ||||
public void should_show_usage_on_bad_syntax() { | public void should_show_usage_on_bad_syntax() { | ||||
logs = mock(Logs.class); | logs = mock(Logs.class); |
} | } | ||||
@Test | @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); | Exception e = createException(false); | ||||
testException(e, false); | testException(e, false); | ||||
testException(e, false); | testException(e, false); | ||||
verify(logs).error("Error during SonarQube Scanner execution"); | 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."); | verify(logs).error("Re-run SonarQube Scanner using the -X switch to enable full debug logging."); | ||||
} | } | ||||
private Exception createException(boolean messageException) { | private Exception createException(boolean messageException) { | ||||
Exception e; | Exception e; | ||||
if (messageException) { | if (messageException) { | ||||
e = new MessageException("my message"); | |||||
e = new MessageException("my message", new NullPointerException("NPE")); | |||||
} else { | } else { | ||||
e = new IllegalStateException("Error", new NullPointerException("NPE")); | e = new IllegalStateException("Error", new NullPointerException("NPE")); | ||||
} | } |