diff options
Diffstat (limited to 'taskcluster/taskgraph/test/test_util_schema.py')
-rw-r--r-- | taskcluster/taskgraph/test/test_util_schema.py | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/taskcluster/taskgraph/test/test_util_schema.py b/taskcluster/taskgraph/test/test_util_schema.py new file mode 100644 index 0000000000..854f027d45 --- /dev/null +++ b/taskcluster/taskgraph/test/test_util_schema.py @@ -0,0 +1,206 @@ +# 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/. + +from __future__ import absolute_import, print_function, unicode_literals + +from six import text_type +import unittest +from mozunit import main +from taskgraph.util.schema import ( + validate_schema, + resolve_keyed_by, + Schema, +) + +schema = Schema( + { + "x": int, + "y": text_type, + } +) + + +class TestValidateSchema(unittest.TestCase): + def test_valid(self): + validate_schema(schema, {"x": 10, "y": "foo"}, "pfx") + + def test_invalid(self): + try: + validate_schema(schema, {"x": "not-int"}, "pfx") + self.fail("no exception raised") + except Exception as e: + self.assertTrue(str(e).startswith("pfx\n")) + + +class TestCheckSchema(unittest.TestCase): + def test_schema(self): + "Creating a schema applies taskgraph checks." + with self.assertRaises(Exception): + Schema({"camelCase": int}) + + def test_extend_schema(self): + "Extending a schema applies taskgraph checks." + with self.assertRaises(Exception): + Schema({"kebab-case": int}).extend({"camelCase": int}) + + def test_extend_schema_twice(self): + "Extending a schema twice applies taskgraph checks." + with self.assertRaises(Exception): + Schema({"kebab-case": int}).extend({"more-kebab": int}).extend( + {"camelCase": int} + ) + + +class TestResolveKeyedBy(unittest.TestCase): + def test_no_by(self): + self.assertEqual(resolve_keyed_by({"x": 10}, "z", "n"), {"x": 10}) + + def test_no_by_dotted(self): + self.assertEqual( + resolve_keyed_by({"x": {"y": 10}}, "x.z", "n"), {"x": {"y": 10}} + ) + + def test_no_by_not_dict(self): + self.assertEqual(resolve_keyed_by({"x": 10}, "x.y", "n"), {"x": 10}) + + def test_no_by_not_by(self): + self.assertEqual(resolve_keyed_by({"x": {"a": 10}}, "x", "n"), {"x": {"a": 10}}) + + def test_nested(self): + x = { + "by-foo": { + "F1": { + "by-bar": { + "B1": 11, + "B2": 12, + }, + }, + "F2": 20, + "default": 0, + }, + } + self.assertEqual( + resolve_keyed_by({"x": x}, "x", "x", foo="F1", bar="B1"), {"x": 11} + ) + self.assertEqual( + resolve_keyed_by({"x": x}, "x", "x", foo="F1", bar="B2"), {"x": 12} + ) + self.assertEqual(resolve_keyed_by({"x": x}, "x", "x", foo="F2"), {"x": 20}) + self.assertEqual( + resolve_keyed_by({"x": x}, "x", "x", foo="F99", bar="B1"), {"x": 0} + ) + + # bar is deferred + self.assertEqual( + resolve_keyed_by({"x": x}, "x", "x", defer=["bar"], foo="F1", bar="B1"), + {"x": {"by-bar": {"B1": 11, "B2": 12}}}, + ) + + def test_no_by_empty_dict(self): + self.assertEqual(resolve_keyed_by({"x": {}}, "x", "n"), {"x": {}}) + + def test_no_by_not_only_by(self): + self.assertEqual( + resolve_keyed_by({"x": {"by-y": True, "a": 10}}, "x", "n"), + {"x": {"by-y": True, "a": 10}}, + ) + + def test_match_nested_exact(self): + self.assertEqual( + resolve_keyed_by( + { + "f": "shoes", + "x": {"y": {"by-f": {"shoes": "feet", "gloves": "hands"}}}, + }, + "x.y", + "n", + ), + {"f": "shoes", "x": {"y": "feet"}}, + ) + + def test_match_regexp(self): + self.assertEqual( + resolve_keyed_by( + { + "f": "shoes", + "x": {"by-f": {"s?[hH]oes?": "feet", "gloves": "hands"}}, + }, + "x", + "n", + ), + {"f": "shoes", "x": "feet"}, + ) + + def test_match_partial_regexp(self): + self.assertEqual( + resolve_keyed_by( + {"f": "shoes", "x": {"by-f": {"sh": "feet", "default": "hands"}}}, + "x", + "n", + ), + {"f": "shoes", "x": "hands"}, + ) + + def test_match_default(self): + self.assertEqual( + resolve_keyed_by( + {"f": "shoes", "x": {"by-f": {"hat": "head", "default": "anywhere"}}}, + "x", + "n", + ), + {"f": "shoes", "x": "anywhere"}, + ) + + def test_match_extra_value(self): + self.assertEqual( + resolve_keyed_by({"f": {"by-foo": {"x": 10, "y": 20}}}, "f", "n", foo="y"), + {"f": 20}, + ) + + def test_no_match(self): + self.assertRaises( + Exception, + resolve_keyed_by, + {"f": "shoes", "x": {"by-f": {"hat": "head"}}}, + "x", + "n", + ) + + def test_multiple_matches(self): + self.assertRaises( + Exception, + resolve_keyed_by, + {"f": "hats", "x": {"by-f": {"hat.*": "head", "ha.*": "hair"}}}, + "x", + "n", + ) + + def test_no_key_no_default(self): + """ + When the key referenced in `by-*` doesn't exist, and there is not default value, + an exception is raised. + """ + self.assertRaises( + Exception, + resolve_keyed_by, + {"x": {"by-f": {"hat.*": "head", "ha.*": "hair"}}}, + "x", + "n", + ) + + def test_no_key(self): + """ + When the key referenced in `by-*` doesn't exist, and there is a default value, + that value is used as the result. + """ + self.assertEqual( + resolve_keyed_by( + {"x": {"by-f": {"hat": "head", "default": "anywhere"}}}, "x", "n" + ), + {"x": "anywhere"}, + ) + + +if __name__ == "__main__": + main() |