diff options
author | Eric Rowe <erowe@google.com> | 2013-03-04 15:34:53 -0800 |
---|---|---|
committer | Eric Rowe <erowe@google.com> | 2013-05-02 10:53:03 -0700 |
commit | 511abd69a77c39e67e03e84651005687fb654d52 (patch) | |
tree | b3cd81e2787b932a8b30c57fa4cd066f774b62c3 /tests | |
parent | 399ac3a3d0dce227fee66eb81c289cea9150428a (diff) | |
download | loganalysis-511abd69a77c39e67e03e84651005687fb654d52.tar.gz |
Add support for @Option and command line parsing
Change-Id: I496356112e6f6b8003231414f21185937e32d0f0
Diffstat (limited to 'tests')
4 files changed, 1564 insertions, 0 deletions
diff --git a/tests/src/com/android/loganalysis/UnitTests.java b/tests/src/com/android/loganalysis/UnitTests.java index d3b63e1..6fb0ad3 100644 --- a/tests/src/com/android/loganalysis/UnitTests.java +++ b/tests/src/com/android/loganalysis/UnitTests.java @@ -51,6 +51,9 @@ import com.android.loganalysis.util.ArrayUtilTest; import com.android.loganalysis.util.LogPatternUtilTest; import com.android.loganalysis.util.LogTailUtilTest; import com.android.loganalysis.util.RegexTrieTest; +import com.android.loganalysis.util.config.ArgsOptionParserTest; +import com.android.loganalysis.util.config.OptionSetterTest; +import com.android.loganalysis.util.config.OptionUpdateRuleTest; import junit.framework.Test; import junit.framework.TestSuite; @@ -107,6 +110,11 @@ public class UnitTests extends TestSuite { addTestSuite(LogPatternUtilTest.class); addTestSuite(LogTailUtilTest.class); addTestSuite(RegexTrieTest.class); + + // util.config + addTestSuite(ArgsOptionParserTest.class); + addTestSuite(OptionSetterTest.class); + addTestSuite(OptionUpdateRuleTest.class); } public static Test suite() { diff --git a/tests/src/com/android/loganalysis/util/config/ArgsOptionParserTest.java b/tests/src/com/android/loganalysis/util/config/ArgsOptionParserTest.java new file mode 100644 index 0000000..524cfa0 --- /dev/null +++ b/tests/src/com/android/loganalysis/util/config/ArgsOptionParserTest.java @@ -0,0 +1,629 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * 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.android.loganalysis.util.config; + +import com.android.loganalysis.util.config.Option.Importance; + +import junit.framework.TestCase; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Unit tests for {@link ArgsOptionParser}. + */ +@SuppressWarnings("unused") +public class ArgsOptionParserTest extends TestCase { + + /** + * An option source with one {@link Option} specified. + */ + private static class OneOptionSource { + + private static final String DEFAULT_VALUE = "default"; + private static final String OPTION_NAME = "my_option"; + private static final String OPTION_DESC = "option description"; + + @Option(name=OPTION_NAME, shortName='o', description=OPTION_DESC) + private String mMyOption = DEFAULT_VALUE; + } + + /** + * An option source with one {@link Option} specified. + */ + private static class MapOptionSource { + + private static final String OPTION_NAME = "my_option"; + private static final String OPTION_DESC = "option description"; + + @Option(name=OPTION_NAME, shortName='o', description=OPTION_DESC) + private Map<Integer, Boolean> mMyOption = new HashMap<Integer, Boolean>(); + } + + /** + * An option source with boolean {@link Option} specified. + */ + private static class BooleanOptionSource { + + private static final boolean DEFAULT_BOOL = false; + private static final String DEFAULT_VALUE = "default"; + + @Option(name="my_boolean", shortName='b') + private boolean mMyBool = DEFAULT_BOOL; + + @Option(name="my_option", shortName='o') + protected String mMyOption = DEFAULT_VALUE; + } + + /** + * An option source with boolean {@link Option} specified with default = true. + */ + private static class BooleanTrueOptionSource { + + private static final boolean DEFAULT_BOOL = true; + + @Option(name="my_boolean", shortName='b') + private boolean mMyBool = DEFAULT_BOOL; + } + + /** + * An option source that has a superclass with options + */ + private static class InheritedOptionSource extends OneOptionSource { + + private static final String OPTION_NAME = "my_sub_option"; + private static final String OPTION_DESC = "sub description"; + + @Option(name=OPTION_NAME, description=OPTION_DESC) + private String mMySubOption = ""; + } + + /** + * An option source for testing the {@link Option#importance()} settings + */ + private static class ImportantOptionSource { + + private static final String IMPORTANT_OPTION_NAME = "important_option"; + private static final String IMPORTANT_UNSET_OPTION_NAME = "unset_important_option"; + private static final String UNIMPORTANT_OPTION_NAME = "unimportant_option"; + + @Option(name = IMPORTANT_OPTION_NAME, description = IMPORTANT_OPTION_NAME, + importance = Importance.ALWAYS) + private String mImportantOption = "foo"; + + @Option(name = IMPORTANT_UNSET_OPTION_NAME, description = IMPORTANT_UNSET_OPTION_NAME, + importance = Importance.IF_UNSET) + private String mImportantUnsetOption = null; + + @Option(name = UNIMPORTANT_OPTION_NAME, description = UNIMPORTANT_OPTION_NAME, + importance = Importance.NEVER) + private String mUnimportantOption = null; + + ImportantOptionSource(String setOption) { + mImportantUnsetOption = setOption; + } + + ImportantOptionSource() { + } + } + + /** + * Option source whose options shouldn't end up in the global namespace + */ + @OptionClass(alias = "ngos", global_namespace = false) + private static class NonGlobalOptionSource { + @Option(name = "option") + Boolean mOption = null; + } + + /** + * Option source with mandatory options + */ + private static class MandatoryOptionSourceNoDefault { + @Option(name = "no-default", mandatory = true) + private String mNoDefaultOption; + } + + /** + * Option source with mandatory options + */ + private static class MandatoryOptionSourceNull { + @Option(name = "null", mandatory = true) + private String mNullOption = null; + } + + /** + * Option source with mandatory options + */ + private static class MandatoryOptionSourceEmptyCollection { + @Option(name = "empty-collection", mandatory = true) + private Collection<String> mEmptyCollection = new ArrayList<String>(0); + } + + /** + * Option source with mandatory options + */ + private static class MandatoryOptionSourceEmptyMap { + @Option(name = "empty-map", mandatory = true) + private Map<String, String> mEmptyMap = new HashMap<String, String>(); + } + + /** + * An option source that exercises the {@link OptionUpdateRule}s. + */ + private static class OptionUpdateRuleSource { + + public static final String DEFAULT_VALUE = "5 default"; + public static final String BIGGER_VALUE = "9 bigger"; + public static final String SMALLER_VALUE = "0 smaller"; + + @Option(name = "default") + private String mDefaultOption = DEFAULT_VALUE; + + @Option(name = "first", updateRule = OptionUpdateRule.FIRST) + private String mFirstOption = DEFAULT_VALUE; + + @Option(name = "last", updateRule = OptionUpdateRule.LAST) + private String mLastOption = DEFAULT_VALUE; + + @Option(name = "greatest", updateRule = OptionUpdateRule.GREATEST) + private String mGreatestOption = DEFAULT_VALUE; + + @Option(name = "least", updateRule = OptionUpdateRule.LEAST) + private String mLeastOption = DEFAULT_VALUE; + + @Option(name = "immutable", updateRule = OptionUpdateRule.IMMUTABLE) + private String mImmutableOption = DEFAULT_VALUE; + + @Option(name = "null-immutable", updateRule = OptionUpdateRule.IMMUTABLE) + private String mNullImmutableOption = null; + } + + /** + * Verify that {@link OptionUpdateRule}s work properly when the update compares to greater-than + * the default value. + */ + public void testOptionUpdateRule_greater() throws Exception { + OptionUpdateRuleSource object = new OptionUpdateRuleSource(); + ArgsOptionParser parser = new ArgsOptionParser(object); + final String current = OptionUpdateRuleSource.DEFAULT_VALUE; + final String big = OptionUpdateRuleSource.BIGGER_VALUE; + + parser.parse(new String[] {"--default", big, "--first", big, "--last", big, + "--greatest", big, "--least", big}); + assertEquals(current, object.mFirstOption); + assertEquals(big, object.mLastOption); + assertEquals(big, object.mDefaultOption); // default should be LAST + assertEquals(big, object.mGreatestOption); + assertEquals(current, object.mLeastOption); + } + + /** + * Verify that {@link OptionUpdateRule}s work properly when the update compares to greater-than + * the default value. + */ + public void testOptionUpdateRule_lesser() throws Exception { + OptionUpdateRuleSource object = new OptionUpdateRuleSource(); + ArgsOptionParser parser = new ArgsOptionParser(object); + final String current = OptionUpdateRuleSource.DEFAULT_VALUE; + final String small = OptionUpdateRuleSource.SMALLER_VALUE; + + parser.parse(new String[] {"--default", small, "--first", small, "--last", small, + "--greatest", small, "--least", small}); + assertEquals(current, object.mFirstOption); + assertEquals(small, object.mLastOption); + assertEquals(small, object.mDefaultOption); // default should be LAST + assertEquals(current, object.mGreatestOption); + assertEquals(small, object.mLeastOption); + } + + /** + * Verify that {@link OptionUpdateRule}s work properly when the update compares to greater-than + * the default value. + */ + public void testOptionUpdateRule_immutable() throws Exception { + OptionUpdateRuleSource object = new OptionUpdateRuleSource(); + ArgsOptionParser parser = new ArgsOptionParser(object); + final String update = OptionUpdateRuleSource.BIGGER_VALUE; + + try { + parser.parse(new String[] {"--immutable", update}); + fail("ConfigurationException not thrown when updating an IMMUTABLE option"); + } catch (ConfigurationException e) { + // expected + } + + assertNull(object.mNullImmutableOption); + parser.parse(new String[] {"--null-immutable", update}); + assertEquals(update, object.mNullImmutableOption); + + try { + parser.parse(new String[] {"--null-immutable", update}); + fail("ConfigurationException not thrown when updating an IMMUTABLE option"); + } catch (ConfigurationException e) { + // expected + } + } + + /** + * Setting an option with a namespace alias should work fine + */ + public void testNonGlobalOptionSource_alias() throws Exception { + NonGlobalOptionSource source = new NonGlobalOptionSource(); + ArgsOptionParser parser = new ArgsOptionParser(source); + + assertNull(source.mOption); + parser.parse(new String[] {"--ngos:option"}); + assertTrue(source.mOption); + parser.parse(new String[] {"--ngos:no-option"}); + assertFalse(source.mOption); + } + + /** + * Setting an option with a classname namespace should work fine + */ + public void testNonGlobalOptionSource_className() throws Exception { + NonGlobalOptionSource source = new NonGlobalOptionSource(); + ArgsOptionParser parser = new ArgsOptionParser(source); + + assertNull(source.mOption); + parser.parse(new String[] {String.format("--%s:option", source.getClass().getName())}); + assertTrue(source.mOption); + parser.parse(new String[] {String.format("--%s:no-option", source.getClass().getName())}); + assertFalse(source.mOption); + } + + /** + * Setting an option without a namespace should fail + */ + public void testNonGlobalOptionSource_global() throws Exception { + NonGlobalOptionSource source = new NonGlobalOptionSource(); + ArgsOptionParser parser = new ArgsOptionParser(source); + + assertNull(source.mOption); + try { + parser.parse(new String[] {"--option"}); + fail("ConfigurationException not thrown when assigning a global option to an @Option " + + "field in a non-global-namespace class"); + } catch (ConfigurationException e) { + // expected + } + + try { + parser.parse(new String[] {"--no-option"}); + fail("ConfigurationException not thrown when assigning a global option to an @Option " + + "field in a non-global-namespace class"); + } catch (ConfigurationException e) { + // expected + } + } + + /** + * Test passing an empty argument list for an object that has one option specified. + * <p/> + * Expected that the option field should retain its default value. + */ + public void testParse_noArg() throws ConfigurationException { + OneOptionSource object = new OneOptionSource(); + ArgsOptionParser parser = new ArgsOptionParser(object); + parser.parse(new String[] {}); + assertEquals(OneOptionSource.DEFAULT_VALUE, object.mMyOption); + } + + /** + * Test passing an single argument for an object that has one option specified. + */ + public void testParse_oneArg() throws ConfigurationException { + OneOptionSource object = new OneOptionSource(); + ArgsOptionParser parser = new ArgsOptionParser(object); + final String expectedValue = "set"; + parser.parse(new String[] {"--my_option", expectedValue}); + assertEquals(expectedValue, object.mMyOption); + } + + /** + * Test passing an single argument for an object that has one option specified. + */ + public void testParse_oneMapArg() throws ConfigurationException { + MapOptionSource object = new MapOptionSource(); + ArgsOptionParser parser = new ArgsOptionParser(object); + final int expectedKey = 13; + final boolean expectedValue = true; + parser.parse(new String[] {"--my_option", Integer.toString(expectedKey), + Boolean.toString(expectedValue)}); + assertNotNull(object.mMyOption); + assertEquals(1, object.mMyOption.size()); + assertEquals(expectedValue, (boolean) object.mMyOption.get(expectedKey)); + } + + /** + * Test passing an single argument for an object that has one option specified. + */ + public void testParseMapArg_mismatchKeyType() throws ConfigurationException { + MapOptionSource object = new MapOptionSource(); + ArgsOptionParser parser = new ArgsOptionParser(object); + final String expectedKey = "istanbul"; + final boolean expectedValue = true; + try { + parser.parse(new String[] {"--my_option", expectedKey, Boolean.toString(expectedValue)}); + fail("ConfigurationException not thrown"); + } catch (ConfigurationException e) { + // expect an exception that explicitly mentions that the "key" is incorrect + assertTrue(String.format("Expected exception message to contain 'key': %s", + e.getMessage()), e.getMessage().contains("key")); + assertTrue(String.format("Expected exception message to contain '%s': %s", + expectedKey, e.getMessage()), e.getMessage().contains(expectedKey)); + } + } + + /** + * Test passing an single argument for an object that has one option specified. + */ + public void testParseMapArg_mismatchValueType() throws ConfigurationException { + MapOptionSource object = new MapOptionSource(); + ArgsOptionParser parser = new ArgsOptionParser(object); + final int expectedKey = 13; + final String expectedValue = "notconstantinople"; + try { + parser.parse(new String[] {"--my_option", Integer.toString(expectedKey), expectedValue}); + fail("ConfigurationException not thrown"); + } catch (ConfigurationException e) { + // expect an exception that explicitly mentions that the "value" is incorrect + assertTrue(String.format("Expected exception message to contain 'value': '%s'", + e.getMessage()), e.getMessage().contains("value")); + assertTrue(String.format("Expected exception message to contain '%s': %s", + expectedValue, e.getMessage()), e.getMessage().contains(expectedValue)); + } + } + + /** + * Test passing an single argument for an object that has one option specified. + */ + public void testParseMapArg_missingKey() throws ConfigurationException { + MapOptionSource object = new MapOptionSource(); + ArgsOptionParser parser = new ArgsOptionParser(object); + try { + parser.parse(new String[] {"--my_option"}); + fail("ConfigurationException not thrown"); + } catch (ConfigurationException e) { + // expect an exception that explicitly mentions that the "key" is incorrect + assertTrue(String.format("Expected exception message to contain 'key': '%s'", + e.getMessage()), e.getMessage().contains("key")); + } + } + + /** + * Test passing an single argument for an object that has one option specified. + */ + public void testParseMapArg_missingValue() throws ConfigurationException { + MapOptionSource object = new MapOptionSource(); + ArgsOptionParser parser = new ArgsOptionParser(object); + final int expectedKey = 13; + try { + parser.parse(new String[] {"--my_option", Integer.toString(expectedKey)}); + fail("ConfigurationException not thrown"); + } catch (ConfigurationException e) { + // expect an exception that explicitly mentions that the "value" is incorrect + assertTrue(String.format("Expected exception message to contain 'value': '%s'", + e.getMessage()), e.getMessage().contains("value")); + } + } + + /** + * Test passing an single argument for an object that has one option specified, using the + * option=value notation. + */ + public void testParse_oneArgEquals() throws ConfigurationException { + OneOptionSource object = new OneOptionSource(); + ArgsOptionParser parser = new ArgsOptionParser(object); + final String expectedValue = "set"; + parser.parse(new String[] {String.format("--my_option=%s", expectedValue)}); + assertEquals(expectedValue, object.mMyOption); + } + + /** + * Test passing a single argument for an object that has one option specified, using the + * short option notation. + */ + public void testParse_oneShortArg() throws ConfigurationException { + OneOptionSource object = new OneOptionSource(); + ArgsOptionParser parser = new ArgsOptionParser(object); + final String expectedValue = "set"; + parser.parse(new String[] {"-o", expectedValue}); + assertEquals(expectedValue, object.mMyOption); + } + + /** + * Test that "--" marks the beginning of positional arguments + */ + public void testParse_posArgs() throws ConfigurationException { + OneOptionSource object = new OneOptionSource(); + ArgsOptionParser parser = new ArgsOptionParser(object); + final String expectedValue = "set"; + // have a position argument with a long option prefix, to try to confuse the parser + final String posArg = "--unused"; + List<String> leftOver = parser.parse(new String[] {"-o", expectedValue, "--", posArg}); + assertEquals(expectedValue, object.mMyOption); + assertTrue(leftOver.contains(posArg)); + } + + /** + * Test passing a single boolean argument. + */ + public void testParse_boolArg() throws ConfigurationException { + BooleanOptionSource object = new BooleanOptionSource(); + ArgsOptionParser parser = new ArgsOptionParser(object); + parser.parse(new String[] {"-b"}); + assertTrue(object.mMyBool); + } + + /** + * Test passing a boolean argument with another short argument. + */ + public void testParse_boolTwoArg() throws ConfigurationException { + BooleanOptionSource object = new BooleanOptionSource(); + ArgsOptionParser parser = new ArgsOptionParser(object); + final String expectedValue = "set"; + parser.parse(new String[] {"-bo", expectedValue}); + assertTrue(object.mMyBool); + assertEquals(expectedValue, object.mMyOption); + } + + /** + * Test passing a boolean argument with another short argument, with value concatenated. + * e.g -bovalue + */ + public void testParse_boolTwoArgValue() throws ConfigurationException { + BooleanOptionSource object = new BooleanOptionSource(); + ArgsOptionParser parser = new ArgsOptionParser(object); + final String expectedValue = "set"; + parser.parse(new String[] {String.format("-bo%s", expectedValue)}); + assertTrue(object.mMyBool); + assertEquals(expectedValue, object.mMyOption); + } + + /** + * Test the "--no-<bool option>" syntax + */ + public void testParse_boolFalse() throws ConfigurationException { + BooleanTrueOptionSource object = new BooleanTrueOptionSource(); + ArgsOptionParser parser = new ArgsOptionParser(object); + parser.parse(new String[] {"--no-my_boolean"}); + assertFalse(object.mMyBool); + } + + /** + * Test the boolean long option syntax + */ + public void testParse_boolLong() throws ConfigurationException { + BooleanOptionSource object = new BooleanOptionSource(); + ArgsOptionParser parser = new ArgsOptionParser(object); + parser.parse(new String[] {"--my_boolean"}); + assertTrue(object.mMyBool); + } + + /** + * Test passing arg string where value is missing + */ + public void testParse_missingValue() throws ConfigurationException { + OneOptionSource object = new OneOptionSource(); + ArgsOptionParser parser = new ArgsOptionParser(object); + try { + parser.parse(new String[] {"--my_option"}); + fail("ConfigurationException not thrown"); + } catch (ConfigurationException e) { + // expected + } + } + + /** + * Test parsing args for an option that does not exist. + */ + public void testParse_optionNotPresent() throws ConfigurationException { + OneOptionSource object = new OneOptionSource(); + ArgsOptionParser parser = new ArgsOptionParser(object); + try { + parser.parse(new String[] {"--my_option", "set", "--not_here", "value"}); + fail("ConfigurationException not thrown"); + } catch (ConfigurationException e) { + // expected + } + } + + /** + * Test that help text is displayed for all fields + */ + public void testGetOptionHelp() { + String help = ArgsOptionParser.getOptionHelp(false, new InheritedOptionSource()); + assertTrue(help.contains(InheritedOptionSource.OPTION_NAME)); + assertTrue(help.contains(InheritedOptionSource.OPTION_DESC)); + assertTrue(help.contains(OneOptionSource.OPTION_NAME)); + assertTrue(help.contains(OneOptionSource.OPTION_DESC)); + assertTrue(help.contains(OneOptionSource.DEFAULT_VALUE)); + } + + /** + * Test displaying important only help text + */ + public void testGetOptionHelp_important() { + String help = ArgsOptionParser.getOptionHelp(true, new ImportantOptionSource()); + assertTrue(help.contains(ImportantOptionSource.IMPORTANT_OPTION_NAME)); + assertTrue(help.contains(ImportantOptionSource.IMPORTANT_UNSET_OPTION_NAME)); + assertFalse(help.contains(ImportantOptionSource.UNIMPORTANT_OPTION_NAME)); + } + + /** + * Test that {@link Importance#IF_UNSET} {@link Option}s are hidden from help if set. + */ + public void testGetOptionHelp_importantUnset() { + String help = ArgsOptionParser.getOptionHelp(true, new ImportantOptionSource("foo")); + assertTrue(help.contains(ImportantOptionSource.IMPORTANT_OPTION_NAME)); + assertFalse(help.contains(ImportantOptionSource.IMPORTANT_UNSET_OPTION_NAME)); + assertFalse(help.contains(ImportantOptionSource.UNIMPORTANT_OPTION_NAME)); + } + + public void testMandatoryOption_noDefault() throws Exception { + MandatoryOptionSourceNoDefault object = new MandatoryOptionSourceNoDefault(); + ArgsOptionParser parser = new ArgsOptionParser(object); + // expect success + parser.parse(new String[] {}); + try { + parser.validateMandatoryOptions(); + fail("ConfigurationException not thrown"); + } catch (ConfigurationException e) { + // expected + } + } + + public void testMandatoryOption_null() throws Exception { + MandatoryOptionSourceNull object = new MandatoryOptionSourceNull(); + ArgsOptionParser parser = new ArgsOptionParser(object); + parser.parse(new String[] {}); + try { + parser.validateMandatoryOptions(); + fail("ConfigurationException not thrown"); + } catch (ConfigurationException e) { + // expected + } + } + + public void testMandatoryOption_emptyCollection() throws Exception { + MandatoryOptionSourceEmptyCollection object = new MandatoryOptionSourceEmptyCollection(); + ArgsOptionParser parser = new ArgsOptionParser(object); + parser.parse(new String[] {}); + try { + parser.validateMandatoryOptions(); + fail("ConfigurationException not thrown"); + } catch (ConfigurationException e) { + // expected + } + } + + public void testMandatoryOption_emptyMap() throws Exception { + MandatoryOptionSourceEmptyMap object = new MandatoryOptionSourceEmptyMap(); + ArgsOptionParser parser = new ArgsOptionParser(object); + parser.parse(new String[] {}); + try { + parser.validateMandatoryOptions(); + fail("ConfigurationException not thrown"); + } catch (ConfigurationException e) { + // expected + } + } +} diff --git a/tests/src/com/android/loganalysis/util/config/OptionSetterTest.java b/tests/src/com/android/loganalysis/util/config/OptionSetterTest.java new file mode 100644 index 0000000..dbac942 --- /dev/null +++ b/tests/src/com/android/loganalysis/util/config/OptionSetterTest.java @@ -0,0 +1,836 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * 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.android.loganalysis.util.config; + +import junit.framework.TestCase; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +/** + * Unit tests for {@link OptionSetter}. + */ +public class OptionSetterTest extends TestCase { + + /** Option source with generic type. */ + private static class GenericTypeOptionSource { + @SuppressWarnings("unused") + @Option(name = "my_option", shortName = 'o') + private Collection<?> mMyOption; + } + + /** Option source with unparameterized type. */ + private static class CollectionTypeOptionSource { + @SuppressWarnings("unused") + @Option(name = "my_option", shortName = 'o') + private Collection mMyOption; + } + + private static class MyGeneric<T> { + } + + /** Option source with unparameterized type. */ + private static class NonCollectionGenericTypeOptionSource { + @SuppressWarnings("unused") + @Option(name = "my_option", shortName = 'o') + private MyGeneric<String> mMyOption; + } + + /** Option source with options with same name. */ + private static class DuplicateOptionSource { + @SuppressWarnings("unused") + @Option(name = "string", shortName = 's') + private String mMyOption; + + @SuppressWarnings("unused") + @Option(name = "string", shortName = 's') + private String mMyDuplicateOption; + } + + /** Option source with an option with same name as AllTypesOptionSource. */ + @OptionClass(alias = "shared") + private static class SharedOptionSource { + @Option(name = "string", shortName = 's') + private String mMyOption; + + @SuppressWarnings("unused") + @Option(name = "enum") + private DefaultEnumClass mEnum = null; + + @SuppressWarnings("unused") + @Option(name = "string_collection") + private Collection<String> mStringCollection = new ArrayList<String>(); + + @Option(name = "enumMap") + private Map<DefaultEnumClass, CustomEnumClass> mEnumMap = + new HashMap<DefaultEnumClass, CustomEnumClass>(); + + @Option(name = "enumCollection") + private Collection<DefaultEnumClass> mEnumCollection = + new ArrayList<DefaultEnumClass>(); + } + + /** + * Option source with an option with same name as AllTypesOptionSource, but a different type. + */ + private static class SharedOptionWrongTypeSource { + @SuppressWarnings("unused") + @Option(name = "string", shortName = 's') + private int mMyOption; + } + + /** option source with all supported types. */ + @OptionClass(alias = "all") + private static class AllTypesOptionSource { + @Option(name = "string_collection") + private final Collection<String> mStringCollection = new ArrayList<String>(); + + @Option(name = "string_string_map") + private Map<String, String> mStringMap = new HashMap<String, String>(); + + @Option(name = "string") + private String mString = null; + + @Option(name = "boolean") + private boolean mBool = false; + + @Option(name = "booleanObj") + private Boolean mBooleanObj = false; + + @Option(name = "byte") + private byte mByte = 0; + + @Option(name = "byteObj") + private Byte mByteObj = 0; + + @Option(name = "short") + private short mShort = 0; + + @Option(name = "shortObj") + private Short mShortObj = null; + + @Option(name = "int") + private int mInt = 0; + + @Option(name = "intObj") + private Integer mIntObj = 0; + + @Option(name = "long") + private long mLong = 0; + + @Option(name = "longObj") + private Long mLongObj = null; + + @Option(name = "float") + private float mFloat = 0; + + @Option(name = "floatObj") + private Float mFloatObj = null; + + @Option(name = "double") + private double mDouble = 0; + + @Option(name = "doubleObj") + private Double mDoubleObj = null; + + @Option(name = "file") + private File mFile = null; + + @Option(name = "enum") + private DefaultEnumClass mEnum = null; + + @Option(name = "customEnum") + private CustomEnumClass mCustomEnum = null; + + @Option(name = "enumMap") + private Map<DefaultEnumClass, CustomEnumClass> mEnumMap = + new HashMap<DefaultEnumClass, CustomEnumClass>(); + + @Option(name = "enumCollection") + private Collection<DefaultEnumClass> mEnumCollection = + new ArrayList<DefaultEnumClass>(); + } + + private static class ParentOptionSource { + @Option(name = "string") + private String mString = null; + + protected String getParentString() { + return mString; + } + } + + private static class ChildOptionSource extends ParentOptionSource { + @Option(name = "child-string") + private String mChildString = null; + } + + /** + * Option source with invalid option name. + */ + private static class BadOptionNameSource { + @SuppressWarnings("unused") + @Option(name = "bad:string", shortName = 's') + private int mMyOption; + } + + private static enum DefaultEnumClass { + VAL1, VAL3, VAL2; + } + + private static enum CustomEnumClass { + VAL1(42); + + private int mVal; + + CustomEnumClass(int val) { + mVal = val; + } + + public int getVal() { + return mVal; + } + } + + private static class FinalOption { + @Option(name = "final-string", description="final field, not allowed") + private final String mFinal= "foo"; + } + + /** + * Test creating an {@link OptionSetter} for a source with invalid option type. + */ + public void testOptionSetter_noType() { + try { + new OptionSetter(new GenericTypeOptionSource()); + fail("ConfigurationException not thrown"); + } catch (ConfigurationException e) { + // expected + } + } + + /** + * Test creating an {@link OptionSetter} for a source with duplicate option names. + */ + public void testOptionSetter_duplicateOptions() { + try { + new OptionSetter(new DuplicateOptionSource()); + fail("ConfigurationException not thrown"); + } catch (ConfigurationException e) { + // expected + } + } + + /** + * Test option with same name can be used in multiple option sources. + */ + public void testOptionSetter_sharedOptions() throws ConfigurationException { + AllTypesOptionSource object1 = new AllTypesOptionSource(); + SharedOptionSource object2 = new SharedOptionSource(); + OptionSetter setter = new OptionSetter(object1, object2); + setter.setOptionValue("string", "test"); + assertEquals("test", object1.mString); + assertEquals("test", object2.mMyOption); + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for Enums used as the key and value + * of a {@link Map}. + */ + public void testOptionSetter_sharedEnumMap() throws ConfigurationException { + AllTypesOptionSource object1 = new AllTypesOptionSource(); + SharedOptionSource object2 = new SharedOptionSource(); + + final String key = "VAL1"; + final String value = "VAL1"; + final DefaultEnumClass expectedKey = DefaultEnumClass.VAL1; + final CustomEnumClass expectedValue = CustomEnumClass.VAL1; + + // Actually set the key/value pair + OptionSetter parser = new OptionSetter(object1, object2); + parser.setOptionMapValue("enumMap", key, value); + + // verify object1 + assertEquals(1, object1.mEnumMap.size()); + assertNotNull(object1.mEnumMap.get(expectedKey)); + assertEquals(expectedValue, object1.mEnumMap.get(expectedKey)); + + // verify object2 + assertEquals(1, object2.mEnumMap.size()); + assertNotNull(object2.mEnumMap.get(expectedKey)); + assertEquals(expectedValue, object2.mEnumMap.get(expectedKey)); + } + + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for Enums used as the key and value + * of a {@link Map}. + */ + public void testOptionSetter_sharedEnumCollection() throws ConfigurationException { + AllTypesOptionSource object1 = new AllTypesOptionSource(); + SharedOptionSource object2 = new SharedOptionSource(); + + final String value = "VAL1"; + final DefaultEnumClass expectedValue = DefaultEnumClass.VAL1; + + // Actually add the element + OptionSetter parser = new OptionSetter(object1, object2); + parser.setOptionValue("enumCollection", value); + + // verify object1 + assertEquals(1, object1.mEnumCollection.size()); + assertTrue(object1.mEnumCollection.contains(expectedValue)); + + // verify object2 + assertEquals(1, object2.mEnumCollection.size()); + assertTrue(object2.mEnumCollection.contains(expectedValue)); + } + + + /** + * Test that multiple options with same name must have the same type. + */ + public void testOptionSetter_sharedOptionsDiffType() throws ConfigurationException { + try { + new OptionSetter(new AllTypesOptionSource(), new SharedOptionWrongTypeSource()); + fail("ConfigurationException not thrown"); + } catch (ConfigurationException e) { + // expected + } + } + + /** + * Test namespaced options using class names. + */ + public void testOptionSetter_namespacedClassName() throws ConfigurationException { + AllTypesOptionSource object1 = new AllTypesOptionSource(); + SharedOptionSource object2 = new SharedOptionSource(); + OptionSetter setter = new OptionSetter(object1, object2); + setter.setOptionValue(AllTypesOptionSource.class.getName() + ":string", "alltest"); + setter.setOptionValue(SharedOptionSource.class.getName() + ":string", "sharedtest"); + assertEquals("alltest", object1.mString); + assertEquals("sharedtest", object2.mMyOption); + } + + /** + * Test namespaced options using OptionClass aliases + */ + public void testOptionSetter_namespacedAlias() throws ConfigurationException { + AllTypesOptionSource object1 = new AllTypesOptionSource(); + SharedOptionSource object2 = new SharedOptionSource(); + OptionSetter setter = new OptionSetter(object1, object2); + setter.setOptionValue("all:string", "alltest"); + setter.setOptionValue("shared:string", "sharedtest"); + assertEquals("alltest", object1.mString); + assertEquals("sharedtest", object2.mMyOption); + } + + /** + * Test creating an {@link OptionSetter} for a Collection with no type. + */ + public void testOptionSetter_unparamType() { + try { + new OptionSetter(new CollectionTypeOptionSource()); + fail("ConfigurationException not thrown"); + } catch (ConfigurationException e) { + // expected + } + } + + /** + * Test creating an {@link OptionSetter} for a non collection option with generic type + */ + public void testOptionSetter_genericType() { + try { + new OptionSetter(new NonCollectionGenericTypeOptionSource()); + fail("ConfigurationException not thrown"); + } catch (ConfigurationException e) { + // expected + } + } + + /** + * Test creating an {@link OptionSetter} for class with inherited options + */ + public void testOptionSetter_inheritedOptions() throws ConfigurationException { + ChildOptionSource source = new ChildOptionSource(); + OptionSetter setter = new OptionSetter(source); + setter.setOptionValue("string", "parent"); + setter.setOptionValue("child-string", "child"); + assertEquals("parent", source.getParentString()); + assertEquals("child", source.mChildString); + } + + /** + * Test that options with {@link OptionSetter#NAMESPACE_SEPARATOR} are rejected + */ + public void testOptionSetter_badOptionName() throws ConfigurationException { + try { + new OptionSetter(new BadOptionNameSource()); + fail("ConfigurationException not thrown"); + } catch (ConfigurationException e) { + // expected + } + } + + /** + * Test {@link OptionSetter#isBooleanOption(String)} when passed an unknown option name + */ + public void testIsBooleanOption_unknown() throws ConfigurationException { + OptionSetter parser = new OptionSetter(new AllTypesOptionSource()); + try { + parser.isBooleanOption("unknown"); + fail("ConfigurationException not thrown"); + } catch (ConfigurationException e) { + // expected + } + } + + /** + * Test {@link OptionSetter#isBooleanOption(String)} when passed boolean option name + */ + public void testIsBooleanOption_true() throws ConfigurationException { + OptionSetter parser = new OptionSetter(new AllTypesOptionSource()); + assertTrue(parser.isBooleanOption("boolean")); + } + + /** + * Test {@link OptionSetter#isBooleanOption(String)} when passed boolean option name for a + * Boolean object + */ + public void testIsBooleanOption_objTrue() throws ConfigurationException { + OptionSetter parser = new OptionSetter(new AllTypesOptionSource()); + assertTrue(parser.isBooleanOption("booleanObj")); + } + + /** + * Test {@link OptionSetter#isBooleanOption(String)} when passed non-boolean option + */ + public void testIsBooleanOption_false() throws ConfigurationException { + OptionSetter parser = new OptionSetter(new AllTypesOptionSource()); + assertFalse(parser.isBooleanOption("string")); + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} when passed an unknown option name + */ + public void testSetOptionValue_unknown() throws ConfigurationException { + OptionSetter parser = new OptionSetter(new AllTypesOptionSource()); + try { + parser.setOptionValue("unknown", "foo"); + fail("ConfigurationException not thrown"); + } catch (ConfigurationException e) { + // expected + } + } + + /** + * Test setting a value for a option with an unknown generic type. + */ + public void testSetOptionValue_unknownType() throws ConfigurationException { + OptionSetter parser = new OptionSetter(new AllTypesOptionSource()); + try { + parser.setOptionValue("my_option", "foo"); + fail("ConfigurationException not thrown"); + } catch (ConfigurationException e) { + // expected + } + } + + /** + * Test setting a value for a non-parameterized Collection + */ + public void testSetOptionValue_unparameterizedType() throws ConfigurationException { + OptionSetter parser = new OptionSetter(new AllTypesOptionSource()); + try { + parser.setOptionValue("my_option", "foo"); + fail("ConfigurationException not thrown"); + } catch (ConfigurationException e) { + // expected + } + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for a String. + */ + public void testSetOptionValue_string() throws ConfigurationException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + final String expectedValue = "stringvalue"; + assertSetOptionValue(optionSource, "string", expectedValue); + assertEquals(expectedValue, optionSource.mString); + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for a Collection. + */ + public void testSetOptionValue_collection() throws ConfigurationException, IOException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + final String expectedValue = "stringvalue"; + assertSetOptionValue(optionSource, "string_collection", expectedValue); + assertEquals(1, optionSource.mStringCollection.size()); + assertTrue(optionSource.mStringCollection.contains(expectedValue)); + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for a Map. + */ + public void testSetOptionValue_map() throws ConfigurationException, IOException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + final String expectedKey = "stringkey"; + final String expectedValue = "stringvalue"; + + // Actually set the key/value pair + OptionSetter parser = new OptionSetter(optionSource); + parser.setOptionMapValue("string_string_map", expectedKey, expectedValue); + + assertEquals(1, optionSource.mStringMap.size()); + assertNotNull(optionSource.mStringMap.get(expectedKey)); + assertEquals(expectedValue, optionSource.mStringMap.get(expectedKey)); + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for a boolean. + */ + public void testSetOptionValue_boolean() throws ConfigurationException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + assertSetOptionValue(optionSource, "boolean", "true"); + assertEquals(true, optionSource.mBool); + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for a boolean for a non-boolean + * value. + */ + public void testSetOptionValue_booleanInvalid() throws ConfigurationException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + assertSetOptionValueInvalid(optionSource, "boolean", "blah"); + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for a Boolean. + */ + public void testSetOptionValue_booleanObj() throws ConfigurationException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + assertSetOptionValue(optionSource, "booleanObj", "true"); + assertTrue(optionSource.mBooleanObj); + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for a byte. + */ + public void testSetOptionValue_byte() throws ConfigurationException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + assertSetOptionValue(optionSource, "byte", "2"); + assertEquals(2, optionSource.mByte); + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for a byte for an invalid value. + */ + public void testSetOptionValue_byteInvalid() throws ConfigurationException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + assertSetOptionValueInvalid(optionSource, "byte", "blah"); + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for a Byte. + */ + public void testSetOptionValue_byteObj() throws ConfigurationException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + assertSetOptionValue(optionSource, "byteObj", "2"); + assertTrue(2 == optionSource.mByteObj); + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for a short. + */ + public void testSetOptionValue_short() throws ConfigurationException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + assertSetOptionValue(optionSource, "short", "2"); + assertTrue(2 == optionSource.mShort); + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for a Short. + */ + public void testSetOptionValue_shortObj() throws ConfigurationException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + assertSetOptionValue(optionSource, "shortObj", "2"); + assertTrue(2 == optionSource.mShortObj); + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for a short for an invalid value. + */ + public void testSetOptionValue_shortInvalid() throws ConfigurationException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + assertSetOptionValueInvalid(optionSource, "short", "blah"); + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for a int. + */ + public void testSetOptionValue_int() throws ConfigurationException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + assertSetOptionValue(optionSource, "int", "2"); + assertTrue(2 == optionSource.mInt); + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for a Integer. + */ + public void testSetOptionValue_intObj() throws ConfigurationException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + assertSetOptionValue(optionSource, "intObj", "2"); + assertTrue(2 == optionSource.mIntObj); + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for a int for an invalid value. + */ + public void testSetOptionValue_intInvalid() throws ConfigurationException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + assertSetOptionValueInvalid(optionSource, "int", "blah"); + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for a long. + */ + public void testSetOptionValue_long() throws ConfigurationException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + assertSetOptionValue(optionSource, "long", "2"); + assertTrue(2 == optionSource.mLong); + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for a Long. + */ + public void testSetOptionValue_longObj() throws ConfigurationException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + assertSetOptionValue(optionSource, "longObj", "2"); + assertTrue(2 == optionSource.mLongObj); + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for a long for an invalid value. + */ + public void testSetOptionValue_longInvalid() throws ConfigurationException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + assertSetOptionValueInvalid(optionSource, "long", "blah"); + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for a float. + */ + public void testSetOptionValue_float() throws ConfigurationException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + assertSetOptionValue(optionSource, "float", "2.1"); + assertEquals(2.1, optionSource.mFloat, 0.01); + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for a Float. + */ + public void testSetOptionValue_floatObj() throws ConfigurationException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + assertSetOptionValue(optionSource, "floatObj", "2.1"); + assertEquals(2.1, optionSource.mFloatObj, 0.01); + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for a float for an invalid value. + */ + public void testSetOptionValue_floatInvalid() throws ConfigurationException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + assertSetOptionValueInvalid(optionSource, "float", "blah"); + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for a float. + */ + public void testSetOptionValue_double() throws ConfigurationException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + assertSetOptionValue(optionSource, "double", "2.1"); + assertEquals(2.1, optionSource.mDouble, 0.01); + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for a Float. + */ + public void testSetOptionValue_doubleObj() throws ConfigurationException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + assertSetOptionValue(optionSource, "doubleObj", "2.1"); + assertEquals(2.1, optionSource.mDoubleObj, 0.01); + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for a double for an invalid value. + */ + public void testSetOptionValue_doubleInvalid() throws ConfigurationException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + assertSetOptionValueInvalid(optionSource, "double", "blah"); + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for a File. + */ + public void testSetOptionValue_file() throws ConfigurationException, IOException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + File tmpFile = File.createTempFile("testSetOptionValue_file", "txt"); + try { + assertSetOptionValue(optionSource, "file", tmpFile.getAbsolutePath()); + assertEquals(tmpFile.getAbsolutePath(), optionSource.mFile.getAbsolutePath()); + } finally { + tmpFile.delete(); + } + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for an Enum. + */ + public void testSetOptionValue_enum() throws ConfigurationException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + assertSetOptionValue(optionSource, "enum", "VAL1"); + assertEquals(DefaultEnumClass.VAL1, optionSource.mEnum); + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for an Enum. Specifically make sure + * that we fall back properly, so that a mixed-case value will be silently mapped to an + * uppercase version, since Enum constants tend to be uppercase by convention. + */ + public void testSetOptionValue_enumMixedCase() throws ConfigurationException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + assertSetOptionValue(optionSource, "enum", "Val1"); + assertEquals(DefaultEnumClass.VAL1, optionSource.mEnum); + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for an Enum with custom values. + */ + public void testSetOptionValue_customEnum() throws ConfigurationException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + assertSetOptionValue(optionSource, "customEnum", "VAL1"); + assertEquals(CustomEnumClass.VAL1, optionSource.mCustomEnum); + assertEquals(42, optionSource.mCustomEnum.getVal()); + } + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for Enums used as the key and value + * of a {@link Map}. + */ + public void testSetOptionValue_enumMap() throws ConfigurationException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + + final String key = "VAL1"; + final String value = "VAL1"; + final DefaultEnumClass expectedKey = DefaultEnumClass.VAL1; + final CustomEnumClass expectedValue = CustomEnumClass.VAL1; + + // Actually set the key/value pair + OptionSetter parser = new OptionSetter(optionSource); + parser.setOptionMapValue("enumMap", key, value); + + assertEquals(1, optionSource.mEnumMap.size()); + assertNotNull(optionSource.mEnumMap.get(expectedKey)); + assertEquals(expectedValue, optionSource.mEnumMap.get(expectedKey)); + } + + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for Enums used as the key and value + * of a {@link Map}. + */ + public void testSetOptionValue_enumCollection() throws ConfigurationException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + + final String value = "VAL1"; + final DefaultEnumClass expectedValue = DefaultEnumClass.VAL1; + + assertSetOptionValue(optionSource, "enumCollection", value); + + assertEquals(1, optionSource.mEnumCollection.size()); + assertTrue(optionSource.mEnumCollection.contains(expectedValue)); + } + + + /** + * Test {@link OptionSetter#setOptionValue(String, String)} for an Enum. + */ + public void testSetOptionValue_enumBadValue() throws ConfigurationException { + AllTypesOptionSource optionSource = new AllTypesOptionSource(); + try { + assertSetOptionValue(optionSource, "enum", "noexist"); + fail("ConfigurationException not thrown"); + } catch (ConfigurationException e) { + // expected + } + } + + /** + * Make sure that Enum documentation shows the defaults properly + */ + public void testEnumDocs() throws Exception { + // We assume here that the fields are returned in declaration order, as documented in the + // {@link Enum} javadoc. + String expectedValues = " Valid values: [VAL1, VAL3, VAL2]"; + Field field = AllTypesOptionSource.class.getDeclaredField("mEnum"); + String actualValues = OptionSetter.getEnumFieldValuesAsString(field); + assertEquals(expectedValues, actualValues); + } + + /** + * Test {@link OptionSetter} for a final field + */ + public void testOptionSetter_finalField() throws ConfigurationException { + FinalOption optionSource = new FinalOption(); + try { + new OptionSetter(optionSource); + fail("ConfigurationException not thrown"); + } catch (ConfigurationException e) { + // expected + } + } + + /** + * Perform {@link OptionSetter#setOptionValue(String, String)} for a given option. + */ + private void assertSetOptionValue(AllTypesOptionSource optionSource, final String optionName, + final String expectedValue) throws ConfigurationException { + OptionSetter parser = new OptionSetter(optionSource); + parser.setOptionValue(optionName, expectedValue); + } + + /** + * Perform {@link OptionSetter#setOptionValue(String, String)} for a given option, with an + * invalid value for the option type. + */ + private void assertSetOptionValueInvalid(AllTypesOptionSource optionSource, + final String optionName, final String expectedValue) { + try { + assertSetOptionValue(optionSource, optionName, expectedValue); + fail("ConfigurationException not thrown"); + } catch (ConfigurationException e) { + // expected + } + } +} diff --git a/tests/src/com/android/loganalysis/util/config/OptionUpdateRuleTest.java b/tests/src/com/android/loganalysis/util/config/OptionUpdateRuleTest.java new file mode 100644 index 0000000..3dc187f --- /dev/null +++ b/tests/src/com/android/loganalysis/util/config/OptionUpdateRuleTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * 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.android.loganalysis.util.config; + +import junit.framework.TestCase; + +/** + * Unit tests for {@link OptionUpdateRule} + */ +public class OptionUpdateRuleTest extends TestCase { + private static final String mOptionName = "option-name"; + private static final Object mCurrent = "5 current value"; + private static final Object mUpdate = "5 update value"; + private static final Object mSmallUpdate = "0 update value"; + private static final Object mBigUpdate = "9 update value"; + + public void testFirst_simple() throws Exception { + assertEquals(mUpdate, OptionUpdateRule.FIRST.update(mOptionName, null, mUpdate)); + assertEquals(mCurrent, OptionUpdateRule.FIRST.update(mOptionName, mCurrent, mUpdate)); + } + + public void testLast_simple() throws Exception { + assertEquals(mUpdate, OptionUpdateRule.LAST.update(mOptionName, null, mUpdate)); + assertEquals(mUpdate, OptionUpdateRule.LAST.update(mOptionName, mCurrent, mUpdate)); + } + + public void testGreatest_simple() throws Exception { + assertEquals(mSmallUpdate, + OptionUpdateRule.GREATEST.update(mOptionName, null, mSmallUpdate)); + assertEquals(mCurrent, + OptionUpdateRule.GREATEST.update(mOptionName, mCurrent, mSmallUpdate)); + assertEquals(mBigUpdate, + OptionUpdateRule.GREATEST.update(mOptionName, mCurrent, mBigUpdate)); + } + + public void testLeast_simple() throws Exception { + assertEquals(mBigUpdate, + OptionUpdateRule.LEAST.update(mOptionName, null, mBigUpdate)); + assertEquals(mSmallUpdate, + OptionUpdateRule.LEAST.update(mOptionName, mCurrent, mSmallUpdate)); + assertEquals(mCurrent, + OptionUpdateRule.LEAST.update(mOptionName, mCurrent, mBigUpdate)); + } + + public void testImmutable_simple() throws Exception { + assertEquals(mUpdate, OptionUpdateRule.IMMUTABLE.update(mOptionName, null, mUpdate)); + try { + OptionUpdateRule.IMMUTABLE.update(mOptionName, mCurrent, mUpdate); + fail("ConfigurationException not thrown when updating an IMMUTABLE option"); + } catch (ConfigurationException e) { + // expected + } + } + + public void testInvalidComparison() throws Exception { + try { + // Strings aren't comparable with integers + OptionUpdateRule.GREATEST.update(mOptionName, 13, mUpdate); + fail("ConfigurationException not thrown for invalid comparison."); + } catch (ConfigurationException e) { + // Expected. Moreover, the exception should be actionable, so make sure we mention the + // specific mismatching types. + final String msg = e.getMessage(); + assertTrue(msg.contains("Integer")); + assertTrue(msg.contains("String")); + } + } + + public void testNotComparable() throws Exception { + try { + OptionUpdateRule.LEAST.update(mOptionName, new Exception("hi"), mUpdate); + } catch (ConfigurationException e) { + // expected + } + } +} + |