diff options
Diffstat (limited to 'python/mozbuild/mozpack/test/test_packager.py')
-rw-r--r-- | python/mozbuild/mozpack/test/test_packager.py | 490 |
1 files changed, 490 insertions, 0 deletions
diff --git a/python/mozbuild/mozpack/test/test_packager.py b/python/mozbuild/mozpack/test/test_packager.py new file mode 100644 index 000000000..397f40538 --- /dev/null +++ b/python/mozbuild/mozpack/test/test_packager.py @@ -0,0 +1,490 @@ +# 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 +import mozunit +import os +from mozpack.packager import ( + preprocess_manifest, + CallDeque, + Component, + SimplePackager, + SimpleManifestSink, +) +from mozpack.files import GeneratedFile +from mozpack.chrome.manifest import ( + ManifestBinaryComponent, + ManifestContent, + ManifestResource, +) +from mozunit import MockedOpen +from mozbuild.preprocessor import Preprocessor +from mozpack.errors import ( + errors, + ErrorMessage, +) +import mozpack.path as mozpath + +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 = os.path.join(os.path.abspath(os.curdir), '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() + + 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'), + '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'), + '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(''.join([ + 'resource qux qux/\n', + 'binary-component qux.so\n', + ]))) + bar_xpt = GeneratedFile('bar.xpt') + qux_xpt = GeneratedFile('qux.xpt') + foo_html = GeneratedFile('foo_html') + bar_html = GeneratedFile('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'), + ''.join([ + 'manifest foo/bar.manifest\n', + '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'), + 'resource hoge hoge/') + with errors.context('manifest', 8): + packager.add('addon/chrome.manifest', file) + + install_rdf = GeneratedFile('<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('binary-component addon2.so')) + + with errors.context('manifest', 11): + packager.add('addon3/install.rdf', install_rdf) + packager.add('addon3/chrome.manifest', GeneratedFile( + 'manifest components/components.manifest')) + packager.add('addon3/components/components.manifest', + GeneratedFile('binary-component addon3.so')) + + with errors.context('manifest', 12): + install_rdf_addon4 = GeneratedFile( + '<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( + '<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( + '<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( + '<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( + '<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( + '<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( + '<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( + '<RDF>\n<... em:unpack=\'false\'>\n<...>\n</RDF>') + packager.add('addon11/install.rdf', install_rdf_addon11) + + 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', 'qux', False), + ((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), + ]) + + self.assertEqual(packager.get_bases(), + set(['', 'addon', 'addon2', 'addon3', 'addon4', + 'addon5', 'addon6', 'addon7', 'addon8', + 'addon9', 'addon10', 'addon11', 'qux'])) + 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( + 'manifest foo/bar.manifest\n' + 'manifest bar/baz.manifest\n' + )) + packager.add('foo/bar.manifest', GeneratedFile('resource bar bar')) + packager.add('bar/baz.manifest', GeneratedFile('resource baz baz')) + packager.add('bar/install.rdf', GeneratedFile('')) + + with self.assertRaises(ErrorMessage) as e: + packager.close() + + self.assertEqual(e.exception.message, + '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( + 'manifest foo/bar.manifest\n' + 'manifest bar/baz.manifest\n' + )) + packager.add('foo/bar.manifest', GeneratedFile('resource bar bar')) + packager.add('bar/baz.manifest', GeneratedFile('resource baz baz')) + packager.add('bar/chrome.manifest', GeneratedFile('resource baz baz')) + + with self.assertRaises(ErrorMessage) as e: + packager.close() + + self.assertEqual(e.exception.message, + '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( + 'manifest foo/bar.manifest\n' + )) + packager.add('foo/bar.manifest', GeneratedFile('resource bar bar')) + packager.add('bar/baz.manifest', GeneratedFile('resource baz baz')) + packager.add('bar/chrome.manifest', + GeneratedFile('manifest baz.manifest')) + packager.close() + + +class TestSimpleManifestSink(unittest.TestCase): + def test_simple_manifest_parser(self): + formatter = MockFormatter() + foobar = GeneratedFile('foobar') + foobaz = GeneratedFile('foobaz') + fooqux = GeneratedFile('fooqux') + foozot = GeneratedFile('foozot') + finder = MockFinder({ + 'bin/foo/bar': foobar, + 'bin/foo/baz': foobaz, + 'bin/foo/qux': fooqux, + 'bin/foo/zot': foozot, + 'bin/foo/chrome.manifest': GeneratedFile('resource foo foo/'), + 'bin/chrome.manifest': + GeneratedFile('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() |