Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing code.
1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 import unittest
6 import mozunit
7 import os
8 from mozpack.packager import (
9 preprocess_manifest,
10 CallDeque,
11 Component,
12 SimplePackager,
13 SimpleManifestSink,
14 )
15 from mozpack.files import GeneratedFile
16 from mozpack.chrome.manifest import (
17 ManifestResource,
18 ManifestContent,
19 )
20 from mozunit import MockedOpen
21 from mozbuild.preprocessor import Preprocessor
22 from mozpack.errors import (
23 errors,
24 ErrorMessage,
25 )
26 import mozpack.path
28 MANIFEST = '''
29 bar/*
30 [foo]
31 foo/*
32 -foo/bar
33 chrome.manifest
34 [zot destdir="destdir"]
35 foo/zot
36 ; comment
37 #ifdef baz
38 [baz]
39 baz@SUFFIX@
40 #endif
41 '''
44 class TestPreprocessManifest(unittest.TestCase):
45 MANIFEST_PATH = os.path.join(os.path.abspath(os.curdir), 'manifest')
47 EXPECTED_LOG = [
48 ((MANIFEST_PATH, 2), 'add', '', 'bar/*'),
49 ((MANIFEST_PATH, 4), 'add', 'foo', 'foo/*'),
50 ((MANIFEST_PATH, 5), 'remove', 'foo', 'foo/bar'),
51 ((MANIFEST_PATH, 6), 'add', 'foo', 'chrome.manifest'),
52 ((MANIFEST_PATH, 8), 'add', 'zot destdir="destdir"', 'foo/zot'),
53 ]
55 def setUp(self):
56 class MockSink(object):
57 def __init__(self):
58 self.log = []
60 def add(self, component, path):
61 self._log(errors.get_context(), 'add', repr(component), path)
63 def remove(self, component, path):
64 self._log(errors.get_context(), 'remove', repr(component), path)
66 def _log(self, *args):
67 self.log.append(args)
69 self.sink = MockSink()
71 def test_preprocess_manifest(self):
72 with MockedOpen({'manifest': MANIFEST}):
73 preprocess_manifest(self.sink, 'manifest')
74 self.assertEqual(self.sink.log, self.EXPECTED_LOG)
76 def test_preprocess_manifest_missing_define(self):
77 with MockedOpen({'manifest': MANIFEST}):
78 self.assertRaises(
79 Preprocessor.Error,
80 preprocess_manifest,
81 self.sink,
82 'manifest',
83 {'baz': 1}
84 )
86 def test_preprocess_manifest_defines(self):
87 with MockedOpen({'manifest': MANIFEST}):
88 preprocess_manifest(self.sink, 'manifest',
89 {'baz': 1, 'SUFFIX': '.exe'})
90 self.assertEqual(self.sink.log, self.EXPECTED_LOG +
91 [((self.MANIFEST_PATH, 12), 'add', 'baz', 'baz.exe')])
94 class MockFinder(object):
95 def __init__(self, files):
96 self.files = files
97 self.log = []
99 def find(self, path):
100 self.log.append(path)
101 for f in sorted(self.files):
102 if mozpack.path.match(f, path):
103 yield f, self.files[f]
105 def __iter__(self):
106 return self.find('')
109 class MockFormatter(object):
110 def __init__(self):
111 self.log = []
113 def add_base(self, *args):
114 self._log(errors.get_context(), 'add_base', *args)
116 def add_manifest(self, *args):
117 self._log(errors.get_context(), 'add_manifest', *args)
119 def add_interfaces(self, *args):
120 self._log(errors.get_context(), 'add_interfaces', *args)
122 def add(self, *args):
123 self._log(errors.get_context(), 'add', *args)
125 def _log(self, *args):
126 self.log.append(args)
129 class TestSimplePackager(unittest.TestCase):
130 def test_simple_packager(self):
131 class GeneratedFileWithPath(GeneratedFile):
132 def __init__(self, path, content):
133 GeneratedFile.__init__(self, content)
134 self.path = path
136 formatter = MockFormatter()
137 packager = SimplePackager(formatter)
138 curdir = os.path.abspath(os.curdir)
139 file = GeneratedFileWithPath(os.path.join(curdir, 'foo',
140 'bar.manifest'),
141 'resource bar bar/\ncontent bar bar/')
142 with errors.context('manifest', 1):
143 packager.add('foo/bar.manifest', file)
145 file = GeneratedFileWithPath(os.path.join(curdir, 'foo',
146 'baz.manifest'),
147 'resource baz baz/')
148 with errors.context('manifest', 2):
149 packager.add('bar/baz.manifest', file)
151 with errors.context('manifest', 3):
152 packager.add('qux/qux.manifest',
153 GeneratedFile('resource qux qux/'))
154 bar_xpt = GeneratedFile('bar.xpt')
155 qux_xpt = GeneratedFile('qux.xpt')
156 foo_html = GeneratedFile('foo_html')
157 bar_html = GeneratedFile('bar_html')
158 with errors.context('manifest', 4):
159 packager.add('foo/bar.xpt', bar_xpt)
160 with errors.context('manifest', 5):
161 packager.add('foo/bar/foo.html', foo_html)
162 packager.add('foo/bar/bar.html', bar_html)
164 file = GeneratedFileWithPath(os.path.join(curdir, 'foo.manifest'),
165 ''.join([
166 'manifest foo/bar.manifest\n',
167 'manifest bar/baz.manifest\n',
168 ]))
169 with errors.context('manifest', 6):
170 packager.add('foo.manifest', file)
171 with errors.context('manifest', 7):
172 packager.add('foo/qux.xpt', qux_xpt)
174 self.assertEqual(formatter.log, [])
176 with errors.context('dummy', 1):
177 packager.close()
178 self.maxDiff = None
179 # The formatter is expected to reorder the manifest entries so that
180 # chrome entries appear before the others.
181 self.assertEqual(formatter.log, [
182 (('dummy', 1), 'add_base', 'qux'),
183 ((os.path.join(curdir, 'foo', 'bar.manifest'), 2),
184 'add_manifest', ManifestContent('foo', 'bar', 'bar/')),
185 ((os.path.join(curdir, 'foo', 'bar.manifest'), 1),
186 'add_manifest', ManifestResource('foo', 'bar', 'bar/')),
187 (('bar/baz.manifest', 1),
188 'add_manifest', ManifestResource('bar', 'baz', 'baz/')),
189 (('qux/qux.manifest', 1),
190 'add_manifest', ManifestResource('qux', 'qux', 'qux/')),
191 (('manifest', 4), 'add_interfaces', 'foo/bar.xpt', bar_xpt),
192 (('manifest', 7), 'add_interfaces', 'foo/qux.xpt', qux_xpt),
193 (('manifest', 5), 'add', 'foo/bar/foo.html', foo_html),
194 (('manifest', 5), 'add', 'foo/bar/bar.html', bar_html),
195 ])
197 self.assertEqual(packager.get_bases(), set(['', 'qux']))
200 class TestSimpleManifestSink(unittest.TestCase):
201 def test_simple_manifest_parser(self):
202 formatter = MockFormatter()
203 foobar = GeneratedFile('foobar')
204 foobaz = GeneratedFile('foobaz')
205 fooqux = GeneratedFile('fooqux')
206 foozot = GeneratedFile('foozot')
207 finder = MockFinder({
208 'bin/foo/bar': foobar,
209 'bin/foo/baz': foobaz,
210 'bin/foo/qux': fooqux,
211 'bin/foo/zot': foozot,
212 'bin/foo/chrome.manifest': GeneratedFile('resource foo foo/'),
213 'bin/chrome.manifest':
214 GeneratedFile('manifest foo/chrome.manifest'),
215 })
216 parser = SimpleManifestSink(finder, formatter)
217 component0 = Component('component0')
218 component1 = Component('component1')
219 component2 = Component('component2', destdir='destdir')
220 parser.add(component0, 'bin/foo/b*')
221 parser.add(component1, 'bin/foo/qux')
222 parser.add(component1, 'bin/foo/chrome.manifest')
223 parser.add(component2, 'bin/foo/zot')
224 self.assertRaises(ErrorMessage, parser.add, 'component1', 'bin/bar')
226 self.assertEqual(formatter.log, [])
227 parser.close()
228 self.assertEqual(formatter.log, [
229 (('foo/chrome.manifest', 1),
230 'add_manifest', ManifestResource('foo', 'foo', 'foo/')),
231 (None, 'add', 'foo/bar', foobar),
232 (None, 'add', 'foo/baz', foobaz),
233 (None, 'add', 'foo/qux', fooqux),
234 (None, 'add', 'destdir/foo/zot', foozot),
235 ])
237 self.assertEqual(finder.log, [
238 'bin/foo/b*',
239 'bin/foo/qux',
240 'bin/foo/chrome.manifest',
241 'bin/foo/zot',
242 'bin/bar',
243 'bin/chrome.manifest'
244 ])
247 class TestCallDeque(unittest.TestCase):
248 def test_call_deque(self):
249 class Logger(object):
250 def __init__(self):
251 self._log = []
253 def log(self, str):
254 self._log.append(str)
256 @staticmethod
257 def staticlog(logger, str):
258 logger.log(str)
260 def do_log(logger, str):
261 logger.log(str)
263 logger = Logger()
264 d = CallDeque()
265 d.append(logger.log, 'foo')
266 d.append(logger.log, 'bar')
267 d.append(logger.staticlog, logger, 'baz')
268 d.append(do_log, logger, 'qux')
269 self.assertEqual(logger._log, [])
270 d.execute()
271 self.assertEqual(logger._log, ['foo', 'bar', 'baz', 'qux'])
274 class TestComponent(unittest.TestCase):
275 def do_split(self, string, name, options):
276 n, o = Component._split_component_and_options(string)
277 self.assertEqual(name, n)
278 self.assertEqual(options, o)
280 def test_component_split_component_and_options(self):
281 self.do_split('component', 'component', {})
282 self.do_split('trailingspace ', 'trailingspace', {})
283 self.do_split(' leadingspace', 'leadingspace', {})
284 self.do_split(' trim ', 'trim', {})
285 self.do_split(' trim key="value"', 'trim', {'key':'value'})
286 self.do_split(' trim empty=""', 'trim', {'empty':''})
287 self.do_split(' trim space=" "', 'trim', {'space':' '})
288 self.do_split('component key="value" key2="second" ',
289 'component', {'key':'value', 'key2':'second'})
290 self.do_split( 'trim key=" value with spaces " key2="spaces again"',
291 'trim', {'key':' value with spaces ', 'key2': 'spaces again'})
293 def do_split_error(self, string):
294 self.assertRaises(ValueError, Component._split_component_and_options, string)
296 def test_component_split_component_and_options_errors(self):
297 self.do_split_error('"component')
298 self.do_split_error('comp"onent')
299 self.do_split_error('component"')
300 self.do_split_error('"component"')
301 self.do_split_error('=component')
302 self.do_split_error('comp=onent')
303 self.do_split_error('component=')
304 self.do_split_error('key="val"')
305 self.do_split_error('component key=')
306 self.do_split_error('component key="val')
307 self.do_split_error('component key=val"')
308 self.do_split_error('component key="val" x')
309 self.do_split_error('component x key="val"')
310 self.do_split_error('component key1="val" x key2="val"')
312 def do_from_string(self, string, name, destdir=''):
313 component = Component.from_string(string)
314 self.assertEqual(name, component.name)
315 self.assertEqual(destdir, component.destdir)
317 def test_component_from_string(self):
318 self.do_from_string('component', 'component')
319 self.do_from_string('component-with-hyphen', 'component-with-hyphen')
320 self.do_from_string('component destdir="foo/bar"', 'component', 'foo/bar')
321 self.do_from_string('component destdir="bar spc"', 'component', 'bar spc')
322 self.assertRaises(ErrorMessage, Component.from_string, '')
323 self.assertRaises(ErrorMessage, Component.from_string, 'component novalue=')
324 self.assertRaises(ErrorMessage, Component.from_string, 'component badoption=badvalue')
327 if __name__ == '__main__':
328 mozunit.main()