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