Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
- Manifest: python/mozbuild/mozpack/test/python.toml
# 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
import os
import unittest
import mozunit
from buildconfig import topobjdir
from mozunit import MockedOpen
import mozpack.path as mozpath
from mozbuild.preprocessor import Preprocessor
from mozpack.chrome.manifest import (
ManifestBinaryComponent,
ManifestContent,
ManifestResource,
)
from mozpack.errors import ErrorMessage, errors
from mozpack.files import GeneratedFile
from mozpack.packager import (
CallDeque,
Component,
SimpleManifestSink,
SimplePackager,
preprocess_manifest,
)
MANIFEST = """
bar/*
[foo]
foo/*
-foo/bar
chrome.manifest
[zot destdir="destdir"]
foo/zot
; comment
#ifdef baz
[baz]
baz@SUFFIX@
#endif
"""
class TestPreprocessManifest(unittest.TestCase):
MANIFEST_PATH = mozpath.join("$OBJDIR", "manifest")
EXPECTED_LOG = [
((MANIFEST_PATH, 2), "add", "", "bar/*"),
((MANIFEST_PATH, 4), "add", "foo", "foo/*"),
((MANIFEST_PATH, 5), "remove", "foo", "foo/bar"),
((MANIFEST_PATH, 6), "add", "foo", "chrome.manifest"),
((MANIFEST_PATH, 8), "add", 'zot destdir="destdir"', "foo/zot"),
]
def setUp(self):
class MockSink(object):
def __init__(self):
self.log = []
def add(self, component, path):
self._log(errors.get_context(), "add", repr(component), path)
def remove(self, component, path):
self._log(errors.get_context(), "remove", repr(component), path)
def _log(self, *args):
self.log.append(args)
self.sink = MockSink()
self.cwd = os.getcwd()
os.chdir(topobjdir)
def tearDown(self):
os.chdir(self.cwd)
def test_preprocess_manifest(self):
with MockedOpen({"manifest": MANIFEST}):
preprocess_manifest(self.sink, "manifest")
self.assertEqual(self.sink.log, self.EXPECTED_LOG)
def test_preprocess_manifest_missing_define(self):
with MockedOpen({"manifest": MANIFEST}):
self.assertRaises(
Preprocessor.Error,
preprocess_manifest,
self.sink,
"manifest",
{"baz": 1},
)
def test_preprocess_manifest_defines(self):
with MockedOpen({"manifest": MANIFEST}):
preprocess_manifest(self.sink, "manifest", {"baz": 1, "SUFFIX": ".exe"})
self.assertEqual(
self.sink.log,
self.EXPECTED_LOG + [((self.MANIFEST_PATH, 12), "add", "baz", "baz.exe")],
)
class MockFinder(object):
def __init__(self, files):
self.files = files
self.log = []
def find(self, path):
self.log.append(path)
for f in sorted(self.files):
if mozpath.match(f, path):
yield f, self.files[f]
def __iter__(self):
return self.find("")
class MockFormatter(object):
def __init__(self):
self.log = []
def add_base(self, *args):
self._log(errors.get_context(), "add_base", *args)
def add_manifest(self, *args):
self._log(errors.get_context(), "add_manifest", *args)
def add_interfaces(self, *args):
self._log(errors.get_context(), "add_interfaces", *args)
def add(self, *args):
self._log(errors.get_context(), "add", *args)
def _log(self, *args):
self.log.append(args)
class TestSimplePackager(unittest.TestCase):
def test_simple_packager(self):
class GeneratedFileWithPath(GeneratedFile):
def __init__(self, path, content):
GeneratedFile.__init__(self, content)
self.path = path
formatter = MockFormatter()
packager = SimplePackager(formatter)
curdir = os.path.abspath(os.curdir)
file = GeneratedFileWithPath(
os.path.join(curdir, "foo", "bar.manifest"),
b"resource bar bar/\ncontent bar bar/",
)
with errors.context("manifest", 1):
packager.add("foo/bar.manifest", file)
file = GeneratedFileWithPath(
os.path.join(curdir, "foo", "baz.manifest"), b"resource baz baz/"
)
with errors.context("manifest", 2):
packager.add("bar/baz.manifest", file)
with errors.context("manifest", 3):
packager.add(
"qux/qux.manifest",
GeneratedFile(
b"".join(
[
b"resource qux qux/\n",
b"binary-component qux.so\n",
]
)
),
)
bar_xpt = GeneratedFile(b"bar.xpt")
qux_xpt = GeneratedFile(b"qux.xpt")
foo_html = GeneratedFile(b"foo_html")
bar_html = GeneratedFile(b"bar_html")
with errors.context("manifest", 4):
packager.add("foo/bar.xpt", bar_xpt)
with errors.context("manifest", 5):
packager.add("foo/bar/foo.html", foo_html)
packager.add("foo/bar/bar.html", bar_html)
file = GeneratedFileWithPath(
os.path.join(curdir, "foo.manifest"),
b"".join(
[
b"manifest foo/bar.manifest\n",
b"manifest bar/baz.manifest\n",
]
),
)
with errors.context("manifest", 6):
packager.add("foo.manifest", file)
with errors.context("manifest", 7):
packager.add("foo/qux.xpt", qux_xpt)
file = GeneratedFileWithPath(
os.path.join(curdir, "addon", "chrome.manifest"), b"resource hoge hoge/"
)
with errors.context("manifest", 8):
packager.add("addon/chrome.manifest", file)
install_rdf = GeneratedFile(b"<RDF></RDF>")
with errors.context("manifest", 9):
packager.add("addon/install.rdf", install_rdf)
with errors.context("manifest", 10):
packager.add("addon2/install.rdf", install_rdf)
packager.add(
"addon2/chrome.manifest", GeneratedFile(b"binary-component addon2.so")
)
with errors.context("manifest", 11):
packager.add("addon3/install.rdf", install_rdf)
packager.add(
"addon3/chrome.manifest",
GeneratedFile(b"manifest components/components.manifest"),
)
packager.add(
"addon3/components/components.manifest",
GeneratedFile(b"binary-component addon3.so"),
)
with errors.context("manifest", 12):
install_rdf_addon4 = GeneratedFile(
b"<RDF>\n<...>\n<em:unpack>true</em:unpack>\n<...>\n</RDF>"
)
packager.add("addon4/install.rdf", install_rdf_addon4)
with errors.context("manifest", 13):
install_rdf_addon5 = GeneratedFile(
b"<RDF>\n<...>\n<em:unpack>false</em:unpack>\n<...>\n</RDF>"
)
packager.add("addon5/install.rdf", install_rdf_addon5)
with errors.context("manifest", 14):
install_rdf_addon6 = GeneratedFile(
b"<RDF>\n<... em:unpack=true>\n<...>\n</RDF>"
)
packager.add("addon6/install.rdf", install_rdf_addon6)
with errors.context("manifest", 15):
install_rdf_addon7 = GeneratedFile(
b"<RDF>\n<... em:unpack=false>\n<...>\n</RDF>"
)
packager.add("addon7/install.rdf", install_rdf_addon7)
with errors.context("manifest", 16):
install_rdf_addon8 = GeneratedFile(
b'<RDF>\n<... em:unpack="true">\n<...>\n</RDF>'
)
packager.add("addon8/install.rdf", install_rdf_addon8)
with errors.context("manifest", 17):
install_rdf_addon9 = GeneratedFile(
b'<RDF>\n<... em:unpack="false">\n<...>\n</RDF>'
)
packager.add("addon9/install.rdf", install_rdf_addon9)
with errors.context("manifest", 18):
install_rdf_addon10 = GeneratedFile(
b"<RDF>\n<... em:unpack='true'>\n<...>\n</RDF>"
)
packager.add("addon10/install.rdf", install_rdf_addon10)
with errors.context("manifest", 19):
install_rdf_addon11 = GeneratedFile(
b"<RDF>\n<... em:unpack='false'>\n<...>\n</RDF>"
)
packager.add("addon11/install.rdf", install_rdf_addon11)
we_manifest = GeneratedFile(
b'{"manifest_version": 2, "name": "Test WebExtension", "version": "1.0"}'
)
# hybrid and hybrid2 are both bootstrapped extensions with
# embedded webextensions, they differ in the order in which
# the manifests are added to the packager.
with errors.context("manifest", 20):
packager.add("hybrid/install.rdf", install_rdf)
with errors.context("manifest", 21):
packager.add("hybrid/webextension/manifest.json", we_manifest)
with errors.context("manifest", 22):
packager.add("hybrid2/webextension/manifest.json", we_manifest)
with errors.context("manifest", 23):
packager.add("hybrid2/install.rdf", install_rdf)
with errors.context("manifest", 24):
packager.add("webextension/manifest.json", we_manifest)
non_we_manifest = GeneratedFile(b'{"not a webextension": true}')
with errors.context("manifest", 25):
packager.add("nonwebextension/manifest.json", non_we_manifest)
self.assertEqual(formatter.log, [])
with errors.context("dummy", 1):
packager.close()
self.maxDiff = None
# The formatter is expected to reorder the manifest entries so that
# chrome entries appear before the others.
self.assertEqual(
formatter.log,
[
(("dummy", 1), "add_base", "", False),
(("dummy", 1), "add_base", "addon", True),
(("dummy", 1), "add_base", "addon10", "unpacked"),
(("dummy", 1), "add_base", "addon11", True),
(("dummy", 1), "add_base", "addon2", "unpacked"),
(("dummy", 1), "add_base", "addon3", "unpacked"),
(("dummy", 1), "add_base", "addon4", "unpacked"),
(("dummy", 1), "add_base", "addon5", True),
(("dummy", 1), "add_base", "addon6", "unpacked"),
(("dummy", 1), "add_base", "addon7", True),
(("dummy", 1), "add_base", "addon8", "unpacked"),
(("dummy", 1), "add_base", "addon9", True),
(("dummy", 1), "add_base", "hybrid", True),
(("dummy", 1), "add_base", "hybrid2", True),
(("dummy", 1), "add_base", "qux", False),
(("dummy", 1), "add_base", "webextension", True),
(
(os.path.join(curdir, "foo", "bar.manifest"), 2),
"add_manifest",
ManifestContent("foo", "bar", "bar/"),
),
(
(os.path.join(curdir, "foo", "bar.manifest"), 1),
"add_manifest",
ManifestResource("foo", "bar", "bar/"),
),
(
("bar/baz.manifest", 1),
"add_manifest",
ManifestResource("bar", "baz", "baz/"),
),
(
("qux/qux.manifest", 1),
"add_manifest",
ManifestResource("qux", "qux", "qux/"),
),
(
("qux/qux.manifest", 2),
"add_manifest",
ManifestBinaryComponent("qux", "qux.so"),
),
(("manifest", 4), "add_interfaces", "foo/bar.xpt", bar_xpt),
(("manifest", 7), "add_interfaces", "foo/qux.xpt", qux_xpt),
(
(os.path.join(curdir, "addon", "chrome.manifest"), 1),
"add_manifest",
ManifestResource("addon", "hoge", "hoge/"),
),
(
("addon2/chrome.manifest", 1),
"add_manifest",
ManifestBinaryComponent("addon2", "addon2.so"),
),
(
("addon3/components/components.manifest", 1),
"add_manifest",
ManifestBinaryComponent("addon3/components", "addon3.so"),
),
(("manifest", 5), "add", "foo/bar/foo.html", foo_html),
(("manifest", 5), "add", "foo/bar/bar.html", bar_html),
(("manifest", 9), "add", "addon/install.rdf", install_rdf),
(("manifest", 10), "add", "addon2/install.rdf", install_rdf),
(("manifest", 11), "add", "addon3/install.rdf", install_rdf),
(("manifest", 12), "add", "addon4/install.rdf", install_rdf_addon4),
(("manifest", 13), "add", "addon5/install.rdf", install_rdf_addon5),
(("manifest", 14), "add", "addon6/install.rdf", install_rdf_addon6),
(("manifest", 15), "add", "addon7/install.rdf", install_rdf_addon7),
(("manifest", 16), "add", "addon8/install.rdf", install_rdf_addon8),
(("manifest", 17), "add", "addon9/install.rdf", install_rdf_addon9),
(("manifest", 18), "add", "addon10/install.rdf", install_rdf_addon10),
(("manifest", 19), "add", "addon11/install.rdf", install_rdf_addon11),
(("manifest", 20), "add", "hybrid/install.rdf", install_rdf),
(
("manifest", 21),
"add",
"hybrid/webextension/manifest.json",
we_manifest,
),
(
("manifest", 22),
"add",
"hybrid2/webextension/manifest.json",
we_manifest,
),
(("manifest", 23), "add", "hybrid2/install.rdf", install_rdf),
(("manifest", 24), "add", "webextension/manifest.json", we_manifest),
(
("manifest", 25),
"add",
"nonwebextension/manifest.json",
non_we_manifest,
),
],
)
self.assertEqual(
packager.get_bases(),
set(
[
"",
"addon",
"addon2",
"addon3",
"addon4",
"addon5",
"addon6",
"addon7",
"addon8",
"addon9",
"addon10",
"addon11",
"qux",
"hybrid",
"hybrid2",
"webextension",
]
),
)
self.assertEqual(packager.get_bases(addons=False), set(["", "qux"]))
def test_simple_packager_manifest_consistency(self):
formatter = MockFormatter()
# bar/ is detected as an addon because of install.rdf, but top-level
# includes a manifest inside bar/.
packager = SimplePackager(formatter)
packager.add(
"base.manifest",
GeneratedFile(
b"manifest foo/bar.manifest\n" b"manifest bar/baz.manifest\n"
),
)
packager.add("foo/bar.manifest", GeneratedFile(b"resource bar bar"))
packager.add("bar/baz.manifest", GeneratedFile(b"resource baz baz"))
packager.add("bar/install.rdf", GeneratedFile(b""))
with self.assertRaises(ErrorMessage) as e:
packager.close()
self.assertEqual(
str(e.exception),
'error: "bar/baz.manifest" is included from "base.manifest", '
'which is outside "bar"',
)
# bar/ is detected as a separate base because of chrome.manifest that
# is included nowhere, but top-level includes another manifest inside
# bar/.
packager = SimplePackager(formatter)
packager.add(
"base.manifest",
GeneratedFile(
b"manifest foo/bar.manifest\n" b"manifest bar/baz.manifest\n"
),
)
packager.add("foo/bar.manifest", GeneratedFile(b"resource bar bar"))
packager.add("bar/baz.manifest", GeneratedFile(b"resource baz baz"))
packager.add("bar/chrome.manifest", GeneratedFile(b"resource baz baz"))
with self.assertRaises(ErrorMessage) as e:
packager.close()
self.assertEqual(
str(e.exception),
'error: "bar/baz.manifest" is included from "base.manifest", '
'which is outside "bar"',
)
# bar/ is detected as a separate base because of chrome.manifest that
# is included nowhere, but chrome.manifest includes baz.manifest from
# the same directory. This shouldn't error out.
packager = SimplePackager(formatter)
packager.add("base.manifest", GeneratedFile(b"manifest foo/bar.manifest\n"))
packager.add("foo/bar.manifest", GeneratedFile(b"resource bar bar"))
packager.add("bar/baz.manifest", GeneratedFile(b"resource baz baz"))
packager.add("bar/chrome.manifest", GeneratedFile(b"manifest baz.manifest"))
packager.close()
class TestSimpleManifestSink(unittest.TestCase):
def test_simple_manifest_parser(self):
formatter = MockFormatter()
foobar = GeneratedFile(b"foobar")
foobaz = GeneratedFile(b"foobaz")
fooqux = GeneratedFile(b"fooqux")
foozot = GeneratedFile(b"foozot")
finder = MockFinder(
{
"bin/foo/bar": foobar,
"bin/foo/baz": foobaz,
"bin/foo/qux": fooqux,
"bin/foo/zot": foozot,
"bin/foo/chrome.manifest": GeneratedFile(b"resource foo foo/"),
"bin/chrome.manifest": GeneratedFile(b"manifest foo/chrome.manifest"),
}
)
parser = SimpleManifestSink(finder, formatter)
component0 = Component("component0")
component1 = Component("component1")
component2 = Component("component2", destdir="destdir")
parser.add(component0, "bin/foo/b*")
parser.add(component1, "bin/foo/qux")
parser.add(component1, "bin/foo/chrome.manifest")
parser.add(component2, "bin/foo/zot")
self.assertRaises(ErrorMessage, parser.add, "component1", "bin/bar")
self.assertEqual(formatter.log, [])
parser.close()
self.assertEqual(
formatter.log,
[
(None, "add_base", "", False),
(
("foo/chrome.manifest", 1),
"add_manifest",
ManifestResource("foo", "foo", "foo/"),
),
(None, "add", "foo/bar", foobar),
(None, "add", "foo/baz", foobaz),
(None, "add", "foo/qux", fooqux),
(None, "add", "destdir/foo/zot", foozot),
],
)
self.assertEqual(
finder.log,
[
"bin/foo/b*",
"bin/foo/qux",
"bin/foo/chrome.manifest",
"bin/foo/zot",
"bin/bar",
"bin/chrome.manifest",
],
)
class TestCallDeque(unittest.TestCase):
def test_call_deque(self):
class Logger(object):
def __init__(self):
self._log = []
def log(self, str):
self._log.append(str)
@staticmethod
def staticlog(logger, str):
logger.log(str)
def do_log(logger, str):
logger.log(str)
logger = Logger()
d = CallDeque()
d.append(logger.log, "foo")
d.append(logger.log, "bar")
d.append(logger.staticlog, logger, "baz")
d.append(do_log, logger, "qux")
self.assertEqual(logger._log, [])
d.execute()
self.assertEqual(logger._log, ["foo", "bar", "baz", "qux"])
class TestComponent(unittest.TestCase):
def do_split(self, string, name, options):
n, o = Component._split_component_and_options(string)
self.assertEqual(name, n)
self.assertEqual(options, o)
def test_component_split_component_and_options(self):
self.do_split("component", "component", {})
self.do_split("trailingspace ", "trailingspace", {})
self.do_split(" leadingspace", "leadingspace", {})
self.do_split(" trim ", "trim", {})
self.do_split(' trim key="value"', "trim", {"key": "value"})
self.do_split(' trim empty=""', "trim", {"empty": ""})
self.do_split(' trim space=" "', "trim", {"space": " "})
self.do_split(
'component key="value" key2="second" ',
"component",
{"key": "value", "key2": "second"},
)
self.do_split(
'trim key=" value with spaces " key2="spaces again"',
"trim",
{"key": " value with spaces ", "key2": "spaces again"},
)
def do_split_error(self, string):
self.assertRaises(ValueError, Component._split_component_and_options, string)
def test_component_split_component_and_options_errors(self):
self.do_split_error('"component')
self.do_split_error('comp"onent')
self.do_split_error('component"')
self.do_split_error('"component"')
self.do_split_error("=component")
self.do_split_error("comp=onent")
self.do_split_error("component=")
self.do_split_error('key="val"')
self.do_split_error("component key=")
self.do_split_error('component key="val')
self.do_split_error('component key=val"')
self.do_split_error('component key="val" x')
self.do_split_error('component x key="val"')
self.do_split_error('component key1="val" x key2="val"')
def do_from_string(self, string, name, destdir=""):
component = Component.from_string(string)
self.assertEqual(name, component.name)
self.assertEqual(destdir, component.destdir)
def test_component_from_string(self):
self.do_from_string("component", "component")
self.do_from_string("component-with-hyphen", "component-with-hyphen")
self.do_from_string('component destdir="foo/bar"', "component", "foo/bar")
self.do_from_string('component destdir="bar spc"', "component", "bar spc")
self.assertRaises(ErrorMessage, Component.from_string, "")
self.assertRaises(ErrorMessage, Component.from_string, "component novalue=")
self.assertRaises(
ErrorMessage, Component.from_string, "component badoption=badvalue"
)
if __name__ == "__main__":
mozunit.main()