michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: import unittest michael@0: import mozunit michael@0: import os michael@0: from mozpack.packager import ( michael@0: preprocess_manifest, michael@0: CallDeque, michael@0: Component, michael@0: SimplePackager, michael@0: SimpleManifestSink, michael@0: ) michael@0: from mozpack.files import GeneratedFile michael@0: from mozpack.chrome.manifest import ( michael@0: ManifestResource, michael@0: ManifestContent, michael@0: ) michael@0: from mozunit import MockedOpen michael@0: from mozbuild.preprocessor import Preprocessor michael@0: from mozpack.errors import ( michael@0: errors, michael@0: ErrorMessage, michael@0: ) michael@0: import mozpack.path michael@0: michael@0: MANIFEST = ''' michael@0: bar/* michael@0: [foo] michael@0: foo/* michael@0: -foo/bar michael@0: chrome.manifest michael@0: [zot destdir="destdir"] michael@0: foo/zot michael@0: ; comment michael@0: #ifdef baz michael@0: [baz] michael@0: baz@SUFFIX@ michael@0: #endif michael@0: ''' michael@0: michael@0: michael@0: class TestPreprocessManifest(unittest.TestCase): michael@0: MANIFEST_PATH = os.path.join(os.path.abspath(os.curdir), 'manifest') michael@0: michael@0: EXPECTED_LOG = [ michael@0: ((MANIFEST_PATH, 2), 'add', '', 'bar/*'), michael@0: ((MANIFEST_PATH, 4), 'add', 'foo', 'foo/*'), michael@0: ((MANIFEST_PATH, 5), 'remove', 'foo', 'foo/bar'), michael@0: ((MANIFEST_PATH, 6), 'add', 'foo', 'chrome.manifest'), michael@0: ((MANIFEST_PATH, 8), 'add', 'zot destdir="destdir"', 'foo/zot'), michael@0: ] michael@0: michael@0: def setUp(self): michael@0: class MockSink(object): michael@0: def __init__(self): michael@0: self.log = [] michael@0: michael@0: def add(self, component, path): michael@0: self._log(errors.get_context(), 'add', repr(component), path) michael@0: michael@0: def remove(self, component, path): michael@0: self._log(errors.get_context(), 'remove', repr(component), path) michael@0: michael@0: def _log(self, *args): michael@0: self.log.append(args) michael@0: michael@0: self.sink = MockSink() michael@0: michael@0: def test_preprocess_manifest(self): michael@0: with MockedOpen({'manifest': MANIFEST}): michael@0: preprocess_manifest(self.sink, 'manifest') michael@0: self.assertEqual(self.sink.log, self.EXPECTED_LOG) michael@0: michael@0: def test_preprocess_manifest_missing_define(self): michael@0: with MockedOpen({'manifest': MANIFEST}): michael@0: self.assertRaises( michael@0: Preprocessor.Error, michael@0: preprocess_manifest, michael@0: self.sink, michael@0: 'manifest', michael@0: {'baz': 1} michael@0: ) michael@0: michael@0: def test_preprocess_manifest_defines(self): michael@0: with MockedOpen({'manifest': MANIFEST}): michael@0: preprocess_manifest(self.sink, 'manifest', michael@0: {'baz': 1, 'SUFFIX': '.exe'}) michael@0: self.assertEqual(self.sink.log, self.EXPECTED_LOG + michael@0: [((self.MANIFEST_PATH, 12), 'add', 'baz', 'baz.exe')]) michael@0: michael@0: michael@0: class MockFinder(object): michael@0: def __init__(self, files): michael@0: self.files = files michael@0: self.log = [] michael@0: michael@0: def find(self, path): michael@0: self.log.append(path) michael@0: for f in sorted(self.files): michael@0: if mozpack.path.match(f, path): michael@0: yield f, self.files[f] michael@0: michael@0: def __iter__(self): michael@0: return self.find('') michael@0: michael@0: michael@0: class MockFormatter(object): michael@0: def __init__(self): michael@0: self.log = [] michael@0: michael@0: def add_base(self, *args): michael@0: self._log(errors.get_context(), 'add_base', *args) michael@0: michael@0: def add_manifest(self, *args): michael@0: self._log(errors.get_context(), 'add_manifest', *args) michael@0: michael@0: def add_interfaces(self, *args): michael@0: self._log(errors.get_context(), 'add_interfaces', *args) michael@0: michael@0: def add(self, *args): michael@0: self._log(errors.get_context(), 'add', *args) michael@0: michael@0: def _log(self, *args): michael@0: self.log.append(args) michael@0: michael@0: michael@0: class TestSimplePackager(unittest.TestCase): michael@0: def test_simple_packager(self): michael@0: class GeneratedFileWithPath(GeneratedFile): michael@0: def __init__(self, path, content): michael@0: GeneratedFile.__init__(self, content) michael@0: self.path = path michael@0: michael@0: formatter = MockFormatter() michael@0: packager = SimplePackager(formatter) michael@0: curdir = os.path.abspath(os.curdir) michael@0: file = GeneratedFileWithPath(os.path.join(curdir, 'foo', michael@0: 'bar.manifest'), michael@0: 'resource bar bar/\ncontent bar bar/') michael@0: with errors.context('manifest', 1): michael@0: packager.add('foo/bar.manifest', file) michael@0: michael@0: file = GeneratedFileWithPath(os.path.join(curdir, 'foo', michael@0: 'baz.manifest'), michael@0: 'resource baz baz/') michael@0: with errors.context('manifest', 2): michael@0: packager.add('bar/baz.manifest', file) michael@0: michael@0: with errors.context('manifest', 3): michael@0: packager.add('qux/qux.manifest', michael@0: GeneratedFile('resource qux qux/')) michael@0: bar_xpt = GeneratedFile('bar.xpt') michael@0: qux_xpt = GeneratedFile('qux.xpt') michael@0: foo_html = GeneratedFile('foo_html') michael@0: bar_html = GeneratedFile('bar_html') michael@0: with errors.context('manifest', 4): michael@0: packager.add('foo/bar.xpt', bar_xpt) michael@0: with errors.context('manifest', 5): michael@0: packager.add('foo/bar/foo.html', foo_html) michael@0: packager.add('foo/bar/bar.html', bar_html) michael@0: michael@0: file = GeneratedFileWithPath(os.path.join(curdir, 'foo.manifest'), michael@0: ''.join([ michael@0: 'manifest foo/bar.manifest\n', michael@0: 'manifest bar/baz.manifest\n', michael@0: ])) michael@0: with errors.context('manifest', 6): michael@0: packager.add('foo.manifest', file) michael@0: with errors.context('manifest', 7): michael@0: packager.add('foo/qux.xpt', qux_xpt) michael@0: michael@0: self.assertEqual(formatter.log, []) michael@0: michael@0: with errors.context('dummy', 1): michael@0: packager.close() michael@0: self.maxDiff = None michael@0: # The formatter is expected to reorder the manifest entries so that michael@0: # chrome entries appear before the others. michael@0: self.assertEqual(formatter.log, [ michael@0: (('dummy', 1), 'add_base', 'qux'), michael@0: ((os.path.join(curdir, 'foo', 'bar.manifest'), 2), michael@0: 'add_manifest', ManifestContent('foo', 'bar', 'bar/')), michael@0: ((os.path.join(curdir, 'foo', 'bar.manifest'), 1), michael@0: 'add_manifest', ManifestResource('foo', 'bar', 'bar/')), michael@0: (('bar/baz.manifest', 1), michael@0: 'add_manifest', ManifestResource('bar', 'baz', 'baz/')), michael@0: (('qux/qux.manifest', 1), michael@0: 'add_manifest', ManifestResource('qux', 'qux', 'qux/')), michael@0: (('manifest', 4), 'add_interfaces', 'foo/bar.xpt', bar_xpt), michael@0: (('manifest', 7), 'add_interfaces', 'foo/qux.xpt', qux_xpt), michael@0: (('manifest', 5), 'add', 'foo/bar/foo.html', foo_html), michael@0: (('manifest', 5), 'add', 'foo/bar/bar.html', bar_html), michael@0: ]) michael@0: michael@0: self.assertEqual(packager.get_bases(), set(['', 'qux'])) michael@0: michael@0: michael@0: class TestSimpleManifestSink(unittest.TestCase): michael@0: def test_simple_manifest_parser(self): michael@0: formatter = MockFormatter() michael@0: foobar = GeneratedFile('foobar') michael@0: foobaz = GeneratedFile('foobaz') michael@0: fooqux = GeneratedFile('fooqux') michael@0: foozot = GeneratedFile('foozot') michael@0: finder = MockFinder({ michael@0: 'bin/foo/bar': foobar, michael@0: 'bin/foo/baz': foobaz, michael@0: 'bin/foo/qux': fooqux, michael@0: 'bin/foo/zot': foozot, michael@0: 'bin/foo/chrome.manifest': GeneratedFile('resource foo foo/'), michael@0: 'bin/chrome.manifest': michael@0: GeneratedFile('manifest foo/chrome.manifest'), michael@0: }) michael@0: parser = SimpleManifestSink(finder, formatter) michael@0: component0 = Component('component0') michael@0: component1 = Component('component1') michael@0: component2 = Component('component2', destdir='destdir') michael@0: parser.add(component0, 'bin/foo/b*') michael@0: parser.add(component1, 'bin/foo/qux') michael@0: parser.add(component1, 'bin/foo/chrome.manifest') michael@0: parser.add(component2, 'bin/foo/zot') michael@0: self.assertRaises(ErrorMessage, parser.add, 'component1', 'bin/bar') michael@0: michael@0: self.assertEqual(formatter.log, []) michael@0: parser.close() michael@0: self.assertEqual(formatter.log, [ michael@0: (('foo/chrome.manifest', 1), michael@0: 'add_manifest', ManifestResource('foo', 'foo', 'foo/')), michael@0: (None, 'add', 'foo/bar', foobar), michael@0: (None, 'add', 'foo/baz', foobaz), michael@0: (None, 'add', 'foo/qux', fooqux), michael@0: (None, 'add', 'destdir/foo/zot', foozot), michael@0: ]) michael@0: michael@0: self.assertEqual(finder.log, [ michael@0: 'bin/foo/b*', michael@0: 'bin/foo/qux', michael@0: 'bin/foo/chrome.manifest', michael@0: 'bin/foo/zot', michael@0: 'bin/bar', michael@0: 'bin/chrome.manifest' michael@0: ]) michael@0: michael@0: michael@0: class TestCallDeque(unittest.TestCase): michael@0: def test_call_deque(self): michael@0: class Logger(object): michael@0: def __init__(self): michael@0: self._log = [] michael@0: michael@0: def log(self, str): michael@0: self._log.append(str) michael@0: michael@0: @staticmethod michael@0: def staticlog(logger, str): michael@0: logger.log(str) michael@0: michael@0: def do_log(logger, str): michael@0: logger.log(str) michael@0: michael@0: logger = Logger() michael@0: d = CallDeque() michael@0: d.append(logger.log, 'foo') michael@0: d.append(logger.log, 'bar') michael@0: d.append(logger.staticlog, logger, 'baz') michael@0: d.append(do_log, logger, 'qux') michael@0: self.assertEqual(logger._log, []) michael@0: d.execute() michael@0: self.assertEqual(logger._log, ['foo', 'bar', 'baz', 'qux']) michael@0: michael@0: michael@0: class TestComponent(unittest.TestCase): michael@0: def do_split(self, string, name, options): michael@0: n, o = Component._split_component_and_options(string) michael@0: self.assertEqual(name, n) michael@0: self.assertEqual(options, o) michael@0: michael@0: def test_component_split_component_and_options(self): michael@0: self.do_split('component', 'component', {}) michael@0: self.do_split('trailingspace ', 'trailingspace', {}) michael@0: self.do_split(' leadingspace', 'leadingspace', {}) michael@0: self.do_split(' trim ', 'trim', {}) michael@0: self.do_split(' trim key="value"', 'trim', {'key':'value'}) michael@0: self.do_split(' trim empty=""', 'trim', {'empty':''}) michael@0: self.do_split(' trim space=" "', 'trim', {'space':' '}) michael@0: self.do_split('component key="value" key2="second" ', michael@0: 'component', {'key':'value', 'key2':'second'}) michael@0: self.do_split( 'trim key=" value with spaces " key2="spaces again"', michael@0: 'trim', {'key':' value with spaces ', 'key2': 'spaces again'}) michael@0: michael@0: def do_split_error(self, string): michael@0: self.assertRaises(ValueError, Component._split_component_and_options, string) michael@0: michael@0: def test_component_split_component_and_options_errors(self): michael@0: self.do_split_error('"component') michael@0: self.do_split_error('comp"onent') michael@0: self.do_split_error('component"') michael@0: self.do_split_error('"component"') michael@0: self.do_split_error('=component') michael@0: self.do_split_error('comp=onent') michael@0: self.do_split_error('component=') michael@0: self.do_split_error('key="val"') michael@0: self.do_split_error('component key=') michael@0: self.do_split_error('component key="val') michael@0: self.do_split_error('component key=val"') michael@0: self.do_split_error('component key="val" x') michael@0: self.do_split_error('component x key="val"') michael@0: self.do_split_error('component key1="val" x key2="val"') michael@0: michael@0: def do_from_string(self, string, name, destdir=''): michael@0: component = Component.from_string(string) michael@0: self.assertEqual(name, component.name) michael@0: self.assertEqual(destdir, component.destdir) michael@0: michael@0: def test_component_from_string(self): michael@0: self.do_from_string('component', 'component') michael@0: self.do_from_string('component-with-hyphen', 'component-with-hyphen') michael@0: self.do_from_string('component destdir="foo/bar"', 'component', 'foo/bar') michael@0: self.do_from_string('component destdir="bar spc"', 'component', 'bar spc') michael@0: self.assertRaises(ErrorMessage, Component.from_string, '') michael@0: self.assertRaises(ErrorMessage, Component.from_string, 'component novalue=') michael@0: self.assertRaises(ErrorMessage, Component.from_string, 'component badoption=badvalue') michael@0: michael@0: michael@0: if __name__ == '__main__': michael@0: mozunit.main()