python/mozbuild/mozpack/test/test_files.py

branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
equal deleted inserted replaced
-1:000000000000 0:67b5ebd7e3db
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 mozbuild.util import ensureParentDir
6
7 from mozpack.errors import ErrorMessage
8 from mozpack.files import (
9 AbsoluteSymlinkFile,
10 DeflatedFile,
11 Dest,
12 ExistingFile,
13 FileFinder,
14 File,
15 GeneratedFile,
16 JarFinder,
17 ManifestFile,
18 MinifiedJavaScript,
19 MinifiedProperties,
20 PreprocessedFile,
21 XPTFile,
22 )
23 from mozpack.mozjar import (
24 JarReader,
25 JarWriter,
26 )
27 from mozpack.chrome.manifest import (
28 ManifestContent,
29 ManifestResource,
30 ManifestLocale,
31 ManifestOverride,
32 )
33 import unittest
34 import mozfile
35 import mozunit
36 import os
37 import random
38 import string
39 import sys
40 import mozpack.path
41 from tempfile import mkdtemp
42 from io import BytesIO
43 from xpt import Typelib
44
45
46 class TestWithTmpDir(unittest.TestCase):
47 def setUp(self):
48 self.tmpdir = mkdtemp()
49
50 self.symlink_supported = False
51
52 if not hasattr(os, 'symlink'):
53 return
54
55 dummy_path = self.tmppath('dummy_file')
56 with open(dummy_path, 'a'):
57 pass
58
59 try:
60 os.symlink(dummy_path, self.tmppath('dummy_symlink'))
61 os.remove(self.tmppath('dummy_symlink'))
62 except EnvironmentError:
63 pass
64 finally:
65 os.remove(dummy_path)
66
67 self.symlink_supported = True
68
69
70 def tearDown(self):
71 mozfile.rmtree(self.tmpdir)
72
73 def tmppath(self, relpath):
74 return os.path.normpath(os.path.join(self.tmpdir, relpath))
75
76
77 class MockDest(BytesIO, Dest):
78 def __init__(self):
79 BytesIO.__init__(self)
80 self.mode = None
81
82 def read(self, length=-1):
83 if self.mode != 'r':
84 self.seek(0)
85 self.mode = 'r'
86 return BytesIO.read(self, length)
87
88 def write(self, data):
89 if self.mode != 'w':
90 self.seek(0)
91 self.truncate(0)
92 self.mode = 'w'
93 return BytesIO.write(self, data)
94
95 def exists(self):
96 return True
97
98 def close(self):
99 if self.mode:
100 self.mode = None
101
102
103 class DestNoWrite(Dest):
104 def write(self, data):
105 raise RuntimeError
106
107
108 class TestDest(TestWithTmpDir):
109 def test_dest(self):
110 dest = Dest(self.tmppath('dest'))
111 self.assertFalse(dest.exists())
112 dest.write('foo')
113 self.assertTrue(dest.exists())
114 dest.write('foo')
115 self.assertEqual(dest.read(4), 'foof')
116 self.assertEqual(dest.read(), 'oo')
117 self.assertEqual(dest.read(), '')
118 dest.write('bar')
119 self.assertEqual(dest.read(4), 'bar')
120 dest.close()
121 self.assertEqual(dest.read(), 'bar')
122 dest.write('foo')
123 dest.close()
124 dest.write('qux')
125 self.assertEqual(dest.read(), 'qux')
126
127 rand = ''.join(random.choice(string.letters) for i in xrange(131597))
128 samples = [
129 '',
130 'test',
131 'fooo',
132 'same',
133 'same',
134 'Different and longer',
135 rand,
136 rand,
137 rand[:-1] + '_',
138 'test'
139 ]
140
141
142 class TestFile(TestWithTmpDir):
143 def test_file(self):
144 '''
145 Check that File.copy yields the proper content in the destination file
146 in all situations that trigger different code paths:
147 - different content
148 - different content of the same size
149 - same content
150 - long content
151 '''
152 src = self.tmppath('src')
153 dest = self.tmppath('dest')
154
155 for content in samples:
156 with open(src, 'wb') as tmp:
157 tmp.write(content)
158 # Ensure the destination file, when it exists, is older than the
159 # source
160 if os.path.exists(dest):
161 time = os.path.getmtime(src) - 1
162 os.utime(dest, (time, time))
163 f = File(src)
164 f.copy(dest)
165 self.assertEqual(content, open(dest, 'rb').read())
166 self.assertEqual(content, f.open().read())
167 self.assertEqual(content, f.open().read())
168
169 def test_file_dest(self):
170 '''
171 Similar to test_file, but for a destination object instead of
172 a destination file. This ensures the destination object is being
173 used properly by File.copy, ensuring that other subclasses of Dest
174 will work.
175 '''
176 src = self.tmppath('src')
177 dest = MockDest()
178
179 for content in samples:
180 with open(src, 'wb') as tmp:
181 tmp.write(content)
182 f = File(src)
183 f.copy(dest)
184 self.assertEqual(content, dest.getvalue())
185
186 def test_file_open(self):
187 '''
188 Test whether File.open returns an appropriately reset file object.
189 '''
190 src = self.tmppath('src')
191 content = ''.join(samples)
192 with open(src, 'wb') as tmp:
193 tmp.write(content)
194
195 f = File(src)
196 self.assertEqual(content[:42], f.open().read(42))
197 self.assertEqual(content, f.open().read())
198
199 def test_file_no_write(self):
200 '''
201 Test various conditions where File.copy is expected not to write
202 in the destination file.
203 '''
204 src = self.tmppath('src')
205 dest = self.tmppath('dest')
206
207 with open(src, 'wb') as tmp:
208 tmp.write('test')
209
210 # Initial copy
211 f = File(src)
212 f.copy(dest)
213
214 # Ensure subsequent copies won't trigger writes
215 f.copy(DestNoWrite(dest))
216 self.assertEqual('test', open(dest, 'rb').read())
217
218 # When the source file is newer, but with the same content, no copy
219 # should occur
220 time = os.path.getmtime(src) - 1
221 os.utime(dest, (time, time))
222 f.copy(DestNoWrite(dest))
223 self.assertEqual('test', open(dest, 'rb').read())
224
225 # When the source file is older than the destination file, even with
226 # different content, no copy should occur.
227 with open(src, 'wb') as tmp:
228 tmp.write('fooo')
229 time = os.path.getmtime(dest) - 1
230 os.utime(src, (time, time))
231 f.copy(DestNoWrite(dest))
232 self.assertEqual('test', open(dest, 'rb').read())
233
234 # Double check that under conditions where a copy occurs, we would get
235 # an exception.
236 time = os.path.getmtime(src) - 1
237 os.utime(dest, (time, time))
238 self.assertRaises(RuntimeError, f.copy, DestNoWrite(dest))
239
240 # skip_if_older=False is expected to force a copy in this situation.
241 f.copy(dest, skip_if_older=False)
242 self.assertEqual('fooo', open(dest, 'rb').read())
243
244
245 class TestAbsoluteSymlinkFile(TestWithTmpDir):
246 def test_absolute_relative(self):
247 AbsoluteSymlinkFile('/foo')
248
249 with self.assertRaisesRegexp(ValueError, 'Symlink target not absolute'):
250 AbsoluteSymlinkFile('./foo')
251
252 def test_symlink_file(self):
253 source = self.tmppath('test_path')
254 with open(source, 'wt') as fh:
255 fh.write('Hello world')
256
257 s = AbsoluteSymlinkFile(source)
258 dest = self.tmppath('symlink')
259 self.assertTrue(s.copy(dest))
260
261 if self.symlink_supported:
262 self.assertTrue(os.path.islink(dest))
263 link = os.readlink(dest)
264 self.assertEqual(link, source)
265 else:
266 self.assertTrue(os.path.isfile(dest))
267 content = open(dest).read()
268 self.assertEqual(content, 'Hello world')
269
270 def test_replace_file_with_symlink(self):
271 # If symlinks are supported, an existing file should be replaced by a
272 # symlink.
273 source = self.tmppath('test_path')
274 with open(source, 'wt') as fh:
275 fh.write('source')
276
277 dest = self.tmppath('dest')
278 with open(dest, 'a'):
279 pass
280
281 s = AbsoluteSymlinkFile(source)
282 s.copy(dest, skip_if_older=False)
283
284 if self.symlink_supported:
285 self.assertTrue(os.path.islink(dest))
286 link = os.readlink(dest)
287 self.assertEqual(link, source)
288 else:
289 self.assertTrue(os.path.isfile(dest))
290 content = open(dest).read()
291 self.assertEqual(content, 'source')
292
293 def test_replace_symlink(self):
294 if not self.symlink_supported:
295 return
296
297 source = self.tmppath('source')
298 with open(source, 'a'):
299 pass
300
301 dest = self.tmppath('dest')
302
303 os.symlink(self.tmppath('bad'), dest)
304 self.assertTrue(os.path.islink(dest))
305
306 s = AbsoluteSymlinkFile(source)
307 self.assertTrue(s.copy(dest))
308
309 self.assertTrue(os.path.islink(dest))
310 link = os.readlink(dest)
311 self.assertEqual(link, source)
312
313 def test_noop(self):
314 if not hasattr(os, 'symlink'):
315 return
316
317 source = self.tmppath('source')
318 dest = self.tmppath('dest')
319
320 with open(source, 'a'):
321 pass
322
323 os.symlink(source, dest)
324 link = os.readlink(dest)
325 self.assertEqual(link, source)
326
327 s = AbsoluteSymlinkFile(source)
328 self.assertFalse(s.copy(dest))
329
330 link = os.readlink(dest)
331 self.assertEqual(link, source)
332
333 class TestPreprocessedFile(TestWithTmpDir):
334 def test_preprocess(self):
335 '''
336 Test that copying the file invokes the preprocessor
337 '''
338 src = self.tmppath('src')
339 dest = self.tmppath('dest')
340
341 with open(src, 'wb') as tmp:
342 tmp.write('#ifdef FOO\ntest\n#endif')
343
344 f = PreprocessedFile(src, depfile_path=None, marker='#', defines={'FOO': True})
345 self.assertTrue(f.copy(dest))
346
347 self.assertEqual('test\n', open(dest, 'rb').read())
348
349 def test_preprocess_file_no_write(self):
350 '''
351 Test various conditions where PreprocessedFile.copy is expected not to
352 write in the destination file.
353 '''
354 src = self.tmppath('src')
355 dest = self.tmppath('dest')
356 depfile = self.tmppath('depfile')
357
358 with open(src, 'wb') as tmp:
359 tmp.write('#ifdef FOO\ntest\n#endif')
360
361 # Initial copy
362 f = PreprocessedFile(src, depfile_path=depfile, marker='#', defines={'FOO': True})
363 self.assertTrue(f.copy(dest))
364
365 # Ensure subsequent copies won't trigger writes
366 self.assertFalse(f.copy(DestNoWrite(dest)))
367 self.assertEqual('test\n', open(dest, 'rb').read())
368
369 # When the source file is older than the destination file, even with
370 # different content, no copy should occur.
371 with open(src, 'wb') as tmp:
372 tmp.write('#ifdef FOO\nfooo\n#endif')
373 time = os.path.getmtime(dest) - 1
374 os.utime(src, (time, time))
375 self.assertFalse(f.copy(DestNoWrite(dest)))
376 self.assertEqual('test\n', open(dest, 'rb').read())
377
378 # skip_if_older=False is expected to force a copy in this situation.
379 self.assertTrue(f.copy(dest, skip_if_older=False))
380 self.assertEqual('fooo\n', open(dest, 'rb').read())
381
382 def test_preprocess_file_dependencies(self):
383 '''
384 Test that the preprocess runs if the dependencies of the source change
385 '''
386 src = self.tmppath('src')
387 dest = self.tmppath('dest')
388 incl = self.tmppath('incl')
389 deps = self.tmppath('src.pp')
390
391 with open(src, 'wb') as tmp:
392 tmp.write('#ifdef FOO\ntest\n#endif')
393
394 with open(incl, 'wb') as tmp:
395 tmp.write('foo bar')
396
397 # Initial copy
398 f = PreprocessedFile(src, depfile_path=deps, marker='#', defines={'FOO': True})
399 self.assertTrue(f.copy(dest))
400
401 # Update the source so it #includes the include file.
402 with open(src, 'wb') as tmp:
403 tmp.write('#include incl\n')
404 time = os.path.getmtime(dest) + 1
405 os.utime(src, (time, time))
406 self.assertTrue(f.copy(dest))
407 self.assertEqual('foo bar', open(dest, 'rb').read())
408
409 # If one of the dependencies changes, the file should be updated. The
410 # mtime of the dependency is set after the destination file, to avoid
411 # both files having the same time.
412 with open(incl, 'wb') as tmp:
413 tmp.write('quux')
414 time = os.path.getmtime(dest) + 1
415 os.utime(incl, (time, time))
416 self.assertTrue(f.copy(dest))
417 self.assertEqual('quux', open(dest, 'rb').read())
418
419 # Perform one final copy to confirm that we don't run the preprocessor
420 # again. We update the mtime of the destination so it's newer than the
421 # input files. This would "just work" if we weren't changing
422 time = os.path.getmtime(incl) + 1
423 os.utime(dest, (time, time))
424 self.assertFalse(f.copy(DestNoWrite(dest)))
425
426 def test_replace_symlink(self):
427 '''
428 Test that if the destination exists, and is a symlink, the target of
429 the symlink is not overwritten by the preprocessor output.
430 '''
431 if not self.symlink_supported:
432 return
433
434 source = self.tmppath('source')
435 dest = self.tmppath('dest')
436 pp_source = self.tmppath('pp_in')
437 deps = self.tmppath('deps')
438
439 with open(source, 'a'):
440 pass
441
442 os.symlink(source, dest)
443 self.assertTrue(os.path.islink(dest))
444
445 with open(pp_source, 'wb') as tmp:
446 tmp.write('#define FOO\nPREPROCESSED')
447
448 f = PreprocessedFile(pp_source, depfile_path=deps, marker='#',
449 defines={'FOO': True})
450 self.assertTrue(f.copy(dest))
451
452 self.assertEqual('PREPROCESSED', open(dest, 'rb').read())
453 self.assertFalse(os.path.islink(dest))
454 self.assertEqual('', open(source, 'rb').read())
455
456 class TestExistingFile(TestWithTmpDir):
457 def test_required_missing_dest(self):
458 with self.assertRaisesRegexp(ErrorMessage, 'Required existing file'):
459 f = ExistingFile(required=True)
460 f.copy(self.tmppath('dest'))
461
462 def test_required_existing_dest(self):
463 p = self.tmppath('dest')
464 with open(p, 'a'):
465 pass
466
467 f = ExistingFile(required=True)
468 f.copy(p)
469
470 def test_optional_missing_dest(self):
471 f = ExistingFile(required=False)
472 f.copy(self.tmppath('dest'))
473
474 def test_optional_existing_dest(self):
475 p = self.tmppath('dest')
476 with open(p, 'a'):
477 pass
478
479 f = ExistingFile(required=False)
480 f.copy(p)
481
482
483 class TestGeneratedFile(TestWithTmpDir):
484 def test_generated_file(self):
485 '''
486 Check that GeneratedFile.copy yields the proper content in the
487 destination file in all situations that trigger different code paths
488 (see TestFile.test_file)
489 '''
490 dest = self.tmppath('dest')
491
492 for content in samples:
493 f = GeneratedFile(content)
494 f.copy(dest)
495 self.assertEqual(content, open(dest, 'rb').read())
496
497 def test_generated_file_open(self):
498 '''
499 Test whether GeneratedFile.open returns an appropriately reset file
500 object.
501 '''
502 content = ''.join(samples)
503 f = GeneratedFile(content)
504 self.assertEqual(content[:42], f.open().read(42))
505 self.assertEqual(content, f.open().read())
506
507 def test_generated_file_no_write(self):
508 '''
509 Test various conditions where GeneratedFile.copy is expected not to
510 write in the destination file.
511 '''
512 dest = self.tmppath('dest')
513
514 # Initial copy
515 f = GeneratedFile('test')
516 f.copy(dest)
517
518 # Ensure subsequent copies won't trigger writes
519 f.copy(DestNoWrite(dest))
520 self.assertEqual('test', open(dest, 'rb').read())
521
522 # When using a new instance with the same content, no copy should occur
523 f = GeneratedFile('test')
524 f.copy(DestNoWrite(dest))
525 self.assertEqual('test', open(dest, 'rb').read())
526
527 # Double check that under conditions where a copy occurs, we would get
528 # an exception.
529 f = GeneratedFile('fooo')
530 self.assertRaises(RuntimeError, f.copy, DestNoWrite(dest))
531
532
533 class TestDeflatedFile(TestWithTmpDir):
534 def test_deflated_file(self):
535 '''
536 Check that DeflatedFile.copy yields the proper content in the
537 destination file in all situations that trigger different code paths
538 (see TestFile.test_file)
539 '''
540 src = self.tmppath('src.jar')
541 dest = self.tmppath('dest')
542
543 contents = {}
544 with JarWriter(src) as jar:
545 for content in samples:
546 name = ''.join(random.choice(string.letters)
547 for i in xrange(8))
548 jar.add(name, content, compress=True)
549 contents[name] = content
550
551 for j in JarReader(src):
552 f = DeflatedFile(j)
553 f.copy(dest)
554 self.assertEqual(contents[j.filename], open(dest, 'rb').read())
555
556 def test_deflated_file_open(self):
557 '''
558 Test whether DeflatedFile.open returns an appropriately reset file
559 object.
560 '''
561 src = self.tmppath('src.jar')
562 content = ''.join(samples)
563 with JarWriter(src) as jar:
564 jar.add('content', content)
565
566 f = DeflatedFile(JarReader(src)['content'])
567 self.assertEqual(content[:42], f.open().read(42))
568 self.assertEqual(content, f.open().read())
569
570 def test_deflated_file_no_write(self):
571 '''
572 Test various conditions where DeflatedFile.copy is expected not to
573 write in the destination file.
574 '''
575 src = self.tmppath('src.jar')
576 dest = self.tmppath('dest')
577
578 with JarWriter(src) as jar:
579 jar.add('test', 'test')
580 jar.add('test2', 'test')
581 jar.add('fooo', 'fooo')
582
583 jar = JarReader(src)
584 # Initial copy
585 f = DeflatedFile(jar['test'])
586 f.copy(dest)
587
588 # Ensure subsequent copies won't trigger writes
589 f.copy(DestNoWrite(dest))
590 self.assertEqual('test', open(dest, 'rb').read())
591
592 # When using a different file with the same content, no copy should
593 # occur
594 f = DeflatedFile(jar['test2'])
595 f.copy(DestNoWrite(dest))
596 self.assertEqual('test', open(dest, 'rb').read())
597
598 # Double check that under conditions where a copy occurs, we would get
599 # an exception.
600 f = DeflatedFile(jar['fooo'])
601 self.assertRaises(RuntimeError, f.copy, DestNoWrite(dest))
602
603
604 class TestManifestFile(TestWithTmpDir):
605 def test_manifest_file(self):
606 f = ManifestFile('chrome')
607 f.add(ManifestContent('chrome', 'global', 'toolkit/content/global/'))
608 f.add(ManifestResource('chrome', 'gre-resources', 'toolkit/res/'))
609 f.add(ManifestResource('chrome/pdfjs', 'pdfjs', './'))
610 f.add(ManifestContent('chrome/pdfjs', 'pdfjs', 'pdfjs'))
611 f.add(ManifestLocale('chrome', 'browser', 'en-US',
612 'en-US/locale/browser/'))
613
614 f.copy(self.tmppath('chrome.manifest'))
615 self.assertEqual(open(self.tmppath('chrome.manifest')).readlines(), [
616 'content global toolkit/content/global/\n',
617 'resource gre-resources toolkit/res/\n',
618 'resource pdfjs pdfjs/\n',
619 'content pdfjs pdfjs/pdfjs\n',
620 'locale browser en-US en-US/locale/browser/\n',
621 ])
622
623 self.assertRaises(
624 ValueError,
625 f.remove,
626 ManifestContent('', 'global', 'toolkit/content/global/')
627 )
628 self.assertRaises(
629 ValueError,
630 f.remove,
631 ManifestOverride('chrome', 'chrome://global/locale/netError.dtd',
632 'chrome://browser/locale/netError.dtd')
633 )
634
635 f.remove(ManifestContent('chrome', 'global',
636 'toolkit/content/global/'))
637 self.assertRaises(
638 ValueError,
639 f.remove,
640 ManifestContent('chrome', 'global', 'toolkit/content/global/')
641 )
642
643 f.copy(self.tmppath('chrome.manifest'))
644 content = open(self.tmppath('chrome.manifest')).read()
645 self.assertEqual(content[:42], f.open().read(42))
646 self.assertEqual(content, f.open().read())
647
648 # Compiled typelib for the following IDL:
649 # interface foo;
650 # [uuid(5f70da76-519c-4858-b71e-e3c92333e2d6)]
651 # interface bar {
652 # void bar(in foo f);
653 # };
654 bar_xpt = GeneratedFile(
655 b'\x58\x50\x43\x4F\x4D\x0A\x54\x79\x70\x65\x4C\x69\x62\x0D\x0A\x1A' +
656 b'\x01\x02\x00\x02\x00\x00\x00\x7B\x00\x00\x00\x24\x00\x00\x00\x5C' +
657 b'\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' +
658 b'\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x5F' +
659 b'\x70\xDA\x76\x51\x9C\x48\x58\xB7\x1E\xE3\xC9\x23\x33\xE2\xD6\x00' +
660 b'\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x0D\x00\x66\x6F\x6F\x00' +
661 b'\x62\x61\x72\x00\x62\x61\x72\x00\x00\x00\x00\x01\x00\x00\x00\x00' +
662 b'\x09\x01\x80\x92\x00\x01\x80\x06\x00\x00\x00'
663 )
664
665 # Compiled typelib for the following IDL:
666 # [uuid(3271bebc-927e-4bef-935e-44e0aaf3c1e5)]
667 # interface foo {
668 # void foo();
669 # };
670 foo_xpt = GeneratedFile(
671 b'\x58\x50\x43\x4F\x4D\x0A\x54\x79\x70\x65\x4C\x69\x62\x0D\x0A\x1A' +
672 b'\x01\x02\x00\x01\x00\x00\x00\x57\x00\x00\x00\x24\x00\x00\x00\x40' +
673 b'\x80\x00\x00\x32\x71\xBE\xBC\x92\x7E\x4B\xEF\x93\x5E\x44\xE0\xAA' +
674 b'\xF3\xC1\xE5\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x09\x00' +
675 b'\x66\x6F\x6F\x00\x66\x6F\x6F\x00\x00\x00\x00\x01\x00\x00\x00\x00' +
676 b'\x05\x00\x80\x06\x00\x00\x00'
677 )
678
679 # Compiled typelib for the following IDL:
680 # [uuid(7057f2aa-fdc2-4559-abde-08d939f7e80d)]
681 # interface foo {
682 # void foo();
683 # };
684 foo2_xpt = GeneratedFile(
685 b'\x58\x50\x43\x4F\x4D\x0A\x54\x79\x70\x65\x4C\x69\x62\x0D\x0A\x1A' +
686 b'\x01\x02\x00\x01\x00\x00\x00\x57\x00\x00\x00\x24\x00\x00\x00\x40' +
687 b'\x80\x00\x00\x70\x57\xF2\xAA\xFD\xC2\x45\x59\xAB\xDE\x08\xD9\x39' +
688 b'\xF7\xE8\x0D\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x09\x00' +
689 b'\x66\x6F\x6F\x00\x66\x6F\x6F\x00\x00\x00\x00\x01\x00\x00\x00\x00' +
690 b'\x05\x00\x80\x06\x00\x00\x00'
691 )
692
693
694 def read_interfaces(file):
695 return dict((i.name, i) for i in Typelib.read(file).interfaces)
696
697
698 class TestXPTFile(TestWithTmpDir):
699 def test_xpt_file(self):
700 x = XPTFile()
701 x.add(foo_xpt)
702 x.add(bar_xpt)
703 x.copy(self.tmppath('interfaces.xpt'))
704
705 foo = read_interfaces(foo_xpt.open())
706 foo2 = read_interfaces(foo2_xpt.open())
707 bar = read_interfaces(bar_xpt.open())
708 linked = read_interfaces(self.tmppath('interfaces.xpt'))
709 self.assertEqual(foo['foo'], linked['foo'])
710 self.assertEqual(bar['bar'], linked['bar'])
711
712 x.remove(foo_xpt)
713 x.copy(self.tmppath('interfaces2.xpt'))
714 linked = read_interfaces(self.tmppath('interfaces2.xpt'))
715 self.assertEqual(bar['foo'], linked['foo'])
716 self.assertEqual(bar['bar'], linked['bar'])
717
718 x.add(foo_xpt)
719 x.copy(DestNoWrite(self.tmppath('interfaces.xpt')))
720 linked = read_interfaces(self.tmppath('interfaces.xpt'))
721 self.assertEqual(foo['foo'], linked['foo'])
722 self.assertEqual(bar['bar'], linked['bar'])
723
724 x = XPTFile()
725 x.add(foo2_xpt)
726 x.add(bar_xpt)
727 x.copy(self.tmppath('interfaces.xpt'))
728 linked = read_interfaces(self.tmppath('interfaces.xpt'))
729 self.assertEqual(foo2['foo'], linked['foo'])
730 self.assertEqual(bar['bar'], linked['bar'])
731
732 x = XPTFile()
733 x.add(foo_xpt)
734 x.add(foo2_xpt)
735 x.add(bar_xpt)
736 from xpt import DataError
737 self.assertRaises(DataError, x.copy, self.tmppath('interfaces.xpt'))
738
739
740 class TestMinifiedProperties(TestWithTmpDir):
741 def test_minified_properties(self):
742 propLines = [
743 '# Comments are removed',
744 'foo = bar',
745 '',
746 '# Another comment',
747 ]
748 prop = GeneratedFile('\n'.join(propLines))
749 self.assertEqual(MinifiedProperties(prop).open().readlines(),
750 ['foo = bar\n', '\n'])
751 open(self.tmppath('prop'), 'wb').write('\n'.join(propLines))
752 MinifiedProperties(File(self.tmppath('prop'))) \
753 .copy(self.tmppath('prop2'))
754 self.assertEqual(open(self.tmppath('prop2')).readlines(),
755 ['foo = bar\n', '\n'])
756
757
758 class TestMinifiedJavaScript(TestWithTmpDir):
759 orig_lines = [
760 '// Comment line',
761 'let foo = "bar";',
762 'var bar = true;',
763 '',
764 '// Another comment',
765 ]
766
767 def test_minified_javascript(self):
768 orig_f = GeneratedFile('\n'.join(self.orig_lines))
769 min_f = MinifiedJavaScript(orig_f)
770
771 mini_lines = min_f.open().readlines()
772 self.assertTrue(mini_lines)
773 self.assertTrue(len(mini_lines) < len(self.orig_lines))
774
775 def _verify_command(self, code):
776 our_dir = os.path.abspath(os.path.dirname(__file__))
777 return [
778 sys.executable,
779 os.path.join(our_dir, 'support', 'minify_js_verify.py'),
780 code,
781 ]
782
783 def test_minified_verify_success(self):
784 orig_f = GeneratedFile('\n'.join(self.orig_lines))
785 min_f = MinifiedJavaScript(orig_f,
786 verify_command=self._verify_command('0'))
787
788 mini_lines = min_f.open().readlines()
789 self.assertTrue(mini_lines)
790 self.assertTrue(len(mini_lines) < len(self.orig_lines))
791
792 def test_minified_verify_failure(self):
793 orig_f = GeneratedFile('\n'.join(self.orig_lines))
794 min_f = MinifiedJavaScript(orig_f,
795 verify_command=self._verify_command('1'))
796
797 mini_lines = min_f.open().readlines()
798 self.assertEqual(mini_lines, orig_f.open().readlines())
799
800
801 class MatchTestTemplate(object):
802 def prepare_match_test(self, with_dotfiles=False):
803 self.add('bar')
804 self.add('foo/bar')
805 self.add('foo/baz')
806 self.add('foo/qux/1')
807 self.add('foo/qux/bar')
808 self.add('foo/qux/2/test')
809 self.add('foo/qux/2/test2')
810 if with_dotfiles:
811 self.add('foo/.foo')
812 self.add('foo/.bar/foo')
813
814 def do_match_test(self):
815 self.do_check('', [
816 'bar', 'foo/bar', 'foo/baz', 'foo/qux/1', 'foo/qux/bar',
817 'foo/qux/2/test', 'foo/qux/2/test2'
818 ])
819 self.do_check('*', [
820 'bar', 'foo/bar', 'foo/baz', 'foo/qux/1', 'foo/qux/bar',
821 'foo/qux/2/test', 'foo/qux/2/test2'
822 ])
823 self.do_check('foo/qux', [
824 'foo/qux/1', 'foo/qux/bar', 'foo/qux/2/test', 'foo/qux/2/test2'
825 ])
826 self.do_check('foo/b*', ['foo/bar', 'foo/baz'])
827 self.do_check('baz', [])
828 self.do_check('foo/foo', [])
829 self.do_check('foo/*ar', ['foo/bar'])
830 self.do_check('*ar', ['bar'])
831 self.do_check('*/bar', ['foo/bar'])
832 self.do_check('foo/*ux', [
833 'foo/qux/1', 'foo/qux/bar', 'foo/qux/2/test', 'foo/qux/2/test2'
834 ])
835 self.do_check('foo/q*ux', [
836 'foo/qux/1', 'foo/qux/bar', 'foo/qux/2/test', 'foo/qux/2/test2'
837 ])
838 self.do_check('foo/*/2/test*', ['foo/qux/2/test', 'foo/qux/2/test2'])
839 self.do_check('**/bar', ['bar', 'foo/bar', 'foo/qux/bar'])
840 self.do_check('foo/**/test', ['foo/qux/2/test'])
841 self.do_check('foo/**', [
842 'foo/bar', 'foo/baz', 'foo/qux/1', 'foo/qux/bar',
843 'foo/qux/2/test', 'foo/qux/2/test2'
844 ])
845 self.do_check('**/2/test*', ['foo/qux/2/test', 'foo/qux/2/test2'])
846 self.do_check('**/foo', [
847 'foo/bar', 'foo/baz', 'foo/qux/1', 'foo/qux/bar',
848 'foo/qux/2/test', 'foo/qux/2/test2'
849 ])
850 self.do_check('**/barbaz', [])
851 self.do_check('f**/bar', ['foo/bar'])
852
853 def do_finder_test(self, finder):
854 self.assertTrue(finder.contains('foo/.foo'))
855 self.assertTrue(finder.contains('foo/.bar'))
856 self.assertTrue('foo/.foo' in [f for f, c in
857 finder.find('foo/.foo')])
858 self.assertTrue('foo/.bar/foo' in [f for f, c in
859 finder.find('foo/.bar')])
860 self.assertEqual(sorted([f for f, c in finder.find('foo/.*')]),
861 ['foo/.bar/foo', 'foo/.foo'])
862 for pattern in ['foo', '**', '**/*', '**/foo', 'foo/*']:
863 self.assertFalse('foo/.foo' in [f for f, c in
864 finder.find(pattern)])
865 self.assertFalse('foo/.bar/foo' in [f for f, c in
866 finder.find(pattern)])
867 self.assertEqual(sorted([f for f, c in finder.find(pattern)]),
868 sorted([f for f, c in finder
869 if mozpack.path.match(f, pattern)]))
870
871
872 def do_check(test, finder, pattern, result):
873 if result:
874 test.assertTrue(finder.contains(pattern))
875 else:
876 test.assertFalse(finder.contains(pattern))
877 test.assertEqual(sorted(list(f for f, c in finder.find(pattern))),
878 sorted(result))
879
880
881 class TestFileFinder(MatchTestTemplate, TestWithTmpDir):
882 def add(self, path):
883 ensureParentDir(self.tmppath(path))
884 open(self.tmppath(path), 'wb').write(path)
885
886 def do_check(self, pattern, result):
887 do_check(self, self.finder, pattern, result)
888
889 def test_file_finder(self):
890 self.prepare_match_test(with_dotfiles=True)
891 self.finder = FileFinder(self.tmpdir)
892 self.do_match_test()
893 self.do_finder_test(self.finder)
894
895 def test_ignored_dirs(self):
896 """Ignored directories should not have results returned."""
897 self.prepare_match_test()
898 self.add('fooz')
899
900 # Present to ensure prefix matching doesn't exclude.
901 self.add('foo/quxz')
902
903 self.finder = FileFinder(self.tmpdir, ignore=['foo/qux'])
904
905 self.do_check('**', ['bar', 'foo/bar', 'foo/baz', 'foo/quxz', 'fooz'])
906 self.do_check('foo/*', ['foo/bar', 'foo/baz', 'foo/quxz'])
907 self.do_check('foo/**', ['foo/bar', 'foo/baz', 'foo/quxz'])
908 self.do_check('foo/qux/**', [])
909 self.do_check('foo/qux/*', [])
910 self.do_check('foo/qux/bar', [])
911 self.do_check('foo/quxz', ['foo/quxz'])
912 self.do_check('fooz', ['fooz'])
913
914 def test_ignored_files(self):
915 """Ignored files should not have results returned."""
916 self.prepare_match_test()
917
918 # Be sure prefix match doesn't get ignored.
919 self.add('barz')
920
921 self.finder = FileFinder(self.tmpdir, ignore=['foo/bar', 'bar'])
922 self.do_check('**', ['barz', 'foo/baz', 'foo/qux/1', 'foo/qux/2/test',
923 'foo/qux/2/test2', 'foo/qux/bar'])
924 self.do_check('foo/**', ['foo/baz', 'foo/qux/1', 'foo/qux/2/test',
925 'foo/qux/2/test2', 'foo/qux/bar'])
926
927 def test_ignored_patterns(self):
928 """Ignore entries with patterns should be honored."""
929 self.prepare_match_test()
930
931 self.add('foo/quxz')
932
933 self.finder = FileFinder(self.tmpdir, ignore=['foo/qux/*'])
934 self.do_check('**', ['foo/bar', 'foo/baz', 'foo/quxz', 'bar'])
935 self.do_check('foo/**', ['foo/bar', 'foo/baz', 'foo/quxz'])
936
937
938 class TestJarFinder(MatchTestTemplate, TestWithTmpDir):
939 def add(self, path):
940 self.jar.add(path, path, compress=True)
941
942 def do_check(self, pattern, result):
943 do_check(self, self.finder, pattern, result)
944
945 def test_jar_finder(self):
946 self.jar = JarWriter(file=self.tmppath('test.jar'))
947 self.prepare_match_test()
948 self.jar.finish()
949 reader = JarReader(file=self.tmppath('test.jar'))
950 self.finder = JarFinder(self.tmppath('test.jar'), reader)
951 self.do_match_test()
952
953
954 if __name__ == '__main__':
955 mozunit.main()

mercurial