diff options
Diffstat (limited to 'python/mach/mach/test/test_config.py')
-rw-r--r-- | python/mach/mach/test/test_config.py | 292 |
1 files changed, 292 insertions, 0 deletions
diff --git a/python/mach/mach/test/test_config.py b/python/mach/mach/test/test_config.py new file mode 100644 index 0000000000..25b75c8685 --- /dev/null +++ b/python/mach/mach/test/test_config.py @@ -0,0 +1,292 @@ +# 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 sys +import unittest +from pathlib import Path + +from mozfile.mozfile import NamedTemporaryFile +from mozunit import main +from six import string_types + +from mach.config import ( + BooleanType, + ConfigException, + ConfigSettings, + IntegerType, + PathType, + PositiveIntegerType, + StringType, +) +from mach.decorators import SettingsProvider + +CONFIG1 = r""" +[foo] + +bar = bar_value +baz = /baz/foo.c +""" + +CONFIG2 = r""" +[foo] + +bar = value2 +""" + + +@SettingsProvider +class Provider1(object): + config_settings = [ + ("foo.bar", StringType, "desc"), + ("foo.baz", PathType, "desc"), + ] + + +@SettingsProvider +class ProviderDuplicate(object): + config_settings = [ + ("dupesect.foo", StringType, "desc"), + ("dupesect.foo", StringType, "desc"), + ] + + +@SettingsProvider +class Provider2(object): + config_settings = [ + ("a.string", StringType, "desc"), + ("a.boolean", BooleanType, "desc"), + ("a.pos_int", PositiveIntegerType, "desc"), + ("a.int", IntegerType, "desc"), + ("a.path", PathType, "desc"), + ] + + +@SettingsProvider +class Provider3(object): + @classmethod + def config_settings(cls): + return [ + ("a.string", "string", "desc"), + ("a.boolean", "boolean", "desc"), + ("a.pos_int", "pos_int", "desc"), + ("a.int", "int", "desc"), + ("a.path", "path", "desc"), + ] + + +@SettingsProvider +class Provider4(object): + config_settings = [ + ("foo.abc", StringType, "desc", "a", {"choices": set("abc")}), + ("foo.xyz", StringType, "desc", "w", {"choices": set("xyz")}), + ] + + +@SettingsProvider +class Provider5(object): + config_settings = [ + ("foo.*", "string", "desc"), + ("foo.bar", "string", "desc"), + ] + + +class TestConfigSettings(unittest.TestCase): + def test_empty(self): + s = ConfigSettings() + + self.assertEqual(len(s), 0) + self.assertNotIn("foo", s) + + def test_duplicate_option(self): + s = ConfigSettings() + + with self.assertRaises(ConfigException): + s.register_provider(ProviderDuplicate) + + def test_simple(self): + s = ConfigSettings() + s.register_provider(Provider1) + + self.assertEqual(len(s), 1) + self.assertIn("foo", s) + + foo = s["foo"] + foo = s.foo + + self.assertEqual(len(foo), 0) + self.assertEqual(len(foo._settings), 2) + + self.assertIn("bar", foo._settings) + self.assertIn("baz", foo._settings) + + self.assertNotIn("bar", foo) + foo["bar"] = "value1" + self.assertIn("bar", foo) + + self.assertEqual(foo["bar"], "value1") + self.assertEqual(foo.bar, "value1") + + def test_assignment_validation(self): + s = ConfigSettings() + s.register_provider(Provider2) + + a = s.a + + # Assigning an undeclared setting raises. + exc_type = AttributeError if sys.version_info < (3, 0) else KeyError + with self.assertRaises(exc_type): + a.undefined = True + + with self.assertRaises(KeyError): + a["undefined"] = True + + # Basic type validation. + a.string = "foo" + a.string = "foo" + + with self.assertRaises(TypeError): + a.string = False + + a.boolean = True + a.boolean = False + + with self.assertRaises(TypeError): + a.boolean = "foo" + + a.pos_int = 5 + a.pos_int = 0 + + with self.assertRaises(ValueError): + a.pos_int = -1 + + with self.assertRaises(TypeError): + a.pos_int = "foo" + + a.int = 5 + a.int = 0 + a.int = -5 + + with self.assertRaises(TypeError): + a.int = 1.24 + + with self.assertRaises(TypeError): + a.int = "foo" + + a.path = "/home/gps" + a.path = "foo.c" + a.path = "foo/bar" + a.path = "./foo" + + def retrieval_type_helper(self, provider): + s = ConfigSettings() + s.register_provider(provider) + + a = s.a + + a.string = "foo" + a.boolean = True + a.pos_int = 12 + a.int = -4 + a.path = "./foo/bar" + + self.assertIsInstance(a.string, string_types) + self.assertIsInstance(a.boolean, bool) + self.assertIsInstance(a.pos_int, int) + self.assertIsInstance(a.int, int) + self.assertIsInstance(a.path, string_types) + + def test_retrieval_type(self): + self.retrieval_type_helper(Provider2) + self.retrieval_type_helper(Provider3) + + def test_choices_validation(self): + s = ConfigSettings() + s.register_provider(Provider4) + + foo = s.foo + foo.abc + with self.assertRaises(ValueError): + foo.xyz + + with self.assertRaises(ValueError): + foo.abc = "e" + + foo.abc = "b" + foo.xyz = "y" + + def test_wildcard_options(self): + s = ConfigSettings() + s.register_provider(Provider5) + + foo = s.foo + + self.assertIn("*", foo._settings) + self.assertNotIn("*", foo) + + foo.baz = "value1" + foo.bar = "value2" + + self.assertIn("baz", foo) + self.assertEqual(foo.baz, "value1") + + self.assertIn("bar", foo) + self.assertEqual(foo.bar, "value2") + + def test_file_reading_single(self): + temp = NamedTemporaryFile(mode="wt") + temp.write(CONFIG1) + temp.flush() + + s = ConfigSettings() + s.register_provider(Provider1) + + s.load_file(Path(temp.name)) + + self.assertEqual(s.foo.bar, "bar_value") + + def test_file_reading_multiple(self): + """Loading multiple files has proper overwrite behavior.""" + temp1 = NamedTemporaryFile(mode="wt") + temp1.write(CONFIG1) + temp1.flush() + + temp2 = NamedTemporaryFile(mode="wt") + temp2.write(CONFIG2) + temp2.flush() + + s = ConfigSettings() + s.register_provider(Provider1) + + s.load_files([Path(temp1.name), Path(temp2.name)]) + + self.assertEqual(s.foo.bar, "value2") + + def test_file_reading_missing(self): + """Missing files should silently be ignored.""" + + s = ConfigSettings() + + s.load_file("/tmp/foo.ini") + + def test_file_writing(self): + s = ConfigSettings() + s.register_provider(Provider2) + + s.a.string = "foo" + s.a.boolean = False + + temp = NamedTemporaryFile("wt") + s.write(temp) + temp.flush() + + s2 = ConfigSettings() + s2.register_provider(Provider2) + + s2.load_file(temp.name) + + self.assertEqual(s.a.string, s2.a.string) + self.assertEqual(s.a.boolean, s2.a.boolean) + + +if __name__ == "__main__": + main() |