diff --git a/pom.xml b/pom.xml index e3dd6f02..4a8e842d 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,7 @@ spring-shell2-core + spring-shell2-core-tests spring-shell2-standard spring-shell2-standard-commands spring-shell2-jcommander-adapter @@ -36,6 +37,11 @@ spring-shell2-core ${project.version} + + org.springframework.shell + spring-shell2-core-tests + ${project.version} + org.springframework.shell spring-shell2-standard diff --git a/spring-shell2-core-tests/pom.xml b/spring-shell2-core-tests/pom.xml new file mode 100644 index 00000000..01dff2f5 --- /dev/null +++ b/spring-shell2-core-tests/pom.xml @@ -0,0 +1,27 @@ + + 4.0.0 + + spring-shell2-core-tests + jar + + + org.springframework.shell + spring-shell2-parent + 2.0.0.BUILD-SNAPSHOT + + + Core API test classes for Spring Shell 2 + + + + org.springframework.shell + spring-shell2-core + + + org.assertj + assertj-core + compile + + + diff --git a/spring-shell2-core-tests/src/main/java/org/springframework/shell2/ValueResultAsserts.java b/spring-shell2-core-tests/src/main/java/org/springframework/shell2/ValueResultAsserts.java new file mode 100644 index 00000000..b6f85b1e --- /dev/null +++ b/spring-shell2-core-tests/src/main/java/org/springframework/shell2/ValueResultAsserts.java @@ -0,0 +1,69 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.shell2; + +import org.assertj.core.api.AbstractAssert; +import org.assertj.core.api.Assertions; + +/** + * Assertions for {@link ValueResult}. + * + * @author Camilo Gonzalez + */ +public class ValueResultAsserts extends AbstractAssert { + + public ValueResultAsserts(ValueResult actual) { + super(actual, ValueResultAsserts.class); + } + + public ValueResultAsserts hasValue(Object expectedValue) { + isNotNull(); + Assertions.assertThat(actual.resolvedValue()).isEqualTo(expectedValue); + return this; + } + + public ValueResultAsserts usesWords(int... expectedWordsUsed) { + isNotNull(); + + Assertions.assertThat(actual.wordsUsed().stream().toArray()).containsExactly(expectedWordsUsed); + + return this; + } + + public ValueResultAsserts notUsesWords() { + isNotNull(); + Assertions.assertThat(actual.wordsUsed().isEmpty()); + return this; + } + + public ValueResultAsserts usesWordsForValue(int... expectedWordsUsedForValue) { + isNotNull(); + + Assertions.assertThat(actual.wordsUsedForValue().stream().toArray()).containsExactly(expectedWordsUsedForValue); + + return this; + } + + public ValueResultAsserts notUsesWordsForValue() { + isNotNull(); + Assertions.assertThat(actual.wordsUsedForValue().isEmpty()); + return this; + } + + public static ValueResultAsserts assertThat(ValueResult valueResult) { + return new ValueResultAsserts(valueResult); + } +} diff --git a/spring-shell2-core/src/main/java/org/springframework/shell2/ParameterResolver.java b/spring-shell2-core/src/main/java/org/springframework/shell2/ParameterResolver.java index 6dcc12f4..53702ded 100644 --- a/spring-shell2-core/src/main/java/org/springframework/shell2/ParameterResolver.java +++ b/spring-shell2-core/src/main/java/org/springframework/shell2/ParameterResolver.java @@ -36,7 +36,7 @@ public interface ParameterResolver { /** * Turn the given textual input into an actual object, maybe using some conversion or lookup mechanism. */ - Object resolve(MethodParameter methodParameter, List words); + ValueResult resolve(MethodParameter methodParameter, List words); /** * Describe a supported parameter, so that integrated help can be generated. diff --git a/spring-shell2-core/src/main/java/org/springframework/shell2/Shell.java b/spring-shell2-core/src/main/java/org/springframework/shell2/Shell.java index 880ea325..8fd50ba6 100644 --- a/spring-shell2-core/src/main/java/org/springframework/shell2/Shell.java +++ b/spring-shell2-core/src/main/java/org/springframework/shell2/Shell.java @@ -222,7 +222,7 @@ public class Shell implements CommandRegistry { Arrays.fill(args, UNRESOLVED); for (int i = 0; i < parameters.length; i++) { MethodParameter methodParameter = Utils.createMethodParameter(method, i); - args[i] = findResolver(methodParameter).resolve(methodParameter, wordsForArgs); + args[i] = findResolver(methodParameter).resolve(methodParameter, wordsForArgs).resolvedValue(); } return args; } diff --git a/spring-shell2-core/src/main/java/org/springframework/shell2/ValueResult.java b/spring-shell2-core/src/main/java/org/springframework/shell2/ValueResult.java new file mode 100644 index 00000000..a86524d4 --- /dev/null +++ b/spring-shell2-core/src/main/java/org/springframework/shell2/ValueResult.java @@ -0,0 +1,92 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.shell2; + +import java.util.BitSet; +import java.util.List; +import java.util.stream.Collectors; + +import javax.naming.spi.ResolveResult; + +import org.springframework.core.MethodParameter; + +/** + * A {@link ResolveResult} for a successful {@link ParameterResolver#resolve} operation. + * + * @author Camilo Gonzalez + */ +public class ValueResult { + + private final MethodParameter methodParameter; + + private final Object resolvedValue; + + private final BitSet wordsUsed; + + private final BitSet wordsUsedForValue; + + public ValueResult(MethodParameter methodParameter, Object resolvedValue) { + this(methodParameter, resolvedValue, new BitSet(), new BitSet()); + } + + public ValueResult(MethodParameter methodParameter, Object resolvedValue, BitSet wordsUsed, + BitSet wordsUsedForValue) { + + this.methodParameter = methodParameter; + this.resolvedValue = resolvedValue; + this.wordsUsed = wordsUsed == null ? new BitSet() : wordsUsed; + this.wordsUsedForValue = wordsUsedForValue == null ? new BitSet() : wordsUsedForValue; + } + + /** + * The {@link MethodParameter} that was the target of the {@link ParameterResolver#resolve} + * operation. + */ + public MethodParameter methodParameter() { + return methodParameter; + } + + /** + * Represents the resolved value for the {@link MethodParameter} associated with this result. + */ + public Object resolvedValue() { + return resolvedValue; + } + + /** + * Represents the full set of words used to resolve the {@link MethodParameter}. This includes + * any tags/keys consumed from the input. + */ + public BitSet wordsUsed() { + return wordsUsed; + } + + /** + * Represents the full set of words used to resolve the value of this {@link MethodParameter}. + */ + public BitSet wordsUsedForValue() { + return wordsUsedForValue; + } + + public List wordsUsed(List words) { + return wordsUsed.stream().mapToObj(index -> words.get(index)).collect(Collectors.toList()); + } + + public List wordsUsedForValue(List words) { + return wordsUsedForValue.stream().mapToObj(index -> words.get(index)).collect(Collectors.toList()); + } + +} diff --git a/spring-shell2-core/src/test/java/org/springframework/shell2/ShellTest.java b/spring-shell2-core/src/test/java/org/springframework/shell2/ShellTest.java index e1bdc3a0..8cac4511 100644 --- a/spring-shell2-core/src/test/java/org/springframework/shell2/ShellTest.java +++ b/spring-shell2-core/src/test/java/org/springframework/shell2/ShellTest.java @@ -52,6 +52,8 @@ public class ShellTest { @Mock private ParameterResolver parameterResolver; + + private ValueResult valueResult; @InjectMocks private Shell shell; @@ -67,6 +69,8 @@ public class ShellTest { public void commandMatch() throws IOException { when(parameterResolver.supports(any())).thenReturn(true); when(inputProvider.readInput()).thenReturn(() -> "hello world how are you doing ?"); + valueResult = new ValueResult(null, "test"); + when(parameterResolver.resolve(any(), any())).thenReturn(valueResult); doThrow(new Exit()).when(resultHandler).handleResult(any()); shell.methodTargets = Collections.singletonMap("hello world", MethodTarget.of("helloWorld", this, "Say hello")); @@ -102,6 +106,8 @@ public class ShellTest { public void noCommand() throws IOException { when(parameterResolver.supports(any())).thenReturn(true); when(inputProvider.readInput()).thenReturn(() -> "", () -> "hello world how are you doing ?"); + valueResult = new ValueResult(null, "test"); + when(parameterResolver.resolve(any(), any())).thenReturn(valueResult); doThrow(new Exit()).when(resultHandler).handleResult(any()); shell.methodTargets = Collections.singletonMap("hello world", MethodTarget.of("helloWorld", this, "Say hello")); diff --git a/spring-shell2-jcommander-adapter/src/main/java/org/springframework/shell2/jcommander/JCommanderParameterResolver.java b/spring-shell2-jcommander-adapter/src/main/java/org/springframework/shell2/jcommander/JCommanderParameterResolver.java index 0afa6813..fb1d0c5b 100644 --- a/spring-shell2-jcommander-adapter/src/main/java/org/springframework/shell2/jcommander/JCommanderParameterResolver.java +++ b/spring-shell2-jcommander-adapter/src/main/java/org/springframework/shell2/jcommander/JCommanderParameterResolver.java @@ -19,7 +19,6 @@ package org.springframework.shell2.jcommander; import static org.springframework.shell2.Utils.unCamelify; import java.lang.annotation.Annotation; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -28,22 +27,22 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; import java.util.stream.Stream; -import com.beust.jcommander.DynamicParameter; -import com.beust.jcommander.JCommander; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParameterException; -import com.beust.jcommander.ParametersDelegate; - import org.springframework.beans.BeanUtils; import org.springframework.core.MethodParameter; import org.springframework.shell2.CompletionContext; import org.springframework.shell2.CompletionProposal; import org.springframework.shell2.ParameterDescription; import org.springframework.shell2.ParameterResolver; -import org.springframework.shell2.Utils; +import org.springframework.shell2.ValueResult; import org.springframework.stereotype.Component; import org.springframework.util.ReflectionUtils; +import com.beust.jcommander.DynamicParameter; +import com.beust.jcommander.JCommander; +import com.beust.jcommander.Parameter; +import com.beust.jcommander.ParameterException; +import com.beust.jcommander.ParametersDelegate; + /** * Provides integration with JCommander. * @@ -80,10 +79,10 @@ public class JCommanderParameterResolver implements ParameterResolver { } @Override - public Object resolve(MethodParameter methodParameter, List words) { + public ValueResult resolve(MethodParameter methodParameter, List words) { JCommander jCommander = createJCommander(methodParameter); jCommander.parse(words.toArray(new String[words.size()])); - return jCommander.getObjects().get(0); + return new ValueResult(methodParameter, jCommander.getObjects().get(0)); } private JCommander createJCommander(MethodParameter methodParameter) { diff --git a/spring-shell2-jcommander-adapter/src/test/java/org/springframework/shell2/jcommander/JCommanderParameterResolverTest.java b/spring-shell2-jcommander-adapter/src/test/java/org/springframework/shell2/jcommander/JCommanderParameterResolverTest.java index 09195055..1201b90f 100644 --- a/spring-shell2-jcommander-adapter/src/test/java/org/springframework/shell2/jcommander/JCommanderParameterResolverTest.java +++ b/spring-shell2-jcommander-adapter/src/test/java/org/springframework/shell2/jcommander/JCommanderParameterResolverTest.java @@ -63,7 +63,9 @@ public class JCommanderParameterResolverTest { public void testPojoValuesAreCorrectlySet() { MethodParameter methodParameter = Utils.createMethodParameter(COMMAND_METHOD, 0); - FieldCollins resolved = (FieldCollins) resolver.resolve(methodParameter, asList("--name foo -level 2 something-else yet-something-else".split(" "))); + FieldCollins resolved = (FieldCollins) resolver + .resolve(methodParameter, asList("--name foo -level 2 something-else yet-something-else".split(" "))) + .resolvedValue(); assertThat(resolved.getName()).isEqualTo("foo"); assertThat(resolved.getLevel()).isEqualTo(2); diff --git a/spring-shell2-shell1-adapter/pom.xml b/spring-shell2-shell1-adapter/pom.xml index 6eb83809..80ea95ce 100644 --- a/spring-shell2-shell1-adapter/pom.xml +++ b/spring-shell2-shell1-adapter/pom.xml @@ -27,6 +27,11 @@ org.springframework.boot spring-boot-starter-test + + org.springframework.shell + spring-shell2-core-tests + test + org.assertj assertj-core diff --git a/spring-shell2-shell1-adapter/src/main/java/org/springframework/shell2/legacy/LegacyParameterResolver.java b/spring-shell2-shell1-adapter/src/main/java/org/springframework/shell2/legacy/LegacyParameterResolver.java index c3da1053..4f3a2942 100644 --- a/spring-shell2-shell1-adapter/src/main/java/org/springframework/shell2/legacy/LegacyParameterResolver.java +++ b/spring-shell2-shell1-adapter/src/main/java/org/springframework/shell2/legacy/LegacyParameterResolver.java @@ -19,6 +19,7 @@ package org.springframework.shell2.legacy; import java.lang.reflect.Parameter; import java.util.ArrayList; import java.util.Arrays; +import java.util.BitSet; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -36,6 +37,7 @@ import org.springframework.shell2.CompletionContext; import org.springframework.shell2.CompletionProposal; import org.springframework.shell2.ParameterDescription; import org.springframework.shell2.ParameterResolver; +import org.springframework.shell2.ValueResult; import org.springframework.stereotype.Component; import org.springframework.util.Assert; @@ -64,21 +66,23 @@ public class LegacyParameterResolver implements ParameterResolver { } @Override - public Object resolve(MethodParameter methodParameter, List words) { + public ValueResult resolve(MethodParameter methodParameter, List words) { CliOption cliOption = methodParameter.getParameterAnnotation(CliOption.class); Optional> converter = converters.stream() .filter(c -> c.supports(methodParameter.getParameterType(), cliOption.optionContext())) .findFirst(); - Map values = parseOptions(words); - Map seenValues = convertValues(values, methodParameter, converter); + Map values = parseOptions(words); + Map seenValues = convertValues(values, methodParameter, converter); switch (seenValues.size()) { case 0: if (!cliOption.mandatory()) { String value = cliOption.unspecifiedDefaultValue(); - return converter + Object resolvedValue = converter .orElseThrow(noConverterFound(cliOption.key()[0], value, methodParameter.getParameterType())) .convertFromText(value, methodParameter.getParameterType(), cliOption.optionContext()); + + return new ValueResult(methodParameter, resolvedValue); } else { throw new IllegalArgumentException("Could not find parameter values for " + prettifyKeys(Arrays.asList(cliOption.key())) + " in " + words); @@ -117,38 +121,49 @@ public class LegacyParameterResolver implements ParameterResolver { return null; } - private Map parseOptions(List words) { - Map values = new HashMap<>(); + private Map parseOptions(List words) { + Map values = new HashMap<>(); for (int i = 0; i < words.size(); i++) { + int from = i; String word = words.get(i); - if (word.startsWith("--")) { - String key = word.substring("--".length()); + if (word.startsWith(CLI_PREFIX)) { + String key = word.substring(CLI_PREFIX.length()); // If next word doesn't exist or starts with '--', this is an unary option. Store null - String value = i < words.size() - 1 && !words.get(i + 1).startsWith("--") ? words.get(++i) : null; - Assert.isTrue(!values.containsKey(key), String.format("Option --%s has already been set", key)); - values.put(key, value); + String value = i < words.size() - 1 && !words.get(i + 1).startsWith(CLI_PREFIX) ? words.get(++i) : null; + Assert.isTrue(!values.containsKey(key), String.format("Option %s%s has already been set", CLI_PREFIX, key)); + values.put(key, new ParseResult(value, from)); } // Must be the 'anonymous' option else { Assert.isTrue(!values.containsKey(""), "Anonymous option has already been set"); - values.put("", word); + values.put("", new ParseResult(word, from)); } } return values; } - private Map convertValues(Map values, MethodParameter methodParameter, Optional> converter) { - Map seenValues = new HashMap<>(); + private Map convertValues(Map values, MethodParameter methodParameter, Optional> converter) { + Map seenValues = new HashMap<>(); CliOption option = methodParameter.getParameterAnnotation(CliOption.class); for (String key : option.key()) { if (values.containsKey(key)) { - String value = values.get(key); + ParseResult parseResult = values.get(key); + String value = parseResult.value; if (value == null && !CLI_OPTION_NULL.equals(option.specifiedDefaultValue())) { value = option.specifiedDefaultValue(); } Class parameterType = methodParameter.getParameterType(); - seenValues.put(key, converter + Object resolvedValue = converter .orElseThrow(noConverterFound(key, value, parameterType)) - .convertFromText(value, parameterType, option.optionContext())); + .convertFromText(value, parameterType, option.optionContext()); + int from = parseResult.from; + int to = key.isEmpty() || parseResult.value == null ? from : from + 1; + BitSet wordsUsed = new BitSet(); + wordsUsed.set(from, to + 1); + BitSet wordsUsedForValues = new BitSet(); + if (parseResult.value != null) { + wordsUsedForValues.set(to); + } + seenValues.put(key, new ValueResult(methodParameter, resolvedValue, wordsUsed, wordsUsedForValues)); } } return seenValues; @@ -158,11 +173,23 @@ public class LegacyParameterResolver implements ParameterResolver { * Return the list of possible keys for an option, suitable for displaying in an error message. */ private String prettifyKeys(Collection keys) { - return keys.stream().map(s -> "".equals(s) ? "" : "--" + s).collect(Collectors.joining(", ", "[", "]")); + return keys.stream().map(s -> "".equals(s) ? "" : CLI_PREFIX + s).collect(Collectors.joining(", ", "[", "]")); } private Supplier noConverterFound(String key, String value, Class parameterType) { - return () -> new IllegalStateException("No converter found for --" + key + " from '" + value + "' to type " + parameterType); + return () -> new IllegalStateException("No converter found for " + CLI_PREFIX + key + " from '" + value + "' to type " + parameterType); + } + + private static class ParseResult { + private final String value; + + private final Integer from; + + public ParseResult(String value, Integer from) { + this.value = value; + this.from = from; + } + } } diff --git a/spring-shell2-shell1-adapter/src/test/java/org/springframework/shell2/legacy/LegacyParameterResolverTest.java b/spring-shell2-shell1-adapter/src/test/java/org/springframework/shell2/legacy/LegacyParameterResolverTest.java index 44cea25c..5ecbe21b 100644 --- a/spring-shell2-shell1-adapter/src/test/java/org/springframework/shell2/legacy/LegacyParameterResolverTest.java +++ b/spring-shell2-shell1-adapter/src/test/java/org/springframework/shell2/legacy/LegacyParameterResolverTest.java @@ -18,13 +18,13 @@ package org.springframework.shell2.legacy; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.shell2.ValueResultAsserts.assertThat; import static org.springframework.shell2.legacy.LegacyCommands.REGISTER_METHOD; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -37,6 +37,7 @@ import org.springframework.shell.core.annotation.CliOption; import org.springframework.shell2.ParameterDescription; import org.springframework.shell2.ParameterResolver; import org.springframework.shell2.Utils; +import org.springframework.shell2.ValueResult; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -68,47 +69,46 @@ public class LegacyParameterResolverTest { public void resolvesParameterAnnotatedWithCliOption() throws Exception { MethodParameter methodParameter = Utils.createMethodParameter(REGISTER_METHOD, NAME_OR_ANONYMOUS); - Object result = resolve(methodParameter, "--foo bar --name baz --qix bux"); - - assertThat(result).isEqualTo("baz"); + assertThat(resolve(methodParameter, "--foo bar --name baz --qix bux")).hasValue("baz").usesWords(2, 3) + .usesWordsForValue(3); } @Test public void resolvesAnonymousParameterAnnotatedWithCliOption() throws Exception { MethodParameter methodParameter = Utils.createMethodParameter(REGISTER_METHOD, NAME_OR_ANONYMOUS); - Object result = resolve(methodParameter, "--foo bar baz --qix bux"); - assertThat(result).isEqualTo("baz"); + assertThat(resolve(methodParameter, "--foo bar baz --qix bux")).hasValue("baz").usesWords(2) + .usesWordsForValue(2); - // As first param - result = resolve(methodParameter, "baz --foo bar --qix bux"); - assertThat(result).isEqualTo("baz"); + assertThat(resolve(methodParameter, "baz --foo bar --qix bux")).hasValue("baz").usesWords(0) + .usesWordsForValue(0); } @Test public void usesLegacyConverters() throws Exception { MethodParameter methodParameter = Utils.createMethodParameter(REGISTER_METHOD, TYPE); - Object result = resolve(methodParameter, "--foo bar --name baz --qix bux --type processor"); - - assertThat(result).isSameAs(ArtifactType.processor); + assertThat(resolve(methodParameter, "--foo bar --name baz --qix bux --type processor")) + .hasValue(ArtifactType.processor).usesWords(6, 7).usesWordsForValue(7); } @Test public void testUnspecifiedDefaultValue() throws Exception { MethodParameter methodParameter = Utils.createMethodParameter(REGISTER_METHOD, FORCE); - Object result = resolve(methodParameter, "--foo bar --name baz --qix bux"); - - assertThat(result).isEqualTo(false); + assertThat(resolve(methodParameter, "--foo bar --name baz --qix bux")) + .hasValue(false).notUsesWords().notUsesWordsForValue(); } @Test public void testSpecifiedDefaultValue() throws Exception { MethodParameter methodParameter = Utils.createMethodParameter(REGISTER_METHOD, FORCE); - assertThat(resolve(methodParameter, "--force --foo bar --name baz --qix bux")).isEqualTo(true); - assertThat(resolve(methodParameter, "--foo bar --name baz --qix bux --force")).isEqualTo(true); + assertThat(resolve(methodParameter, "--force --foo bar --name baz --qix bux")) + .hasValue(true).usesWords(0).notUsesWordsForValue(); + + assertThat(resolve(methodParameter, "--foo bar --name baz --qix bux --force")) + .hasValue(true).usesWords(6).notUsesWordsForValue(); } @Test @@ -116,7 +116,8 @@ public class LegacyParameterResolverTest { MethodParameter methodParameter = Utils.createMethodParameter(REGISTER_METHOD, COORDINATES); thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("Could not find parameter values for [--coordinates, --coords] in [--force, --foo, bar, --name, baz, --qix, bux]"); + thrown.expectMessage( + "Could not find parameter values for [--coordinates, --coords] in [--force, --foo, bar, --name, baz, --qix, bux]"); resolve(methodParameter, "--force --foo bar --name baz --qix bux"); } @@ -155,101 +156,102 @@ public class LegacyParameterResolverTest { thrown.expectMessage("No converter found for --v2 from '42' to type int"); resolve(methodParameter, "--v1 1 --v2"); } - + @Test public void testDescribeBothDefaultsNotDeclared() { MethodParameter methodParameter = Utils.createMethodParameter(LegacyCommands.REGISTER_METHOD, 1); - + ParameterDescription description = parameterResolver.describe(methodParameter).findFirst().get(); - + assertThat(description.keys()).containsExactly("--type"); assertThat(description.formal()).isEqualTo(Utils.unCamelify(ArtifactType.class.getSimpleName())); assertThat(description.defaultValue().isPresent()).isFalse(); assertThat(description.mandatoryKey()).isTrue(); - + String expectedHelp = methodParameter.getParameterAnnotation(CliOption.class).help(); assertThat(description.help()).isEqualTo(expectedHelp); } - + @Test public void testDescribeBothDefaultsDeclared() { MethodParameter methodParameter = Utils.createMethodParameter(LegacyCommands.SOME_METHOD, 1); - + ParameterDescription description = parameterResolver.describe(methodParameter).findFirst().get(); - + assertThat(description.keys()).containsExactly("--option"); assertThat(description.formal()).isEqualTo(boolean.class.getName()); assertThat(description.defaultValue().get()).isEqualTo("false"); assertThat(description.defaultValueWhenFlag().get()).isEqualTo("true"); assertThat(description.mandatoryKey()).isTrue(); - + String expectedHelp = methodParameter.getParameterAnnotation(CliOption.class).help(); assertThat(description.help()).isEqualTo(expectedHelp); } - + @Test public void testDescribeOnlySpecifiedDefaultDeclared() { MethodParameter methodParameter = Utils.createMethodParameter(LegacyCommands.SUM_METHOD, 1); - + ParameterDescription description = parameterResolver.describe(methodParameter).findFirst().get(); - + assertThat(description.keys()).containsExactly("--v2"); assertThat(description.formal()).isEqualTo(int.class.getName()); assertThat(description.defaultValue().get()).isEqualTo("null"); assertThat(description.defaultValueWhenFlag().get()).isEqualTo("42"); assertThat(description.mandatoryKey()).isTrue(); - - String expectedHelp = methodParameter.getParameterAnnotation(CliOption.class).help(); - assertThat(description.help()).isEqualTo(expectedHelp); - } - - @Test - public void testDescribeOnlyUnspecifiedDefaultDeclared() { - MethodParameter methodParameter = Utils.createMethodParameter(LegacyCommands.SUM_METHOD, 0); - - ParameterDescription description = parameterResolver.describe(methodParameter).findFirst().get(); - - assertThat(description.keys()).containsExactly("--v1"); - assertThat(description.formal()).isEqualTo(int.class.getName()); - assertThat(description.defaultValue().get()).isEqualTo("38"); - assertThat(description.mandatoryKey()).isTrue(); - - String expectedHelp = methodParameter.getParameterAnnotation(CliOption.class).help(); - assertThat(description.help()).isEqualTo(expectedHelp); - } - - @Test - public void testDescribeDefaultKey() { - MethodParameter methodParameter = Utils.createMethodParameter(LegacyCommands.LEGACY_ECHO_METHOD, 0); - - ParameterDescription description = parameterResolver.describe(methodParameter).findFirst().get(); - - assertThat(description.keys()).isEmpty(); - assertThat(description.formal()).isEqualTo(Utils.unCamelify(String.class.getSimpleName())); - assertThat(description.defaultValue().isPresent()).isFalse(); - assertThat(description.mandatoryKey()).isFalse(); - - String expectedHelp = methodParameter.getParameterAnnotation(CliOption.class).help(); - assertThat(description.help()).isEqualTo(expectedHelp); - } - - @Test - public void testDescribeNonMandatoryNoDefaults() { - MethodParameter methodParameter = Utils.createMethodParameter(LegacyCommands.SOME_METHOD, 0); - - ParameterDescription description = parameterResolver.describe(methodParameter).findFirst().get(); - - assertThat(description.keys()).containsExactly("--key"); - assertThat(description.formal()).isEqualTo(Utils.unCamelify(String.class.getSimpleName())); - assertThat(description.defaultValue().get()).isEqualTo("null"); - assertThat(description.mandatoryKey()).isTrue(); - + String expectedHelp = methodParameter.getParameterAnnotation(CliOption.class).help(); assertThat(description.help()).isEqualTo(expectedHelp); } - private Object resolve(MethodParameter methodParameter, String command) { - return parameterResolver.resolve(methodParameter, asList(command.split(" "))); + @Test + public void testDescribeOnlyUnspecifiedDefaultDeclared() { + MethodParameter methodParameter = Utils.createMethodParameter(LegacyCommands.SUM_METHOD, 0); + + ParameterDescription description = parameterResolver.describe(methodParameter).findFirst().get(); + + assertThat(description.keys()).containsExactly("--v1"); + assertThat(description.formal()).isEqualTo(int.class.getName()); + assertThat(description.defaultValue().get()).isEqualTo("38"); + assertThat(description.mandatoryKey()).isTrue(); + + String expectedHelp = methodParameter.getParameterAnnotation(CliOption.class).help(); + assertThat(description.help()).isEqualTo(expectedHelp); + } + + @Test + public void testDescribeDefaultKey() { + MethodParameter methodParameter = Utils.createMethodParameter(LegacyCommands.LEGACY_ECHO_METHOD, 0); + + ParameterDescription description = parameterResolver.describe(methodParameter).findFirst().get(); + + assertThat(description.keys()).isEmpty(); + assertThat(description.formal()).isEqualTo(Utils.unCamelify(String.class.getSimpleName())); + assertThat(description.defaultValue().isPresent()).isFalse(); + assertThat(description.mandatoryKey()).isFalse(); + + String expectedHelp = methodParameter.getParameterAnnotation(CliOption.class).help(); + assertThat(description.help()).isEqualTo(expectedHelp); + } + + @Test + public void testDescribeNonMandatoryNoDefaults() { + MethodParameter methodParameter = Utils.createMethodParameter(LegacyCommands.SOME_METHOD, 0); + + ParameterDescription description = parameterResolver.describe(methodParameter).findFirst().get(); + + assertThat(description.keys()).containsExactly("--key"); + assertThat(description.formal()).isEqualTo(Utils.unCamelify(String.class.getSimpleName())); + assertThat(description.defaultValue().get()).isEqualTo("null"); + assertThat(description.mandatoryKey()).isTrue(); + + String expectedHelp = methodParameter.getParameterAnnotation(CliOption.class).help(); + assertThat(description.help()).isEqualTo(expectedHelp); + } + + private ValueResult resolve(MethodParameter methodParameter, String command) { + ValueResult result = parameterResolver.resolve(methodParameter, asList(command.split(" "))); + return result; } @Configuration diff --git a/spring-shell2-standard/pom.xml b/spring-shell2-standard/pom.xml index 3c381e95..6ea5b41f 100644 --- a/spring-shell2-standard/pom.xml +++ b/spring-shell2-standard/pom.xml @@ -17,16 +17,19 @@ org.springframework.shell spring-shell2-core - org.springframework.boot spring-boot-starter-test + + org.springframework.shell + spring-shell2-core-tests + test + org.assertj assertj-core test - diff --git a/spring-shell2-standard/src/main/java/org/springframework/shell2/standard/StandardParameterResolver.java b/spring-shell2-standard/src/main/java/org/springframework/shell2/standard/StandardParameterResolver.java index 13aba9f0..e9c29d20 100644 --- a/spring-shell2-standard/src/main/java/org/springframework/shell2/standard/StandardParameterResolver.java +++ b/spring-shell2-standard/src/main/java/org/springframework/shell2/standard/StandardParameterResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2015-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +16,14 @@ package org.springframework.shell2.standard; +import static org.springframework.shell2.Utils.unCamelify; + import java.lang.reflect.Array; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.ArrayList; import java.util.Arrays; +import java.util.BitSet; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -44,13 +47,12 @@ import org.springframework.shell2.ParameterMissingResolutionException; import org.springframework.shell2.ParameterResolver; import org.springframework.shell2.UnfinishedParameterResolutionException; import org.springframework.shell2.Utils; +import org.springframework.shell2.ValueResult; import org.springframework.stereotype.Component; import org.springframework.util.Assert; import org.springframework.util.ConcurrentReferenceHashMap; import org.springframework.util.ObjectUtils; -import static org.springframework.shell2.Utils.unCamelify; - /** * Default ParameterResolver implementation that supports the following features: