python/mozbuild/mozpack/test/test_files.py

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

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 mozbuild.util import ensureParentDir
     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
    46 class TestWithTmpDir(unittest.TestCase):
    47     def setUp(self):
    48         self.tmpdir = mkdtemp()
    50         self.symlink_supported = False
    52         if not hasattr(os, 'symlink'):
    53             return
    55         dummy_path = self.tmppath('dummy_file')
    56         with open(dummy_path, 'a'):
    57             pass
    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)
    67         self.symlink_supported = True
    70     def tearDown(self):
    71         mozfile.rmtree(self.tmpdir)
    73     def tmppath(self, relpath):
    74         return os.path.normpath(os.path.join(self.tmpdir, relpath))
    77 class MockDest(BytesIO, Dest):
    78     def __init__(self):
    79         BytesIO.__init__(self)
    80         self.mode = None
    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)
    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)
    95     def exists(self):
    96         return True
    98     def close(self):
    99         if self.mode:
   100             self.mode = None
   103 class DestNoWrite(Dest):
   104     def write(self, data):
   105         raise RuntimeError
   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')
   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 ]
   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')
   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())
   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()
   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())
   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)
   195         f = File(src)
   196         self.assertEqual(content[:42], f.open().read(42))
   197         self.assertEqual(content, f.open().read())
   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')
   207         with open(src, 'wb') as tmp:
   208             tmp.write('test')
   210         # Initial copy
   211         f = File(src)
   212         f.copy(dest)
   214         # Ensure subsequent copies won't trigger writes
   215         f.copy(DestNoWrite(dest))
   216         self.assertEqual('test', open(dest, 'rb').read())
   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())
   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())
   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))
   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())
   245 class TestAbsoluteSymlinkFile(TestWithTmpDir):
   246     def test_absolute_relative(self):
   247         AbsoluteSymlinkFile('/foo')
   249         with self.assertRaisesRegexp(ValueError, 'Symlink target not absolute'):
   250             AbsoluteSymlinkFile('./foo')
   252     def test_symlink_file(self):
   253         source = self.tmppath('test_path')
   254         with open(source, 'wt') as fh:
   255             fh.write('Hello world')
   257         s = AbsoluteSymlinkFile(source)
   258         dest = self.tmppath('symlink')
   259         self.assertTrue(s.copy(dest))
   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')
   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')
   277         dest = self.tmppath('dest')
   278         with open(dest, 'a'):
   279             pass
   281         s = AbsoluteSymlinkFile(source)
   282         s.copy(dest, skip_if_older=False)
   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')
   293     def test_replace_symlink(self):
   294         if not self.symlink_supported:
   295             return
   297         source = self.tmppath('source')
   298         with open(source, 'a'):
   299             pass
   301         dest = self.tmppath('dest')
   303         os.symlink(self.tmppath('bad'), dest)
   304         self.assertTrue(os.path.islink(dest))
   306         s = AbsoluteSymlinkFile(source)
   307         self.assertTrue(s.copy(dest))
   309         self.assertTrue(os.path.islink(dest))
   310         link = os.readlink(dest)
   311         self.assertEqual(link, source)
   313     def test_noop(self):
   314         if not hasattr(os, 'symlink'):
   315             return
   317         source = self.tmppath('source')
   318         dest = self.tmppath('dest')
   320         with open(source, 'a'):
   321             pass
   323         os.symlink(source, dest)
   324         link = os.readlink(dest)
   325         self.assertEqual(link, source)
   327         s = AbsoluteSymlinkFile(source)
   328         self.assertFalse(s.copy(dest))
   330         link = os.readlink(dest)
   331         self.assertEqual(link, source)
   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')
   341         with open(src, 'wb') as tmp:
   342             tmp.write('#ifdef FOO\ntest\n#endif')
   344         f = PreprocessedFile(src, depfile_path=None, marker='#', defines={'FOO': True})
   345         self.assertTrue(f.copy(dest))
   347         self.assertEqual('test\n', open(dest, 'rb').read())
   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')
   358         with open(src, 'wb') as tmp:
   359             tmp.write('#ifdef FOO\ntest\n#endif')
   361         # Initial copy
   362         f = PreprocessedFile(src, depfile_path=depfile, marker='#', defines={'FOO': True})
   363         self.assertTrue(f.copy(dest))
   365         # Ensure subsequent copies won't trigger writes
   366         self.assertFalse(f.copy(DestNoWrite(dest)))
   367         self.assertEqual('test\n', open(dest, 'rb').read())
   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())
   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())
   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')
   391         with open(src, 'wb') as tmp:
   392             tmp.write('#ifdef FOO\ntest\n#endif')
   394         with open(incl, 'wb') as tmp:
   395             tmp.write('foo bar')
   397         # Initial copy
   398         f = PreprocessedFile(src, depfile_path=deps, marker='#', defines={'FOO': True})
   399         self.assertTrue(f.copy(dest))
   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())
   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())
   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)))
   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
   434         source = self.tmppath('source')
   435         dest = self.tmppath('dest')
   436         pp_source = self.tmppath('pp_in')
   437         deps = self.tmppath('deps')
   439         with open(source, 'a'):
   440             pass
   442         os.symlink(source, dest)
   443         self.assertTrue(os.path.islink(dest))
   445         with open(pp_source, 'wb') as tmp:
   446             tmp.write('#define FOO\nPREPROCESSED')
   448         f = PreprocessedFile(pp_source, depfile_path=deps, marker='#',
   449             defines={'FOO': True})
   450         self.assertTrue(f.copy(dest))
   452         self.assertEqual('PREPROCESSED', open(dest, 'rb').read())
   453         self.assertFalse(os.path.islink(dest))
   454         self.assertEqual('', open(source, 'rb').read())
   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'))
   462     def test_required_existing_dest(self):
   463         p = self.tmppath('dest')
   464         with open(p, 'a'):
   465             pass
   467         f = ExistingFile(required=True)
   468         f.copy(p)
   470     def test_optional_missing_dest(self):
   471         f = ExistingFile(required=False)
   472         f.copy(self.tmppath('dest'))
   474     def test_optional_existing_dest(self):
   475         p = self.tmppath('dest')
   476         with open(p, 'a'):
   477             pass
   479         f = ExistingFile(required=False)
   480         f.copy(p)
   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')
   492         for content in samples:
   493             f = GeneratedFile(content)
   494             f.copy(dest)
   495             self.assertEqual(content, open(dest, 'rb').read())
   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())
   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')
   514         # Initial copy
   515         f = GeneratedFile('test')
   516         f.copy(dest)
   518         # Ensure subsequent copies won't trigger writes
   519         f.copy(DestNoWrite(dest))
   520         self.assertEqual('test', open(dest, 'rb').read())
   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())
   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))
   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')
   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
   551         for j in JarReader(src):
   552             f = DeflatedFile(j)
   553             f.copy(dest)
   554             self.assertEqual(contents[j.filename], open(dest, 'rb').read())
   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)
   566         f = DeflatedFile(JarReader(src)['content'])
   567         self.assertEqual(content[:42], f.open().read(42))
   568         self.assertEqual(content, f.open().read())
   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')
   578         with JarWriter(src) as jar:
   579             jar.add('test', 'test')
   580             jar.add('test2', 'test')
   581             jar.add('fooo', 'fooo')
   583         jar = JarReader(src)
   584         # Initial copy
   585         f = DeflatedFile(jar['test'])
   586         f.copy(dest)
   588         # Ensure subsequent copies won't trigger writes
   589         f.copy(DestNoWrite(dest))
   590         self.assertEqual('test', open(dest, 'rb').read())
   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())
   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))
   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/'))
   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         ])
   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         )
   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         )
   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())
   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 )
   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 )
   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 )
   694 def read_interfaces(file):
   695     return dict((i.name, i) for i in Typelib.read(file).interfaces)
   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'))
   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'])
   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'])
   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'])
   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'])
   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'))
   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'])
   758 class TestMinifiedJavaScript(TestWithTmpDir):
   759     orig_lines = [
   760         '// Comment line',
   761         'let foo = "bar";',
   762         'var bar = true;',
   763         '',
   764         '// Another comment',
   765     ]
   767     def test_minified_javascript(self):
   768         orig_f = GeneratedFile('\n'.join(self.orig_lines))
   769         min_f = MinifiedJavaScript(orig_f)
   771         mini_lines = min_f.open().readlines()
   772         self.assertTrue(mini_lines)
   773         self.assertTrue(len(mini_lines) < len(self.orig_lines))
   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         ]
   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'))
   788         mini_lines = min_f.open().readlines()
   789         self.assertTrue(mini_lines)
   790         self.assertTrue(len(mini_lines) < len(self.orig_lines))
   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'))
   797         mini_lines = min_f.open().readlines()
   798         self.assertEqual(mini_lines, orig_f.open().readlines())
   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')
   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'])
   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)]))
   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))
   881 class TestFileFinder(MatchTestTemplate, TestWithTmpDir):
   882     def add(self, path):
   883         ensureParentDir(self.tmppath(path))
   884         open(self.tmppath(path), 'wb').write(path)
   886     def do_check(self, pattern, result):
   887         do_check(self, self.finder, pattern, result)
   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)
   895     def test_ignored_dirs(self):
   896         """Ignored directories should not have results returned."""
   897         self.prepare_match_test()
   898         self.add('fooz')
   900         # Present to ensure prefix matching doesn't exclude.
   901         self.add('foo/quxz')
   903         self.finder = FileFinder(self.tmpdir, ignore=['foo/qux'])
   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'])
   914     def test_ignored_files(self):
   915         """Ignored files should not have results returned."""
   916         self.prepare_match_test()
   918         # Be sure prefix match doesn't get ignored.
   919         self.add('barz')
   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'])
   927     def test_ignored_patterns(self):
   928         """Ignore entries with patterns should be honored."""
   929         self.prepare_match_test()
   931         self.add('foo/quxz')
   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'])
   938 class TestJarFinder(MatchTestTemplate, TestWithTmpDir):
   939     def add(self, path):
   940         self.jar.add(path, path, compress=True)
   942     def do_check(self, pattern, result):
   943         do_check(self, self.finder, pattern, result)
   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()
   954 if __name__ == '__main__':
   955     mozunit.main()

mercurial