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 mozpack.path as mozpath
from mozunit import main
from mozbuild import schedules
from mozbuild.frontend.context import BugzillaComponent
from mozbuild.frontend.reader import BuildReader, BuildReaderError
from mozbuild.test.common import MockConfig
data_path = mozpath.abspath(mozpath.dirname(__file__))
data_path = mozpath.join(data_path, "data")
class TestBuildReader(unittest.TestCase):
def setUp(self):
self._old_env = dict(os.environ)
os.environ.pop("MOZ_OBJDIR", None)
def tearDown(self):
os.environ.clear()
os.environ.update(self._old_env)
def config(self, name, **kwargs):
path = mozpath.join(data_path, name)
return MockConfig(path, **kwargs)
def reader(self, name, enable_tests=False, error_is_fatal=True, **kwargs):
extra = {}
if enable_tests:
extra["ENABLE_TESTS"] = "1"
config = self.config(name, extra_substs=extra, error_is_fatal=error_is_fatal)
return BuildReader(config, **kwargs)
def file_path(self, name, *args):
return mozpath.join(data_path, name, *args)
def test_dirs_traversal_simple(self):
reader = self.reader("traversal-simple")
contexts = list(reader.read_topsrcdir())
self.assertEqual(len(contexts), 4)
def test_dirs_traversal_no_descend(self):
reader = self.reader("traversal-simple")
path = mozpath.join(reader.config.topsrcdir, "moz.build")
self.assertTrue(os.path.exists(path))
contexts = list(reader.read_mozbuild(path, reader.config, descend=False))
self.assertEqual(len(contexts), 1)
def test_dirs_traversal_all_variables(self):
reader = self.reader("traversal-all-vars")
contexts = list(reader.read_topsrcdir())
self.assertEqual(len(contexts), 2)
reader = self.reader("traversal-all-vars", enable_tests=True)
contexts = list(reader.read_topsrcdir())
self.assertEqual(len(contexts), 3)
def test_relative_dirs(self):
# Ensure relative directories are traversed.
reader = self.reader("traversal-relative-dirs")
contexts = list(reader.read_topsrcdir())
self.assertEqual(len(contexts), 3)
def test_repeated_dirs_error(self):
reader = self.reader("traversal-repeated-dirs")
with self.assertRaises(BuildReaderError) as bre:
list(reader.read_topsrcdir())
e = bre.exception
self.assertEqual(
e.actual_file, self.file_path("traversal-repeated-dirs", "bar", "moz.build")
)
self.assertIn(
"File already read. A directory should not be added to DIRS twice: foo/moz.build is referred from moz.build as 'foo', and bar/moz.build as '../foo'",
str(e),
)
def test_outside_topsrcdir(self):
# References to directories outside the topsrcdir should fail.
reader = self.reader("traversal-outside-topsrcdir")
with self.assertRaises(Exception):
list(reader.read_topsrcdir())
def test_error_basic(self):
reader = self.reader("reader-error-basic")
with self.assertRaises(BuildReaderError) as bre:
list(reader.read_topsrcdir())
e = bre.exception
self.assertEqual(
e.actual_file, self.file_path("reader-error-basic", "moz.build")
)
self.assertIn("The error occurred while processing the", str(e))
def test_error_included_from(self):
reader = self.reader("reader-error-included-from")
with self.assertRaises(BuildReaderError) as bre:
list(reader.read_topsrcdir())
e = bre.exception
self.assertEqual(
e.actual_file, self.file_path("reader-error-included-from", "child.build")
)
self.assertEqual(
e.main_file, self.file_path("reader-error-included-from", "moz.build")
)
self.assertIn("This file was included as part of processing", str(e))
def test_error_syntax_error(self):
reader = self.reader("reader-error-syntax")
with self.assertRaises(BuildReaderError) as bre:
list(reader.read_topsrcdir())
e = bre.exception
self.assertIn("Python syntax error on line 5", str(e))
self.assertIn(" foo =", str(e))
self.assertIn(" ^", str(e))
def test_error_read_unknown_global(self):
reader = self.reader("reader-error-read-unknown-global")
with self.assertRaises(BuildReaderError) as bre:
list(reader.read_topsrcdir())
e = bre.exception
self.assertIn("The error was triggered on line 5", str(e))
self.assertIn("The underlying problem is an attempt to read", str(e))
self.assertIn(" FOO", str(e))
def test_error_write_unknown_global(self):
reader = self.reader("reader-error-write-unknown-global")
with self.assertRaises(BuildReaderError) as bre:
list(reader.read_topsrcdir())
e = bre.exception
self.assertIn("The error was triggered on line 7", str(e))
self.assertIn("The underlying problem is an attempt to write", str(e))
self.assertIn(" FOO", str(e))
def test_error_write_bad_value(self):
reader = self.reader("reader-error-write-bad-value")
with self.assertRaises(BuildReaderError) as bre:
list(reader.read_topsrcdir())
e = bre.exception
self.assertIn("The error was triggered on line 5", str(e))
self.assertIn("is an attempt to write an illegal value to a special", str(e))
self.assertIn("variable whose value was rejected is:\n\n DIRS", str(e))
self.assertIn(
"written to it was of the following type:\n\n %s" % "str", str(e)
)
self.assertIn("expects the following type(s):\n\n list", str(e))
def test_error_illegal_path(self):
reader = self.reader("reader-error-outside-topsrcdir")
with self.assertRaises(BuildReaderError) as bre:
list(reader.read_topsrcdir())
e = bre.exception
self.assertIn("The underlying problem is an illegal file access", str(e))
def test_error_missing_include_path(self):
reader = self.reader("reader-error-missing-include")
with self.assertRaises(BuildReaderError) as bre:
list(reader.read_topsrcdir())
e = bre.exception
self.assertIn("we referenced a path that does not exist", str(e))
def test_error_script_error(self):
reader = self.reader("reader-error-script-error")
with self.assertRaises(BuildReaderError) as bre:
list(reader.read_topsrcdir())
e = bre.exception
self.assertIn("The error appears to be the fault of the script", str(e))
self.assertIn(' ["TypeError: unsupported operand', str(e))
def test_error_bad_dir(self):
reader = self.reader("reader-error-bad-dir")
with self.assertRaises(BuildReaderError) as bre:
list(reader.read_topsrcdir())
e = bre.exception
self.assertIn("we referenced a path that does not exist", str(e))
def test_error_repeated_dir(self):
reader = self.reader("reader-error-repeated-dir")
with self.assertRaises(BuildReaderError) as bre:
list(reader.read_topsrcdir())
e = bre.exception
self.assertIn("Directory (foo) registered multiple times", str(e))
def test_error_error_func(self):
reader = self.reader("reader-error-error-func")
with self.assertRaises(BuildReaderError) as bre:
list(reader.read_topsrcdir())
e = bre.exception
self.assertIn("A moz.build file called the error() function.", str(e))
self.assertIn(" Some error.", str(e))
def test_error_error_func_ok(self):
reader = self.reader("reader-error-error-func", error_is_fatal=False)
list(reader.read_topsrcdir())
def test_error_empty_list(self):
reader = self.reader("reader-error-empty-list")
with self.assertRaises(BuildReaderError) as bre:
list(reader.read_topsrcdir())
e = bre.exception
self.assertIn("Variable DIRS assigned an empty value.", str(e))
def test_inheriting_variables(self):
reader = self.reader("inheriting-variables")
contexts = list(reader.read_topsrcdir())
self.assertEqual(len(contexts), 4)
self.assertEqual(
[context.relsrcdir for context in contexts], ["", "foo", "foo/baz", "bar"]
)
self.assertEqual(
[context["XPIDL_MODULE"] for context in contexts],
["foobar", "foobar", "baz", "foobar"],
)
def test_find_relevant_mozbuilds(self):
reader = self.reader("reader-relevant-mozbuild")
# Absolute paths outside topsrcdir are rejected.
with self.assertRaises(Exception):
reader._find_relevant_mozbuilds(["/foo"])
# File in root directory.
paths = reader._find_relevant_mozbuilds(["file"])
self.assertEqual(paths, {"file": ["moz.build"]})
# File in child directory.
paths = reader._find_relevant_mozbuilds(["d1/file1"])
self.assertEqual(paths, {"d1/file1": ["moz.build", "d1/moz.build"]})
# Multiple files in same directory.
paths = reader._find_relevant_mozbuilds(["d1/file1", "d1/file2"])
self.assertEqual(
paths,
{
"d1/file1": ["moz.build", "d1/moz.build"],
"d1/file2": ["moz.build", "d1/moz.build"],
},
)
# Missing moz.build from missing intermediate directory.
paths = reader._find_relevant_mozbuilds(
["d1/no-intermediate-moz-build/child/file"]
)
self.assertEqual(
paths,
{
"d1/no-intermediate-moz-build/child/file": [
"moz.build",
"d1/moz.build",
"d1/no-intermediate-moz-build/child/moz.build",
]
},
)
# Lots of empty directories.
paths = reader._find_relevant_mozbuilds(
["d1/parent-is-far/dir1/dir2/dir3/file"]
)
self.assertEqual(
paths,
{
"d1/parent-is-far/dir1/dir2/dir3/file": [
"moz.build",
"d1/moz.build",
"d1/parent-is-far/moz.build",
]
},
)
# Lots of levels.
paths = reader._find_relevant_mozbuilds(
["d1/every-level/a/file", "d1/every-level/b/file"]
)
self.assertEqual(
paths,
{
"d1/every-level/a/file": [
"moz.build",
"d1/moz.build",
"d1/every-level/moz.build",
"d1/every-level/a/moz.build",
],
"d1/every-level/b/file": [
"moz.build",
"d1/moz.build",
"d1/every-level/moz.build",
"d1/every-level/b/moz.build",
],
},
)
# Different root directories.
paths = reader._find_relevant_mozbuilds(["d1/file", "d2/file", "file"])
self.assertEqual(
paths,
{
"file": ["moz.build"],
"d1/file": ["moz.build", "d1/moz.build"],
"d2/file": ["moz.build", "d2/moz.build"],
},
)
def test_read_relevant_mozbuilds(self):
reader = self.reader("reader-relevant-mozbuild")
paths, contexts = reader.read_relevant_mozbuilds(
["d1/every-level/a/file", "d1/every-level/b/file", "d2/file"]
)
self.assertEqual(len(paths), 3)
self.assertEqual(len(contexts), 6)
self.assertEqual(
[ctx.relsrcdir for ctx in paths["d1/every-level/a/file"]],
["", "d1", "d1/every-level", "d1/every-level/a"],
)
self.assertEqual(
[ctx.relsrcdir for ctx in paths["d1/every-level/b/file"]],
["", "d1", "d1/every-level", "d1/every-level/b"],
)
self.assertEqual([ctx.relsrcdir for ctx in paths["d2/file"]], ["", "d2"])
def test_all_mozbuild_paths(self):
reader = self.reader("reader-relevant-mozbuild")
paths = list(reader.all_mozbuild_paths())
# Ensure no duplicate paths.
self.assertEqual(sorted(paths), sorted(set(paths)))
self.assertEqual(len(paths), 10)
def test_files_bad_bug_component(self):
reader = self.reader("files-info")
with self.assertRaises(BuildReaderError):
reader.files_info(["bug_component/bad-assignment/moz.build"])
def test_files_bug_component_static(self):
reader = self.reader("files-info")
v = reader.files_info(
[
"bug_component/static/foo",
"bug_component/static/bar",
"bug_component/static/foo/baz",
]
)
self.assertEqual(len(v), 3)
self.assertEqual(
v["bug_component/static/foo"]["BUG_COMPONENT"],
BugzillaComponent("FooProduct", "FooComponent"),
)
self.assertEqual(
v["bug_component/static/bar"]["BUG_COMPONENT"],
BugzillaComponent("BarProduct", "BarComponent"),
)
self.assertEqual(
v["bug_component/static/foo/baz"]["BUG_COMPONENT"],
BugzillaComponent("default_product", "default_component"),
)
def test_files_bug_component_simple(self):
reader = self.reader("files-info")
v = reader.files_info(["bug_component/simple/moz.build"])
self.assertEqual(len(v), 1)
flags = v["bug_component/simple/moz.build"]
self.assertEqual(flags["BUG_COMPONENT"].product, "Firefox Build System")
self.assertEqual(flags["BUG_COMPONENT"].component, "General")
def test_files_bug_component_different_matchers(self):
reader = self.reader("files-info")
v = reader.files_info(
[
"bug_component/different-matchers/foo.jsm",
"bug_component/different-matchers/bar.cpp",
"bug_component/different-matchers/baz.misc",
]
)
self.assertEqual(len(v), 3)
js_flags = v["bug_component/different-matchers/foo.jsm"]
cpp_flags = v["bug_component/different-matchers/bar.cpp"]
misc_flags = v["bug_component/different-matchers/baz.misc"]
self.assertEqual(js_flags["BUG_COMPONENT"], BugzillaComponent("Firefox", "JS"))
self.assertEqual(
cpp_flags["BUG_COMPONENT"], BugzillaComponent("Firefox", "C++")
)
self.assertEqual(
misc_flags["BUG_COMPONENT"],
BugzillaComponent("default_product", "default_component"),
)
def test_files_bug_component_final(self):
reader = self.reader("files-info")
v = reader.files_info(
[
"bug_component/final/foo",
"bug_component/final/Makefile.in",
"bug_component/final/subcomponent/Makefile.in",
"bug_component/final/subcomponent/bar",
]
)
self.assertEqual(
v["bug_component/final/foo"]["BUG_COMPONENT"],
BugzillaComponent("default_product", "default_component"),
)
self.assertEqual(
v["bug_component/final/Makefile.in"]["BUG_COMPONENT"],
BugzillaComponent("Firefox Build System", "General"),
)
self.assertEqual(
v["bug_component/final/subcomponent/Makefile.in"]["BUG_COMPONENT"],
BugzillaComponent("Firefox Build System", "General"),
)
self.assertEqual(
v["bug_component/final/subcomponent/bar"]["BUG_COMPONENT"],
BugzillaComponent("Another", "Component"),
)
def test_invalid_flavor(self):
reader = self.reader("invalid-files-flavor")
with self.assertRaises(BuildReaderError):
reader.files_info(["foo.js"])
def test_schedules(self):
reader = self.reader("schedules")
info = reader.files_info(
[
"win.and.osx",
"somefile",
"foo.win",
"foo.osx",
"subd/aa.py",
"subd/yaml.py",
"subd/win.js",
]
)
# default: all exclusive, no inclusive
self.assertEqual(info["somefile"]["SCHEDULES"].inclusive, [])
self.assertEqual(
info["somefile"]["SCHEDULES"].exclusive, schedules.EXCLUSIVE_COMPONENTS
)
# windows-only
self.assertEqual(info["foo.win"]["SCHEDULES"].inclusive, [])
self.assertEqual(info["foo.win"]["SCHEDULES"].exclusive, ["windows"])
# osx-only
self.assertEqual(info["foo.osx"]["SCHEDULES"].inclusive, [])
self.assertEqual(info["foo.osx"]["SCHEDULES"].exclusive, ["macosx"])
# top-level moz.build specifies subd/**.py with an inclusive option
self.assertEqual(info["subd/aa.py"]["SCHEDULES"].inclusive, ["py-lint"])
self.assertEqual(
info["subd/aa.py"]["SCHEDULES"].exclusive, schedules.EXCLUSIVE_COMPONENTS
)
# Files('yaml.py') in subd/moz.build combines with Files('subdir/**.py')
self.assertEqual(
info["subd/yaml.py"]["SCHEDULES"].inclusive, ["py-lint", "yaml-lint"]
)
self.assertEqual(
info["subd/yaml.py"]["SCHEDULES"].exclusive, schedules.EXCLUSIVE_COMPONENTS
)
# .. but exlusive does not override inclusive
self.assertEqual(info["subd/win.js"]["SCHEDULES"].inclusive, ["js-lint"])
self.assertEqual(info["subd/win.js"]["SCHEDULES"].exclusive, ["windows"])
self.assertEqual(
set(info["subd/yaml.py"]["SCHEDULES"].components),
set(schedules.EXCLUSIVE_COMPONENTS + ["py-lint", "yaml-lint"]),
)
# win.and.osx is defined explicitly, and matches *.osx, and the two have
# conflicting SCHEDULES.exclusive settings, so the later one is used
self.assertEqual(
set(info["win.and.osx"]["SCHEDULES"].exclusive), set(["macosx", "windows"])
)
def test_hook(self):
extra = {
"MOZ_BUILD_HOOK": mozpath.abspath(self.file_path("mozbuild.hook")),
}
config = self.config(
"traversal-simple", extra_substs=extra, error_is_fatal=True
)
reader = BuildReader(config)
contexts = {ctx.relsrcdir: ctx for ctx in reader.read_topsrcdir()}
self.assertEqual(len(contexts), 4)
self.assertEqual(contexts[""]["DEFINES"], {"ALL": True})
self.assertEqual(
contexts["foo"]["DEFINES"], {"ALL": True, "FOO": True, "FOO_ONLY": True}
)
self.assertEqual(contexts["foo/biz"]["DEFINES"], {"ALL": True, "FOO": True})
self.assertEqual(contexts["bar"]["DEFINES"], {"ALL": True, "BAR_ONLY": True})
if __name__ == "__main__":
main()