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 from __future__ import unicode_literals
7 import os
9 import mozunit
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
21 class TestInstallManifest(TestWithTmpDir):
22 def test_construct(self):
23 m = InstallManifest()
24 self.assertEqual(len(m), 0)
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')
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)
43 with self.assertRaises(ValueError):
44 m.add_symlink('s_other', 's_dest')
46 with self.assertRaises(ValueError):
47 m.add_copy('c_other', 'c_dest')
49 with self.assertRaises(ValueError):
50 m.add_preprocess('p_other', 'p_dest', 'p_other.pp')
52 with self.assertRaises(ValueError):
53 m.add_required_exists('e_dest')
55 with self.assertRaises(ValueError):
56 m.add_optional_exists('o_dest')
58 with self.assertRaises(ValueError):
59 m.add_pattern_symlink('ps_base', 'ps/*', 'ps_dest')
61 with self.assertRaises(ValueError):
62 m.add_pattern_copy('pc_base', 'pc/**', 'pc_dest')
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')
74 return m
76 def test_serialization(self):
77 m = self._get_test_manifest()
79 p = self.tmppath('m')
80 m.write(path=p)
81 self.assertTrue(os.path.isfile(p))
83 with open(p, 'rb') as fh:
84 c = fh.read()
86 self.assertEqual(c.count('\n'), 8)
88 lines = c.splitlines()
89 self.assertEqual(len(lines), 8)
91 self.assertEqual(lines[0], '4')
93 m2 = InstallManifest(path=p)
94 self.assertEqual(m, m2)
95 p2 = self.tmppath('m2')
96 m2.write(path=p2)
98 with open(p2, 'rb') as fh:
99 c2 = fh.read()
101 self.assertEqual(c, c2)
103 def test_populate_registry(self):
104 m = self._get_test_manifest()
105 r = FileRegistry()
106 m.populate_registry(r)
108 self.assertEqual(len(r), 5)
109 self.assertEqual(r.paths(), ['c_dest', 'e_dest', 'o_dest', 'p_dest', 's_dest'])
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)
117 with open('%s/base/foo/file1' % source, 'a'):
118 pass
120 with open('%s/base/foo/file2' % source, 'a'):
121 pass
123 m = InstallManifest()
124 m.add_pattern_symlink('%s/base' % source, '**', 'dest')
126 c = FileCopier()
127 m.populate_registry(c)
128 self.assertEqual(c.paths(), ['dest/foo/file1', 'dest/foo/file2'])
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')
137 m1 |= m2
139 self.assertEqual(len(m2), 2)
140 self.assertEqual(len(m1), orig_length + 2)
142 self.assertIn('s_dest2', m1)
143 self.assertIn('c_dest2', m1)
145 def test_copier_application(self):
146 dest = self.tmppath('dest')
147 os.mkdir(dest)
149 to_delete = self.tmppath('dest/to_delete')
150 with open(to_delete, 'a'):
151 pass
153 with open(self.tmppath('s_source'), 'wt') as fh:
154 fh.write('symlink!')
156 with open(self.tmppath('c_source'), 'wt') as fh:
157 fh.write('copy!')
159 with open(self.tmppath('p_source'), 'wt') as fh:
160 fh.write('#define FOO 1\npreprocess!')
162 with open(self.tmppath('dest/e_dest'), 'a'):
163 pass
165 with open(self.tmppath('dest/o_dest'), 'a'):
166 pass
168 m = self._get_test_manifest()
169 c = FileCopier()
170 m.populate_registry(c)
171 result = c.copy(dest)
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))
180 with open(self.tmppath('dest/s_dest'), 'rt') as fh:
181 self.assertEqual(fh.read(), 'symlink!')
183 with open(self.tmppath('dest/c_dest'), 'rt') as fh:
184 self.assertEqual(fh.read(), 'copy!')
186 with open(self.tmppath('dest/p_dest'), 'rt') as fh:
187 self.assertEqual(fh.read(), 'preprocess!')
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())
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')
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))
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))
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)
220 m = InstallManifest(path=manifest)
221 c = FileCopier()
222 m.populate_registry(c)
223 c.copy(dest)
225 self.assertTrue(os.path.exists(self.tmppath('dest/p_dest')))
227 with open(self.tmppath('dest/p_dest'), 'rt') as fh:
228 self.assertEqual(fh.read(), 'PASS1\n')
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)
239 self.assertFalse(self.tmppath('dest/p_dest') in result.updated_files)
240 self.assertTrue(self.tmppath('dest/p_dest') in result.existing_files)
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))
255 with open(self.tmppath('dest/p_dest'), 'rt') as fh:
256 self.assertEqual(fh.read(), 'PASS2\n')
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))
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')
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))
274 with open(self.tmppath('dest/p_dest'), 'rt') as fh:
275 self.assertEqual(fh.read(), 'PASS2\nPASS3\n')
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)
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))
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))
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)
301 time = os.path.getmtime(source) - 5
302 os.utime(manifest, (time, time))
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))
311 with open(destfile, 'rt') as fh:
312 self.assertEqual(fh.read(), 'SOURCE\n')
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))
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)
327 with open(destfile, 'rt') as fh:
328 self.assertEqual(fh.read(), 'SOURCE\nINCLUDE\n')
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))
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))
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)
348 with open(destfile, 'rt') as fh:
349 self.assertEqual(fh.read(), 'SOURCE\nINCLUDE MODIFIED\n')
351 if __name__ == '__main__':
352 mozunit.main()