aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/test/java/com/google/devtools/common/options/OptionsParserTest.java
diff options
context:
space:
mode:
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.java1026
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);
+ }
+}