Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

# 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()