summaryrefslogtreecommitdiffstats
path: root/python/mach/mach/test/test_config.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/mach/mach/test/test_config.py')
-rw-r--r--python/mach/mach/test/test_config.py292
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()