|
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/. |
|
4 |
|
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 |
|
27 |
|
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 ''' |
|
42 |
|
43 |
|
44 class TestPreprocessManifest(unittest.TestCase): |
|
45 MANIFEST_PATH = os.path.join(os.path.abspath(os.curdir), 'manifest') |
|
46 |
|
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 ] |
|
54 |
|
55 def setUp(self): |
|
56 class MockSink(object): |
|
57 def __init__(self): |
|
58 self.log = [] |
|
59 |
|
60 def add(self, component, path): |
|
61 self._log(errors.get_context(), 'add', repr(component), path) |
|
62 |
|
63 def remove(self, component, path): |
|
64 self._log(errors.get_context(), 'remove', repr(component), path) |
|
65 |
|
66 def _log(self, *args): |
|
67 self.log.append(args) |
|
68 |
|
69 self.sink = MockSink() |
|
70 |
|
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) |
|
75 |
|
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 ) |
|
85 |
|
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')]) |
|
92 |
|
93 |
|
94 class MockFinder(object): |
|
95 def __init__(self, files): |
|
96 self.files = files |
|
97 self.log = [] |
|
98 |
|
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] |
|
104 |
|
105 def __iter__(self): |
|
106 return self.find('') |
|
107 |
|
108 |
|
109 class MockFormatter(object): |
|
110 def __init__(self): |
|
111 self.log = [] |
|
112 |
|
113 def add_base(self, *args): |
|
114 self._log(errors.get_context(), 'add_base', *args) |
|
115 |
|
116 def add_manifest(self, *args): |
|
117 self._log(errors.get_context(), 'add_manifest', *args) |
|
118 |
|
119 def add_interfaces(self, *args): |
|
120 self._log(errors.get_context(), 'add_interfaces', *args) |
|
121 |
|
122 def add(self, *args): |
|
123 self._log(errors.get_context(), 'add', *args) |
|
124 |
|
125 def _log(self, *args): |
|
126 self.log.append(args) |
|
127 |
|
128 |
|
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 |
|
135 |
|
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) |
|
144 |
|
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) |
|
150 |
|
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) |
|
163 |
|
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) |
|
173 |
|
174 self.assertEqual(formatter.log, []) |
|
175 |
|
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 ]) |
|
196 |
|
197 self.assertEqual(packager.get_bases(), set(['', 'qux'])) |
|
198 |
|
199 |
|
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') |
|
225 |
|
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 ]) |
|
236 |
|
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 ]) |
|
245 |
|
246 |
|
247 class TestCallDeque(unittest.TestCase): |
|
248 def test_call_deque(self): |
|
249 class Logger(object): |
|
250 def __init__(self): |
|
251 self._log = [] |
|
252 |
|
253 def log(self, str): |
|
254 self._log.append(str) |
|
255 |
|
256 @staticmethod |
|
257 def staticlog(logger, str): |
|
258 logger.log(str) |
|
259 |
|
260 def do_log(logger, str): |
|
261 logger.log(str) |
|
262 |
|
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']) |
|
272 |
|
273 |
|
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) |
|
279 |
|
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'}) |
|
292 |
|
293 def do_split_error(self, string): |
|
294 self.assertRaises(ValueError, Component._split_component_and_options, string) |
|
295 |
|
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"') |
|
311 |
|
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) |
|
316 |
|
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') |
|
325 |
|
326 |
|
327 if __name__ == '__main__': |
|
328 mozunit.main() |