diff options
Diffstat (limited to 'python/mozbuild/mozpack/test/test_packager_formats.py')
-rw-r--r-- | python/mozbuild/mozpack/test/test_packager_formats.py | 536 |
1 files changed, 536 insertions, 0 deletions
diff --git a/python/mozbuild/mozpack/test/test_packager_formats.py b/python/mozbuild/mozpack/test/test_packager_formats.py new file mode 100644 index 0000000000..e9bc56c856 --- /dev/null +++ b/python/mozbuild/mozpack/test/test_packager_formats.py @@ -0,0 +1,536 @@ +# 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 unittest +from itertools import chain + +import mozpack.path as mozpath +import mozunit +import six +from mozpack.chrome.manifest import ( + ManifestBinaryComponent, + ManifestComponent, + ManifestContent, + ManifestLocale, + ManifestResource, + ManifestSkin, +) +from mozpack.copier import FileRegistry +from mozpack.errors import ErrorMessage +from mozpack.files import GeneratedFile, ManifestFile +from mozpack.packager.formats import FlatFormatter, JarFormatter, OmniJarFormatter +from mozpack.test.test_files import bar_xpt, foo2_xpt, foo_xpt +from test_errors import TestErrors + +CONTENTS = { + "bases": { + # base_path: is_addon? + "": False, + "app": False, + "addon0": "unpacked", + "addon1": True, + "app/chrome/addons/addon2": True, + }, + "manifests": [ + ManifestContent("chrome/f", "oo", "oo/"), + ManifestContent("chrome/f", "bar", "oo/bar/"), + ManifestResource("chrome/f", "foo", "resource://bar/"), + ManifestBinaryComponent("components", "foo.so"), + ManifestContent("app/chrome", "content", "foo/"), + ManifestComponent("app/components", "{foo-id}", "foo.js"), + ManifestContent("addon0/chrome", "addon0", "foo/bar/"), + ManifestContent("addon1/chrome", "addon1", "foo/bar/"), + ManifestContent("app/chrome/addons/addon2/chrome", "addon2", "foo/bar/"), + ], + "files": { + "chrome/f/oo/bar/baz": GeneratedFile(b"foobarbaz"), + "chrome/f/oo/baz": GeneratedFile(b"foobaz"), + "chrome/f/oo/qux": GeneratedFile(b"fooqux"), + "components/foo.so": GeneratedFile(b"foo.so"), + "components/foo.xpt": foo_xpt, + "components/bar.xpt": bar_xpt, + "foo": GeneratedFile(b"foo"), + "app/chrome/foo/foo": GeneratedFile(b"appfoo"), + "app/components/foo.js": GeneratedFile(b"foo.js"), + "addon0/chrome/foo/bar/baz": GeneratedFile(b"foobarbaz"), + "addon0/components/foo.xpt": foo2_xpt, + "addon0/components/bar.xpt": bar_xpt, + "addon1/chrome/foo/bar/baz": GeneratedFile(b"foobarbaz"), + "addon1/components/foo.xpt": foo2_xpt, + "addon1/components/bar.xpt": bar_xpt, + "app/chrome/addons/addon2/chrome/foo/bar/baz": GeneratedFile(b"foobarbaz"), + "app/chrome/addons/addon2/components/foo.xpt": foo2_xpt, + "app/chrome/addons/addon2/components/bar.xpt": bar_xpt, + }, +} + +FILES = CONTENTS["files"] + +RESULT_FLAT = { + "chrome.manifest": [ + "manifest chrome/chrome.manifest", + "manifest components/components.manifest", + ], + "chrome/chrome.manifest": [ + "manifest f/f.manifest", + ], + "chrome/f/f.manifest": [ + "content oo oo/", + "content bar oo/bar/", + "resource foo resource://bar/", + ], + "chrome/f/oo/bar/baz": FILES["chrome/f/oo/bar/baz"], + "chrome/f/oo/baz": FILES["chrome/f/oo/baz"], + "chrome/f/oo/qux": FILES["chrome/f/oo/qux"], + "components/components.manifest": [ + "binary-component foo.so", + "interfaces bar.xpt", + "interfaces foo.xpt", + ], + "components/foo.so": FILES["components/foo.so"], + "components/foo.xpt": foo_xpt, + "components/bar.xpt": bar_xpt, + "foo": FILES["foo"], + "app/chrome.manifest": [ + "manifest chrome/chrome.manifest", + "manifest components/components.manifest", + ], + "app/chrome/chrome.manifest": [ + "content content foo/", + ], + "app/chrome/foo/foo": FILES["app/chrome/foo/foo"], + "app/components/components.manifest": [ + "component {foo-id} foo.js", + ], + "app/components/foo.js": FILES["app/components/foo.js"], +} + +for addon in ("addon0", "addon1", "app/chrome/addons/addon2"): + RESULT_FLAT.update( + { + mozpath.join(addon, p): f + for p, f in six.iteritems( + { + "chrome.manifest": [ + "manifest chrome/chrome.manifest", + "manifest components/components.manifest", + ], + "chrome/chrome.manifest": [ + "content %s foo/bar/" % mozpath.basename(addon), + ], + "chrome/foo/bar/baz": FILES[ + mozpath.join(addon, "chrome/foo/bar/baz") + ], + "components/components.manifest": [ + "interfaces bar.xpt", + "interfaces foo.xpt", + ], + "components/bar.xpt": bar_xpt, + "components/foo.xpt": foo2_xpt, + } + ) + } + ) + +RESULT_JAR = { + p: RESULT_FLAT[p] + for p in ( + "chrome.manifest", + "chrome/chrome.manifest", + "components/components.manifest", + "components/foo.so", + "components/foo.xpt", + "components/bar.xpt", + "foo", + "app/chrome.manifest", + "app/components/components.manifest", + "app/components/foo.js", + "addon0/chrome.manifest", + "addon0/components/components.manifest", + "addon0/components/foo.xpt", + "addon0/components/bar.xpt", + ) +} + +RESULT_JAR.update( + { + "chrome/f/f.manifest": [ + "content oo jar:oo.jar!/", + "content bar jar:oo.jar!/bar/", + "resource foo resource://bar/", + ], + "chrome/f/oo.jar": { + "bar/baz": FILES["chrome/f/oo/bar/baz"], + "baz": FILES["chrome/f/oo/baz"], + "qux": FILES["chrome/f/oo/qux"], + }, + "app/chrome/chrome.manifest": [ + "content content jar:foo.jar!/", + ], + "app/chrome/foo.jar": { + "foo": FILES["app/chrome/foo/foo"], + }, + "addon0/chrome/chrome.manifest": [ + "content addon0 jar:foo.jar!/bar/", + ], + "addon0/chrome/foo.jar": { + "bar/baz": FILES["addon0/chrome/foo/bar/baz"], + }, + "addon1.xpi": { + mozpath.relpath(p, "addon1"): f + for p, f in six.iteritems(RESULT_FLAT) + if p.startswith("addon1/") + }, + "app/chrome/addons/addon2.xpi": { + mozpath.relpath(p, "app/chrome/addons/addon2"): f + for p, f in six.iteritems(RESULT_FLAT) + if p.startswith("app/chrome/addons/addon2/") + }, + } +) + +RESULT_OMNIJAR = { + p: RESULT_FLAT[p] + for p in ( + "components/foo.so", + "foo", + ) +} + +RESULT_OMNIJAR.update({p: RESULT_JAR[p] for p in RESULT_JAR if p.startswith("addon")}) + +RESULT_OMNIJAR.update( + { + "omni.foo": { + "components/components.manifest": [ + "interfaces bar.xpt", + "interfaces foo.xpt", + ], + }, + "chrome.manifest": [ + "manifest components/components.manifest", + ], + "components/components.manifest": [ + "binary-component foo.so", + ], + "app/omni.foo": { + p: RESULT_FLAT["app/" + p] + for p in chain( + ( + "chrome.manifest", + "chrome/chrome.manifest", + "chrome/foo/foo", + "components/components.manifest", + "components/foo.js", + ), + ( + mozpath.relpath(p, "app") + for p in six.iterkeys(RESULT_FLAT) + if p.startswith("app/chrome/addons/addon2/") + ), + ) + }, + } +) + +RESULT_OMNIJAR["omni.foo"].update( + { + p: RESULT_FLAT[p] + for p in ( + "chrome.manifest", + "chrome/chrome.manifest", + "chrome/f/f.manifest", + "chrome/f/oo/bar/baz", + "chrome/f/oo/baz", + "chrome/f/oo/qux", + "components/foo.xpt", + "components/bar.xpt", + ) + } +) + +RESULT_OMNIJAR_WITH_SUBPATH = { + k.replace("omni.foo", "bar/omni.foo"): v for k, v in RESULT_OMNIJAR.items() +} + +CONTENTS_WITH_BASE = { + "bases": { + mozpath.join("base/root", b) if b else "base/root": a + for b, a in six.iteritems(CONTENTS["bases"]) + }, + "manifests": [ + m.move(mozpath.join("base/root", m.base)) for m in CONTENTS["manifests"] + ], + "files": { + mozpath.join("base/root", p): f for p, f in six.iteritems(CONTENTS["files"]) + }, +} + +EXTRA_CONTENTS = { + "extra/file": GeneratedFile(b"extra file"), +} + +CONTENTS_WITH_BASE["files"].update(EXTRA_CONTENTS) + + +def result_with_base(results): + result = {mozpath.join("base/root", p): v for p, v in six.iteritems(results)} + result.update(EXTRA_CONTENTS) + return result + + +RESULT_FLAT_WITH_BASE = result_with_base(RESULT_FLAT) +RESULT_JAR_WITH_BASE = result_with_base(RESULT_JAR) +RESULT_OMNIJAR_WITH_BASE = result_with_base(RESULT_OMNIJAR) + + +def fill_formatter(formatter, contents): + for base, is_addon in sorted(contents["bases"].items()): + formatter.add_base(base, is_addon) + + for manifest in contents["manifests"]: + formatter.add_manifest(manifest) + + for k, v in sorted(six.iteritems(contents["files"])): + if k.endswith(".xpt"): + formatter.add_interfaces(k, v) + else: + formatter.add(k, v) + + +def get_contents(registry, read_all=False, mode="rt"): + result = {} + for k, v in registry: + if isinstance(v, FileRegistry): + result[k] = get_contents(v) + elif isinstance(v, ManifestFile) or read_all: + if "b" in mode: + result[k] = v.open().read() + else: + result[k] = six.ensure_text(v.open().read()).splitlines() + else: + result[k] = v + return result + + +class TestFormatters(TestErrors, unittest.TestCase): + maxDiff = None + + def test_bases(self): + formatter = FlatFormatter(FileRegistry()) + formatter.add_base("") + formatter.add_base("addon0", addon=True) + formatter.add_base("browser") + self.assertEqual(formatter._get_base("platform.ini"), ("", "platform.ini")) + self.assertEqual( + formatter._get_base("browser/application.ini"), + ("browser", "application.ini"), + ) + self.assertEqual( + formatter._get_base("addon0/install.rdf"), ("addon0", "install.rdf") + ) + + def do_test_contents(self, formatter, contents): + for f in contents["files"]: + # .xpt files are merged, so skip them. + if not f.endswith(".xpt"): + self.assertTrue(formatter.contains(f)) + + def test_flat_formatter(self): + registry = FileRegistry() + formatter = FlatFormatter(registry) + + fill_formatter(formatter, CONTENTS) + self.assertEqual(get_contents(registry), RESULT_FLAT) + self.do_test_contents(formatter, CONTENTS) + + def test_jar_formatter(self): + registry = FileRegistry() + formatter = JarFormatter(registry) + + fill_formatter(formatter, CONTENTS) + self.assertEqual(get_contents(registry), RESULT_JAR) + self.do_test_contents(formatter, CONTENTS) + + def test_omnijar_formatter(self): + registry = FileRegistry() + formatter = OmniJarFormatter(registry, "omni.foo") + + fill_formatter(formatter, CONTENTS) + self.assertEqual(get_contents(registry), RESULT_OMNIJAR) + self.do_test_contents(formatter, CONTENTS) + + def test_flat_formatter_with_base(self): + registry = FileRegistry() + formatter = FlatFormatter(registry) + + fill_formatter(formatter, CONTENTS_WITH_BASE) + self.assertEqual(get_contents(registry), RESULT_FLAT_WITH_BASE) + self.do_test_contents(formatter, CONTENTS_WITH_BASE) + + def test_jar_formatter_with_base(self): + registry = FileRegistry() + formatter = JarFormatter(registry) + + fill_formatter(formatter, CONTENTS_WITH_BASE) + self.assertEqual(get_contents(registry), RESULT_JAR_WITH_BASE) + self.do_test_contents(formatter, CONTENTS_WITH_BASE) + + def test_omnijar_formatter_with_base(self): + registry = FileRegistry() + formatter = OmniJarFormatter(registry, "omni.foo") + + fill_formatter(formatter, CONTENTS_WITH_BASE) + self.assertEqual(get_contents(registry), RESULT_OMNIJAR_WITH_BASE) + self.do_test_contents(formatter, CONTENTS_WITH_BASE) + + def test_omnijar_formatter_with_subpath(self): + registry = FileRegistry() + formatter = OmniJarFormatter(registry, "bar/omni.foo") + + fill_formatter(formatter, CONTENTS) + self.assertEqual(get_contents(registry), RESULT_OMNIJAR_WITH_SUBPATH) + self.do_test_contents(formatter, CONTENTS) + + def test_omnijar_is_resource(self): + def is_resource(base, path): + registry = FileRegistry() + f = OmniJarFormatter( + registry, + "omni.foo", + non_resources=[ + "defaults/messenger/mailViews.dat", + "defaults/foo/*", + "*/dummy", + ], + ) + f.add_base("") + f.add_base("app") + f.add(mozpath.join(base, path), GeneratedFile(b"")) + if f.copier.contains(mozpath.join(base, path)): + return False + self.assertTrue(f.copier.contains(mozpath.join(base, "omni.foo"))) + self.assertTrue(f.copier[mozpath.join(base, "omni.foo")].contains(path)) + return True + + for base in ["", "app/"]: + self.assertTrue(is_resource(base, "chrome")) + self.assertTrue(is_resource(base, "chrome/foo/bar/baz.properties")) + self.assertFalse(is_resource(base, "chrome/icons/foo.png")) + self.assertTrue(is_resource(base, "components/foo.js")) + self.assertFalse(is_resource(base, "components/foo.so")) + self.assertTrue(is_resource(base, "res/foo.css")) + self.assertFalse(is_resource(base, "res/cursors/foo.png")) + self.assertFalse(is_resource(base, "res/MainMenu.nib/foo")) + self.assertTrue(is_resource(base, "defaults/pref/foo.js")) + self.assertFalse(is_resource(base, "defaults/pref/channel-prefs.js")) + self.assertTrue(is_resource(base, "defaults/preferences/foo.js")) + self.assertFalse(is_resource(base, "defaults/preferences/channel-prefs.js")) + self.assertTrue(is_resource(base, "modules/foo.jsm")) + self.assertTrue(is_resource(base, "greprefs.js")) + self.assertTrue(is_resource(base, "hyphenation/foo")) + self.assertTrue(is_resource(base, "update.locale")) + self.assertFalse(is_resource(base, "foo")) + self.assertFalse(is_resource(base, "foo/bar/greprefs.js")) + self.assertTrue(is_resource(base, "defaults/messenger/foo.dat")) + self.assertFalse(is_resource(base, "defaults/messenger/mailViews.dat")) + self.assertTrue(is_resource(base, "defaults/pref/foo.js")) + self.assertFalse(is_resource(base, "defaults/foo/bar.dat")) + self.assertFalse(is_resource(base, "defaults/foo/bar/baz.dat")) + self.assertTrue(is_resource(base, "chrome/foo/bar/baz/dummy_")) + self.assertFalse(is_resource(base, "chrome/foo/bar/baz/dummy")) + self.assertTrue(is_resource(base, "chrome/foo/bar/dummy_")) + self.assertFalse(is_resource(base, "chrome/foo/bar/dummy")) + + def test_chrome_override(self): + registry = FileRegistry() + f = FlatFormatter(registry) + f.add_base("") + f.add_manifest(ManifestContent("chrome", "foo", "foo/unix")) + # A more specific entry for a given chrome name can override a more + # generic one. + f.add_manifest(ManifestContent("chrome", "foo", "foo/win", "os=WINNT")) + f.add_manifest(ManifestContent("chrome", "foo", "foo/osx", "os=Darwin")) + + # Chrome with the same name overrides the previous registration. + with self.assertRaises(ErrorMessage) as e: + f.add_manifest(ManifestContent("chrome", "foo", "foo/")) + + self.assertEqual( + str(e.exception), + 'error: "content foo foo/" overrides ' '"content foo foo/unix"', + ) + + # Chrome with the same name and same flags overrides the previous + # registration. + with self.assertRaises(ErrorMessage) as e: + f.add_manifest(ManifestContent("chrome", "foo", "foo/", "os=WINNT")) + + self.assertEqual( + str(e.exception), + 'error: "content foo foo/ os=WINNT" overrides ' + '"content foo foo/win os=WINNT"', + ) + + # We may start with the more specific entry first + f.add_manifest(ManifestContent("chrome", "bar", "bar/win", "os=WINNT")) + # Then adding a more generic one overrides it. + with self.assertRaises(ErrorMessage) as e: + f.add_manifest(ManifestContent("chrome", "bar", "bar/unix")) + + self.assertEqual( + str(e.exception), + 'error: "content bar bar/unix" overrides ' '"content bar bar/win os=WINNT"', + ) + + # Adding something more specific still works. + f.add_manifest( + ManifestContent("chrome", "bar", "bar/win", "os=WINNT osversion>=7.0") + ) + + # Variations of skin/locales are allowed. + f.add_manifest( + ManifestSkin("chrome", "foo", "classic/1.0", "foo/skin/classic/") + ) + f.add_manifest(ManifestSkin("chrome", "foo", "modern/1.0", "foo/skin/modern/")) + + f.add_manifest(ManifestLocale("chrome", "foo", "en-US", "foo/locale/en-US/")) + f.add_manifest(ManifestLocale("chrome", "foo", "ja-JP", "foo/locale/ja-JP/")) + + # But same-skin/locale still error out. + with self.assertRaises(ErrorMessage) as e: + f.add_manifest( + ManifestSkin("chrome", "foo", "classic/1.0", "foo/skin/classic/foo") + ) + + self.assertEqual( + str(e.exception), + 'error: "skin foo classic/1.0 foo/skin/classic/foo" overrides ' + '"skin foo classic/1.0 foo/skin/classic/"', + ) + + with self.assertRaises(ErrorMessage) as e: + f.add_manifest( + ManifestLocale("chrome", "foo", "en-US", "foo/locale/en-US/foo") + ) + + self.assertEqual( + str(e.exception), + 'error: "locale foo en-US foo/locale/en-US/foo" overrides ' + '"locale foo en-US foo/locale/en-US/"', + ) + + # Duplicating existing manifest entries is not an error. + f.add_manifest(ManifestContent("chrome", "foo", "foo/unix")) + + self.assertEqual( + self.get_output(), + [ + 'warning: "content foo foo/unix" is duplicated. Skipping.', + ], + ) + + +if __name__ == "__main__": + mozunit.main() |