|
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 from __future__ import unicode_literals |
|
6 |
|
7 import os |
|
8 |
|
9 import mozunit |
|
10 |
|
11 from mozpack.copier import ( |
|
12 FileCopier, |
|
13 FileRegistry, |
|
14 ) |
|
15 from mozpack.manifests import ( |
|
16 InstallManifest, |
|
17 ) |
|
18 from mozpack.test.test_files import TestWithTmpDir |
|
19 |
|
20 |
|
21 class TestInstallManifest(TestWithTmpDir): |
|
22 def test_construct(self): |
|
23 m = InstallManifest() |
|
24 self.assertEqual(len(m), 0) |
|
25 |
|
26 def test_adds(self): |
|
27 m = InstallManifest() |
|
28 m.add_symlink('s_source', 's_dest') |
|
29 m.add_copy('c_source', 'c_dest') |
|
30 m.add_required_exists('e_dest') |
|
31 m.add_optional_exists('o_dest') |
|
32 m.add_pattern_symlink('ps_base', 'ps/*', 'ps_dest') |
|
33 m.add_pattern_copy('pc_base', 'pc/**', 'pc_dest') |
|
34 m.add_preprocess('p_source', 'p_dest', 'p_source.pp') |
|
35 |
|
36 self.assertEqual(len(m), 7) |
|
37 self.assertIn('s_dest', m) |
|
38 self.assertIn('c_dest', m) |
|
39 self.assertIn('p_dest', m) |
|
40 self.assertIn('e_dest', m) |
|
41 self.assertIn('o_dest', m) |
|
42 |
|
43 with self.assertRaises(ValueError): |
|
44 m.add_symlink('s_other', 's_dest') |
|
45 |
|
46 with self.assertRaises(ValueError): |
|
47 m.add_copy('c_other', 'c_dest') |
|
48 |
|
49 with self.assertRaises(ValueError): |
|
50 m.add_preprocess('p_other', 'p_dest', 'p_other.pp') |
|
51 |
|
52 with self.assertRaises(ValueError): |
|
53 m.add_required_exists('e_dest') |
|
54 |
|
55 with self.assertRaises(ValueError): |
|
56 m.add_optional_exists('o_dest') |
|
57 |
|
58 with self.assertRaises(ValueError): |
|
59 m.add_pattern_symlink('ps_base', 'ps/*', 'ps_dest') |
|
60 |
|
61 with self.assertRaises(ValueError): |
|
62 m.add_pattern_copy('pc_base', 'pc/**', 'pc_dest') |
|
63 |
|
64 def _get_test_manifest(self): |
|
65 m = InstallManifest() |
|
66 m.add_symlink(self.tmppath('s_source'), 's_dest') |
|
67 m.add_copy(self.tmppath('c_source'), 'c_dest') |
|
68 m.add_preprocess(self.tmppath('p_source'), 'p_dest', self.tmppath('p_source.pp'), '#', {'FOO':'BAR', 'BAZ':'QUX'}) |
|
69 m.add_required_exists('e_dest') |
|
70 m.add_optional_exists('o_dest') |
|
71 m.add_pattern_symlink('ps_base', '*', 'ps_dest') |
|
72 m.add_pattern_copy('pc_base', '**', 'pc_dest') |
|
73 |
|
74 return m |
|
75 |
|
76 def test_serialization(self): |
|
77 m = self._get_test_manifest() |
|
78 |
|
79 p = self.tmppath('m') |
|
80 m.write(path=p) |
|
81 self.assertTrue(os.path.isfile(p)) |
|
82 |
|
83 with open(p, 'rb') as fh: |
|
84 c = fh.read() |
|
85 |
|
86 self.assertEqual(c.count('\n'), 8) |
|
87 |
|
88 lines = c.splitlines() |
|
89 self.assertEqual(len(lines), 8) |
|
90 |
|
91 self.assertEqual(lines[0], '4') |
|
92 |
|
93 m2 = InstallManifest(path=p) |
|
94 self.assertEqual(m, m2) |
|
95 p2 = self.tmppath('m2') |
|
96 m2.write(path=p2) |
|
97 |
|
98 with open(p2, 'rb') as fh: |
|
99 c2 = fh.read() |
|
100 |
|
101 self.assertEqual(c, c2) |
|
102 |
|
103 def test_populate_registry(self): |
|
104 m = self._get_test_manifest() |
|
105 r = FileRegistry() |
|
106 m.populate_registry(r) |
|
107 |
|
108 self.assertEqual(len(r), 5) |
|
109 self.assertEqual(r.paths(), ['c_dest', 'e_dest', 'o_dest', 'p_dest', 's_dest']) |
|
110 |
|
111 def test_pattern_expansion(self): |
|
112 source = self.tmppath('source') |
|
113 os.mkdir(source) |
|
114 os.mkdir('%s/base' % source) |
|
115 os.mkdir('%s/base/foo' % source) |
|
116 |
|
117 with open('%s/base/foo/file1' % source, 'a'): |
|
118 pass |
|
119 |
|
120 with open('%s/base/foo/file2' % source, 'a'): |
|
121 pass |
|
122 |
|
123 m = InstallManifest() |
|
124 m.add_pattern_symlink('%s/base' % source, '**', 'dest') |
|
125 |
|
126 c = FileCopier() |
|
127 m.populate_registry(c) |
|
128 self.assertEqual(c.paths(), ['dest/foo/file1', 'dest/foo/file2']) |
|
129 |
|
130 def test_or(self): |
|
131 m1 = self._get_test_manifest() |
|
132 orig_length = len(m1) |
|
133 m2 = InstallManifest() |
|
134 m2.add_symlink('s_source2', 's_dest2') |
|
135 m2.add_copy('c_source2', 'c_dest2') |
|
136 |
|
137 m1 |= m2 |
|
138 |
|
139 self.assertEqual(len(m2), 2) |
|
140 self.assertEqual(len(m1), orig_length + 2) |
|
141 |
|
142 self.assertIn('s_dest2', m1) |
|
143 self.assertIn('c_dest2', m1) |
|
144 |
|
145 def test_copier_application(self): |
|
146 dest = self.tmppath('dest') |
|
147 os.mkdir(dest) |
|
148 |
|
149 to_delete = self.tmppath('dest/to_delete') |
|
150 with open(to_delete, 'a'): |
|
151 pass |
|
152 |
|
153 with open(self.tmppath('s_source'), 'wt') as fh: |
|
154 fh.write('symlink!') |
|
155 |
|
156 with open(self.tmppath('c_source'), 'wt') as fh: |
|
157 fh.write('copy!') |
|
158 |
|
159 with open(self.tmppath('p_source'), 'wt') as fh: |
|
160 fh.write('#define FOO 1\npreprocess!') |
|
161 |
|
162 with open(self.tmppath('dest/e_dest'), 'a'): |
|
163 pass |
|
164 |
|
165 with open(self.tmppath('dest/o_dest'), 'a'): |
|
166 pass |
|
167 |
|
168 m = self._get_test_manifest() |
|
169 c = FileCopier() |
|
170 m.populate_registry(c) |
|
171 result = c.copy(dest) |
|
172 |
|
173 self.assertTrue(os.path.exists(self.tmppath('dest/s_dest'))) |
|
174 self.assertTrue(os.path.exists(self.tmppath('dest/c_dest'))) |
|
175 self.assertTrue(os.path.exists(self.tmppath('dest/p_dest'))) |
|
176 self.assertTrue(os.path.exists(self.tmppath('dest/e_dest'))) |
|
177 self.assertTrue(os.path.exists(self.tmppath('dest/o_dest'))) |
|
178 self.assertFalse(os.path.exists(to_delete)) |
|
179 |
|
180 with open(self.tmppath('dest/s_dest'), 'rt') as fh: |
|
181 self.assertEqual(fh.read(), 'symlink!') |
|
182 |
|
183 with open(self.tmppath('dest/c_dest'), 'rt') as fh: |
|
184 self.assertEqual(fh.read(), 'copy!') |
|
185 |
|
186 with open(self.tmppath('dest/p_dest'), 'rt') as fh: |
|
187 self.assertEqual(fh.read(), 'preprocess!') |
|
188 |
|
189 self.assertEqual(result.updated_files, set(self.tmppath(p) for p in ( |
|
190 'dest/s_dest', 'dest/c_dest', 'dest/p_dest'))) |
|
191 self.assertEqual(result.existing_files, |
|
192 set([self.tmppath('dest/e_dest'), self.tmppath('dest/o_dest')])) |
|
193 self.assertEqual(result.removed_files, {to_delete}) |
|
194 self.assertEqual(result.removed_directories, set()) |
|
195 |
|
196 def test_preprocessor(self): |
|
197 manifest = self.tmppath('m') |
|
198 deps = self.tmppath('m.pp') |
|
199 dest = self.tmppath('dest') |
|
200 include = self.tmppath('p_incl') |
|
201 |
|
202 with open(include, 'wt') as fh: |
|
203 fh.write('#define INCL\n') |
|
204 time = os.path.getmtime(include) - 3 |
|
205 os.utime(include, (time, time)) |
|
206 |
|
207 with open(self.tmppath('p_source'), 'wt') as fh: |
|
208 fh.write('#ifdef FOO\n#if BAZ == QUX\nPASS1\n#endif\n#endif\n') |
|
209 fh.write('#ifdef DEPTEST\nPASS2\n#endif\n') |
|
210 fh.write('#include p_incl\n#ifdef INCLTEST\nPASS3\n#endif\n') |
|
211 time = os.path.getmtime(self.tmppath('p_source')) - 3 |
|
212 os.utime(self.tmppath('p_source'), (time, time)) |
|
213 |
|
214 # Create and write a manifest with the preprocessed file, then apply it. |
|
215 # This should write out our preprocessed file. |
|
216 m = InstallManifest() |
|
217 m.add_preprocess(self.tmppath('p_source'), 'p_dest', deps, '#', {'FOO':'BAR', 'BAZ':'QUX'}) |
|
218 m.write(path=manifest) |
|
219 |
|
220 m = InstallManifest(path=manifest) |
|
221 c = FileCopier() |
|
222 m.populate_registry(c) |
|
223 c.copy(dest) |
|
224 |
|
225 self.assertTrue(os.path.exists(self.tmppath('dest/p_dest'))) |
|
226 |
|
227 with open(self.tmppath('dest/p_dest'), 'rt') as fh: |
|
228 self.assertEqual(fh.read(), 'PASS1\n') |
|
229 |
|
230 # Create a second manifest with the preprocessed file, then apply it. |
|
231 # Since this manifest does not exist on the disk, there should not be a |
|
232 # dependency on it, and the preprocessed file should not be modified. |
|
233 m2 = InstallManifest() |
|
234 m2.add_preprocess(self.tmppath('p_source'), 'p_dest', deps, '#', {'DEPTEST':True}) |
|
235 c = FileCopier() |
|
236 m2.populate_registry(c) |
|
237 result = c.copy(dest) |
|
238 |
|
239 self.assertFalse(self.tmppath('dest/p_dest') in result.updated_files) |
|
240 self.assertTrue(self.tmppath('dest/p_dest') in result.existing_files) |
|
241 |
|
242 # Write out the second manifest, then load it back in from the disk. |
|
243 # This should add the dependency on the manifest file, so our |
|
244 # preprocessed file should be regenerated with the new defines. |
|
245 # We also set the mtime on the destination file back, so it will be |
|
246 # older than the manifest file. |
|
247 m2.write(path=manifest) |
|
248 time = os.path.getmtime(manifest) - 1 |
|
249 os.utime(self.tmppath('dest/p_dest'), (time, time)) |
|
250 m2 = InstallManifest(path=manifest) |
|
251 c = FileCopier() |
|
252 m2.populate_registry(c) |
|
253 self.assertTrue(c.copy(dest)) |
|
254 |
|
255 with open(self.tmppath('dest/p_dest'), 'rt') as fh: |
|
256 self.assertEqual(fh.read(), 'PASS2\n') |
|
257 |
|
258 # Set the time on the manifest back, so it won't be picked up as |
|
259 # modified in the next test |
|
260 time = os.path.getmtime(manifest) - 1 |
|
261 os.utime(manifest, (time, time)) |
|
262 |
|
263 # Update the contents of a file included by the source file. This should |
|
264 # cause the destination to be regenerated. |
|
265 with open(include, 'wt') as fh: |
|
266 fh.write('#define INCLTEST\n') |
|
267 |
|
268 time = os.path.getmtime(include) - 1 |
|
269 os.utime(self.tmppath('dest/p_dest'), (time, time)) |
|
270 c = FileCopier() |
|
271 m2.populate_registry(c) |
|
272 self.assertTrue(c.copy(dest)) |
|
273 |
|
274 with open(self.tmppath('dest/p_dest'), 'rt') as fh: |
|
275 self.assertEqual(fh.read(), 'PASS2\nPASS3\n') |
|
276 |
|
277 def test_preprocessor_dependencies(self): |
|
278 manifest = self.tmppath('m') |
|
279 deps = self.tmppath('m.pp') |
|
280 dest = self.tmppath('dest') |
|
281 source = self.tmppath('p_source') |
|
282 destfile = self.tmppath('dest/p_dest') |
|
283 include = self.tmppath('p_incl') |
|
284 os.mkdir(dest) |
|
285 |
|
286 with open(source, 'wt') as fh: |
|
287 fh.write('#define SRC\nSOURCE\n') |
|
288 time = os.path.getmtime(source) - 3 |
|
289 os.utime(source, (time, time)) |
|
290 |
|
291 with open(include, 'wt') as fh: |
|
292 fh.write('INCLUDE\n') |
|
293 time = os.path.getmtime(source) - 3 |
|
294 os.utime(include, (time, time)) |
|
295 |
|
296 # Create and write a manifest with the preprocessed file. |
|
297 m = InstallManifest() |
|
298 m.add_preprocess(source, 'p_dest', deps, '#', {'FOO':'BAR', 'BAZ':'QUX'}) |
|
299 m.write(path=manifest) |
|
300 |
|
301 time = os.path.getmtime(source) - 5 |
|
302 os.utime(manifest, (time, time)) |
|
303 |
|
304 # Now read the manifest back in, and apply it. This should write out |
|
305 # our preprocessed file. |
|
306 m = InstallManifest(path=manifest) |
|
307 c = FileCopier() |
|
308 m.populate_registry(c) |
|
309 self.assertTrue(c.copy(dest)) |
|
310 |
|
311 with open(destfile, 'rt') as fh: |
|
312 self.assertEqual(fh.read(), 'SOURCE\n') |
|
313 |
|
314 # Next, modify the source to #INCLUDE another file. |
|
315 with open(source, 'wt') as fh: |
|
316 fh.write('SOURCE\n#include p_incl\n') |
|
317 time = os.path.getmtime(source) - 1 |
|
318 os.utime(destfile, (time, time)) |
|
319 |
|
320 # Apply the manifest, and confirm that it also reads the newly included |
|
321 # file. |
|
322 m = InstallManifest(path=manifest) |
|
323 c = FileCopier() |
|
324 m.populate_registry(c) |
|
325 c.copy(dest) |
|
326 |
|
327 with open(destfile, 'rt') as fh: |
|
328 self.assertEqual(fh.read(), 'SOURCE\nINCLUDE\n') |
|
329 |
|
330 # Set the time on the source file back, so it won't be picked up as |
|
331 # modified in the next test. |
|
332 time = os.path.getmtime(source) - 1 |
|
333 os.utime(source, (time, time)) |
|
334 |
|
335 # Now, modify the include file (but not the original source). |
|
336 with open(include, 'wt') as fh: |
|
337 fh.write('INCLUDE MODIFIED\n') |
|
338 time = os.path.getmtime(include) - 1 |
|
339 os.utime(destfile, (time, time)) |
|
340 |
|
341 # Apply the manifest, and confirm that the change to the include file |
|
342 # is detected. That should cause the preprocessor to run again. |
|
343 m = InstallManifest(path=manifest) |
|
344 c = FileCopier() |
|
345 m.populate_registry(c) |
|
346 c.copy(dest) |
|
347 |
|
348 with open(destfile, 'rt') as fh: |
|
349 self.assertEqual(fh.read(), 'SOURCE\nINCLUDE MODIFIED\n') |
|
350 |
|
351 if __name__ == '__main__': |
|
352 mozunit.main() |