| // 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); | |
| } | |
| } |