diff options
Diffstat (limited to 'python/mozbuild/mozbuild/test/configure/test_options.py')
-rw-r--r-- | python/mozbuild/mozbuild/test/configure/test_options.py | 905 |
1 files changed, 905 insertions, 0 deletions
diff --git a/python/mozbuild/mozbuild/test/configure/test_options.py b/python/mozbuild/mozbuild/test/configure/test_options.py new file mode 100644 index 0000000000..59ba616355 --- /dev/null +++ b/python/mozbuild/mozbuild/test/configure/test_options.py @@ -0,0 +1,905 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import unittest + +from mozunit import main + +from mozbuild.configure.options import ( + CommandLineHelper, + ConflictingOptionError, + InvalidOptionError, + NegativeOptionValue, + Option, + OptionValue, + PositiveOptionValue, +) + + +class Option(Option): + def __init__(self, *args, **kwargs): + kwargs["help"] = "Dummy help" + super(Option, self).__init__(*args, **kwargs) + + +class TestOption(unittest.TestCase): + def test_option(self): + option = Option("--option") + self.assertEqual(option.prefix, "") + self.assertEqual(option.name, "option") + self.assertEqual(option.env, None) + self.assertFalse(option.default) + + option = Option("--enable-option") + self.assertEqual(option.prefix, "enable") + self.assertEqual(option.name, "option") + self.assertEqual(option.env, None) + self.assertFalse(option.default) + + option = Option("--disable-option") + self.assertEqual(option.prefix, "disable") + self.assertEqual(option.name, "option") + self.assertEqual(option.env, None) + self.assertTrue(option.default) + + option = Option("--with-option") + self.assertEqual(option.prefix, "with") + self.assertEqual(option.name, "option") + self.assertEqual(option.env, None) + self.assertFalse(option.default) + + option = Option("--without-option") + self.assertEqual(option.prefix, "without") + self.assertEqual(option.name, "option") + self.assertEqual(option.env, None) + self.assertTrue(option.default) + + option = Option("--without-option-foo", env="MOZ_OPTION") + self.assertEqual(option.env, "MOZ_OPTION") + + option = Option(env="MOZ_OPTION") + self.assertEqual(option.prefix, "") + self.assertEqual(option.name, None) + self.assertEqual(option.env, "MOZ_OPTION") + self.assertFalse(option.default) + + with self.assertRaises(InvalidOptionError) as e: + Option("--option", nargs=0, default=("a",)) + self.assertEqual( + str(e.exception), "The given `default` doesn't satisfy `nargs`" + ) + + with self.assertRaises(InvalidOptionError) as e: + Option("--option", nargs=1, default=()) + self.assertEqual( + str(e.exception), "default must be a bool, a string or a tuple of strings" + ) + + with self.assertRaises(InvalidOptionError) as e: + Option("--option", nargs=1, default=True) + self.assertEqual( + str(e.exception), "The given `default` doesn't satisfy `nargs`" + ) + + with self.assertRaises(InvalidOptionError) as e: + Option("--option", nargs=1, default=("a", "b")) + self.assertEqual( + str(e.exception), "The given `default` doesn't satisfy `nargs`" + ) + + with self.assertRaises(InvalidOptionError) as e: + Option("--option", nargs=2, default=()) + self.assertEqual( + str(e.exception), "default must be a bool, a string or a tuple of strings" + ) + + with self.assertRaises(InvalidOptionError) as e: + Option("--option", nargs=2, default=True) + self.assertEqual( + str(e.exception), "The given `default` doesn't satisfy `nargs`" + ) + + with self.assertRaises(InvalidOptionError) as e: + Option("--option", nargs=2, default=("a",)) + self.assertEqual( + str(e.exception), "The given `default` doesn't satisfy `nargs`" + ) + + with self.assertRaises(InvalidOptionError) as e: + Option("--option", nargs="?", default=("a", "b")) + self.assertEqual( + str(e.exception), "The given `default` doesn't satisfy `nargs`" + ) + + with self.assertRaises(InvalidOptionError) as e: + Option("--option", nargs="+", default=()) + self.assertEqual( + str(e.exception), "default must be a bool, a string or a tuple of strings" + ) + + with self.assertRaises(InvalidOptionError) as e: + Option("--option", nargs="+", default=True) + self.assertEqual( + str(e.exception), "The given `default` doesn't satisfy `nargs`" + ) + + # --disable options with a nargs value that requires at least one + # argument need to be given a default. + with self.assertRaises(InvalidOptionError) as e: + Option("--disable-option", nargs=1) + self.assertEqual( + str(e.exception), "The given `default` doesn't satisfy `nargs`" + ) + + with self.assertRaises(InvalidOptionError) as e: + Option("--disable-option", nargs="+") + self.assertEqual( + str(e.exception), "The given `default` doesn't satisfy `nargs`" + ) + + # Test nargs inference from default value + option = Option("--with-foo", default=True) + self.assertEqual(option.nargs, 0) + + option = Option("--with-foo", default=False) + self.assertEqual(option.nargs, 0) + + option = Option("--with-foo", default="a") + self.assertEqual(option.nargs, "?") + + option = Option("--with-foo", default=("a",)) + self.assertEqual(option.nargs, "?") + + option = Option("--with-foo", default=("a", "b")) + self.assertEqual(option.nargs, "*") + + option = Option(env="FOO", default=True) + self.assertEqual(option.nargs, 0) + + option = Option(env="FOO", default=False) + self.assertEqual(option.nargs, 0) + + option = Option(env="FOO", default="a") + self.assertEqual(option.nargs, "?") + + option = Option(env="FOO", default=("a",)) + self.assertEqual(option.nargs, "?") + + option = Option(env="FOO", default=("a", "b")) + self.assertEqual(option.nargs, "*") + + def test_option_option(self): + for option in ( + "--option", + "--enable-option", + "--disable-option", + "--with-option", + "--without-option", + ): + self.assertEqual(Option(option).option, option) + self.assertEqual(Option(option, env="FOO").option, option) + + opt = Option(option, default=False) + self.assertEqual( + opt.option, + option.replace("-disable-", "-enable-").replace("-without-", "-with-"), + ) + + opt = Option(option, default=True) + self.assertEqual( + opt.option, + option.replace("-enable-", "-disable-").replace("-with-", "-without-"), + ) + + self.assertEqual(Option(env="FOO").option, "FOO") + + def test_option_choices(self): + with self.assertRaises(InvalidOptionError) as e: + Option("--option", nargs=3, choices=("a", "b")) + self.assertEqual(str(e.exception), "Not enough `choices` for `nargs`") + + with self.assertRaises(InvalidOptionError) as e: + Option("--without-option", nargs=1, choices=("a", "b")) + self.assertEqual( + str(e.exception), "A `default` must be given along with `choices`" + ) + + with self.assertRaises(InvalidOptionError) as e: + Option("--without-option", nargs="+", choices=("a", "b")) + self.assertEqual( + str(e.exception), "A `default` must be given along with `choices`" + ) + + with self.assertRaises(InvalidOptionError) as e: + Option("--without-option", default="c", choices=("a", "b")) + self.assertEqual( + str(e.exception), "The `default` value must be one of 'a', 'b'" + ) + + with self.assertRaises(InvalidOptionError) as e: + Option( + "--without-option", + default=( + "a", + "c", + ), + choices=("a", "b"), + ) + self.assertEqual( + str(e.exception), "The `default` value must be one of 'a', 'b'" + ) + + with self.assertRaises(InvalidOptionError) as e: + Option("--without-option", default=("c",), choices=("a", "b")) + self.assertEqual( + str(e.exception), "The `default` value must be one of 'a', 'b'" + ) + + option = Option("--with-option", nargs="+", choices=("a", "b")) + with self.assertRaises(InvalidOptionError) as e: + option.get_value("--with-option=c") + self.assertEqual(str(e.exception), "'c' is not one of 'a', 'b'") + + value = option.get_value("--with-option=b,a") + self.assertTrue(value) + self.assertEqual(PositiveOptionValue(("b", "a")), value) + + option = Option("--without-option", nargs="*", default="a", choices=("a", "b")) + with self.assertRaises(InvalidOptionError) as e: + option.get_value("--with-option=c") + self.assertEqual(str(e.exception), "'c' is not one of 'a', 'b'") + + value = option.get_value("--with-option=b,a") + self.assertTrue(value) + self.assertEqual(PositiveOptionValue(("b", "a")), value) + + # Test nargs inference from choices + option = Option("--with-option", choices=("a", "b")) + self.assertEqual(option.nargs, 1) + + # Test "relative" values + option = Option( + "--with-option", nargs="*", default=("b", "c"), choices=("a", "b", "c", "d") + ) + + value = option.get_value("--with-option=+d") + self.assertEqual(PositiveOptionValue(("b", "c", "d")), value) + + value = option.get_value("--with-option=-b") + self.assertEqual(PositiveOptionValue(("c",)), value) + + value = option.get_value("--with-option=-b,+d") + self.assertEqual(PositiveOptionValue(("c", "d")), value) + + # Adding something that is in the default is fine + value = option.get_value("--with-option=+b") + self.assertEqual(PositiveOptionValue(("b", "c")), value) + + # Removing something that is not in the default is fine, as long as it + # is one of the choices + value = option.get_value("--with-option=-a") + self.assertEqual(PositiveOptionValue(("b", "c")), value) + + with self.assertRaises(InvalidOptionError) as e: + option.get_value("--with-option=-e") + self.assertEqual(str(e.exception), "'e' is not one of 'a', 'b', 'c', 'd'") + + # Other "not a choice" errors. + with self.assertRaises(InvalidOptionError) as e: + option.get_value("--with-option=+e") + self.assertEqual(str(e.exception), "'e' is not one of 'a', 'b', 'c', 'd'") + + with self.assertRaises(InvalidOptionError) as e: + option.get_value("--with-option=e") + self.assertEqual(str(e.exception), "'e' is not one of 'a', 'b', 'c', 'd'") + + def test_option_value_compare(self): + # OptionValue are tuple and equivalence should compare as tuples. + val = PositiveOptionValue(("foo",)) + + self.assertEqual(val[0], "foo") + self.assertEqual(val, PositiveOptionValue(("foo",))) + self.assertNotEqual(val, PositiveOptionValue(("foo", "bar"))) + + # Can compare a tuple to an OptionValue. + self.assertEqual(val, ("foo",)) + self.assertNotEqual(val, ("foo", "bar")) + + # Different OptionValue types are never equal. + self.assertNotEqual(val, OptionValue(("foo",))) + + # For usability reasons, we raise TypeError when attempting to compare + # against a non-tuple. + with self.assertRaisesRegexp(TypeError, "cannot compare a"): + val == "foo" + + # But we allow empty option values to compare otherwise we can't + # easily compare value-less types like PositiveOptionValue and + # NegativeOptionValue. + empty_positive = PositiveOptionValue() + empty_negative = NegativeOptionValue() + self.assertEqual(empty_positive, ()) + self.assertEqual(empty_positive, PositiveOptionValue()) + self.assertEqual(empty_negative, ()) + self.assertEqual(empty_negative, NegativeOptionValue()) + self.assertNotEqual(empty_positive, "foo") + self.assertNotEqual(empty_positive, ("foo",)) + self.assertNotEqual(empty_negative, "foo") + self.assertNotEqual(empty_negative, ("foo",)) + + def test_option_value_format(self): + val = PositiveOptionValue() + self.assertEqual("--with-value", val.format("--with-value")) + self.assertEqual("--with-value", val.format("--without-value")) + self.assertEqual("--enable-value", val.format("--enable-value")) + self.assertEqual("--enable-value", val.format("--disable-value")) + self.assertEqual("--value", val.format("--value")) + self.assertEqual("VALUE=1", val.format("VALUE")) + + val = PositiveOptionValue(("a",)) + self.assertEqual("--with-value=a", val.format("--with-value")) + self.assertEqual("--with-value=a", val.format("--without-value")) + self.assertEqual("--enable-value=a", val.format("--enable-value")) + self.assertEqual("--enable-value=a", val.format("--disable-value")) + self.assertEqual("--value=a", val.format("--value")) + self.assertEqual("VALUE=a", val.format("VALUE")) + + val = PositiveOptionValue(("a", "b")) + self.assertEqual("--with-value=a,b", val.format("--with-value")) + self.assertEqual("--with-value=a,b", val.format("--without-value")) + self.assertEqual("--enable-value=a,b", val.format("--enable-value")) + self.assertEqual("--enable-value=a,b", val.format("--disable-value")) + self.assertEqual("--value=a,b", val.format("--value")) + self.assertEqual("VALUE=a,b", val.format("VALUE")) + + val = NegativeOptionValue() + self.assertEqual("--without-value", val.format("--with-value")) + self.assertEqual("--without-value", val.format("--without-value")) + self.assertEqual("--disable-value", val.format("--enable-value")) + self.assertEqual("--disable-value", val.format("--disable-value")) + self.assertEqual("", val.format("--value")) + self.assertEqual("VALUE=", val.format("VALUE")) + + def test_option_value(self, name="option", nargs=0, default=None): + disabled = name.startswith(("disable-", "without-")) + if disabled: + negOptionValue = PositiveOptionValue + posOptionValue = NegativeOptionValue + else: + posOptionValue = PositiveOptionValue + negOptionValue = NegativeOptionValue + defaultValue = PositiveOptionValue(default) if default else negOptionValue() + + option = Option("--%s" % name, nargs=nargs, default=default) + + if nargs in (0, "?", "*") or disabled: + value = option.get_value("--%s" % name, "option") + self.assertEqual(value, posOptionValue()) + self.assertEqual(value.origin, "option") + else: + with self.assertRaises(InvalidOptionError) as e: + option.get_value("--%s" % name) + if nargs == 1: + self.assertEqual(str(e.exception), "--%s takes 1 value" % name) + elif nargs == "+": + self.assertEqual(str(e.exception), "--%s takes 1 or more values" % name) + else: + self.assertEqual(str(e.exception), "--%s takes 2 values" % name) + + value = option.get_value("") + self.assertEqual(value, defaultValue) + self.assertEqual(value.origin, "default") + + value = option.get_value(None) + self.assertEqual(value, defaultValue) + self.assertEqual(value.origin, "default") + + with self.assertRaises(AssertionError): + value = option.get_value("MOZ_OPTION=", "environment") + + with self.assertRaises(AssertionError): + value = option.get_value("MOZ_OPTION=1", "environment") + + with self.assertRaises(AssertionError): + value = option.get_value("--foo") + + if nargs in (1, "?", "*", "+") and not disabled: + value = option.get_value("--%s=" % name, "option") + self.assertEqual(value, PositiveOptionValue(("",))) + self.assertEqual(value.origin, "option") + else: + with self.assertRaises(InvalidOptionError) as e: + option.get_value("--%s=" % name) + if disabled: + self.assertEqual(str(e.exception), "Cannot pass a value to --%s" % name) + else: + self.assertEqual( + str(e.exception), "--%s takes %d values" % (name, nargs) + ) + + if nargs in (1, "?", "*", "+") and not disabled: + value = option.get_value("--%s=foo" % name, "option") + self.assertEqual(value, PositiveOptionValue(("foo",))) + self.assertEqual(value.origin, "option") + else: + with self.assertRaises(InvalidOptionError) as e: + option.get_value("--%s=foo" % name) + if disabled: + self.assertEqual(str(e.exception), "Cannot pass a value to --%s" % name) + else: + self.assertEqual( + str(e.exception), "--%s takes %d values" % (name, nargs) + ) + + if nargs in (2, "*", "+") and not disabled: + value = option.get_value("--%s=foo,bar" % name, "option") + self.assertEqual(value, PositiveOptionValue(("foo", "bar"))) + self.assertEqual(value.origin, "option") + else: + with self.assertRaises(InvalidOptionError) as e: + option.get_value("--%s=foo,bar" % name, "option") + if disabled: + self.assertEqual(str(e.exception), "Cannot pass a value to --%s" % name) + elif nargs == "?": + self.assertEqual(str(e.exception), "--%s takes 0 or 1 values" % name) + else: + self.assertEqual( + str(e.exception), + "--%s takes %d value%s" % (name, nargs, "s" if nargs != 1 else ""), + ) + + option = Option("--%s" % name, env="MOZ_OPTION", nargs=nargs, default=default) + if nargs in (0, "?", "*") or disabled: + value = option.get_value("--%s" % name, "option") + self.assertEqual(value, posOptionValue()) + self.assertEqual(value.origin, "option") + else: + with self.assertRaises(InvalidOptionError) as e: + option.get_value("--%s" % name) + if disabled: + self.assertEqual(str(e.exception), "Cannot pass a value to --%s" % name) + elif nargs == "+": + self.assertEqual(str(e.exception), "--%s takes 1 or more values" % name) + else: + self.assertEqual( + str(e.exception), + "--%s takes %d value%s" % (name, nargs, "s" if nargs != 1 else ""), + ) + + value = option.get_value("") + self.assertEqual(value, defaultValue) + self.assertEqual(value.origin, "default") + + value = option.get_value(None) + self.assertEqual(value, defaultValue) + self.assertEqual(value.origin, "default") + + value = option.get_value("MOZ_OPTION=", "environment") + self.assertEqual(value, NegativeOptionValue()) + self.assertEqual(value.origin, "environment") + + if nargs in (0, "?", "*"): + value = option.get_value("MOZ_OPTION=1", "environment") + self.assertEqual(value, PositiveOptionValue()) + self.assertEqual(value.origin, "environment") + elif nargs in (1, "+"): + value = option.get_value("MOZ_OPTION=1", "environment") + self.assertEqual(value, PositiveOptionValue(("1",))) + self.assertEqual(value.origin, "environment") + else: + with self.assertRaises(InvalidOptionError) as e: + option.get_value("MOZ_OPTION=1", "environment") + self.assertEqual(str(e.exception), "MOZ_OPTION takes 2 values") + + if nargs in (1, "?", "*", "+") and not disabled: + value = option.get_value("--%s=" % name, "option") + self.assertEqual(value, PositiveOptionValue(("",))) + self.assertEqual(value.origin, "option") + else: + with self.assertRaises(InvalidOptionError) as e: + option.get_value("--%s=" % name, "option") + if disabled: + self.assertEqual(str(e.exception), "Cannot pass a value to --%s" % name) + else: + self.assertEqual( + str(e.exception), "--%s takes %d values" % (name, nargs) + ) + + with self.assertRaises(AssertionError): + value = option.get_value("--foo", "option") + + if nargs in (1, "?", "*", "+"): + value = option.get_value("MOZ_OPTION=foo", "environment") + self.assertEqual(value, PositiveOptionValue(("foo",))) + self.assertEqual(value.origin, "environment") + else: + with self.assertRaises(InvalidOptionError) as e: + option.get_value("MOZ_OPTION=foo", "environment") + self.assertEqual(str(e.exception), "MOZ_OPTION takes %d values" % nargs) + + if nargs in (2, "*", "+"): + value = option.get_value("MOZ_OPTION=foo,bar", "environment") + self.assertEqual(value, PositiveOptionValue(("foo", "bar"))) + self.assertEqual(value.origin, "environment") + else: + with self.assertRaises(InvalidOptionError) as e: + option.get_value("MOZ_OPTION=foo,bar", "environment") + if nargs == "?": + self.assertEqual(str(e.exception), "MOZ_OPTION takes 0 or 1 values") + else: + self.assertEqual( + str(e.exception), + "MOZ_OPTION takes %d value%s" % (nargs, "s" if nargs != 1 else ""), + ) + + if disabled: + return option + + env_option = Option(env="MOZ_OPTION", nargs=nargs, default=default) + with self.assertRaises(AssertionError): + env_option.get_value("--%s" % name) + + value = env_option.get_value("") + self.assertEqual(value, defaultValue) + self.assertEqual(value.origin, "default") + + value = env_option.get_value("MOZ_OPTION=", "environment") + self.assertEqual(value, negOptionValue()) + self.assertEqual(value.origin, "environment") + + if nargs in (0, "?", "*"): + value = env_option.get_value("MOZ_OPTION=1", "environment") + self.assertEqual(value, posOptionValue()) + self.assertTrue(value) + self.assertEqual(value.origin, "environment") + elif nargs in (1, "+"): + value = env_option.get_value("MOZ_OPTION=1", "environment") + self.assertEqual(value, PositiveOptionValue(("1",))) + self.assertEqual(value.origin, "environment") + else: + with self.assertRaises(InvalidOptionError) as e: + env_option.get_value("MOZ_OPTION=1", "environment") + self.assertEqual(str(e.exception), "MOZ_OPTION takes 2 values") + + with self.assertRaises(AssertionError) as e: + env_option.get_value("--%s" % name) + + with self.assertRaises(AssertionError) as e: + env_option.get_value("--foo") + + if nargs in (1, "?", "*", "+"): + value = env_option.get_value("MOZ_OPTION=foo", "environment") + self.assertEqual(value, PositiveOptionValue(("foo",))) + self.assertEqual(value.origin, "environment") + else: + with self.assertRaises(InvalidOptionError) as e: + env_option.get_value("MOZ_OPTION=foo", "environment") + self.assertEqual(str(e.exception), "MOZ_OPTION takes %d values" % nargs) + + if nargs in (2, "*", "+"): + value = env_option.get_value("MOZ_OPTION=foo,bar", "environment") + self.assertEqual(value, PositiveOptionValue(("foo", "bar"))) + self.assertEqual(value.origin, "environment") + else: + with self.assertRaises(InvalidOptionError) as e: + env_option.get_value("MOZ_OPTION=foo,bar", "environment") + if nargs == "?": + self.assertEqual(str(e.exception), "MOZ_OPTION takes 0 or 1 values") + else: + self.assertEqual( + str(e.exception), + "MOZ_OPTION takes %d value%s" % (nargs, "s" if nargs != 1 else ""), + ) + + return option + + def test_option_value_enable( + self, enable="enable", disable="disable", nargs=0, default=None + ): + option = self.test_option_value( + "%s-option" % enable, nargs=nargs, default=default + ) + + value = option.get_value("--%s-option" % disable, "option") + self.assertEqual(value, NegativeOptionValue()) + self.assertEqual(value.origin, "option") + + option = self.test_option_value( + "%s-option" % disable, nargs=nargs, default=default + ) + + if nargs in (0, "?", "*"): + value = option.get_value("--%s-option" % enable, "option") + self.assertEqual(value, PositiveOptionValue()) + self.assertEqual(value.origin, "option") + else: + with self.assertRaises(InvalidOptionError) as e: + option.get_value("--%s-option" % enable, "option") + if nargs == 1: + self.assertEqual(str(e.exception), "--%s-option takes 1 value" % enable) + elif nargs == "+": + self.assertEqual( + str(e.exception), "--%s-option takes 1 or more values" % enable + ) + else: + self.assertEqual( + str(e.exception), "--%s-option takes 2 values" % enable + ) + + def test_option_value_with(self): + self.test_option_value_enable("with", "without") + + def test_option_value_invalid_nargs(self): + with self.assertRaises(InvalidOptionError) as e: + Option("--option", nargs="foo") + self.assertEqual( + str(e.exception), "nargs must be a positive integer, '?', '*' or '+'" + ) + + with self.assertRaises(InvalidOptionError) as e: + Option("--option", nargs=-2) + self.assertEqual( + str(e.exception), "nargs must be a positive integer, '?', '*' or '+'" + ) + + def test_option_value_nargs_1(self): + self.test_option_value(nargs=1) + self.test_option_value(nargs=1, default=("a",)) + self.test_option_value_enable(nargs=1, default=("a",)) + + # A default is required + with self.assertRaises(InvalidOptionError) as e: + Option("--disable-option", nargs=1) + self.assertEqual( + str(e.exception), "The given `default` doesn't satisfy `nargs`" + ) + + def test_option_value_nargs_2(self): + self.test_option_value(nargs=2) + self.test_option_value(nargs=2, default=("a", "b")) + self.test_option_value_enable(nargs=2, default=("a", "b")) + + # A default is required + with self.assertRaises(InvalidOptionError) as e: + Option("--disable-option", nargs=2) + self.assertEqual( + str(e.exception), "The given `default` doesn't satisfy `nargs`" + ) + + def test_option_value_nargs_0_or_1(self): + self.test_option_value(nargs="?") + self.test_option_value(nargs="?", default=("a",)) + self.test_option_value_enable(nargs="?") + self.test_option_value_enable(nargs="?", default=("a",)) + + def test_option_value_nargs_0_or_more(self): + self.test_option_value(nargs="*") + self.test_option_value(nargs="*", default=("a",)) + self.test_option_value(nargs="*", default=("a", "b")) + self.test_option_value_enable(nargs="*") + self.test_option_value_enable(nargs="*", default=("a",)) + self.test_option_value_enable(nargs="*", default=("a", "b")) + + def test_option_value_nargs_1_or_more(self): + self.test_option_value(nargs="+") + self.test_option_value(nargs="+", default=("a",)) + self.test_option_value(nargs="+", default=("a", "b")) + self.test_option_value_enable(nargs="+", default=("a",)) + self.test_option_value_enable(nargs="+", default=("a", "b")) + + # A default is required + with self.assertRaises(InvalidOptionError) as e: + Option("--disable-option", nargs="+") + self.assertEqual( + str(e.exception), "The given `default` doesn't satisfy `nargs`" + ) + + +class TestCommandLineHelper(unittest.TestCase): + def test_basic(self): + helper = CommandLineHelper({}, ["cmd", "--foo", "--bar"]) + + self.assertEqual(["--foo", "--bar"], list(helper)) + + helper.add("--enable-qux") + + self.assertEqual(["--foo", "--bar", "--enable-qux"], list(helper)) + + value, option = helper.handle(Option("--bar")) + self.assertEqual(["--foo", "--enable-qux"], list(helper)) + self.assertEqual(PositiveOptionValue(), value) + self.assertEqual("--bar", option) + + value, option = helper.handle(Option("--baz")) + self.assertEqual(["--foo", "--enable-qux"], list(helper)) + self.assertEqual(NegativeOptionValue(), value) + self.assertEqual(None, option) + + with self.assertRaises(AssertionError): + CommandLineHelper({}, ["--foo", "--bar"]) + + def test_precedence(self): + foo = Option("--with-foo", nargs="*") + helper = CommandLineHelper({}, ["cmd", "--with-foo=a,b"]) + value, option = helper.handle(foo) + self.assertEqual(PositiveOptionValue(("a", "b")), value) + self.assertEqual("command-line", value.origin) + self.assertEqual("--with-foo=a,b", option) + + helper = CommandLineHelper({}, ["cmd", "--with-foo=a,b", "--without-foo"]) + value, option = helper.handle(foo) + self.assertEqual(NegativeOptionValue(), value) + self.assertEqual("command-line", value.origin) + self.assertEqual("--without-foo", option) + + helper = CommandLineHelper({}, ["cmd", "--without-foo", "--with-foo=a,b"]) + value, option = helper.handle(foo) + self.assertEqual(PositiveOptionValue(("a", "b")), value) + self.assertEqual("command-line", value.origin) + self.assertEqual("--with-foo=a,b", option) + + foo = Option("--with-foo", env="FOO", nargs="*") + helper = CommandLineHelper({"FOO": ""}, ["cmd", "--with-foo=a,b"]) + value, option = helper.handle(foo) + self.assertEqual(PositiveOptionValue(("a", "b")), value) + self.assertEqual("command-line", value.origin) + self.assertEqual("--with-foo=a,b", option) + + helper = CommandLineHelper({"FOO": "a,b"}, ["cmd", "--without-foo"]) + value, option = helper.handle(foo) + self.assertEqual(NegativeOptionValue(), value) + self.assertEqual("command-line", value.origin) + self.assertEqual("--without-foo", option) + + helper = CommandLineHelper({"FOO": ""}, ["cmd", "--with-bar=a,b"]) + value, option = helper.handle(foo) + self.assertEqual(NegativeOptionValue(), value) + self.assertEqual("environment", value.origin) + self.assertEqual("FOO=", option) + + helper = CommandLineHelper({"FOO": "a,b"}, ["cmd", "--without-bar"]) + value, option = helper.handle(foo) + self.assertEqual(PositiveOptionValue(("a", "b")), value) + self.assertEqual("environment", value.origin) + self.assertEqual("FOO=a,b", option) + + helper = CommandLineHelper({}, ["cmd", "--with-foo=a,b", "FOO="]) + value, option = helper.handle(foo) + self.assertEqual(NegativeOptionValue(), value) + self.assertEqual("command-line", value.origin) + self.assertEqual("FOO=", option) + + helper = CommandLineHelper({}, ["cmd", "--without-foo", "FOO=a,b"]) + value, option = helper.handle(foo) + self.assertEqual(PositiveOptionValue(("a", "b")), value) + self.assertEqual("command-line", value.origin) + self.assertEqual("FOO=a,b", option) + + helper = CommandLineHelper({}, ["cmd", "FOO=", "--with-foo=a,b"]) + value, option = helper.handle(foo) + self.assertEqual(PositiveOptionValue(("a", "b")), value) + self.assertEqual("command-line", value.origin) + self.assertEqual("--with-foo=a,b", option) + + helper = CommandLineHelper({}, ["cmd", "FOO=a,b", "--without-foo"]) + value, option = helper.handle(foo) + self.assertEqual(NegativeOptionValue(), value) + self.assertEqual("command-line", value.origin) + self.assertEqual("--without-foo", option) + + def test_extra_args(self): + foo = Option("--with-foo", env="FOO", nargs="*") + helper = CommandLineHelper({}, ["cmd"]) + helper.add("FOO=a,b,c", "other-origin") + value, option = helper.handle(foo) + self.assertEqual(PositiveOptionValue(("a", "b", "c")), value) + self.assertEqual("other-origin", value.origin) + self.assertEqual("FOO=a,b,c", option) + + helper = CommandLineHelper({}, ["cmd"]) + helper.add("FOO=a,b,c", "other-origin") + helper.add("--with-foo=a,b,c", "other-origin") + value, option = helper.handle(foo) + self.assertEqual(PositiveOptionValue(("a", "b", "c")), value) + self.assertEqual("other-origin", value.origin) + self.assertEqual("--with-foo=a,b,c", option) + + # Adding conflicting options is not allowed. + helper = CommandLineHelper({}, ["cmd"]) + helper.add("FOO=a,b,c", "other-origin") + with self.assertRaises(ConflictingOptionError) as cm: + helper.add("FOO=", "other-origin") + self.assertEqual("FOO=", cm.exception.arg) + self.assertEqual("other-origin", cm.exception.origin) + self.assertEqual("FOO=a,b,c", cm.exception.old_arg) + self.assertEqual("other-origin", cm.exception.old_origin) + with self.assertRaises(ConflictingOptionError) as cm: + helper.add("FOO=a,b", "other-origin") + self.assertEqual("FOO=a,b", cm.exception.arg) + self.assertEqual("other-origin", cm.exception.origin) + self.assertEqual("FOO=a,b,c", cm.exception.old_arg) + self.assertEqual("other-origin", cm.exception.old_origin) + # But adding the same is allowed. + helper.add("FOO=a,b,c", "other-origin") + value, option = helper.handle(foo) + self.assertEqual(PositiveOptionValue(("a", "b", "c")), value) + self.assertEqual("other-origin", value.origin) + self.assertEqual("FOO=a,b,c", option) + + # The same rule as above applies when using the option form vs. the + # variable form. But we can't detect it when .add is called. + helper = CommandLineHelper({}, ["cmd"]) + helper.add("FOO=a,b,c", "other-origin") + helper.add("--without-foo", "other-origin") + with self.assertRaises(ConflictingOptionError) as cm: + helper.handle(foo) + self.assertEqual("--without-foo", cm.exception.arg) + self.assertEqual("other-origin", cm.exception.origin) + self.assertEqual("FOO=a,b,c", cm.exception.old_arg) + self.assertEqual("other-origin", cm.exception.old_origin) + helper = CommandLineHelper({}, ["cmd"]) + helper.add("FOO=a,b,c", "other-origin") + helper.add("--with-foo=a,b", "other-origin") + with self.assertRaises(ConflictingOptionError) as cm: + helper.handle(foo) + self.assertEqual("--with-foo=a,b", cm.exception.arg) + self.assertEqual("other-origin", cm.exception.origin) + self.assertEqual("FOO=a,b,c", cm.exception.old_arg) + self.assertEqual("other-origin", cm.exception.old_origin) + helper = CommandLineHelper({}, ["cmd"]) + helper.add("FOO=a,b,c", "other-origin") + helper.add("--with-foo=a,b,c", "other-origin") + value, option = helper.handle(foo) + self.assertEqual(PositiveOptionValue(("a", "b", "c")), value) + self.assertEqual("other-origin", value.origin) + self.assertEqual("--with-foo=a,b,c", option) + + # Conflicts are also not allowed against what is in the + # environment/on the command line. + helper = CommandLineHelper({}, ["cmd", "--with-foo=a,b"]) + helper.add("FOO=a,b,c", "other-origin") + with self.assertRaises(ConflictingOptionError) as cm: + helper.handle(foo) + self.assertEqual("FOO=a,b,c", cm.exception.arg) + self.assertEqual("other-origin", cm.exception.origin) + self.assertEqual("--with-foo=a,b", cm.exception.old_arg) + self.assertEqual("command-line", cm.exception.old_origin) + + helper = CommandLineHelper({}, ["cmd", "--with-foo=a,b"]) + helper.add("--without-foo", "other-origin") + with self.assertRaises(ConflictingOptionError) as cm: + helper.handle(foo) + self.assertEqual("--without-foo", cm.exception.arg) + self.assertEqual("other-origin", cm.exception.origin) + self.assertEqual("--with-foo=a,b", cm.exception.old_arg) + self.assertEqual("command-line", cm.exception.old_origin) + + def test_possible_origins(self): + with self.assertRaises(InvalidOptionError): + Option("--foo", possible_origins="command-line") + + helper = CommandLineHelper({"BAZ": "1"}, ["cmd", "--foo", "--bar"]) + foo = Option("--foo", possible_origins=("command-line",)) + value, option = helper.handle(foo) + self.assertEqual(PositiveOptionValue(), value) + self.assertEqual("command-line", value.origin) + self.assertEqual("--foo", option) + + bar = Option("--bar", possible_origins=("mozconfig",)) + with self.assertRaisesRegexp( + InvalidOptionError, + "--bar can not be set by command-line. Values are accepted from: mozconfig", + ): + helper.handle(bar) + + baz = Option(env="BAZ", possible_origins=("implied",)) + with self.assertRaisesRegexp( + InvalidOptionError, + "BAZ=1 can not be set by environment. Values are accepted from: implied", + ): + helper.handle(baz) + + +if __name__ == "__main__": + main() |