diff options
Diffstat (limited to 'src/test/java/com/google/devtools/common/options/OptionsParserTest.java')
-rw-r--r-- | src/test/java/com/google/devtools/common/options/OptionsParserTest.java | 1026 |
1 files changed, 1026 insertions, 0 deletions
diff --git a/src/test/java/com/google/devtools/common/options/OptionsParserTest.java b/src/test/java/com/google/devtools/common/options/OptionsParserTest.java new file mode 100644 index 0000000000..190d855b87 --- /dev/null +++ b/src/test/java/com/google/devtools/common/options/OptionsParserTest.java @@ -0,0 +1,1026 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// 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 com.google.devtools.common.options; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.devtools.common.options.OptionsParser.newOptionsParser; +import static java.util.Arrays.asList; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.devtools.common.options.Converters.CommaSeparatedOptionListConverter; +import com.google.devtools.common.options.OptionsParser.OptionValueDescription; +import com.google.devtools.common.options.OptionsParser.UnparsedOptionValueDescription; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Tests {@link OptionsParser}. + */ +@RunWith(JUnit4.class) +public class OptionsParserTest { + + public static class ExampleFoo extends OptionsBase { + + @Option(name = "foo", + category = "one", + defaultValue = "defaultFoo") + public String foo; + + @Option(name = "bar", + category = "two", + defaultValue = "42") + public int bar; + + @Option(name = "bing", + category = "one", + defaultValue = "", + allowMultiple = true) + public List<String> bing; + + @Option(name = "bang", + category = "one", + defaultValue = "", + converter = StringConverter.class, + allowMultiple = true) + public List<String> bang; + + @Option(name = "nodoc", + category = "undocumented", + defaultValue = "", + allowMultiple = false) + public String nodoc; + } + + public static class ExampleBaz extends OptionsBase { + + @Option(name = "baz", + category = "one", + defaultValue = "defaultBaz") + public String baz; + } + + public static class StringConverter implements Converter<String> { + @Override + public String convert(String input) { + return input; + } + @Override + public String getTypeDescription() { + return "a string"; + } + } + + @Test + public void parseWithMultipleOptionsInterfaces() + throws OptionsParsingException { + OptionsParser parser = newOptionsParser(ExampleFoo.class, ExampleBaz.class); + parser.parse("--baz=oops", "--bar", "17"); + ExampleFoo foo = parser.getOptions(ExampleFoo.class); + assertEquals("defaultFoo", foo.foo); + assertEquals(17, foo.bar); + ExampleBaz baz = parser.getOptions(ExampleBaz.class); + assertEquals("oops", baz.baz); + } + + @Test + public void parserWithUnknownOption() { + OptionsParser parser = newOptionsParser(ExampleFoo.class, ExampleBaz.class); + try { + parser.parse("--unknown", "option"); + fail(); + } catch (OptionsParsingException e) { + assertEquals("--unknown", e.getInvalidArgument()); + assertEquals("Unrecognized option: --unknown", e.getMessage()); + } + assertEquals(Collections.<String>emptyList(), parser.getResidue()); + } + + @Test + public void parserWithSingleDashOption() throws OptionsParsingException { + OptionsParser parser = newOptionsParser(ExampleFoo.class, ExampleBaz.class); + try { + parser.parse("-baz=oops", "-bar", "17"); + fail(); + } catch (OptionsParsingException expected) {} + + parser = newOptionsParser(ExampleFoo.class, ExampleBaz.class); + parser.setAllowSingleDashLongOptions(true); + parser.parse("-baz=oops", "-bar", "17"); + ExampleFoo foo = parser.getOptions(ExampleFoo.class); + assertEquals("defaultFoo", foo.foo); + assertEquals(17, foo.bar); + ExampleBaz baz = parser.getOptions(ExampleBaz.class); + assertEquals("oops", baz.baz); + } + + @Test + public void parsingFailsWithUnknownOptions() { + OptionsParser parser = newOptionsParser(ExampleFoo.class, ExampleBaz.class); + List<String> unknownOpts = asList("--unknown", "option", "--more_unknowns"); + try { + parser.parse(unknownOpts); + fail(); + } catch (OptionsParsingException e) { + assertEquals("--unknown", e.getInvalidArgument()); + assertEquals("Unrecognized option: --unknown", e.getMessage()); + assertNotNull(parser.getOptions(ExampleFoo.class)); + assertNotNull(parser.getOptions(ExampleBaz.class)); + } + } + + @Test + public void parseKnownAndUnknownOptions() { + OptionsParser parser = newOptionsParser(ExampleFoo.class, ExampleBaz.class); + List<String> opts = asList("--bar", "17", "--unknown", "option"); + try { + parser.parse(opts); + fail(); + } catch (OptionsParsingException e) { + assertEquals("--unknown", e.getInvalidArgument()); + assertEquals("Unrecognized option: --unknown", e.getMessage()); + assertNotNull(parser.getOptions(ExampleFoo.class)); + assertNotNull(parser.getOptions(ExampleBaz.class)); + } + } + + public static class CategoryTest extends OptionsBase { + @Option(name = "swiss_bank_account_number", + category = "undocumented", // Not printed in usage messages! + defaultValue = "123456789") + public int swissBankAccountNumber; + + @Option(name = "student_bank_account_number", + category = "one", + defaultValue = "987654321") + public int studentBankAccountNumber; + } + + @Test + public void getOptionsAndGetResidueWithNoCallToParse() { + // With no call to parse(), all options are at default values, and there's + // no reside. + assertEquals("defaultFoo", + newOptionsParser(ExampleFoo.class). + getOptions(ExampleFoo.class).foo); + assertEquals(Collections.<String>emptyList(), + newOptionsParser(ExampleFoo.class).getResidue()); + } + + @Test + public void parserCanBeCalledRepeatedly() throws OptionsParsingException { + OptionsParser parser = newOptionsParser(ExampleFoo.class); + parser.parse("--foo", "foo1"); + assertEquals("foo1", parser.getOptions(ExampleFoo.class).foo); + parser.parse(); + assertEquals("foo1", parser.getOptions(ExampleFoo.class).foo); // no change + parser.parse("--foo", "foo2"); + assertEquals("foo2", parser.getOptions(ExampleFoo.class).foo); // updated + } + + @Test + public void multipleOccuringOption() throws OptionsParsingException { + OptionsParser parser = newOptionsParser(ExampleFoo.class); + parser.parse("--bing", "abcdef", "--foo", "foo1", "--bing", "123456" ); + assertThat(parser.getOptions(ExampleFoo.class).bing).containsExactly("abcdef", "123456"); + } + + @Test + public void multipleOccurringOptionWithConverter() throws OptionsParsingException { + // --bang is the same as --bing except that it has a "converter" specified. + // This test also tests option values with embedded commas and spaces. + OptionsParser parser = newOptionsParser(ExampleFoo.class); + parser.parse("--bang", "abc,def ghi", "--foo", "foo1", "--bang", "123456" ); + assertThat(parser.getOptions(ExampleFoo.class).bang).containsExactly("abc,def ghi", "123456"); + } + + @Test + public void parserIgnoresOptionsAfterMinusMinus() + throws OptionsParsingException { + OptionsParser parser = newOptionsParser(ExampleFoo.class, ExampleBaz.class); + parser.parse("--foo", "well", "--baz", "here", "--", "--bar", "ignore"); + ExampleFoo foo = parser.getOptions(ExampleFoo.class); + ExampleBaz baz = parser.getOptions(ExampleBaz.class); + assertEquals("well", foo.foo); + assertEquals("here", baz.baz); + assertEquals(42, foo.bar); // the default! + assertEquals(asList("--bar", "ignore"), parser.getResidue()); + } + + @Test + public void parserThrowsExceptionIfResidueIsNotAllowed() { + OptionsParser parser = newOptionsParser(ExampleFoo.class); + parser.setAllowResidue(false); + try { + parser.parse("residue", "is", "not", "OK"); + fail(); + } catch (OptionsParsingException e) { + assertEquals("Unrecognized arguments: residue is not OK", e.getMessage()); + } + } + + @Test + public void multipleCallsToParse() throws Exception { + OptionsParser parser = newOptionsParser(ExampleFoo.class); + parser.setAllowResidue(true); + parser.parse("--foo", "one", "--bar", "43", "unknown1"); + parser.parse("--foo", "two", "unknown2"); + ExampleFoo foo = parser.getOptions(ExampleFoo.class); + assertEquals("two", foo.foo); // second call takes precedence + assertEquals(43, foo.bar); + assertEquals(Arrays.asList("unknown1", "unknown2"), parser.getResidue()); + } + + // Regression test for a subtle bug! The toString of each options interface + // instance was printing out key=value pairs for all flags in the + // OptionsParser, not just those belonging to the specific interface type. + @Test + public void toStringDoesntIncludeFlagsForOtherOptionsInParserInstance() + throws Exception { + OptionsParser parser = newOptionsParser(ExampleFoo.class, ExampleBaz.class); + parser.parse("--foo", "foo", "--bar", "43", "--baz", "baz"); + + String fooString = parser.getOptions(ExampleFoo.class).toString(); + if (!fooString.contains("foo=foo") || + !fooString.contains("bar=43") || + !fooString.contains("ExampleFoo") || + fooString.contains("baz=baz")) { + fail("ExampleFoo.toString() is incorrect: " + fooString); + } + + String bazString = parser.getOptions(ExampleBaz.class).toString(); + if (!bazString.contains("baz=baz") || + !bazString.contains("ExampleBaz") || + bazString.contains("foo=foo") || + bazString.contains("bar=43")) { + fail("ExampleBaz.toString() is incorrect: " + bazString); + } + } + + // Regression test for another subtle bug! The toString was printing all the + // explicitly-specified options, even if they were at their default values, + // causing toString equivalence to diverge from equals(). + @Test + public void toStringIsIndependentOfExplicitCommandLineOptions() throws Exception { + ExampleFoo foo1 = Options.parse(ExampleFoo.class).getOptions(); + ExampleFoo foo2 = Options.parse(ExampleFoo.class, "--bar", "42").getOptions(); + assertEquals(foo1, foo2); + assertEquals(foo1.toString(), foo2.toString()); + + Map<String, Object> expectedMap = new ImmutableMap.Builder<String, Object>(). + put("bing", Collections.emptyList()). + put("bar", 42). + put("nodoc", ""). + put("bang", Collections.emptyList()). + put("foo", "defaultFoo").build(); + + assertEquals(expectedMap, foo1.asMap()); + assertEquals(expectedMap, foo2.asMap()); + } + + // Regression test for yet another subtle bug! The inherited options weren't + // being printed by toString. One day, a real rain will come and wash all + // this scummy code off the streets. + public static class DerivedBaz extends ExampleBaz { + @Option(name = "derived", defaultValue = "defaultDerived") + public String derived; + } + + @Test + public void toStringPrintsInheritedOptionsToo_Duh() throws Exception { + DerivedBaz derivedBaz = Options.parse(DerivedBaz.class).getOptions(); + String derivedBazString = derivedBaz.toString(); + if (!derivedBazString.contains("derived=defaultDerived") || + !derivedBazString.contains("baz=defaultBaz")) { + fail("DerivedBaz.toString() is incorrect: " + derivedBazString); + } + } + + // Tests for new default value override mechanism + public static class CustomOptions extends OptionsBase { + @Option(name = "simple", + category = "custom", + defaultValue = "simple default") + public String simple; + + @Option(name = "multipart_name", + category = "custom", + defaultValue = "multipart default") + public String multipartName; + } + + public void assertDefaultStringsForCustomOptions() throws OptionsParsingException { + CustomOptions options = Options.parse(CustomOptions.class).getOptions(); + assertEquals("simple default", options.simple); + assertEquals("multipart default", options.multipartName); + } + + public static class NullTestOptions extends OptionsBase { + @Option(name = "simple", + defaultValue = "null") + public String simple; + } + + @Test + public void defaultNullStringGivesNull() throws Exception { + NullTestOptions options = Options.parse(NullTestOptions.class).getOptions(); + assertNull(options.simple); + } + + public static class ImplicitDependencyOptions extends OptionsBase { + @Option(name = "first", + implicitRequirements = "--second=second", + defaultValue = "null") + public String first; + + @Option(name = "second", + implicitRequirements = "--third=third", + defaultValue = "null") + public String second; + + @Option(name = "third", + defaultValue = "null") + public String third; + } + + @Test + public void implicitDependencyHasImplicitDependency() throws Exception { + OptionsParser parser = OptionsParser.newOptionsParser(ImplicitDependencyOptions.class); + parser.parse(OptionPriority.COMMAND_LINE, null, Arrays.asList("--first=first")); + assertEquals("first", parser.getOptions(ImplicitDependencyOptions.class).first); + assertEquals("second", parser.getOptions(ImplicitDependencyOptions.class).second); + assertEquals("third", parser.getOptions(ImplicitDependencyOptions.class).third); + } + + public static class BadImplicitDependencyOptions extends OptionsBase { + @Option(name = "first", + implicitRequirements = "xxx", + defaultValue = "null") + public String first; + } + + @Test + public void badImplicitDependency() throws Exception { + OptionsParser parser = OptionsParser.newOptionsParser(BadImplicitDependencyOptions.class); + try { + parser.parse(OptionPriority.COMMAND_LINE, null, Arrays.asList("--first=first")); + } catch (AssertionError e) { + /* Expected error. */ + return; + } + fail(); + } + + public static class BadExpansionOptions extends OptionsBase { + @Option(name = "first", + expansion = { "xxx" }, + defaultValue = "null") + public Void first; + } + + @Test + public void badExpansionOptions() throws Exception { + OptionsParser parser = OptionsParser.newOptionsParser(BadExpansionOptions.class); + try { + parser.parse(OptionPriority.COMMAND_LINE, null, Arrays.asList("--first")); + } catch (AssertionError e) { + /* Expected error. */ + return; + } + fail(); + } + + public static class ExpansionOptions extends OptionsBase { + @Option(name = "first", + expansion = { "--second=first" }, + defaultValue = "null") + public Void first; + + @Option(name = "second", + defaultValue = "null") + public String second; + } + + @Test + public void overrideExpansionWithExplicit() throws Exception { + OptionsParser parser = OptionsParser.newOptionsParser(ExpansionOptions.class); + parser.parse(OptionPriority.COMMAND_LINE, null, Arrays.asList("--first", "--second=second")); + ExpansionOptions options = parser.getOptions(ExpansionOptions.class); + assertEquals("second", options.second); + assertEquals(0, parser.getWarnings().size()); + } + + @Test + public void overrideExplicitWithExpansion() throws Exception { + OptionsParser parser = OptionsParser.newOptionsParser(ExpansionOptions.class); + parser.parse(OptionPriority.COMMAND_LINE, null, Arrays.asList("--second=second", "--first")); + ExpansionOptions options = parser.getOptions(ExpansionOptions.class); + assertEquals("first", options.second); + } + + @Test + public void overrideWithHigherPriority() throws Exception { + OptionsParser parser = OptionsParser.newOptionsParser(NullTestOptions.class); + parser.parse(OptionPriority.RC_FILE, null, Arrays.asList("--simple=a")); + assertEquals("a", parser.getOptions(NullTestOptions.class).simple); + parser.parse(OptionPriority.COMMAND_LINE, null, Arrays.asList("--simple=b")); + assertEquals("b", parser.getOptions(NullTestOptions.class).simple); + } + + @Test + public void overrideWithLowerPriority() throws Exception { + OptionsParser parser = OptionsParser.newOptionsParser(NullTestOptions.class); + parser.parse(OptionPriority.COMMAND_LINE, null, Arrays.asList("--simple=a")); + assertEquals("a", parser.getOptions(NullTestOptions.class).simple); + parser.parse(OptionPriority.RC_FILE, null, Arrays.asList("--simple=b")); + assertEquals("a", parser.getOptions(NullTestOptions.class).simple); + } + + @Test + public void getOptionValueDescriptionWithNonExistingOption() throws Exception { + OptionsParser parser = OptionsParser.newOptionsParser(NullTestOptions.class); + try { + parser.getOptionValueDescription("notexisting"); + fail(); + } catch (IllegalArgumentException e) { + /* Expected exception. */ + } + } + + @Test + public void getOptionValueDescriptionWithoutValue() throws Exception { + OptionsParser parser = OptionsParser.newOptionsParser(NullTestOptions.class); + assertNull(parser.getOptionValueDescription("simple")); + } + + @Test + public void getOptionValueDescriptionWithValue() throws Exception { + OptionsParser parser = OptionsParser.newOptionsParser(NullTestOptions.class); + parser.parse(OptionPriority.COMMAND_LINE, "my description", + Arrays.asList("--simple=abc")); + OptionValueDescription result = parser.getOptionValueDescription("simple"); + assertNotNull(result); + assertEquals("simple", result.getName()); + assertEquals("abc", result.getValue()); + assertEquals(OptionPriority.COMMAND_LINE, result.getPriority()); + assertEquals("my description", result.getSource()); + assertNull(result.getImplicitDependant()); + assertFalse(result.isImplicitDependency()); + assertNull(result.getExpansionParent()); + assertFalse(result.isExpansion()); + } + + public static class ImplicitDependencyWarningOptions extends OptionsBase { + @Option(name = "first", + implicitRequirements = "--second=second", + defaultValue = "null") + public String first; + + @Option(name = "second", + defaultValue = "null") + public String second; + + @Option(name = "third", + implicitRequirements = "--second=third", + defaultValue = "null") + public String third; + } + + @Test + public void warningForImplicitOverridingExplicitOption() throws Exception { + OptionsParser parser = OptionsParser.newOptionsParser(ImplicitDependencyWarningOptions.class); + parser.parse("--second=second", "--first=first"); + assertThat(parser.getWarnings()) + .containsExactly("Option 'second' is implicitly defined by " + + "option 'first'; the implicitly set value overrides the previous one"); + } + + @Test + public void warningForExplicitOverridingImplicitOption() throws Exception { + OptionsParser parser = OptionsParser.newOptionsParser(ImplicitDependencyWarningOptions.class); + parser.parse("--first=first"); + assertThat(parser.getWarnings()).isEmpty(); + parser.parse("--second=second"); + assertThat(parser.getWarnings()) + .containsExactly("A new value for option 'second' overrides a" + + " previous implicit setting of that option by option 'first'"); + } + + @Test + public void warningForExplicitOverridingImplicitOptionInSameCall() throws Exception { + OptionsParser parser = OptionsParser.newOptionsParser(ImplicitDependencyWarningOptions.class); + parser.parse("--first=first", "--second=second"); + assertThat(parser.getWarnings()) + .containsExactly("Option 'second' is implicitly defined by " + + "option 'first'; the implicitly set value overrides the previous one"); + } + + @Test + public void warningForImplicitOverridingImplicitOption() throws Exception { + OptionsParser parser = OptionsParser.newOptionsParser(ImplicitDependencyWarningOptions.class); + parser.parse("--first=first"); + assertThat(parser.getWarnings()).isEmpty(); + parser.parse("--third=third"); + assertThat(parser.getWarnings()) + .containsExactly("Option 'second' is implicitly defined by both " + + "option 'first' and option 'third'"); + } + + public static class WarningOptions extends OptionsBase { + @Deprecated + @Option(name = "first", + defaultValue = "null") + public Void first; + + @Deprecated + @Option(name = "second", + allowMultiple = true, + defaultValue = "null") + public List<String> second; + + @Deprecated + @Option(name = "third", + expansion = "--fourth=true", + abbrev = 't', + defaultValue = "null") + public Void third; + + @Option(name = "fourth", + defaultValue = "false") + public boolean fourth; + } + + @Test + public void deprecationWarning() throws Exception { + OptionsParser parser = OptionsParser.newOptionsParser(WarningOptions.class); + parser.parse(OptionPriority.COMMAND_LINE, null, Arrays.asList("--first")); + assertEquals(Arrays.asList("Option 'first' is deprecated"), parser.getWarnings()); + } + + @Test + public void deprecationWarningForListOption() throws Exception { + OptionsParser parser = OptionsParser.newOptionsParser(WarningOptions.class); + parser.parse(OptionPriority.COMMAND_LINE, null, Arrays.asList("--second=a")); + assertEquals(Arrays.asList("Option 'second' is deprecated"), parser.getWarnings()); + } + + @Test + public void deprecationWarningForExpansionOption() throws Exception { + OptionsParser parser = OptionsParser.newOptionsParser(WarningOptions.class); + parser.parse(OptionPriority.COMMAND_LINE, null, Arrays.asList("--third")); + assertEquals(Arrays.asList("Option 'third' is deprecated"), parser.getWarnings()); + assertTrue(parser.getOptions(WarningOptions.class).fourth); + } + + @Test + public void deprecationWarningForAbbreviatedExpansionOption() throws Exception { + OptionsParser parser = OptionsParser.newOptionsParser(WarningOptions.class); + parser.parse(OptionPriority.COMMAND_LINE, null, Arrays.asList("-t")); + assertEquals(Arrays.asList("Option 'third' is deprecated"), parser.getWarnings()); + assertTrue(parser.getOptions(WarningOptions.class).fourth); + } + + public static class NewWarningOptions extends OptionsBase { + @Option(name = "first", + defaultValue = "null", + deprecationWarning = "it's gone") + public Void first; + + @Option(name = "second", + allowMultiple = true, + defaultValue = "null", + deprecationWarning = "sorry, no replacement") + public List<String> second; + + @Option(name = "third", + expansion = "--fourth=true", + defaultValue = "null", + deprecationWarning = "use --forth instead") + public Void third; + + @Option(name = "fourth", + defaultValue = "false") + public boolean fourth; + } + + @Test + public void newDeprecationWarning() throws Exception { + OptionsParser parser = OptionsParser.newOptionsParser(NewWarningOptions.class); + parser.parse(OptionPriority.COMMAND_LINE, null, Arrays.asList("--first")); + assertEquals(Arrays.asList("Option 'first' is deprecated: it's gone"), parser.getWarnings()); + } + + @Test + public void newDeprecationWarningForListOption() throws Exception { + OptionsParser parser = OptionsParser.newOptionsParser(NewWarningOptions.class); + parser.parse(OptionPriority.COMMAND_LINE, null, Arrays.asList("--second=a")); + assertEquals(Arrays.asList("Option 'second' is deprecated: sorry, no replacement"), + parser.getWarnings()); + } + + @Test + public void newDeprecationWarningForExpansionOption() throws Exception { + OptionsParser parser = OptionsParser.newOptionsParser(NewWarningOptions.class); + parser.parse(OptionPriority.COMMAND_LINE, null, Arrays.asList("--third")); + assertEquals(Arrays.asList("Option 'third' is deprecated: use --forth instead"), + parser.getWarnings()); + assertTrue(parser.getOptions(NewWarningOptions.class).fourth); + } + + public static class ExpansionWarningOptions extends OptionsBase { + @Option(name = "first", + expansion = "--second=other", + defaultValue = "null") + public Void first; + + @Option(name = "second", + defaultValue = "null") + public String second; + } + + @Test + public void warningForExpansionOverridingExplicitOption() throws Exception { + OptionsParser parser = OptionsParser.newOptionsParser(ExpansionWarningOptions.class); + parser.parse("--second=second", "--first"); + assertThat(parser.getWarnings()) + .containsExactly("The option 'first' was expanded and now overrides a " + + "previous explicitly specified option 'second'"); + } + + public static class InvalidOptionConverter extends OptionsBase { + @Option(name = "foo", + converter = StringConverter.class, + defaultValue = "1") + public Integer foo; + } + + @Test + public void errorForInvalidOptionConverter() throws Exception { + try { + OptionsParser.newOptionsParser(InvalidOptionConverter.class); + } catch (AssertionError e) { + // Expected exception + return; + } + fail(); + } + + public static class InvalidListOptionConverter extends OptionsBase { + @Option(name = "foo", + converter = StringConverter.class, + defaultValue = "1", + allowMultiple = true) + public List<Integer> foo; + } + + @Test + public void errorForInvalidListOptionConverter() throws Exception { + try { + OptionsParser.newOptionsParser(InvalidListOptionConverter.class); + } catch (AssertionError e) { + // Expected exception + return; + } + fail(); + } + + // This test is here to make sure that nobody accidentally changes the + // order of the enum values and breaks the implicit assumptions elsewhere + // in the code. + @Test + public void optionPrioritiesAreCorrectlyOrdered() throws Exception { + assertEquals(5, OptionPriority.values().length); + assertEquals(-1, OptionPriority.DEFAULT.compareTo(OptionPriority.COMPUTED_DEFAULT)); + assertEquals(-1, OptionPriority.COMPUTED_DEFAULT.compareTo(OptionPriority.RC_FILE)); + assertEquals(-1, OptionPriority.RC_FILE.compareTo(OptionPriority.COMMAND_LINE)); + assertEquals(-1, OptionPriority.COMMAND_LINE.compareTo(OptionPriority.SOFTWARE_REQUIREMENT)); + } + + public static class IntrospectionExample extends OptionsBase { + @Option(name = "alpha", + category = "one", + defaultValue = "alpha") + public String alpha; + + @Option(name = "beta", + category = "one", + defaultValue = "beta") + public String beta; + + @Option(name = "gamma", + category = "undocumented", + defaultValue = "gamma") + public String gamma; + + @Option(name = "delta", + category = "undocumented", + defaultValue = "delta") + public String delta; + + @Option(name = "echo", + category = "hidden", + defaultValue = "echo") + public String echo; + } + + @Test + public void asListOfUnparsedOptions() throws Exception { + OptionsParser parser = OptionsParser.newOptionsParser(IntrospectionExample.class); + parser.parse(OptionPriority.COMMAND_LINE, "source", + Arrays.asList("--alpha=one", "--gamma=two", "--echo=three")); + List<UnparsedOptionValueDescription> result = parser.asListOfUnparsedOptions(); + assertNotNull(result); + assertEquals(3, result.size()); + + assertEquals("alpha", result.get(0).getName()); + assertEquals(true, result.get(0).isDocumented()); + assertEquals(false, result.get(0).isHidden()); + assertEquals("one", result.get(0).getUnparsedValue()); + assertEquals("source", result.get(0).getSource()); + assertEquals(OptionPriority.COMMAND_LINE, result.get(0).getPriority()); + + assertEquals("gamma", result.get(1).getName()); + assertEquals(false, result.get(1).isDocumented()); + assertEquals(false, result.get(1).isHidden()); + assertEquals("two", result.get(1).getUnparsedValue()); + assertEquals("source", result.get(1).getSource()); + assertEquals(OptionPriority.COMMAND_LINE, result.get(1).getPriority()); + + assertEquals("echo", result.get(2).getName()); + assertEquals(false, result.get(2).isDocumented()); + assertEquals(true, result.get(2).isHidden()); + assertEquals("three", result.get(2).getUnparsedValue()); + assertEquals("source", result.get(2).getSource()); + assertEquals(OptionPriority.COMMAND_LINE, result.get(2).getPriority()); + } + + @Test + public void asListOfExplicitOptions() throws Exception { + OptionsParser parser = OptionsParser.newOptionsParser(IntrospectionExample.class); + parser.parse(OptionPriority.COMMAND_LINE, "source", + Arrays.asList("--alpha=one", "--gamma=two")); + List<UnparsedOptionValueDescription> result = parser.asListOfExplicitOptions(); + assertNotNull(result); + assertEquals(2, result.size()); + + assertEquals("alpha", result.get(0).getName()); + assertEquals(true, result.get(0).isDocumented()); + assertEquals("one", result.get(0).getUnparsedValue()); + assertEquals("source", result.get(0).getSource()); + assertEquals(OptionPriority.COMMAND_LINE, result.get(0).getPriority()); + + assertEquals("gamma", result.get(1).getName()); + assertEquals(false, result.get(1).isDocumented()); + assertEquals("two", result.get(1).getUnparsedValue()); + assertEquals("source", result.get(1).getSource()); + assertEquals(OptionPriority.COMMAND_LINE, result.get(1).getPriority()); + } + + private void assertOptionValue(String expectedName, Object expectedValue, + OptionPriority expectedPriority, String expectedSource, + OptionValueDescription actual) { + assertNotNull(actual); + assertEquals(expectedName, actual.getName()); + assertEquals(expectedValue, actual.getValue()); + assertEquals(expectedPriority, actual.getPriority()); + assertEquals(expectedSource, actual.getSource()); + } + + @Test + public void asListOfEffectiveOptions() throws Exception { + OptionsParser parser = OptionsParser.newOptionsParser(IntrospectionExample.class); + parser.parse(OptionPriority.COMMAND_LINE, "source", + Arrays.asList("--alpha=one", "--gamma=two")); + List<OptionValueDescription> result = parser.asListOfEffectiveOptions(); + assertNotNull(result); + assertEquals(5, result.size()); + HashMap<String,OptionValueDescription> map = new HashMap<String,OptionValueDescription>(); + for (OptionValueDescription description : result) { + map.put(description.getName(), description); + } + + assertOptionValue("alpha", "one", OptionPriority.COMMAND_LINE, "source", + map.get("alpha")); + assertOptionValue("beta", "beta", OptionPriority.DEFAULT, null, + map.get("beta")); + assertOptionValue("gamma", "two", OptionPriority.COMMAND_LINE, "source", + map.get("gamma")); + assertOptionValue("delta", "delta", OptionPriority.DEFAULT, null, + map.get("delta")); + assertOptionValue("echo", "echo", OptionPriority.DEFAULT, null, + map.get("echo")); + } + + // Regression tests for bug: + // "--option from blazerc unexpectedly overrides --option from command line" + public static class ListExample extends OptionsBase { + @Option(name = "alpha", + converter = StringConverter.class, + allowMultiple = true, + defaultValue = "null") + public List<String> alpha; + } + + @Test + public void overrideListOptions() throws Exception { + OptionsParser parser = OptionsParser.newOptionsParser(ListExample.class); + parser.parse(OptionPriority.COMMAND_LINE, "a", Arrays.asList("--alpha=two")); + parser.parse(OptionPriority.RC_FILE, "b", Arrays.asList("--alpha=one")); + assertEquals(Arrays.asList("one", "two"), parser.getOptions(ListExample.class).alpha); + } + + public static class CommaSeparatedOptionsExample extends OptionsBase { + @Option(name = "alpha", + converter = CommaSeparatedOptionListConverter.class, + allowMultiple = true, + defaultValue = "null") + public List<String> alpha; + } + + @Test + public void commaSeparatedOptionsWithAllowMultiple() throws Exception { + OptionsParser parser = OptionsParser.newOptionsParser(CommaSeparatedOptionsExample.class); + parser.parse(OptionPriority.COMMAND_LINE, "a", Arrays.asList("--alpha=one", + "--alpha=two,three")); + assertEquals(Arrays.asList("one", "two", "three"), + parser.getOptions(CommaSeparatedOptionsExample.class).alpha); + } + + public static class IllegalListTypeExample extends OptionsBase { + @Option(name = "alpha", + converter = CommaSeparatedOptionListConverter.class, + allowMultiple = true, + defaultValue = "null") + public List<Integer> alpha; + } + + @Test + public void illegalListType() throws Exception { + try { + OptionsParser.newOptionsParser(IllegalListTypeExample.class); + } catch (AssertionError e) { + // Expected exception + return; + } + fail(); + } + + public static class Yesterday extends OptionsBase { + + @Option(name = "a", + defaultValue = "a") + public String a; + + @Option(name = "b", + defaultValue = "b") + public String b; + + @Option(name = "c", + defaultValue = "null", + expansion = {"--a=0"}) + public Void c; + + @Option(name = "d", + defaultValue = "null", + allowMultiple = true) + public List<String> d; + + @Option(name = "e", + defaultValue = "null", + implicitRequirements = { "--a==1" }) + public String e; + + @Option(name = "f", + defaultValue = "null", + implicitRequirements = { "--b==1" }) + public String f; + + @Option(name = "g", + abbrev = 'h', + defaultValue = "false") + public boolean g; + } + + public static List<String> canonicalize(Class<? extends OptionsBase> optionsClass, String... args) + throws OptionsParsingException { + return OptionsParser.canonicalize(ImmutableList.<Class<? extends OptionsBase>>of(optionsClass), + Arrays.asList(args)); + } + + @Test + public void canonicalizeEasy() throws Exception { + assertEquals(Arrays.asList("--a=x"), canonicalize(Yesterday.class, "--a=x")); + } + + @Test + public void canonicalizeSkipDuplicate() throws Exception { + assertEquals(Arrays.asList("--a=x"), canonicalize(Yesterday.class, "--a=y", "--a=x")); + } + + @Test + public void canonicalizeExpands() throws Exception { + assertEquals(Arrays.asList("--a=0"), canonicalize(Yesterday.class, "--c")); + } + + @Test + public void canonicalizeExpansionOverridesExplicit() throws Exception { + assertEquals(Arrays.asList("--a=0"), canonicalize(Yesterday.class, "--a=x", "--c")); + } + + @Test + public void canonicalizeExplicitOverridesExpansion() throws Exception { + assertEquals(Arrays.asList("--a=x"), canonicalize(Yesterday.class, "--c", "--a=x")); + } + + @Test + public void canonicalizeSorts() throws Exception { + assertEquals(Arrays.asList("--a=x", "--b=y"), canonicalize(Yesterday.class, "--b=y", "--a=x")); + } + + @Test + public void canonicalizeImplicitDepsAtEnd() throws Exception { + assertEquals(Arrays.asList("--a=x", "--e=y"), canonicalize(Yesterday.class, "--e=y", "--a=x")); + } + + @Test + public void canonicalizeImplicitDepsSkipsDuplicate() throws Exception { + assertEquals(Arrays.asList("--e=y"), canonicalize(Yesterday.class, "--e=x", "--e=y")); + } + + @Test + public void canonicalizeDoesNotSortImplicitDeps() throws Exception { + assertEquals(Arrays.asList("--a=x", "--f=z", "--e=y"), + canonicalize(Yesterday.class, "--f=z", "--e=y", "--a=x")); + } + + @Test + public void canonicalizeDoesNotSkipAllowMultiple() throws Exception { + assertEquals(Arrays.asList("--d=a", "--d=b"), + canonicalize(Yesterday.class, "--d=a", "--d=b")); + } + + @Test + public void canonicalizeReplacesAbbrevWithName() throws Exception { + assertEquals(Arrays.asList("--g=1"), + canonicalize(Yesterday.class, "-h")); + } + + public static class LongValueExample extends OptionsBase { + @Option(name = "longval", + defaultValue = "2147483648") + public long longval; + + @Option(name = "intval", + defaultValue = "2147483647") + public int intval; + } + + @Test + public void parseLong() throws OptionsParsingException { + OptionsParser parser = newOptionsParser(LongValueExample.class); + parser.parse(""); + LongValueExample result = parser.getOptions(LongValueExample.class); + assertEquals(2147483648L, result.longval); + assertEquals(2147483647, result.intval); + + parser.parse("--longval", Long.toString(Long.MIN_VALUE)); + result = parser.getOptions(LongValueExample.class); + assertEquals(Long.MIN_VALUE, result.longval); + + try { + parser.parse("--intval=2147483648"); + fail(); + } catch (OptionsParsingException e) { + } + + parser.parse("--longval", "100"); + result = parser.getOptions(LongValueExample.class); + assertEquals(100, result.longval); + } +} |