summaryrefslogtreecommitdiffstats
path: root/python/mozbuild/mozbuild/test/frontend/test_context.py
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /python/mozbuild/mozbuild/test/frontend/test_context.py
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'python/mozbuild/mozbuild/test/frontend/test_context.py')
-rw-r--r--python/mozbuild/mozbuild/test/frontend/test_context.py736
1 files changed, 736 insertions, 0 deletions
diff --git a/python/mozbuild/mozbuild/test/frontend/test_context.py b/python/mozbuild/mozbuild/test/frontend/test_context.py
new file mode 100644
index 0000000000..fbf35e1c8c
--- /dev/null
+++ b/python/mozbuild/mozbuild/test/frontend/test_context.py
@@ -0,0 +1,736 @@
+# 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 os
+import unittest
+
+import six
+from mozpack import path as mozpath
+from mozunit import main
+
+from mozbuild.frontend.context import (
+ FUNCTIONS,
+ SPECIAL_VARIABLES,
+ SUBCONTEXTS,
+ VARIABLES,
+ AbsolutePath,
+ Context,
+ ContextDerivedTypedHierarchicalStringList,
+ ContextDerivedTypedList,
+ ContextDerivedTypedListWithItems,
+ ContextDerivedTypedRecord,
+ Files,
+ ObjDirPath,
+ Path,
+ SourcePath,
+)
+from mozbuild.util import StrictOrderingOnAppendListWithFlagsFactory
+
+
+class TestContext(unittest.TestCase):
+ def test_defaults(self):
+ test = Context(
+ {
+ "foo": (int, int, ""),
+ "bar": (bool, bool, ""),
+ "baz": (dict, dict, ""),
+ }
+ )
+
+ self.assertEqual(list(test), [])
+
+ self.assertEqual(test["foo"], 0)
+
+ self.assertEqual(set(test.keys()), {"foo"})
+
+ self.assertEqual(test["bar"], False)
+
+ self.assertEqual(set(test.keys()), {"foo", "bar"})
+
+ self.assertEqual(test["baz"], {})
+
+ self.assertEqual(set(test.keys()), {"foo", "bar", "baz"})
+
+ with self.assertRaises(KeyError):
+ test["qux"]
+
+ self.assertEqual(set(test.keys()), {"foo", "bar", "baz"})
+
+ def test_type_check(self):
+ test = Context(
+ {
+ "foo": (int, int, ""),
+ "baz": (dict, list, ""),
+ }
+ )
+
+ test["foo"] = 5
+
+ self.assertEqual(test["foo"], 5)
+
+ with self.assertRaises(ValueError):
+ test["foo"] = {}
+
+ self.assertEqual(test["foo"], 5)
+
+ with self.assertRaises(KeyError):
+ test["bar"] = True
+
+ test["baz"] = [("a", 1), ("b", 2)]
+
+ self.assertEqual(test["baz"], {"a": 1, "b": 2})
+
+ def test_update(self):
+ test = Context(
+ {
+ "foo": (int, int, ""),
+ "bar": (bool, bool, ""),
+ "baz": (dict, list, ""),
+ }
+ )
+
+ self.assertEqual(list(test), [])
+
+ with self.assertRaises(ValueError):
+ test.update(bar=True, foo={})
+
+ self.assertEqual(list(test), [])
+
+ test.update(bar=True, foo=1)
+
+ self.assertEqual(set(test.keys()), {"foo", "bar"})
+ self.assertEqual(test["foo"], 1)
+ self.assertEqual(test["bar"], True)
+
+ test.update([("bar", False), ("foo", 2)])
+ self.assertEqual(test["foo"], 2)
+ self.assertEqual(test["bar"], False)
+
+ test.update([("foo", 0), ("baz", {"a": 1, "b": 2})])
+ self.assertEqual(test["foo"], 0)
+ self.assertEqual(test["baz"], {"a": 1, "b": 2})
+
+ test.update([("foo", 42), ("baz", [("c", 3), ("d", 4)])])
+ self.assertEqual(test["foo"], 42)
+ self.assertEqual(test["baz"], {"c": 3, "d": 4})
+
+ def test_context_paths(self):
+ test = Context()
+
+ # Newly created context has no paths.
+ self.assertIsNone(test.main_path)
+ self.assertIsNone(test.current_path)
+ self.assertEqual(test.all_paths, set())
+ self.assertEqual(test.source_stack, [])
+
+ foo = os.path.abspath("foo")
+ test.add_source(foo)
+
+ # Adding the first source makes it the main and current path.
+ self.assertEqual(test.main_path, foo)
+ self.assertEqual(test.current_path, foo)
+ self.assertEqual(test.all_paths, set([foo]))
+ self.assertEqual(test.source_stack, [foo])
+
+ bar = os.path.abspath("bar")
+ test.add_source(bar)
+
+ # Adding the second source makes leaves main and current paths alone.
+ self.assertEqual(test.main_path, foo)
+ self.assertEqual(test.current_path, foo)
+ self.assertEqual(test.all_paths, set([bar, foo]))
+ self.assertEqual(test.source_stack, [foo])
+
+ qux = os.path.abspath("qux")
+ test.push_source(qux)
+
+ # Pushing a source makes it the current path
+ self.assertEqual(test.main_path, foo)
+ self.assertEqual(test.current_path, qux)
+ self.assertEqual(test.all_paths, set([bar, foo, qux]))
+ self.assertEqual(test.source_stack, [foo, qux])
+
+ hoge = os.path.abspath("hoge")
+ test.push_source(hoge)
+ self.assertEqual(test.main_path, foo)
+ self.assertEqual(test.current_path, hoge)
+ self.assertEqual(test.all_paths, set([bar, foo, hoge, qux]))
+ self.assertEqual(test.source_stack, [foo, qux, hoge])
+
+ fuga = os.path.abspath("fuga")
+
+ # Adding a source after pushing doesn't change the source stack
+ test.add_source(fuga)
+ self.assertEqual(test.main_path, foo)
+ self.assertEqual(test.current_path, hoge)
+ self.assertEqual(test.all_paths, set([bar, foo, fuga, hoge, qux]))
+ self.assertEqual(test.source_stack, [foo, qux, hoge])
+
+ # Adding a source twice doesn't change anything
+ test.add_source(qux)
+ self.assertEqual(test.main_path, foo)
+ self.assertEqual(test.current_path, hoge)
+ self.assertEqual(test.all_paths, set([bar, foo, fuga, hoge, qux]))
+ self.assertEqual(test.source_stack, [foo, qux, hoge])
+
+ last = test.pop_source()
+
+ # Popping a source returns the last pushed one, not the last added one.
+ self.assertEqual(last, hoge)
+ self.assertEqual(test.main_path, foo)
+ self.assertEqual(test.current_path, qux)
+ self.assertEqual(test.all_paths, set([bar, foo, fuga, hoge, qux]))
+ self.assertEqual(test.source_stack, [foo, qux])
+
+ last = test.pop_source()
+ self.assertEqual(last, qux)
+ self.assertEqual(test.main_path, foo)
+ self.assertEqual(test.current_path, foo)
+ self.assertEqual(test.all_paths, set([bar, foo, fuga, hoge, qux]))
+ self.assertEqual(test.source_stack, [foo])
+
+ # Popping the main path is allowed.
+ last = test.pop_source()
+ self.assertEqual(last, foo)
+ self.assertEqual(test.main_path, foo)
+ self.assertIsNone(test.current_path)
+ self.assertEqual(test.all_paths, set([bar, foo, fuga, hoge, qux]))
+ self.assertEqual(test.source_stack, [])
+
+ # Popping past the main path asserts.
+ with self.assertRaises(AssertionError):
+ test.pop_source()
+
+ # Pushing after the main path was popped asserts.
+ with self.assertRaises(AssertionError):
+ test.push_source(foo)
+
+ test = Context()
+ test.push_source(foo)
+ test.push_source(bar)
+
+ # Pushing the same file twice is allowed.
+ test.push_source(bar)
+ test.push_source(foo)
+ self.assertEqual(last, foo)
+ self.assertEqual(test.main_path, foo)
+ self.assertEqual(test.current_path, foo)
+ self.assertEqual(test.all_paths, set([bar, foo]))
+ self.assertEqual(test.source_stack, [foo, bar, bar, foo])
+
+ def test_context_dirs(self):
+ class Config(object):
+ pass
+
+ config = Config()
+ config.topsrcdir = mozpath.abspath(os.curdir)
+ config.topobjdir = mozpath.abspath("obj")
+ test = Context(config=config)
+ foo = mozpath.abspath("foo")
+ test.push_source(foo)
+
+ self.assertEqual(test.srcdir, config.topsrcdir)
+ self.assertEqual(test.relsrcdir, "")
+ self.assertEqual(test.objdir, config.topobjdir)
+ self.assertEqual(test.relobjdir, "")
+
+ foobar = os.path.abspath("foo/bar")
+ test.push_source(foobar)
+ self.assertEqual(test.srcdir, mozpath.join(config.topsrcdir, "foo"))
+ self.assertEqual(test.relsrcdir, "foo")
+ self.assertEqual(test.objdir, config.topobjdir)
+ self.assertEqual(test.relobjdir, "")
+
+
+class TestSymbols(unittest.TestCase):
+ def _verify_doc(self, doc):
+ # Documentation should be of the format:
+ # """SUMMARY LINE
+ #
+ # EXTRA PARAGRAPHS
+ # """
+
+ self.assertNotIn("\r", doc)
+
+ lines = doc.split("\n")
+
+ # No trailing whitespace.
+ for line in lines[0:-1]:
+ self.assertEqual(line, line.rstrip())
+
+ self.assertGreater(len(lines), 0)
+ self.assertGreater(len(lines[0].strip()), 0)
+
+ # Last line should be empty.
+ self.assertEqual(lines[-1].strip(), "")
+
+ def test_documentation_formatting(self):
+ for typ, inp, doc in VARIABLES.values():
+ self._verify_doc(doc)
+
+ for attr, args, doc in FUNCTIONS.values():
+ self._verify_doc(doc)
+
+ for func, typ, doc in SPECIAL_VARIABLES.values():
+ self._verify_doc(doc)
+
+ for name, cls in SUBCONTEXTS.items():
+ self._verify_doc(cls.__doc__)
+
+ for name, v in cls.VARIABLES.items():
+ self._verify_doc(v[2])
+
+
+class TestPaths(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ class Config(object):
+ pass
+
+ cls.config = config = Config()
+ config.topsrcdir = mozpath.abspath(os.curdir)
+ config.topobjdir = mozpath.abspath("obj")
+
+ def test_path(self):
+ config = self.config
+ ctxt1 = Context(config=config)
+ ctxt1.push_source(mozpath.join(config.topsrcdir, "foo", "moz.build"))
+ ctxt2 = Context(config=config)
+ ctxt2.push_source(mozpath.join(config.topsrcdir, "bar", "moz.build"))
+
+ path1 = Path(ctxt1, "qux")
+ self.assertIsInstance(path1, SourcePath)
+ self.assertEqual(path1, "qux")
+ self.assertEqual(path1.full_path, mozpath.join(config.topsrcdir, "foo", "qux"))
+
+ path2 = Path(ctxt2, "../foo/qux")
+ self.assertIsInstance(path2, SourcePath)
+ self.assertEqual(path2, "../foo/qux")
+ self.assertEqual(path2.full_path, mozpath.join(config.topsrcdir, "foo", "qux"))
+
+ self.assertEqual(path1, path2)
+
+ self.assertEqual(
+ path1.join("../../bar/qux").full_path,
+ mozpath.join(config.topsrcdir, "bar", "qux"),
+ )
+
+ path1 = Path(ctxt1, "/qux/qux")
+ self.assertIsInstance(path1, SourcePath)
+ self.assertEqual(path1, "/qux/qux")
+ self.assertEqual(path1.full_path, mozpath.join(config.topsrcdir, "qux", "qux"))
+
+ path2 = Path(ctxt2, "/qux/qux")
+ self.assertIsInstance(path2, SourcePath)
+ self.assertEqual(path2, "/qux/qux")
+ self.assertEqual(path2.full_path, mozpath.join(config.topsrcdir, "qux", "qux"))
+
+ self.assertEqual(path1, path2)
+
+ path1 = Path(ctxt1, "!qux")
+ self.assertIsInstance(path1, ObjDirPath)
+ self.assertEqual(path1, "!qux")
+ self.assertEqual(path1.full_path, mozpath.join(config.topobjdir, "foo", "qux"))
+
+ path2 = Path(ctxt2, "!../foo/qux")
+ self.assertIsInstance(path2, ObjDirPath)
+ self.assertEqual(path2, "!../foo/qux")
+ self.assertEqual(path2.full_path, mozpath.join(config.topobjdir, "foo", "qux"))
+
+ self.assertEqual(path1, path2)
+
+ path1 = Path(ctxt1, "!/qux/qux")
+ self.assertIsInstance(path1, ObjDirPath)
+ self.assertEqual(path1, "!/qux/qux")
+ self.assertEqual(path1.full_path, mozpath.join(config.topobjdir, "qux", "qux"))
+
+ path2 = Path(ctxt2, "!/qux/qux")
+ self.assertIsInstance(path2, ObjDirPath)
+ self.assertEqual(path2, "!/qux/qux")
+ self.assertEqual(path2.full_path, mozpath.join(config.topobjdir, "qux", "qux"))
+
+ self.assertEqual(path1, path2)
+
+ path1 = Path(ctxt1, path1)
+ self.assertIsInstance(path1, ObjDirPath)
+ self.assertEqual(path1, "!/qux/qux")
+ self.assertEqual(path1.full_path, mozpath.join(config.topobjdir, "qux", "qux"))
+
+ path2 = Path(ctxt2, path2)
+ self.assertIsInstance(path2, ObjDirPath)
+ self.assertEqual(path2, "!/qux/qux")
+ self.assertEqual(path2.full_path, mozpath.join(config.topobjdir, "qux", "qux"))
+
+ self.assertEqual(path1, path2)
+
+ path1 = Path(path1)
+ self.assertIsInstance(path1, ObjDirPath)
+ self.assertEqual(path1, "!/qux/qux")
+ self.assertEqual(path1.full_path, mozpath.join(config.topobjdir, "qux", "qux"))
+
+ self.assertEqual(path1, path2)
+
+ path2 = Path(path2)
+ self.assertIsInstance(path2, ObjDirPath)
+ self.assertEqual(path2, "!/qux/qux")
+ self.assertEqual(path2.full_path, mozpath.join(config.topobjdir, "qux", "qux"))
+
+ self.assertEqual(path1, path2)
+
+ def test_source_path(self):
+ config = self.config
+ ctxt = Context(config=config)
+ ctxt.push_source(mozpath.join(config.topsrcdir, "foo", "moz.build"))
+
+ path = SourcePath(ctxt, "qux")
+ self.assertEqual(path, "qux")
+ self.assertEqual(path.full_path, mozpath.join(config.topsrcdir, "foo", "qux"))
+ self.assertEqual(path.translated, mozpath.join(config.topobjdir, "foo", "qux"))
+
+ path = SourcePath(ctxt, "../bar/qux")
+ self.assertEqual(path, "../bar/qux")
+ self.assertEqual(path.full_path, mozpath.join(config.topsrcdir, "bar", "qux"))
+ self.assertEqual(path.translated, mozpath.join(config.topobjdir, "bar", "qux"))
+
+ path = SourcePath(ctxt, "/qux/qux")
+ self.assertEqual(path, "/qux/qux")
+ self.assertEqual(path.full_path, mozpath.join(config.topsrcdir, "qux", "qux"))
+ self.assertEqual(path.translated, mozpath.join(config.topobjdir, "qux", "qux"))
+
+ with self.assertRaises(ValueError):
+ SourcePath(ctxt, "!../bar/qux")
+
+ with self.assertRaises(ValueError):
+ SourcePath(ctxt, "!/qux/qux")
+
+ path = SourcePath(path)
+ self.assertIsInstance(path, SourcePath)
+ self.assertEqual(path, "/qux/qux")
+ self.assertEqual(path.full_path, mozpath.join(config.topsrcdir, "qux", "qux"))
+ self.assertEqual(path.translated, mozpath.join(config.topobjdir, "qux", "qux"))
+
+ path = Path(path)
+ self.assertIsInstance(path, SourcePath)
+
+ def test_objdir_path(self):
+ config = self.config
+ ctxt = Context(config=config)
+ ctxt.push_source(mozpath.join(config.topsrcdir, "foo", "moz.build"))
+
+ path = ObjDirPath(ctxt, "!qux")
+ self.assertEqual(path, "!qux")
+ self.assertEqual(path.full_path, mozpath.join(config.topobjdir, "foo", "qux"))
+
+ path = ObjDirPath(ctxt, "!../bar/qux")
+ self.assertEqual(path, "!../bar/qux")
+ self.assertEqual(path.full_path, mozpath.join(config.topobjdir, "bar", "qux"))
+
+ path = ObjDirPath(ctxt, "!/qux/qux")
+ self.assertEqual(path, "!/qux/qux")
+ self.assertEqual(path.full_path, mozpath.join(config.topobjdir, "qux", "qux"))
+
+ with self.assertRaises(ValueError):
+ path = ObjDirPath(ctxt, "../bar/qux")
+
+ with self.assertRaises(ValueError):
+ path = ObjDirPath(ctxt, "/qux/qux")
+
+ path = ObjDirPath(path)
+ self.assertIsInstance(path, ObjDirPath)
+ self.assertEqual(path, "!/qux/qux")
+ self.assertEqual(path.full_path, mozpath.join(config.topobjdir, "qux", "qux"))
+
+ path = Path(path)
+ self.assertIsInstance(path, ObjDirPath)
+
+ def test_absolute_path(self):
+ config = self.config
+ ctxt = Context(config=config)
+ ctxt.push_source(mozpath.join(config.topsrcdir, "foo", "moz.build"))
+
+ path = AbsolutePath(ctxt, "%/qux")
+ self.assertEqual(path, "%/qux")
+ self.assertEqual(path.full_path, "/qux")
+
+ with self.assertRaises(ValueError):
+ path = AbsolutePath(ctxt, "%qux")
+
+ def test_path_with_mixed_contexts(self):
+ config = self.config
+ ctxt1 = Context(config=config)
+ ctxt1.push_source(mozpath.join(config.topsrcdir, "foo", "moz.build"))
+ ctxt2 = Context(config=config)
+ ctxt2.push_source(mozpath.join(config.topsrcdir, "bar", "moz.build"))
+
+ path1 = Path(ctxt1, "qux")
+ path2 = Path(ctxt2, path1)
+ self.assertEqual(path2, path1)
+ self.assertEqual(path2, "qux")
+ self.assertEqual(path2.context, ctxt1)
+ self.assertEqual(path2.full_path, mozpath.join(config.topsrcdir, "foo", "qux"))
+
+ path1 = Path(ctxt1, "../bar/qux")
+ path2 = Path(ctxt2, path1)
+ self.assertEqual(path2, path1)
+ self.assertEqual(path2, "../bar/qux")
+ self.assertEqual(path2.context, ctxt1)
+ self.assertEqual(path2.full_path, mozpath.join(config.topsrcdir, "bar", "qux"))
+
+ path1 = Path(ctxt1, "/qux/qux")
+ path2 = Path(ctxt2, path1)
+ self.assertEqual(path2, path1)
+ self.assertEqual(path2, "/qux/qux")
+ self.assertEqual(path2.context, ctxt1)
+ self.assertEqual(path2.full_path, mozpath.join(config.topsrcdir, "qux", "qux"))
+
+ path1 = Path(ctxt1, "!qux")
+ path2 = Path(ctxt2, path1)
+ self.assertEqual(path2, path1)
+ self.assertEqual(path2, "!qux")
+ self.assertEqual(path2.context, ctxt1)
+ self.assertEqual(path2.full_path, mozpath.join(config.topobjdir, "foo", "qux"))
+
+ path1 = Path(ctxt1, "!../bar/qux")
+ path2 = Path(ctxt2, path1)
+ self.assertEqual(path2, path1)
+ self.assertEqual(path2, "!../bar/qux")
+ self.assertEqual(path2.context, ctxt1)
+ self.assertEqual(path2.full_path, mozpath.join(config.topobjdir, "bar", "qux"))
+
+ path1 = Path(ctxt1, "!/qux/qux")
+ path2 = Path(ctxt2, path1)
+ self.assertEqual(path2, path1)
+ self.assertEqual(path2, "!/qux/qux")
+ self.assertEqual(path2.context, ctxt1)
+ self.assertEqual(path2.full_path, mozpath.join(config.topobjdir, "qux", "qux"))
+
+ def test_path_typed_list(self):
+ config = self.config
+ ctxt1 = Context(config=config)
+ ctxt1.push_source(mozpath.join(config.topsrcdir, "foo", "moz.build"))
+ ctxt2 = Context(config=config)
+ ctxt2.push_source(mozpath.join(config.topsrcdir, "bar", "moz.build"))
+
+ paths = [
+ "!../bar/qux",
+ "!/qux/qux",
+ "!qux",
+ "../bar/qux",
+ "/qux/qux",
+ "qux",
+ ]
+
+ MyList = ContextDerivedTypedList(Path)
+ l = MyList(ctxt1)
+ l += paths
+
+ for p_str, p_path in zip(paths, l):
+ self.assertEqual(p_str, p_path)
+ self.assertEqual(p_path, Path(ctxt1, p_str))
+ self.assertEqual(
+ p_path.join("foo"), Path(ctxt1, mozpath.join(p_str, "foo"))
+ )
+
+ l2 = MyList(ctxt2)
+ l2 += paths
+
+ for p_str, p_path in zip(paths, l2):
+ self.assertEqual(p_str, p_path)
+ self.assertEqual(p_path, Path(ctxt2, p_str))
+
+ # Assigning with Paths from another context doesn't rebase them
+ l2 = MyList(ctxt2)
+ l2 += l
+
+ for p_str, p_path in zip(paths, l2):
+ self.assertEqual(p_str, p_path)
+ self.assertEqual(p_path, Path(ctxt1, p_str))
+
+ MyListWithFlags = ContextDerivedTypedListWithItems(
+ Path,
+ StrictOrderingOnAppendListWithFlagsFactory(
+ {
+ "foo": bool,
+ }
+ ),
+ )
+ l = MyListWithFlags(ctxt1)
+ l += paths
+
+ for p in paths:
+ l[p].foo = True
+
+ for p_str, p_path in zip(paths, l):
+ self.assertEqual(p_str, p_path)
+ self.assertEqual(p_path, Path(ctxt1, p_str))
+ self.assertEqual(l[p_str].foo, True)
+ self.assertEqual(l[p_path].foo, True)
+
+ def test_path_typed_hierarchy_list(self):
+ config = self.config
+ ctxt1 = Context(config=config)
+ ctxt1.push_source(mozpath.join(config.topsrcdir, "foo", "moz.build"))
+ ctxt2 = Context(config=config)
+ ctxt2.push_source(mozpath.join(config.topsrcdir, "bar", "moz.build"))
+
+ paths = [
+ "!../bar/qux",
+ "!/qux/qux",
+ "!qux",
+ "../bar/qux",
+ "/qux/qux",
+ "qux",
+ ]
+
+ MyList = ContextDerivedTypedHierarchicalStringList(Path)
+ l = MyList(ctxt1)
+ l += paths
+ l.subdir += paths
+
+ for _, files in l.walk():
+ for p_str, p_path in zip(paths, files):
+ self.assertEqual(p_str, p_path)
+ self.assertEqual(p_path, Path(ctxt1, p_str))
+ self.assertEqual(
+ p_path.join("foo"), Path(ctxt1, mozpath.join(p_str, "foo"))
+ )
+
+ l2 = MyList(ctxt2)
+ l2 += paths
+ l2.subdir += paths
+
+ for _, files in l2.walk():
+ for p_str, p_path in zip(paths, files):
+ self.assertEqual(p_str, p_path)
+ self.assertEqual(p_path, Path(ctxt2, p_str))
+
+ # Assigning with Paths from another context doesn't rebase them
+ l2 = MyList(ctxt2)
+ l2 += l
+
+ for _, files in l2.walk():
+ for p_str, p_path in zip(paths, files):
+ self.assertEqual(p_str, p_path)
+ self.assertEqual(p_path, Path(ctxt1, p_str))
+
+
+class TestTypedRecord(unittest.TestCase):
+ def test_fields(self):
+ T = ContextDerivedTypedRecord(("field1", six.text_type), ("field2", list))
+ inst = T(None)
+ self.assertEqual(inst.field1, "")
+ self.assertEqual(inst.field2, [])
+
+ inst.field1 = "foo"
+ inst.field2 += ["bar"]
+
+ self.assertEqual(inst.field1, "foo")
+ self.assertEqual(inst.field2, ["bar"])
+
+ with self.assertRaises(AttributeError):
+ inst.field3 = []
+
+ def test_coercion(self):
+ T = ContextDerivedTypedRecord(("field1", six.text_type), ("field2", list))
+ inst = T(None)
+ inst.field1 = 3
+ inst.field2 += ("bar",)
+ self.assertEqual(inst.field1, "3")
+ self.assertEqual(inst.field2, ["bar"])
+
+ with self.assertRaises(TypeError):
+ inst.field2 = object()
+
+
+class TestFiles(unittest.TestCase):
+ def test_aggregate_empty(self):
+ c = Context({})
+
+ files = {"moz.build": Files(c, "**")}
+
+ self.assertEqual(
+ Files.aggregate(files),
+ {
+ "bug_component_counts": [],
+ "recommended_bug_component": None,
+ },
+ )
+
+ def test_single_bug_component(self):
+ c = Context({})
+ f = Files(c, "**")
+ f["BUG_COMPONENT"] = ("Product1", "Component1")
+
+ files = {"moz.build": f}
+ self.assertEqual(
+ Files.aggregate(files),
+ {
+ "bug_component_counts": [(("Product1", "Component1"), 1)],
+ "recommended_bug_component": ("Product1", "Component1"),
+ },
+ )
+
+ def test_multiple_bug_components(self):
+ c = Context({})
+ f1 = Files(c, "**")
+ f1["BUG_COMPONENT"] = ("Product1", "Component1")
+
+ f2 = Files(c, "**")
+ f2["BUG_COMPONENT"] = ("Product2", "Component2")
+
+ files = {"a": f1, "b": f2, "c": f1}
+ self.assertEqual(
+ Files.aggregate(files),
+ {
+ "bug_component_counts": [
+ (("Product1", "Component1"), 2),
+ (("Product2", "Component2"), 1),
+ ],
+ "recommended_bug_component": ("Product1", "Component1"),
+ },
+ )
+
+ def test_no_recommended_bug_component(self):
+ """If there is no clear count winner, we don't recommend a bug component."""
+ c = Context({})
+ f1 = Files(c, "**")
+ f1["BUG_COMPONENT"] = ("Product1", "Component1")
+
+ f2 = Files(c, "**")
+ f2["BUG_COMPONENT"] = ("Product2", "Component2")
+
+ files = {"a": f1, "b": f2}
+ self.assertEqual(
+ Files.aggregate(files),
+ {
+ "bug_component_counts": [
+ (("Product1", "Component1"), 1),
+ (("Product2", "Component2"), 1),
+ ],
+ "recommended_bug_component": None,
+ },
+ )
+
+ def test_multiple_patterns(self):
+ c = Context({})
+ f1 = Files(c, "a/**")
+ f1["BUG_COMPONENT"] = ("Product1", "Component1")
+ f2 = Files(c, "b/**", "a/bar")
+ f2["BUG_COMPONENT"] = ("Product2", "Component2")
+
+ files = {"a/foo": f1, "a/bar": f2, "b/foo": f2}
+ self.assertEqual(
+ Files.aggregate(files),
+ {
+ "bug_component_counts": [
+ (("Product2", "Component2"), 2),
+ (("Product1", "Component1"), 1),
+ ],
+ "recommended_bug_component": ("Product2", "Component2"),
+ },
+ )
+
+
+if __name__ == "__main__":
+ main()