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 mozpack.mozjar import ( michael@0: JarReaderError, michael@0: JarWriterError, michael@0: JarStruct, michael@0: JarReader, michael@0: JarWriter, michael@0: Deflater, michael@0: JarLog, michael@0: ) michael@0: from collections import OrderedDict michael@0: from mozpack.test.test_files import MockDest michael@0: import unittest michael@0: import mozunit michael@0: from cStringIO import StringIO michael@0: from urllib import pathname2url michael@0: import mozpack.path michael@0: import os michael@0: michael@0: michael@0: class TestJarStruct(unittest.TestCase): michael@0: class Foo(JarStruct): michael@0: MAGIC = 0x01020304 michael@0: STRUCT = OrderedDict([ michael@0: ('foo', 'uint32'), michael@0: ('bar', 'uint16'), michael@0: ('qux', 'uint16'), michael@0: ('length', 'uint16'), michael@0: ('length2', 'uint16'), michael@0: ('string', 'length'), michael@0: ('string2', 'length2'), michael@0: ]) michael@0: michael@0: def test_jar_struct(self): michael@0: foo = TestJarStruct.Foo() michael@0: self.assertEqual(foo.signature, TestJarStruct.Foo.MAGIC) michael@0: self.assertEqual(foo['foo'], 0) michael@0: self.assertEqual(foo['bar'], 0) michael@0: self.assertEqual(foo['qux'], 0) michael@0: self.assertFalse('length' in foo) michael@0: self.assertFalse('length2' in foo) michael@0: self.assertEqual(foo['string'], '') michael@0: self.assertEqual(foo['string2'], '') michael@0: michael@0: self.assertEqual(foo.size, 16) michael@0: michael@0: foo['foo'] = 0x42434445 michael@0: foo['bar'] = 0xabcd michael@0: foo['qux'] = 0xef01 michael@0: foo['string'] = 'abcde' michael@0: foo['string2'] = 'Arbitrarily long string' michael@0: michael@0: serialized = b'\x04\x03\x02\x01\x45\x44\x43\x42\xcd\xab\x01\xef' + \ michael@0: b'\x05\x00\x17\x00abcdeArbitrarily long string' michael@0: self.assertEqual(foo.size, len(serialized)) michael@0: foo_serialized = foo.serialize() michael@0: self.assertEqual(foo_serialized, serialized) michael@0: michael@0: def do_test_read_jar_struct(self, data): michael@0: self.assertRaises(JarReaderError, TestJarStruct.Foo, data) michael@0: self.assertRaises(JarReaderError, TestJarStruct.Foo, data[2:]) michael@0: michael@0: foo = TestJarStruct.Foo(data[1:]) michael@0: self.assertEqual(foo['foo'], 0x45444342) michael@0: self.assertEqual(foo['bar'], 0xcdab) michael@0: self.assertEqual(foo['qux'], 0x01ef) michael@0: self.assertFalse('length' in foo) michael@0: self.assertFalse('length2' in foo) michael@0: self.assertEqual(foo['string'], '012345') michael@0: self.assertEqual(foo['string2'], '67') michael@0: michael@0: def test_read_jar_struct(self): michael@0: data = b'\x00\x04\x03\x02\x01\x42\x43\x44\x45\xab\xcd\xef' + \ michael@0: b'\x01\x06\x00\x02\x0001234567890' michael@0: self.do_test_read_jar_struct(data) michael@0: michael@0: def test_read_jar_struct_memoryview(self): michael@0: data = b'\x00\x04\x03\x02\x01\x42\x43\x44\x45\xab\xcd\xef' + \ michael@0: b'\x01\x06\x00\x02\x0001234567890' michael@0: self.do_test_read_jar_struct(memoryview(data)) michael@0: michael@0: michael@0: class TestDeflater(unittest.TestCase): michael@0: def wrap(self, data): michael@0: return data michael@0: michael@0: def test_deflater_no_compress(self): michael@0: deflater = Deflater(False) michael@0: deflater.write(self.wrap('abc')) michael@0: self.assertFalse(deflater.compressed) michael@0: self.assertEqual(deflater.uncompressed_size, 3) michael@0: self.assertEqual(deflater.compressed_size, deflater.uncompressed_size) michael@0: self.assertEqual(deflater.compressed_data, 'abc') michael@0: self.assertEqual(deflater.crc32, 0x352441c2) michael@0: michael@0: def test_deflater_compress_no_gain(self): michael@0: deflater = Deflater(True) michael@0: deflater.write(self.wrap('abc')) michael@0: self.assertFalse(deflater.compressed) michael@0: self.assertEqual(deflater.uncompressed_size, 3) michael@0: self.assertEqual(deflater.compressed_size, deflater.uncompressed_size) michael@0: self.assertEqual(deflater.compressed_data, 'abc') michael@0: self.assertEqual(deflater.crc32, 0x352441c2) michael@0: michael@0: def test_deflater_compress(self): michael@0: deflater = Deflater(True) michael@0: deflater.write(self.wrap('aaaaaaaaaaaaanopqrstuvwxyz')) michael@0: self.assertTrue(deflater.compressed) michael@0: self.assertEqual(deflater.uncompressed_size, 26) michael@0: self.assertNotEqual(deflater.compressed_size, michael@0: deflater.uncompressed_size) michael@0: self.assertEqual(deflater.crc32, 0xd46b97ed) michael@0: # The CRC is the same as when not compressed michael@0: deflater = Deflater(False) michael@0: self.assertFalse(deflater.compressed) michael@0: deflater.write(self.wrap('aaaaaaaaaaaaanopqrstuvwxyz')) michael@0: self.assertEqual(deflater.crc32, 0xd46b97ed) michael@0: michael@0: michael@0: class TestDeflaterMemoryView(TestDeflater): michael@0: def wrap(self, data): michael@0: return memoryview(data) michael@0: michael@0: michael@0: class TestJar(unittest.TestCase): michael@0: optimize = False michael@0: michael@0: def test_jar(self): michael@0: s = MockDest() michael@0: with JarWriter(fileobj=s, optimize=self.optimize) as jar: michael@0: jar.add('foo', 'foo') michael@0: self.assertRaises(JarWriterError, jar.add, 'foo', 'bar') michael@0: jar.add('bar', 'aaaaaaaaaaaaanopqrstuvwxyz') michael@0: jar.add('baz/qux', 'aaaaaaaaaaaaanopqrstuvwxyz', False) michael@0: michael@0: files = [j for j in JarReader(fileobj=s)] michael@0: michael@0: self.assertEqual(files[0].filename, 'foo') michael@0: self.assertFalse(files[0].compressed) michael@0: self.assertEqual(files[0].read(), 'foo') michael@0: michael@0: self.assertEqual(files[1].filename, 'bar') michael@0: self.assertTrue(files[1].compressed) michael@0: self.assertEqual(files[1].read(), 'aaaaaaaaaaaaanopqrstuvwxyz') michael@0: michael@0: self.assertEqual(files[2].filename, 'baz/qux') michael@0: self.assertFalse(files[2].compressed) michael@0: self.assertEqual(files[2].read(), 'aaaaaaaaaaaaanopqrstuvwxyz') michael@0: michael@0: s = MockDest() michael@0: with JarWriter(fileobj=s, compress=False, michael@0: optimize=self.optimize) as jar: michael@0: jar.add('bar', 'aaaaaaaaaaaaanopqrstuvwxyz') michael@0: jar.add('foo', 'foo') michael@0: jar.add('baz/qux', 'aaaaaaaaaaaaanopqrstuvwxyz', True) michael@0: michael@0: jar = JarReader(fileobj=s) michael@0: files = [j for j in jar] michael@0: michael@0: self.assertEqual(files[0].filename, 'bar') michael@0: self.assertFalse(files[0].compressed) michael@0: self.assertEqual(files[0].read(), 'aaaaaaaaaaaaanopqrstuvwxyz') michael@0: michael@0: self.assertEqual(files[1].filename, 'foo') michael@0: self.assertFalse(files[1].compressed) michael@0: self.assertEqual(files[1].read(), 'foo') michael@0: michael@0: self.assertEqual(files[2].filename, 'baz/qux') michael@0: self.assertTrue(files[2].compressed) michael@0: self.assertEqual(files[2].read(), 'aaaaaaaaaaaaanopqrstuvwxyz') michael@0: michael@0: self.assertTrue('bar' in jar) michael@0: self.assertTrue('foo' in jar) michael@0: self.assertFalse('baz' in jar) michael@0: self.assertTrue('baz/qux' in jar) michael@0: self.assertTrue(jar['bar'], files[1]) michael@0: self.assertTrue(jar['foo'], files[0]) michael@0: self.assertTrue(jar['baz/qux'], files[2]) michael@0: michael@0: s.seek(0) michael@0: jar = JarReader(fileobj=s) michael@0: self.assertTrue('bar' in jar) michael@0: self.assertTrue('foo' in jar) michael@0: self.assertFalse('baz' in jar) michael@0: self.assertTrue('baz/qux' in jar) michael@0: michael@0: files[0].seek(0) michael@0: self.assertEqual(jar['bar'].filename, files[0].filename) michael@0: self.assertEqual(jar['bar'].compressed, files[0].compressed) michael@0: self.assertEqual(jar['bar'].read(), files[0].read()) michael@0: michael@0: files[1].seek(0) michael@0: self.assertEqual(jar['foo'].filename, files[1].filename) michael@0: self.assertEqual(jar['foo'].compressed, files[1].compressed) michael@0: self.assertEqual(jar['foo'].read(), files[1].read()) michael@0: michael@0: files[2].seek(0) michael@0: self.assertEqual(jar['baz/qux'].filename, files[2].filename) michael@0: self.assertEqual(jar['baz/qux'].compressed, files[2].compressed) michael@0: self.assertEqual(jar['baz/qux'].read(), files[2].read()) michael@0: michael@0: def test_rejar(self): michael@0: s = MockDest() michael@0: with JarWriter(fileobj=s, optimize=self.optimize) as jar: michael@0: jar.add('foo', 'foo') michael@0: jar.add('bar', 'aaaaaaaaaaaaanopqrstuvwxyz') michael@0: jar.add('baz/qux', 'aaaaaaaaaaaaanopqrstuvwxyz', False) michael@0: michael@0: new = MockDest() michael@0: with JarWriter(fileobj=new, optimize=self.optimize) as jar: michael@0: for j in JarReader(fileobj=s): michael@0: jar.add(j.filename, j) michael@0: michael@0: jar = JarReader(fileobj=new) michael@0: files = [j for j in jar] michael@0: michael@0: self.assertEqual(files[0].filename, 'foo') michael@0: self.assertFalse(files[0].compressed) michael@0: self.assertEqual(files[0].read(), 'foo') michael@0: michael@0: self.assertEqual(files[1].filename, 'bar') michael@0: self.assertTrue(files[1].compressed) michael@0: self.assertEqual(files[1].read(), 'aaaaaaaaaaaaanopqrstuvwxyz') michael@0: michael@0: self.assertEqual(files[2].filename, 'baz/qux') michael@0: self.assertTrue(files[2].compressed) michael@0: self.assertEqual(files[2].read(), 'aaaaaaaaaaaaanopqrstuvwxyz') michael@0: michael@0: michael@0: class TestOptimizeJar(TestJar): michael@0: optimize = True michael@0: michael@0: michael@0: class TestPreload(unittest.TestCase): michael@0: def test_preload(self): michael@0: s = MockDest() michael@0: with JarWriter(fileobj=s) as jar: michael@0: jar.add('foo', 'foo') michael@0: jar.add('bar', 'abcdefghijklmnopqrstuvwxyz') michael@0: jar.add('baz/qux', 'aaaaaaaaaaaaanopqrstuvwxyz') michael@0: michael@0: jar = JarReader(fileobj=s) michael@0: self.assertEqual(jar.last_preloaded, None) michael@0: michael@0: with JarWriter(fileobj=s) as jar: michael@0: jar.add('foo', 'foo') michael@0: jar.add('bar', 'abcdefghijklmnopqrstuvwxyz') michael@0: jar.add('baz/qux', 'aaaaaaaaaaaaanopqrstuvwxyz') michael@0: jar.preload(['baz/qux', 'bar']) michael@0: michael@0: jar = JarReader(fileobj=s) michael@0: self.assertEqual(jar.last_preloaded, 'bar') michael@0: files = [j for j in jar] michael@0: michael@0: self.assertEqual(files[0].filename, 'baz/qux') michael@0: self.assertEqual(files[1].filename, 'bar') michael@0: self.assertEqual(files[2].filename, 'foo') michael@0: michael@0: michael@0: class TestJarLog(unittest.TestCase): michael@0: def test_jarlog(self): michael@0: base = 'file:' + pathname2url(os.path.abspath(os.curdir)) michael@0: s = StringIO('\n'.join([ michael@0: base + '/bar/baz.jar first', michael@0: base + '/bar/baz.jar second', michael@0: base + '/bar/baz.jar third', michael@0: base + '/bar/baz.jar second', michael@0: base + '/bar/baz.jar second', michael@0: 'jar:' + base + '/qux.zip!/omni.ja stuff', michael@0: base + '/bar/baz.jar first', michael@0: 'jar:' + base + '/qux.zip!/omni.ja other/stuff', michael@0: 'jar:' + base + '/qux.zip!/omni.ja stuff', michael@0: base + '/bar/baz.jar third', michael@0: 'jar:jar:' + base + '/qux.zip!/baz/baz.jar!/omni.ja nested/stuff', michael@0: 'jar:jar:jar:' + base + '/qux.zip!/baz/baz.jar!/foo.zip!/omni.ja' + michael@0: ' deeply/nested/stuff', michael@0: ])) michael@0: log = JarLog(fileobj=s) michael@0: canonicalize = lambda p: \ michael@0: mozpack.path.normsep(os.path.normcase(os.path.realpath(p))) michael@0: baz_jar = canonicalize('bar/baz.jar') michael@0: qux_zip = canonicalize('qux.zip') michael@0: self.assertEqual(set(log.keys()), set([ michael@0: baz_jar, michael@0: (qux_zip, 'omni.ja'), michael@0: (qux_zip, 'baz/baz.jar', 'omni.ja'), michael@0: (qux_zip, 'baz/baz.jar', 'foo.zip', 'omni.ja'), michael@0: ])) michael@0: self.assertEqual(log[baz_jar], [ michael@0: 'first', michael@0: 'second', michael@0: 'third', michael@0: ]) michael@0: self.assertEqual(log[(qux_zip, 'omni.ja')], [ michael@0: 'stuff', michael@0: 'other/stuff', michael@0: ]) michael@0: self.assertEqual(log[(qux_zip, 'baz/baz.jar', 'omni.ja')], michael@0: ['nested/stuff']) michael@0: self.assertEqual(log[(qux_zip, 'baz/baz.jar', 'foo.zip', michael@0: 'omni.ja')], ['deeply/nested/stuff']) michael@0: michael@0: # The above tests also indirectly check the value returned by michael@0: # JarLog.canonicalize for various jar: and file: urls, but michael@0: # JarLog.canonicalize also supports plain paths. michael@0: self.assertEqual(JarLog.canonicalize(os.path.abspath('bar/baz.jar')), michael@0: baz_jar) michael@0: self.assertEqual(JarLog.canonicalize('bar/baz.jar'), baz_jar) michael@0: michael@0: michael@0: if __name__ == '__main__': michael@0: mozunit.main()