michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: from mozbuild.util import ensureParentDir michael@0: michael@0: from mozpack.errors import ErrorMessage michael@0: from mozpack.files import ( michael@0: AbsoluteSymlinkFile, michael@0: DeflatedFile, michael@0: Dest, michael@0: ExistingFile, michael@0: FileFinder, michael@0: File, michael@0: GeneratedFile, michael@0: JarFinder, michael@0: ManifestFile, michael@0: MinifiedJavaScript, michael@0: MinifiedProperties, michael@0: PreprocessedFile, michael@0: XPTFile, michael@0: ) michael@0: from mozpack.mozjar import ( michael@0: JarReader, michael@0: JarWriter, michael@0: ) michael@0: from mozpack.chrome.manifest import ( michael@0: ManifestContent, michael@0: ManifestResource, michael@0: ManifestLocale, michael@0: ManifestOverride, michael@0: ) michael@0: import unittest michael@0: import mozfile michael@0: import mozunit michael@0: import os michael@0: import random michael@0: import string michael@0: import sys michael@0: import mozpack.path michael@0: from tempfile import mkdtemp michael@0: from io import BytesIO michael@0: from xpt import Typelib michael@0: michael@0: michael@0: class TestWithTmpDir(unittest.TestCase): michael@0: def setUp(self): michael@0: self.tmpdir = mkdtemp() michael@0: michael@0: self.symlink_supported = False michael@0: michael@0: if not hasattr(os, 'symlink'): michael@0: return michael@0: michael@0: dummy_path = self.tmppath('dummy_file') michael@0: with open(dummy_path, 'a'): michael@0: pass michael@0: michael@0: try: michael@0: os.symlink(dummy_path, self.tmppath('dummy_symlink')) michael@0: os.remove(self.tmppath('dummy_symlink')) michael@0: except EnvironmentError: michael@0: pass michael@0: finally: michael@0: os.remove(dummy_path) michael@0: michael@0: self.symlink_supported = True michael@0: michael@0: michael@0: def tearDown(self): michael@0: mozfile.rmtree(self.tmpdir) michael@0: michael@0: def tmppath(self, relpath): michael@0: return os.path.normpath(os.path.join(self.tmpdir, relpath)) michael@0: michael@0: michael@0: class MockDest(BytesIO, Dest): michael@0: def __init__(self): michael@0: BytesIO.__init__(self) michael@0: self.mode = None michael@0: michael@0: def read(self, length=-1): michael@0: if self.mode != 'r': michael@0: self.seek(0) michael@0: self.mode = 'r' michael@0: return BytesIO.read(self, length) michael@0: michael@0: def write(self, data): michael@0: if self.mode != 'w': michael@0: self.seek(0) michael@0: self.truncate(0) michael@0: self.mode = 'w' michael@0: return BytesIO.write(self, data) michael@0: michael@0: def exists(self): michael@0: return True michael@0: michael@0: def close(self): michael@0: if self.mode: michael@0: self.mode = None michael@0: michael@0: michael@0: class DestNoWrite(Dest): michael@0: def write(self, data): michael@0: raise RuntimeError michael@0: michael@0: michael@0: class TestDest(TestWithTmpDir): michael@0: def test_dest(self): michael@0: dest = Dest(self.tmppath('dest')) michael@0: self.assertFalse(dest.exists()) michael@0: dest.write('foo') michael@0: self.assertTrue(dest.exists()) michael@0: dest.write('foo') michael@0: self.assertEqual(dest.read(4), 'foof') michael@0: self.assertEqual(dest.read(), 'oo') michael@0: self.assertEqual(dest.read(), '') michael@0: dest.write('bar') michael@0: self.assertEqual(dest.read(4), 'bar') michael@0: dest.close() michael@0: self.assertEqual(dest.read(), 'bar') michael@0: dest.write('foo') michael@0: dest.close() michael@0: dest.write('qux') michael@0: self.assertEqual(dest.read(), 'qux') michael@0: michael@0: rand = ''.join(random.choice(string.letters) for i in xrange(131597)) michael@0: samples = [ michael@0: '', michael@0: 'test', michael@0: 'fooo', michael@0: 'same', michael@0: 'same', michael@0: 'Different and longer', michael@0: rand, michael@0: rand, michael@0: rand[:-1] + '_', michael@0: 'test' michael@0: ] michael@0: michael@0: michael@0: class TestFile(TestWithTmpDir): michael@0: def test_file(self): michael@0: ''' michael@0: Check that File.copy yields the proper content in the destination file michael@0: in all situations that trigger different code paths: michael@0: - different content michael@0: - different content of the same size michael@0: - same content michael@0: - long content michael@0: ''' michael@0: src = self.tmppath('src') michael@0: dest = self.tmppath('dest') michael@0: michael@0: for content in samples: michael@0: with open(src, 'wb') as tmp: michael@0: tmp.write(content) michael@0: # Ensure the destination file, when it exists, is older than the michael@0: # source michael@0: if os.path.exists(dest): michael@0: time = os.path.getmtime(src) - 1 michael@0: os.utime(dest, (time, time)) michael@0: f = File(src) michael@0: f.copy(dest) michael@0: self.assertEqual(content, open(dest, 'rb').read()) michael@0: self.assertEqual(content, f.open().read()) michael@0: self.assertEqual(content, f.open().read()) michael@0: michael@0: def test_file_dest(self): michael@0: ''' michael@0: Similar to test_file, but for a destination object instead of michael@0: a destination file. This ensures the destination object is being michael@0: used properly by File.copy, ensuring that other subclasses of Dest michael@0: will work. michael@0: ''' michael@0: src = self.tmppath('src') michael@0: dest = MockDest() michael@0: michael@0: for content in samples: michael@0: with open(src, 'wb') as tmp: michael@0: tmp.write(content) michael@0: f = File(src) michael@0: f.copy(dest) michael@0: self.assertEqual(content, dest.getvalue()) michael@0: michael@0: def test_file_open(self): michael@0: ''' michael@0: Test whether File.open returns an appropriately reset file object. michael@0: ''' michael@0: src = self.tmppath('src') michael@0: content = ''.join(samples) michael@0: with open(src, 'wb') as tmp: michael@0: tmp.write(content) michael@0: michael@0: f = File(src) michael@0: self.assertEqual(content[:42], f.open().read(42)) michael@0: self.assertEqual(content, f.open().read()) michael@0: michael@0: def test_file_no_write(self): michael@0: ''' michael@0: Test various conditions where File.copy is expected not to write michael@0: in the destination file. michael@0: ''' michael@0: src = self.tmppath('src') michael@0: dest = self.tmppath('dest') michael@0: michael@0: with open(src, 'wb') as tmp: michael@0: tmp.write('test') michael@0: michael@0: # Initial copy michael@0: f = File(src) michael@0: f.copy(dest) michael@0: michael@0: # Ensure subsequent copies won't trigger writes michael@0: f.copy(DestNoWrite(dest)) michael@0: self.assertEqual('test', open(dest, 'rb').read()) michael@0: michael@0: # When the source file is newer, but with the same content, no copy michael@0: # should occur michael@0: time = os.path.getmtime(src) - 1 michael@0: os.utime(dest, (time, time)) michael@0: f.copy(DestNoWrite(dest)) michael@0: self.assertEqual('test', open(dest, 'rb').read()) michael@0: michael@0: # When the source file is older than the destination file, even with michael@0: # different content, no copy should occur. michael@0: with open(src, 'wb') as tmp: michael@0: tmp.write('fooo') michael@0: time = os.path.getmtime(dest) - 1 michael@0: os.utime(src, (time, time)) michael@0: f.copy(DestNoWrite(dest)) michael@0: self.assertEqual('test', open(dest, 'rb').read()) michael@0: michael@0: # Double check that under conditions where a copy occurs, we would get michael@0: # an exception. michael@0: time = os.path.getmtime(src) - 1 michael@0: os.utime(dest, (time, time)) michael@0: self.assertRaises(RuntimeError, f.copy, DestNoWrite(dest)) michael@0: michael@0: # skip_if_older=False is expected to force a copy in this situation. michael@0: f.copy(dest, skip_if_older=False) michael@0: self.assertEqual('fooo', open(dest, 'rb').read()) michael@0: michael@0: michael@0: class TestAbsoluteSymlinkFile(TestWithTmpDir): michael@0: def test_absolute_relative(self): michael@0: AbsoluteSymlinkFile('/foo') michael@0: michael@0: with self.assertRaisesRegexp(ValueError, 'Symlink target not absolute'): michael@0: AbsoluteSymlinkFile('./foo') michael@0: michael@0: def test_symlink_file(self): michael@0: source = self.tmppath('test_path') michael@0: with open(source, 'wt') as fh: michael@0: fh.write('Hello world') michael@0: michael@0: s = AbsoluteSymlinkFile(source) michael@0: dest = self.tmppath('symlink') michael@0: self.assertTrue(s.copy(dest)) michael@0: michael@0: if self.symlink_supported: michael@0: self.assertTrue(os.path.islink(dest)) michael@0: link = os.readlink(dest) michael@0: self.assertEqual(link, source) michael@0: else: michael@0: self.assertTrue(os.path.isfile(dest)) michael@0: content = open(dest).read() michael@0: self.assertEqual(content, 'Hello world') michael@0: michael@0: def test_replace_file_with_symlink(self): michael@0: # If symlinks are supported, an existing file should be replaced by a michael@0: # symlink. michael@0: source = self.tmppath('test_path') michael@0: with open(source, 'wt') as fh: michael@0: fh.write('source') michael@0: michael@0: dest = self.tmppath('dest') michael@0: with open(dest, 'a'): michael@0: pass michael@0: michael@0: s = AbsoluteSymlinkFile(source) michael@0: s.copy(dest, skip_if_older=False) michael@0: michael@0: if self.symlink_supported: michael@0: self.assertTrue(os.path.islink(dest)) michael@0: link = os.readlink(dest) michael@0: self.assertEqual(link, source) michael@0: else: michael@0: self.assertTrue(os.path.isfile(dest)) michael@0: content = open(dest).read() michael@0: self.assertEqual(content, 'source') michael@0: michael@0: def test_replace_symlink(self): michael@0: if not self.symlink_supported: michael@0: return michael@0: michael@0: source = self.tmppath('source') michael@0: with open(source, 'a'): michael@0: pass michael@0: michael@0: dest = self.tmppath('dest') michael@0: michael@0: os.symlink(self.tmppath('bad'), dest) michael@0: self.assertTrue(os.path.islink(dest)) michael@0: michael@0: s = AbsoluteSymlinkFile(source) michael@0: self.assertTrue(s.copy(dest)) michael@0: michael@0: self.assertTrue(os.path.islink(dest)) michael@0: link = os.readlink(dest) michael@0: self.assertEqual(link, source) michael@0: michael@0: def test_noop(self): michael@0: if not hasattr(os, 'symlink'): michael@0: return michael@0: michael@0: source = self.tmppath('source') michael@0: dest = self.tmppath('dest') michael@0: michael@0: with open(source, 'a'): michael@0: pass michael@0: michael@0: os.symlink(source, dest) michael@0: link = os.readlink(dest) michael@0: self.assertEqual(link, source) michael@0: michael@0: s = AbsoluteSymlinkFile(source) michael@0: self.assertFalse(s.copy(dest)) michael@0: michael@0: link = os.readlink(dest) michael@0: self.assertEqual(link, source) michael@0: michael@0: class TestPreprocessedFile(TestWithTmpDir): michael@0: def test_preprocess(self): michael@0: ''' michael@0: Test that copying the file invokes the preprocessor michael@0: ''' michael@0: src = self.tmppath('src') michael@0: dest = self.tmppath('dest') michael@0: michael@0: with open(src, 'wb') as tmp: michael@0: tmp.write('#ifdef FOO\ntest\n#endif') michael@0: michael@0: f = PreprocessedFile(src, depfile_path=None, marker='#', defines={'FOO': True}) michael@0: self.assertTrue(f.copy(dest)) michael@0: michael@0: self.assertEqual('test\n', open(dest, 'rb').read()) michael@0: michael@0: def test_preprocess_file_no_write(self): michael@0: ''' michael@0: Test various conditions where PreprocessedFile.copy is expected not to michael@0: write in the destination file. michael@0: ''' michael@0: src = self.tmppath('src') michael@0: dest = self.tmppath('dest') michael@0: depfile = self.tmppath('depfile') michael@0: michael@0: with open(src, 'wb') as tmp: michael@0: tmp.write('#ifdef FOO\ntest\n#endif') michael@0: michael@0: # Initial copy michael@0: f = PreprocessedFile(src, depfile_path=depfile, marker='#', defines={'FOO': True}) michael@0: self.assertTrue(f.copy(dest)) michael@0: michael@0: # Ensure subsequent copies won't trigger writes michael@0: self.assertFalse(f.copy(DestNoWrite(dest))) michael@0: self.assertEqual('test\n', open(dest, 'rb').read()) michael@0: michael@0: # When the source file is older than the destination file, even with michael@0: # different content, no copy should occur. michael@0: with open(src, 'wb') as tmp: michael@0: tmp.write('#ifdef FOO\nfooo\n#endif') michael@0: time = os.path.getmtime(dest) - 1 michael@0: os.utime(src, (time, time)) michael@0: self.assertFalse(f.copy(DestNoWrite(dest))) michael@0: self.assertEqual('test\n', open(dest, 'rb').read()) michael@0: michael@0: # skip_if_older=False is expected to force a copy in this situation. michael@0: self.assertTrue(f.copy(dest, skip_if_older=False)) michael@0: self.assertEqual('fooo\n', open(dest, 'rb').read()) michael@0: michael@0: def test_preprocess_file_dependencies(self): michael@0: ''' michael@0: Test that the preprocess runs if the dependencies of the source change michael@0: ''' michael@0: src = self.tmppath('src') michael@0: dest = self.tmppath('dest') michael@0: incl = self.tmppath('incl') michael@0: deps = self.tmppath('src.pp') michael@0: michael@0: with open(src, 'wb') as tmp: michael@0: tmp.write('#ifdef FOO\ntest\n#endif') michael@0: michael@0: with open(incl, 'wb') as tmp: michael@0: tmp.write('foo bar') michael@0: michael@0: # Initial copy michael@0: f = PreprocessedFile(src, depfile_path=deps, marker='#', defines={'FOO': True}) michael@0: self.assertTrue(f.copy(dest)) michael@0: michael@0: # Update the source so it #includes the include file. michael@0: with open(src, 'wb') as tmp: michael@0: tmp.write('#include incl\n') michael@0: time = os.path.getmtime(dest) + 1 michael@0: os.utime(src, (time, time)) michael@0: self.assertTrue(f.copy(dest)) michael@0: self.assertEqual('foo bar', open(dest, 'rb').read()) michael@0: michael@0: # If one of the dependencies changes, the file should be updated. The michael@0: # mtime of the dependency is set after the destination file, to avoid michael@0: # both files having the same time. michael@0: with open(incl, 'wb') as tmp: michael@0: tmp.write('quux') michael@0: time = os.path.getmtime(dest) + 1 michael@0: os.utime(incl, (time, time)) michael@0: self.assertTrue(f.copy(dest)) michael@0: self.assertEqual('quux', open(dest, 'rb').read()) michael@0: michael@0: # Perform one final copy to confirm that we don't run the preprocessor michael@0: # again. We update the mtime of the destination so it's newer than the michael@0: # input files. This would "just work" if we weren't changing michael@0: time = os.path.getmtime(incl) + 1 michael@0: os.utime(dest, (time, time)) michael@0: self.assertFalse(f.copy(DestNoWrite(dest))) michael@0: michael@0: def test_replace_symlink(self): michael@0: ''' michael@0: Test that if the destination exists, and is a symlink, the target of michael@0: the symlink is not overwritten by the preprocessor output. michael@0: ''' michael@0: if not self.symlink_supported: michael@0: return michael@0: michael@0: source = self.tmppath('source') michael@0: dest = self.tmppath('dest') michael@0: pp_source = self.tmppath('pp_in') michael@0: deps = self.tmppath('deps') michael@0: michael@0: with open(source, 'a'): michael@0: pass michael@0: michael@0: os.symlink(source, dest) michael@0: self.assertTrue(os.path.islink(dest)) michael@0: michael@0: with open(pp_source, 'wb') as tmp: michael@0: tmp.write('#define FOO\nPREPROCESSED') michael@0: michael@0: f = PreprocessedFile(pp_source, depfile_path=deps, marker='#', michael@0: defines={'FOO': True}) michael@0: self.assertTrue(f.copy(dest)) michael@0: michael@0: self.assertEqual('PREPROCESSED', open(dest, 'rb').read()) michael@0: self.assertFalse(os.path.islink(dest)) michael@0: self.assertEqual('', open(source, 'rb').read()) michael@0: michael@0: class TestExistingFile(TestWithTmpDir): michael@0: def test_required_missing_dest(self): michael@0: with self.assertRaisesRegexp(ErrorMessage, 'Required existing file'): michael@0: f = ExistingFile(required=True) michael@0: f.copy(self.tmppath('dest')) michael@0: michael@0: def test_required_existing_dest(self): michael@0: p = self.tmppath('dest') michael@0: with open(p, 'a'): michael@0: pass michael@0: michael@0: f = ExistingFile(required=True) michael@0: f.copy(p) michael@0: michael@0: def test_optional_missing_dest(self): michael@0: f = ExistingFile(required=False) michael@0: f.copy(self.tmppath('dest')) michael@0: michael@0: def test_optional_existing_dest(self): michael@0: p = self.tmppath('dest') michael@0: with open(p, 'a'): michael@0: pass michael@0: michael@0: f = ExistingFile(required=False) michael@0: f.copy(p) michael@0: michael@0: michael@0: class TestGeneratedFile(TestWithTmpDir): michael@0: def test_generated_file(self): michael@0: ''' michael@0: Check that GeneratedFile.copy yields the proper content in the michael@0: destination file in all situations that trigger different code paths michael@0: (see TestFile.test_file) michael@0: ''' michael@0: dest = self.tmppath('dest') michael@0: michael@0: for content in samples: michael@0: f = GeneratedFile(content) michael@0: f.copy(dest) michael@0: self.assertEqual(content, open(dest, 'rb').read()) michael@0: michael@0: def test_generated_file_open(self): michael@0: ''' michael@0: Test whether GeneratedFile.open returns an appropriately reset file michael@0: object. michael@0: ''' michael@0: content = ''.join(samples) michael@0: f = GeneratedFile(content) michael@0: self.assertEqual(content[:42], f.open().read(42)) michael@0: self.assertEqual(content, f.open().read()) michael@0: michael@0: def test_generated_file_no_write(self): michael@0: ''' michael@0: Test various conditions where GeneratedFile.copy is expected not to michael@0: write in the destination file. michael@0: ''' michael@0: dest = self.tmppath('dest') michael@0: michael@0: # Initial copy michael@0: f = GeneratedFile('test') michael@0: f.copy(dest) michael@0: michael@0: # Ensure subsequent copies won't trigger writes michael@0: f.copy(DestNoWrite(dest)) michael@0: self.assertEqual('test', open(dest, 'rb').read()) michael@0: michael@0: # When using a new instance with the same content, no copy should occur michael@0: f = GeneratedFile('test') michael@0: f.copy(DestNoWrite(dest)) michael@0: self.assertEqual('test', open(dest, 'rb').read()) michael@0: michael@0: # Double check that under conditions where a copy occurs, we would get michael@0: # an exception. michael@0: f = GeneratedFile('fooo') michael@0: self.assertRaises(RuntimeError, f.copy, DestNoWrite(dest)) michael@0: michael@0: michael@0: class TestDeflatedFile(TestWithTmpDir): michael@0: def test_deflated_file(self): michael@0: ''' michael@0: Check that DeflatedFile.copy yields the proper content in the michael@0: destination file in all situations that trigger different code paths michael@0: (see TestFile.test_file) michael@0: ''' michael@0: src = self.tmppath('src.jar') michael@0: dest = self.tmppath('dest') michael@0: michael@0: contents = {} michael@0: with JarWriter(src) as jar: michael@0: for content in samples: michael@0: name = ''.join(random.choice(string.letters) michael@0: for i in xrange(8)) michael@0: jar.add(name, content, compress=True) michael@0: contents[name] = content michael@0: michael@0: for j in JarReader(src): michael@0: f = DeflatedFile(j) michael@0: f.copy(dest) michael@0: self.assertEqual(contents[j.filename], open(dest, 'rb').read()) michael@0: michael@0: def test_deflated_file_open(self): michael@0: ''' michael@0: Test whether DeflatedFile.open returns an appropriately reset file michael@0: object. michael@0: ''' michael@0: src = self.tmppath('src.jar') michael@0: content = ''.join(samples) michael@0: with JarWriter(src) as jar: michael@0: jar.add('content', content) michael@0: michael@0: f = DeflatedFile(JarReader(src)['content']) michael@0: self.assertEqual(content[:42], f.open().read(42)) michael@0: self.assertEqual(content, f.open().read()) michael@0: michael@0: def test_deflated_file_no_write(self): michael@0: ''' michael@0: Test various conditions where DeflatedFile.copy is expected not to michael@0: write in the destination file. michael@0: ''' michael@0: src = self.tmppath('src.jar') michael@0: dest = self.tmppath('dest') michael@0: michael@0: with JarWriter(src) as jar: michael@0: jar.add('test', 'test') michael@0: jar.add('test2', 'test') michael@0: jar.add('fooo', 'fooo') michael@0: michael@0: jar = JarReader(src) michael@0: # Initial copy michael@0: f = DeflatedFile(jar['test']) michael@0: f.copy(dest) michael@0: michael@0: # Ensure subsequent copies won't trigger writes michael@0: f.copy(DestNoWrite(dest)) michael@0: self.assertEqual('test', open(dest, 'rb').read()) michael@0: michael@0: # When using a different file with the same content, no copy should michael@0: # occur michael@0: f = DeflatedFile(jar['test2']) michael@0: f.copy(DestNoWrite(dest)) michael@0: self.assertEqual('test', open(dest, 'rb').read()) michael@0: michael@0: # Double check that under conditions where a copy occurs, we would get michael@0: # an exception. michael@0: f = DeflatedFile(jar['fooo']) michael@0: self.assertRaises(RuntimeError, f.copy, DestNoWrite(dest)) michael@0: michael@0: michael@0: class TestManifestFile(TestWithTmpDir): michael@0: def test_manifest_file(self): michael@0: f = ManifestFile('chrome') michael@0: f.add(ManifestContent('chrome', 'global', 'toolkit/content/global/')) michael@0: f.add(ManifestResource('chrome', 'gre-resources', 'toolkit/res/')) michael@0: f.add(ManifestResource('chrome/pdfjs', 'pdfjs', './')) michael@0: f.add(ManifestContent('chrome/pdfjs', 'pdfjs', 'pdfjs')) michael@0: f.add(ManifestLocale('chrome', 'browser', 'en-US', michael@0: 'en-US/locale/browser/')) michael@0: michael@0: f.copy(self.tmppath('chrome.manifest')) michael@0: self.assertEqual(open(self.tmppath('chrome.manifest')).readlines(), [ michael@0: 'content global toolkit/content/global/\n', michael@0: 'resource gre-resources toolkit/res/\n', michael@0: 'resource pdfjs pdfjs/\n', michael@0: 'content pdfjs pdfjs/pdfjs\n', michael@0: 'locale browser en-US en-US/locale/browser/\n', michael@0: ]) michael@0: michael@0: self.assertRaises( michael@0: ValueError, michael@0: f.remove, michael@0: ManifestContent('', 'global', 'toolkit/content/global/') michael@0: ) michael@0: self.assertRaises( michael@0: ValueError, michael@0: f.remove, michael@0: ManifestOverride('chrome', 'chrome://global/locale/netError.dtd', michael@0: 'chrome://browser/locale/netError.dtd') michael@0: ) michael@0: michael@0: f.remove(ManifestContent('chrome', 'global', michael@0: 'toolkit/content/global/')) michael@0: self.assertRaises( michael@0: ValueError, michael@0: f.remove, michael@0: ManifestContent('chrome', 'global', 'toolkit/content/global/') michael@0: ) michael@0: michael@0: f.copy(self.tmppath('chrome.manifest')) michael@0: content = open(self.tmppath('chrome.manifest')).read() michael@0: self.assertEqual(content[:42], f.open().read(42)) michael@0: self.assertEqual(content, f.open().read()) michael@0: michael@0: # Compiled typelib for the following IDL: michael@0: # interface foo; michael@0: # [uuid(5f70da76-519c-4858-b71e-e3c92333e2d6)] michael@0: # interface bar { michael@0: # void bar(in foo f); michael@0: # }; michael@0: bar_xpt = GeneratedFile( michael@0: b'\x58\x50\x43\x4F\x4D\x0A\x54\x79\x70\x65\x4C\x69\x62\x0D\x0A\x1A' + michael@0: b'\x01\x02\x00\x02\x00\x00\x00\x7B\x00\x00\x00\x24\x00\x00\x00\x5C' + michael@0: b'\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + michael@0: b'\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x5F' + michael@0: b'\x70\xDA\x76\x51\x9C\x48\x58\xB7\x1E\xE3\xC9\x23\x33\xE2\xD6\x00' + michael@0: b'\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x0D\x00\x66\x6F\x6F\x00' + michael@0: b'\x62\x61\x72\x00\x62\x61\x72\x00\x00\x00\x00\x01\x00\x00\x00\x00' + michael@0: b'\x09\x01\x80\x92\x00\x01\x80\x06\x00\x00\x00' michael@0: ) michael@0: michael@0: # Compiled typelib for the following IDL: michael@0: # [uuid(3271bebc-927e-4bef-935e-44e0aaf3c1e5)] michael@0: # interface foo { michael@0: # void foo(); michael@0: # }; michael@0: foo_xpt = GeneratedFile( michael@0: b'\x58\x50\x43\x4F\x4D\x0A\x54\x79\x70\x65\x4C\x69\x62\x0D\x0A\x1A' + michael@0: b'\x01\x02\x00\x01\x00\x00\x00\x57\x00\x00\x00\x24\x00\x00\x00\x40' + michael@0: b'\x80\x00\x00\x32\x71\xBE\xBC\x92\x7E\x4B\xEF\x93\x5E\x44\xE0\xAA' + michael@0: b'\xF3\xC1\xE5\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x09\x00' + michael@0: b'\x66\x6F\x6F\x00\x66\x6F\x6F\x00\x00\x00\x00\x01\x00\x00\x00\x00' + michael@0: b'\x05\x00\x80\x06\x00\x00\x00' michael@0: ) michael@0: michael@0: # Compiled typelib for the following IDL: michael@0: # [uuid(7057f2aa-fdc2-4559-abde-08d939f7e80d)] michael@0: # interface foo { michael@0: # void foo(); michael@0: # }; michael@0: foo2_xpt = GeneratedFile( michael@0: b'\x58\x50\x43\x4F\x4D\x0A\x54\x79\x70\x65\x4C\x69\x62\x0D\x0A\x1A' + michael@0: b'\x01\x02\x00\x01\x00\x00\x00\x57\x00\x00\x00\x24\x00\x00\x00\x40' + michael@0: b'\x80\x00\x00\x70\x57\xF2\xAA\xFD\xC2\x45\x59\xAB\xDE\x08\xD9\x39' + michael@0: b'\xF7\xE8\x0D\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x09\x00' + michael@0: b'\x66\x6F\x6F\x00\x66\x6F\x6F\x00\x00\x00\x00\x01\x00\x00\x00\x00' + michael@0: b'\x05\x00\x80\x06\x00\x00\x00' michael@0: ) michael@0: michael@0: michael@0: def read_interfaces(file): michael@0: return dict((i.name, i) for i in Typelib.read(file).interfaces) michael@0: michael@0: michael@0: class TestXPTFile(TestWithTmpDir): michael@0: def test_xpt_file(self): michael@0: x = XPTFile() michael@0: x.add(foo_xpt) michael@0: x.add(bar_xpt) michael@0: x.copy(self.tmppath('interfaces.xpt')) michael@0: michael@0: foo = read_interfaces(foo_xpt.open()) michael@0: foo2 = read_interfaces(foo2_xpt.open()) michael@0: bar = read_interfaces(bar_xpt.open()) michael@0: linked = read_interfaces(self.tmppath('interfaces.xpt')) michael@0: self.assertEqual(foo['foo'], linked['foo']) michael@0: self.assertEqual(bar['bar'], linked['bar']) michael@0: michael@0: x.remove(foo_xpt) michael@0: x.copy(self.tmppath('interfaces2.xpt')) michael@0: linked = read_interfaces(self.tmppath('interfaces2.xpt')) michael@0: self.assertEqual(bar['foo'], linked['foo']) michael@0: self.assertEqual(bar['bar'], linked['bar']) michael@0: michael@0: x.add(foo_xpt) michael@0: x.copy(DestNoWrite(self.tmppath('interfaces.xpt'))) michael@0: linked = read_interfaces(self.tmppath('interfaces.xpt')) michael@0: self.assertEqual(foo['foo'], linked['foo']) michael@0: self.assertEqual(bar['bar'], linked['bar']) michael@0: michael@0: x = XPTFile() michael@0: x.add(foo2_xpt) michael@0: x.add(bar_xpt) michael@0: x.copy(self.tmppath('interfaces.xpt')) michael@0: linked = read_interfaces(self.tmppath('interfaces.xpt')) michael@0: self.assertEqual(foo2['foo'], linked['foo']) michael@0: self.assertEqual(bar['bar'], linked['bar']) michael@0: michael@0: x = XPTFile() michael@0: x.add(foo_xpt) michael@0: x.add(foo2_xpt) michael@0: x.add(bar_xpt) michael@0: from xpt import DataError michael@0: self.assertRaises(DataError, x.copy, self.tmppath('interfaces.xpt')) michael@0: michael@0: michael@0: class TestMinifiedProperties(TestWithTmpDir): michael@0: def test_minified_properties(self): michael@0: propLines = [ michael@0: '# Comments are removed', michael@0: 'foo = bar', michael@0: '', michael@0: '# Another comment', michael@0: ] michael@0: prop = GeneratedFile('\n'.join(propLines)) michael@0: self.assertEqual(MinifiedProperties(prop).open().readlines(), michael@0: ['foo = bar\n', '\n']) michael@0: open(self.tmppath('prop'), 'wb').write('\n'.join(propLines)) michael@0: MinifiedProperties(File(self.tmppath('prop'))) \ michael@0: .copy(self.tmppath('prop2')) michael@0: self.assertEqual(open(self.tmppath('prop2')).readlines(), michael@0: ['foo = bar\n', '\n']) michael@0: michael@0: michael@0: class TestMinifiedJavaScript(TestWithTmpDir): michael@0: orig_lines = [ michael@0: '// Comment line', michael@0: 'let foo = "bar";', michael@0: 'var bar = true;', michael@0: '', michael@0: '// Another comment', michael@0: ] michael@0: michael@0: def test_minified_javascript(self): michael@0: orig_f = GeneratedFile('\n'.join(self.orig_lines)) michael@0: min_f = MinifiedJavaScript(orig_f) michael@0: michael@0: mini_lines = min_f.open().readlines() michael@0: self.assertTrue(mini_lines) michael@0: self.assertTrue(len(mini_lines) < len(self.orig_lines)) michael@0: michael@0: def _verify_command(self, code): michael@0: our_dir = os.path.abspath(os.path.dirname(__file__)) michael@0: return [ michael@0: sys.executable, michael@0: os.path.join(our_dir, 'support', 'minify_js_verify.py'), michael@0: code, michael@0: ] michael@0: michael@0: def test_minified_verify_success(self): michael@0: orig_f = GeneratedFile('\n'.join(self.orig_lines)) michael@0: min_f = MinifiedJavaScript(orig_f, michael@0: verify_command=self._verify_command('0')) michael@0: michael@0: mini_lines = min_f.open().readlines() michael@0: self.assertTrue(mini_lines) michael@0: self.assertTrue(len(mini_lines) < len(self.orig_lines)) michael@0: michael@0: def test_minified_verify_failure(self): michael@0: orig_f = GeneratedFile('\n'.join(self.orig_lines)) michael@0: min_f = MinifiedJavaScript(orig_f, michael@0: verify_command=self._verify_command('1')) michael@0: michael@0: mini_lines = min_f.open().readlines() michael@0: self.assertEqual(mini_lines, orig_f.open().readlines()) michael@0: michael@0: michael@0: class MatchTestTemplate(object): michael@0: def prepare_match_test(self, with_dotfiles=False): michael@0: self.add('bar') michael@0: self.add('foo/bar') michael@0: self.add('foo/baz') michael@0: self.add('foo/qux/1') michael@0: self.add('foo/qux/bar') michael@0: self.add('foo/qux/2/test') michael@0: self.add('foo/qux/2/test2') michael@0: if with_dotfiles: michael@0: self.add('foo/.foo') michael@0: self.add('foo/.bar/foo') michael@0: michael@0: def do_match_test(self): michael@0: self.do_check('', [ michael@0: 'bar', 'foo/bar', 'foo/baz', 'foo/qux/1', 'foo/qux/bar', michael@0: 'foo/qux/2/test', 'foo/qux/2/test2' michael@0: ]) michael@0: self.do_check('*', [ michael@0: 'bar', 'foo/bar', 'foo/baz', 'foo/qux/1', 'foo/qux/bar', michael@0: 'foo/qux/2/test', 'foo/qux/2/test2' michael@0: ]) michael@0: self.do_check('foo/qux', [ michael@0: 'foo/qux/1', 'foo/qux/bar', 'foo/qux/2/test', 'foo/qux/2/test2' michael@0: ]) michael@0: self.do_check('foo/b*', ['foo/bar', 'foo/baz']) michael@0: self.do_check('baz', []) michael@0: self.do_check('foo/foo', []) michael@0: self.do_check('foo/*ar', ['foo/bar']) michael@0: self.do_check('*ar', ['bar']) michael@0: self.do_check('*/bar', ['foo/bar']) michael@0: self.do_check('foo/*ux', [ michael@0: 'foo/qux/1', 'foo/qux/bar', 'foo/qux/2/test', 'foo/qux/2/test2' michael@0: ]) michael@0: self.do_check('foo/q*ux', [ michael@0: 'foo/qux/1', 'foo/qux/bar', 'foo/qux/2/test', 'foo/qux/2/test2' michael@0: ]) michael@0: self.do_check('foo/*/2/test*', ['foo/qux/2/test', 'foo/qux/2/test2']) michael@0: self.do_check('**/bar', ['bar', 'foo/bar', 'foo/qux/bar']) michael@0: self.do_check('foo/**/test', ['foo/qux/2/test']) michael@0: self.do_check('foo/**', [ michael@0: 'foo/bar', 'foo/baz', 'foo/qux/1', 'foo/qux/bar', michael@0: 'foo/qux/2/test', 'foo/qux/2/test2' michael@0: ]) michael@0: self.do_check('**/2/test*', ['foo/qux/2/test', 'foo/qux/2/test2']) michael@0: self.do_check('**/foo', [ michael@0: 'foo/bar', 'foo/baz', 'foo/qux/1', 'foo/qux/bar', michael@0: 'foo/qux/2/test', 'foo/qux/2/test2' michael@0: ]) michael@0: self.do_check('**/barbaz', []) michael@0: self.do_check('f**/bar', ['foo/bar']) michael@0: michael@0: def do_finder_test(self, finder): michael@0: self.assertTrue(finder.contains('foo/.foo')) michael@0: self.assertTrue(finder.contains('foo/.bar')) michael@0: self.assertTrue('foo/.foo' in [f for f, c in michael@0: finder.find('foo/.foo')]) michael@0: self.assertTrue('foo/.bar/foo' in [f for f, c in michael@0: finder.find('foo/.bar')]) michael@0: self.assertEqual(sorted([f for f, c in finder.find('foo/.*')]), michael@0: ['foo/.bar/foo', 'foo/.foo']) michael@0: for pattern in ['foo', '**', '**/*', '**/foo', 'foo/*']: michael@0: self.assertFalse('foo/.foo' in [f for f, c in michael@0: finder.find(pattern)]) michael@0: self.assertFalse('foo/.bar/foo' in [f for f, c in michael@0: finder.find(pattern)]) michael@0: self.assertEqual(sorted([f for f, c in finder.find(pattern)]), michael@0: sorted([f for f, c in finder michael@0: if mozpack.path.match(f, pattern)])) michael@0: michael@0: michael@0: def do_check(test, finder, pattern, result): michael@0: if result: michael@0: test.assertTrue(finder.contains(pattern)) michael@0: else: michael@0: test.assertFalse(finder.contains(pattern)) michael@0: test.assertEqual(sorted(list(f for f, c in finder.find(pattern))), michael@0: sorted(result)) michael@0: michael@0: michael@0: class TestFileFinder(MatchTestTemplate, TestWithTmpDir): michael@0: def add(self, path): michael@0: ensureParentDir(self.tmppath(path)) michael@0: open(self.tmppath(path), 'wb').write(path) michael@0: michael@0: def do_check(self, pattern, result): michael@0: do_check(self, self.finder, pattern, result) michael@0: michael@0: def test_file_finder(self): michael@0: self.prepare_match_test(with_dotfiles=True) michael@0: self.finder = FileFinder(self.tmpdir) michael@0: self.do_match_test() michael@0: self.do_finder_test(self.finder) michael@0: michael@0: def test_ignored_dirs(self): michael@0: """Ignored directories should not have results returned.""" michael@0: self.prepare_match_test() michael@0: self.add('fooz') michael@0: michael@0: # Present to ensure prefix matching doesn't exclude. michael@0: self.add('foo/quxz') michael@0: michael@0: self.finder = FileFinder(self.tmpdir, ignore=['foo/qux']) michael@0: michael@0: self.do_check('**', ['bar', 'foo/bar', 'foo/baz', 'foo/quxz', 'fooz']) michael@0: self.do_check('foo/*', ['foo/bar', 'foo/baz', 'foo/quxz']) michael@0: self.do_check('foo/**', ['foo/bar', 'foo/baz', 'foo/quxz']) michael@0: self.do_check('foo/qux/**', []) michael@0: self.do_check('foo/qux/*', []) michael@0: self.do_check('foo/qux/bar', []) michael@0: self.do_check('foo/quxz', ['foo/quxz']) michael@0: self.do_check('fooz', ['fooz']) michael@0: michael@0: def test_ignored_files(self): michael@0: """Ignored files should not have results returned.""" michael@0: self.prepare_match_test() michael@0: michael@0: # Be sure prefix match doesn't get ignored. michael@0: self.add('barz') michael@0: michael@0: self.finder = FileFinder(self.tmpdir, ignore=['foo/bar', 'bar']) michael@0: self.do_check('**', ['barz', 'foo/baz', 'foo/qux/1', 'foo/qux/2/test', michael@0: 'foo/qux/2/test2', 'foo/qux/bar']) michael@0: self.do_check('foo/**', ['foo/baz', 'foo/qux/1', 'foo/qux/2/test', michael@0: 'foo/qux/2/test2', 'foo/qux/bar']) michael@0: michael@0: def test_ignored_patterns(self): michael@0: """Ignore entries with patterns should be honored.""" michael@0: self.prepare_match_test() michael@0: michael@0: self.add('foo/quxz') michael@0: michael@0: self.finder = FileFinder(self.tmpdir, ignore=['foo/qux/*']) michael@0: self.do_check('**', ['foo/bar', 'foo/baz', 'foo/quxz', 'bar']) michael@0: self.do_check('foo/**', ['foo/bar', 'foo/baz', 'foo/quxz']) michael@0: michael@0: michael@0: class TestJarFinder(MatchTestTemplate, TestWithTmpDir): michael@0: def add(self, path): michael@0: self.jar.add(path, path, compress=True) michael@0: michael@0: def do_check(self, pattern, result): michael@0: do_check(self, self.finder, pattern, result) michael@0: michael@0: def test_jar_finder(self): michael@0: self.jar = JarWriter(file=self.tmppath('test.jar')) michael@0: self.prepare_match_test() michael@0: self.jar.finish() michael@0: reader = JarReader(file=self.tmppath('test.jar')) michael@0: self.finder = JarFinder(self.tmppath('test.jar'), reader) michael@0: self.do_match_test() michael@0: michael@0: michael@0: if __name__ == '__main__': michael@0: mozunit.main()