/** Prints help if requested, and otherwise executes the most specific {@code Runnable} or {@code Callable} subcommand. * Finally, either a list of result objects is returned, or the JVM is terminated if an exit code {@linkplain #andExit(int) was set}. * If the last (sub)command does not implement either {@code Runnable} or {@code Callable}, an {@code ExecutionException} * is thrown detailing the problem and capturing the offending {@code CommandLine} object. * * @param parsedCommands the {@code CommandLine} objects that resulted from successfully parsing the command line arguments * @param out the {@code PrintStream} to print help to if requested * @param ansi for printing help messages using ANSI styles and colors * @return an empty list if help was requested, or a list containing a single element: the result of calling the * {@code Callable}, or a {@code null} element if the last (sub)command was a {@code Runnable} * @throws ParameterException if the {@link HelpCommand HelpCommand} was invoked for an unknown subcommand. Any {@code ParameterExceptions} * thrown from this method are treated as if this exception was thrown during parsing and passed to the {@link IExceptionHandler} * @throws ExecutionException if a problem occurred while processing the parse results; use * {@link ExecutionException#getCommandLine()} to get the command or subcommand where processing failed */ public List<Object> handleParseResult(List<CommandLine> parsedCommands, PrintStream out, Help.Ansi ansi) { if (printHelpIfRequested(parsedCommands, out, err(), ansi)) { return returnResultOrExit(Collections.emptyList()); } return returnResultOrExit(execute(parsedCommands.get(parsedCommands.size() - 1), new ArrayList<Object>())); } /** Executes the most specific {@code Runnable} or {@code Callable} subcommand.
@Test public void testUnannotatedCommandWithMixin() throws Exception { Method m = CommandLine.getCommandMethods(UnannotatedClassWithMixinAndOptionsAndPositionals.class, "sum").get(0); CommandLine commandLine = new CommandLine(m); List<CommandLine> parsed = commandLine.parse("-y foo -y bar -a 7 -b 11 13 42".split(" ")); assertEquals(1, parsed.size()); // get method args Object[] methodArgValues = parsed.get(0).getCommandSpec().argValues(); assertNotNull(methodArgValues); // verify args String[] arg0 = (String[]) methodArgValues[0]; assertArrayEquals(new String[] {"foo", "bar"}, arg0); SomeMixin arg1 = (SomeMixin) methodArgValues[1]; assertEquals(7, arg1.a); assertEquals(11, arg1.b); int[] arg2 = (int[]) methodArgValues[2]; assertArrayEquals(new int[] {13, 42}, arg2); // verify method is callable with args long result = (Long) m.invoke(new UnannotatedClassWithMixinAndOptionsAndPositionals(), methodArgValues); assertEquals(22, result); // verify same result with result handler List<Object> results = new RunLast().handleParseResult(parsed, System.out, CommandLine.Help.Ansi.OFF); assertEquals(1, results.size()); assertEquals(22L, results.get(0)); }
@Test public void testAnnotatedSubcommandWithDoubleMixin() throws Exception { AnnotatedClassWithMixinParameters command = new AnnotatedClassWithMixinParameters(); CommandLine commandLine = new CommandLine(command); List<CommandLine> parsed = commandLine.parse("-a 3 -b 5 sum -y foo -y bar -a 7 -b 11 13 42".split(" ")); assertEquals(2, parsed.size()); // get method args Object[] methodArgValues = parsed.get(1).getCommandSpec().argValues(); assertNotNull(methodArgValues); // verify args String[] arg0 = (String[]) methodArgValues[0]; assertArrayEquals(new String[] {"foo", "bar"}, arg0); SomeMixin arg1 = (SomeMixin) methodArgValues[1]; assertEquals(7, arg1.a); assertEquals(11, arg1.b); int[] arg2 = (int[]) methodArgValues[2]; assertArrayEquals(new int[] {13, 42}, arg2); // verify method is callable with args Method m = AnnotatedClassWithMixinParameters.class.getDeclaredMethod("sum", String[].class, SomeMixin.class, int[].class); long result = (Long) m.invoke(command, methodArgValues); assertEquals(30, result); // verify same result with result handler List<Object> results = new RunLast().handleParseResult(parsed, System.out, CommandLine.Help.Ansi.OFF); assertEquals(1, results.size()); assertEquals(30L, results.get(0)); }
@Test public void testUnannotatedCommandWithMixin() throws Exception { Method m = CommandLine.getCommandMethods(UnannotatedClassWithMixinAndOptionsAndPositionals.class, "sum").get(0); CommandLine commandLine = new CommandLine(m); List<CommandLine> parsed = commandLine.parse("-y foo -y bar -a 7 -b 11 13 42".split(" ")); assertEquals(1, parsed.size()); // get method args Object[] methodArgValues = parsed.get(0).getCommandSpec().argValues(); assertNotNull(methodArgValues); // verify args String[] arg0 = (String[]) methodArgValues[0]; assertArrayEquals(new String[] {"foo", "bar"}, arg0); SomeMixin arg1 = (SomeMixin) methodArgValues[1]; assertEquals(7, arg1.a); assertEquals(11, arg1.b); int[] arg2 = (int[]) methodArgValues[2]; assertArrayEquals(new int[] {13, 42}, arg2); // verify method is callable with args long result = (Long) m.invoke(new UnannotatedClassWithMixinAndOptionsAndPositionals(), methodArgValues); assertEquals(22, result); // verify same result with result handler List<Object> results = new RunLast().handleParseResult(parsed, System.out, CommandLine.Help.Ansi.OFF); assertEquals(1, results.size()); assertEquals(22L, results.get(0)); }
@Test public void testAnnotatedMethodMultipleMixinsSubcommandWithDoubleMixin() throws Exception { Method m = CommandLine.getCommandMethods(AnnotatedClassWithMultipleMixinParameters.class, "sum").get(0); CommandLine commandLine = new CommandLine(m); List<CommandLine> parsed = commandLine.parse("-a 3 -b 5 -c 7".split(" ")); assertEquals(1, parsed.size()); // get method args Object[] methodArgValues = parsed.get(0).getCommandSpec().argValues(); assertNotNull(methodArgValues); // verify args SomeMixin arg0 = (SomeMixin) methodArgValues[0]; assertEquals(3, arg0.a); assertEquals(5, arg0.b); OtherMixin arg1 = (OtherMixin) methodArgValues[1]; assertEquals(7, arg1.c); // verify method is callable with args long result = (Long) m.invoke(new AnnotatedClassWithMultipleMixinParameters(), methodArgValues); assertEquals(15, result); // verify same result with result handler List<Object> results = new RunLast().handleParseResult(parsed, System.out, CommandLine.Help.Ansi.OFF); assertEquals(1, results.size()); assertEquals(15L, results.get(0)); }
@Test public void testOptionalListParameterShouldNotRememberValuesInCommandMethods() { @Command() class TestCommand { @Command(name="method") public String methodCommand(@Parameters(arity="0..*") List<String> methodValues) { return methodValues == null ? "null" : methodValues.toString(); } } CommandLine commandLine = new CommandLine(new TestCommand()); // problematic for @Command-method @Parameters List<Object> methodFirstExecutionResultWithParametersGiven = commandLine.parseWithHandlers( new RunLast(), new DefaultExceptionHandler<List<Object>>(), new String[] {"method","arg0", "arg1"}); List<Object> methodSecondExecutionResultWithoutParameters = commandLine.parseWithHandlers( new RunLast(), new DefaultExceptionHandler<List<Object>>(), new String[] {"method"}); assertEquals("[arg0, arg1]", methodFirstExecutionResultWithParametersGiven.get(0)); // fails, still "[arg0, arg1]" assertEquals("null", methodSecondExecutionResultWithoutParameters.get(0)); }
@Test public void testParseWithHandlerRunXxxReturnsCallableResultWithSubcommand() { @Command class App implements Callable<Object> { public Object call() { return "RETURN VALUE"; } } @Command(name = "sub") class Sub implements Callable<Object> { public Object call() { return "SUB RETURN VALUE"; } } CommandLineFactory factory = new CommandLineFactory() { public CommandLine create() {return new CommandLine(new App()).addSubcommand("sub", new Sub());} }; Object actual1 = factory.create().parseWithHandler(new RunFirst(), new String[] {"sub"}); assertEquals("RunFirst: return value", Arrays.asList("RETURN VALUE"), actual1); Object actual2 = factory.create().parseWithHandler(new RunLast(), new String[] {"sub"}); assertEquals("RunLast: return value", Arrays.asList("SUB RETURN VALUE"), actual2); Object actual3 = factory.create().parseWithHandler(new RunAll(), new String[] {"sub"}); assertEquals("RunAll: return value", Arrays.asList("RETURN VALUE", "SUB RETURN VALUE"), actual3); } @Test
public static void main(String[] args) { // ensure we init logback before anything else String logbackConfig = System.getProperty(LOGBACK_CONFIG); if (StringUtils.isBlank(logbackConfig)) { System.setProperty(LOGBACK_CONFIG, "logback-netty.xml"); } logger = LoggerFactory.getLogger(Main.class); logger.info("Karate version: {}", FileUtils.getKarateVersion()); CommandLine cmd = new CommandLine(new Main()); DefaultExceptionHandler<List<Object>> exceptionHandler = new DefaultExceptionHandler() { @Override public Object handleExecutionException(ExecutionException ex, ParseResult parseResult) { if (ex.getCause() instanceof KarateException) { throw new ExecutionException(cmd, ex.getCause().getMessage()); // minimum possible stack trace but exit code 1 } else { throw ex; } } }; cmd.parseWithHandlers(new RunLast(), exceptionHandler, args); System.exit(0); }
@Test public void testOptionalListParameterInCommandClass() { @Command() class TestCommand implements Callable<String> { @Parameters(arity="0..*") private List<String> values; public String call() throws Exception { return values == null ? "null" : values.toString(); } } // seems to be working for @Command-class @Parameters CommandLine commandLine = new CommandLine(new TestCommand()); List<Object> firstExecutionResultWithParametersGiven = commandLine.parseWithHandlers( new RunLast(), new DefaultExceptionHandler<List<Object>>(), new String[] {"arg0", "arg1"}); List<Object> secondExecutionResultWithoutParameters = commandLine.parseWithHandlers( new RunLast(), new DefaultExceptionHandler<List<Object>>(), new String[] {}); assertEquals("[arg0, arg1]", firstExecutionResultWithParametersGiven.get(0)); assertEquals("null", secondExecutionResultWithoutParameters.get(0)); }
@Test public void testParseWithHandler2RunXxxReturnsCallableResultWithSubcommand() { @Command class App implements Callable<Object> { public Object call() { return "RETURN VALUE"; } } @Command(name = "sub") class Sub implements Callable<Object> { public Object call() { return "SUB RETURN VALUE"; } } CommandLineFactory factory = new CommandLineFactory() { public CommandLine create() {return new CommandLine(new App()).addSubcommand("sub", new Sub());} }; PrintStream out = new PrintStream(new ByteArrayOutputStream()); Object actual1 = factory.create().parseWithHandler(new RunFirst(), new String[]{"sub"}); assertEquals("RunFirst: return value", Arrays.asList("RETURN VALUE"), actual1); Object actual2 = factory.create().parseWithHandler(new RunLast(), new String[]{"sub"}); assertEquals("RunLast: return value", Arrays.asList("SUB RETURN VALUE"), actual2); Object actual3 = factory.create().parseWithHandler(new RunAll(), new String[]{"sub"}); assertEquals("RunAll: return value", Arrays.asList("RETURN VALUE", "SUB RETURN VALUE"), actual3); }
@Test public void testAnnotatedMethodMultipleMixinsSubcommandWithEmptyMixin() throws Exception { Method m = CommandLine.getCommandMethods(AnnotatedClassWithMultipleEmptyParameters.class, "sum").get(0); CommandLine commandLine = new CommandLine(m); List<CommandLine> parsed = commandLine.parse("-a 3".split(" ")); assertEquals(1, parsed.size()); // get method args Object[] methodArgValues = parsed.get(0).getCommandSpec().argValues(); assertNotNull(methodArgValues); // verify args int arg0 = (Integer) methodArgValues[0]; assertEquals(3, arg0); EmptyMixin arg1 = (EmptyMixin) methodArgValues[1]; // verify method is callable with args long result = (Long) m.invoke(new AnnotatedClassWithMultipleEmptyParameters(), methodArgValues); assertEquals(3, result); // verify same result with result handler List<Object> results = new RunLast().handleParseResult(parsed, System.out, CommandLine.Help.Ansi.OFF); assertEquals(1, results.size()); assertEquals(3L, results.get(0)); }
/** Prints help if requested, and otherwise executes the most specific {@code Runnable} or {@code Callable} subcommand. * Finally, either a list of result objects is returned, or the JVM is terminated if an exit code {@linkplain #andExit(int) was set}. * If the last (sub)command does not implement either {@code Runnable} or {@code Callable}, an {@code ExecutionException} * is thrown detailing the problem and capturing the offending {@code CommandLine} object. * * @param parsedCommands the {@code CommandLine} objects that resulted from successfully parsing the command line arguments * @param out the {@code PrintStream} to print help to if requested * @param ansi for printing help messages using ANSI styles and colors * @return an empty list if help was requested, or a list containing a single element: the result of calling the * {@code Callable}, or a {@code null} element if the last (sub)command was a {@code Runnable} * @throws ParameterException if the {@link HelpCommand HelpCommand} was invoked for an unknown subcommand. Any {@code ParameterExceptions} * thrown from this method are treated as if this exception was thrown during parsing and passed to the {@link IExceptionHandler} * @throws ExecutionException if a problem occurred while processing the parse results; use * {@link ExecutionException#getCommandLine()} to get the command or subcommand where processing failed */ public List<Object> handleParseResult(List<CommandLine> parsedCommands, PrintStream out, Help.Ansi ansi) { if (printHelpIfRequested(parsedCommands, out, err(), ansi)) { return returnResultOrExit(Collections.emptyList()); } return returnResultOrExit(execute(parsedCommands.get(parsedCommands.size() - 1), new ArrayList<Object>())); } /** Executes the most specific {@code Runnable} or {@code Callable} subcommand.
@SuppressWarnings("deprecation") private void verifyAllFail(Factory factory, String prefix, String suffix, String[] args) { IParseResultHandler[] handlers = new IParseResultHandler[] { new RunFirst(), new RunLast(), new RunAll() }; for (IParseResultHandler handler : handlers) { String descr = handler.getClass().getSimpleName(); try { new CommandLine(factory.create()).parseWithHandler(handler, System.out, args); fail(descr + ": expected exception"); } catch (ExecutionException ex) { String actual = ex.getMessage(); assertTrue(descr + ": " + actual, actual.startsWith(prefix)); assertTrue(descr + ": " + actual, actual.endsWith(suffix)); } } }
@Test public void optionsAreNotExpected() { AbstractTestCommand testCommand = new TestCommandWithDeps(mockLogger); testCommand.commandLine.parseWithHandlers( new RunLast(), defaultExceptionHandler(), "--option-enabled", "false", "--option2", "20", "--option3", "30", "--option4", "40"); verifyOptionsConstraintLoggerCall(mockLogger, "--option2 and --option3", "--option-enabled"); }