summaryrefslogtreecommitdiffstats
path: root/taskcluster/taskgraph/test/test_util_schema.py
diff options
context:
space:
mode:
Diffstat (limited to 'taskcluster/taskgraph/test/test_util_schema.py')
-rw-r--r--taskcluster/taskgraph/test/test_util_schema.py206
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()