1#!/usr/bin/python -u 2# 3# Python Bindings for LZMA 4# 5# Copyright (c) 2004-2015 by Joachim Bauch, mail@joachim-bauch.de 6# 7-Zip Copyright (C) 1999-2010 Igor Pavlov 7# LZMA SDK Copyright (C) 1999-2010 Igor Pavlov 8# 9# This library is free software; you can redistribute it and/or 10# modify it under the terms of the GNU Lesser General Public 11# License as published by the Free Software Foundation; either 12# version 2.1 of the License, or (at your option) any later version. 13# 14# This library is distributed in the hope that it will be useful, 15# but WITHOUT ANY WARRANTY; without even the implied warranty of 16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17# Lesser General Public License for more details. 18# 19# You should have received a copy of the GNU Lesser General Public 20# License along with this library; if not, write to the Free Software 21# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22# 23# $Id$ 24# 25from datetime import datetime 26import errno 27import os 28import pylzma 29from py7zlib import Archive7z, NoPasswordGivenError, WrongPasswordError, UTC 30import sys 31import unittest 32 33try: 34 sorted 35except NameError: 36 # Python 2.3 and older 37 def sorted(l): 38 l = list(l) 39 l.sort() 40 return l 41 42try: 43 xrange 44except NameError: 45 # Python 3.x 46 xrange = range 47 48if sys.version_info[:2] < (3, 0): 49 def bytes(s, encoding): 50 return s 51 52 def unicode_string(s): 53 return s.decode('latin-1') 54else: 55 def unicode_string(s): 56 return s 57 58ROOT = os.path.abspath(os.path.split(__file__)[0]) 59 60class Test7ZipFiles(unittest.TestCase): 61 62 def setUp(self): 63 self._open_files = [] 64 unittest.TestCase.setUp(self) 65 66 def tearDown(self): 67 while self._open_files: 68 fp = self._open_files.pop() 69 fp.close() 70 unittest.TestCase.tearDown(self) 71 72 def _open_file(self, filename, mode): 73 fp = open(filename, mode) 74 self._open_files.append(fp) 75 return fp 76 77 def _test_archive(self, filename): 78 fp = self._open_file(os.path.join(ROOT, 'data', filename), 'rb') 79 archive = Archive7z(fp) 80 self.assertEqual(sorted(archive.getnames()), ['test/test2.txt', 'test1.txt']) 81 self.assertEqual(archive.getmember('test2.txt'), None) 82 cf = archive.getmember('test1.txt') 83 self.assertTrue(cf.checkcrc()) 84 self.assertEqual(cf.lastwritetime // 10000000, 12786932628) 85 self.assertEqual(cf.lastwritetime.as_datetime().replace(microsecond=0), \ 86 datetime(2006, 3, 15, 21, 43, 48, 0, UTC)) 87 self.assertEqual(cf.read(), bytes('This file is located in the root.', 'ascii')) 88 cf.reset() 89 self.assertEqual(cf.read(), bytes('This file is located in the root.', 'ascii')) 90 91 cf = archive.getmember('test/test2.txt') 92 self.assertTrue(cf.checkcrc()) 93 self.assertEqual(cf.lastwritetime // 10000000, 12786932616) 94 self.assertEqual(cf.lastwritetime.as_datetime().replace(microsecond=0), \ 95 datetime(2006, 3, 15, 21, 43, 36, 0, UTC)) 96 self.assertEqual(cf.read(), bytes('This file is located in a folder.', 'ascii')) 97 cf.reset() 98 self.assertEqual(cf.read(), bytes('This file is located in a folder.', 'ascii')) 99 100 def _test_decode_all(self, archive): 101 filenames = archive.getnames() 102 for filename in filenames: 103 cf = archive.getmember(filename) 104 self.assertNotEqual(cf, None, filename) 105 self.assertTrue(cf.checkcrc(), 'crc failed for %s' % (filename)) 106 self.assertEqual(len(cf.read()), cf.uncompressed) 107 108 def test_non_solid(self): 109 # test loading of a non-solid archive 110 self._test_archive('non_solid.7z') 111 112 def test_solid(self): 113 # test loading of a solid archive 114 self._test_archive('solid.7z') 115 116 def _test_umlaut_archive(self, filename): 117 fp = self._open_file(os.path.join(ROOT, 'data', filename), 'rb') 118 archive = Archive7z(fp) 119 self.assertEqual(sorted(archive.getnames()), [unicode_string('t\xe4st.txt')]) 120 self.assertEqual(archive.getmember('test.txt'), None) 121 cf = archive.getmember(unicode_string('t\xe4st.txt')) 122 self.assertEqual(cf.read(), bytes('This file contains a german umlaut in the filename.', 'ascii')) 123 cf.reset() 124 self.assertEqual(cf.read(), bytes('This file contains a german umlaut in the filename.', 'ascii')) 125 126 def test_non_solid_umlaut(self): 127 # test loading of a non-solid archive containing files with umlauts 128 self._test_umlaut_archive('umlaut-non_solid.7z') 129 130 def test_solid_umlaut(self): 131 # test loading of a solid archive containing files with umlauts 132 self._test_umlaut_archive('umlaut-solid.7z') 133 134 def test_bugzilla_4(self): 135 # sample file for bugzilla #4 136 fp = self._open_file(os.path.join(ROOT, 'data', 'bugzilla_4.7z'), 'rb') 137 archive = Archive7z(fp) 138 self._test_decode_all(archive) 139 140 def test_encrypted_no_password(self): 141 # test loading of encrypted files without password (can read archived filenames) 142 fp = self._open_file(os.path.join(ROOT, 'data', 'encrypted.7z'), 'rb') 143 archive = Archive7z(fp) 144 filenames = archive.getnames() 145 self.assertEqual(sorted(archive.getnames()), sorted(['test1.txt', 'test/test2.txt'])) 146 self.assertRaises(NoPasswordGivenError, archive.getmember('test1.txt').read) 147 self.assertRaises(NoPasswordGivenError, archive.getmember('test/test2.txt').read) 148 149 def test_encrypted_password(self): 150 # test loading of encrypted files with correct password 151 fp = self._open_file(os.path.join(ROOT, 'data', 'encrypted.7z'), 'rb') 152 archive = Archive7z(fp, password='secret') 153 self._test_decode_all(archive) 154 155 def test_encrypted_wong_password(self): 156 # test loading of encrypted files with wrong password 157 fp = self._open_file(os.path.join(ROOT, 'data', 'encrypted.7z'), 'rb') 158 archive = Archive7z(fp, password='password') 159 filenames = archive.getnames() 160 for filename in filenames: 161 cf = archive.getmember(filename) 162 self.assertNotEqual(cf, None, filename) 163 self.assertRaises(WrongPasswordError, cf.checkcrc) 164 self.assertRaises(WrongPasswordError, cf.read) 165 166 def test_encrypted_short(self): 167 # test loading of encrypted file with short content with correct password 168 fp = self._open_file(os.path.join(ROOT, 'data', 'encrypted-short.7z'), 'rb') 169 archive = Archive7z(fp, password='secret') 170 self._test_decode_all(archive) 171 172 def test_encrypted_names_no_password(self): 173 # test loading of files with encrypted names without password 174 fp = self._open_file(os.path.join(ROOT, 'data', 'encrypted-names.7z'), 'rb') 175 self.assertRaises(NoPasswordGivenError, Archive7z, fp) 176 177 def test_encrypted_names_password(self): 178 # test loading of files with encrypted names with correct password 179 fp = self._open_file(os.path.join(ROOT, 'data', 'encrypted-names.7z'), 'rb') 180 archive = Archive7z(fp, password='secret') 181 self._test_decode_all(archive) 182 183 def test_encrypted_names_wong_password(self): 184 # test loading of files with encrypted names with wrong password 185 fp = self._open_file(os.path.join(ROOT, 'data', 'encrypted-names.7z'), 'rb') 186 self.assertRaises(WrongPasswordError, Archive7z, fp, password='password') 187 188 def test_deflate(self): 189 # test loading of deflate compressed files 190 self._test_archive('deflate.7z') 191 192 def test_deflate64(self): 193 # test loading of deflate64 compressed files 194 self._test_archive('deflate64.7z') 195 196 def test_bzip2(self): 197 # test loading of bzip2 compressed files 198 self._test_archive('bzip2.7z') 199 200 def test_copy(self): 201 # test loading of copy compressed files 202 self._test_archive('copy.7z') 203 204 def test_regress_1(self): 205 # prevent regression bug #1 reported by mail 206 fp = self._open_file(os.path.join(ROOT, 'data', 'regress_1.7z'), 'rb') 207 archive = Archive7z(fp) 208 filenames = list(archive.getnames()) 209 self.assertEqual(len(filenames), 1) 210 cf = archive.getmember(filenames[0]) 211 self.assertNotEqual(cf, None) 212 self.assertTrue(cf.checkcrc()) 213 data = cf.read() 214 self.assertEqual(len(data), cf.size) 215 216 def test_bugzilla_16(self): 217 # sample file for bugzilla #16 218 fp = self._open_file(os.path.join(ROOT, 'data', 'bugzilla_16.7z'), 'rb') 219 archive = Archive7z(fp) 220 self._test_decode_all(archive) 221 222 def test_empty(self): 223 # decompress empty archive 224 fp = self._open_file(os.path.join(ROOT, 'data', 'empty.7z'), 'rb') 225 archive = Archive7z(fp) 226 self.assertEqual(archive.getnames(), []) 227 228 def test_github_14(self): 229 # decompress content without filename 230 fp = self._open_file(os.path.join(ROOT, 'data', 'github_14.7z'), 'rb') 231 archive = Archive7z(fp) 232 self.assertEqual(archive.getnames(), ['github_14']) 233 cf = archive.getmember('github_14') 234 self.assertNotEqual(cf, None) 235 data = cf.read() 236 self.assertEqual(len(data), cf.uncompressed) 237 self.assertEqual(data, bytes('Hello GitHub issue #14.\n', 'ascii')) 238 239 # accessing by name returns an arbitrary compressed streams 240 # if both don't have a name in the archive 241 fp = self._open_file(os.path.join(ROOT, 'data', 'github_14_multi.7z'), 'rb') 242 archive = Archive7z(fp) 243 self.assertEqual(archive.getnames(), ['github_14_multi', 'github_14_multi']) 244 cf = archive.getmember('github_14_multi') 245 self.assertNotEqual(cf, None) 246 data = cf.read() 247 self.assertEqual(len(data), cf.uncompressed) 248 self.assertTrue(data in (bytes('Hello GitHub issue #14 1/2.\n', 'ascii'), bytes('Hello GitHub issue #14 2/2.\n', 'ascii'))) 249 250 # accessing by index returns both values 251 cf = archive.getmember(0) 252 self.assertNotEqual(cf, None) 253 data = cf.read() 254 self.assertEqual(len(data), cf.uncompressed) 255 self.assertEqual(data, bytes('Hello GitHub issue #14 1/2.\n', 'ascii')) 256 cf = archive.getmember(1) 257 self.assertNotEqual(cf, None) 258 data = cf.read() 259 self.assertEqual(len(data), cf.uncompressed) 260 self.assertEqual(data, bytes('Hello GitHub issue #14 2/2.\n', 'ascii')) 261 262 def test_github_17(self): 263 try: 264 fp = self._open_file(os.path.join(ROOT, 'data', 'ux.stackexchange.com.7z'), 'rb') 265 except EnvironmentError: 266 e = sys.exc_info()[1] 267 if e.errno != errno.ENOENT: 268 raise 269 270 # test is optional 271 import warnings 272 warnings.warn("File 'ux.stackexchange.com.7z' was not found, please download manually. Test will be skipped.") 273 return 274 275 archive = Archive7z(fp) 276 self._test_decode_all(archive) 277 278 def test_github_37(self): 279 self._test_archive('github_37_dummy.7z') 280 281 def test_github_33(self): 282 fp = self._open_file(os.path.join(ROOT, 'data', 'github_33.7z'), 'rb') 283 archive = Archive7z(fp, password='abc') 284 # Archive only contains an empty file. 285 self.assertEqual(archive.getnames(), ['successs.txt']) 286 self.assertEqual(archive.header.files.numfiles, 1) 287 self.assertEqual([x['filename'] for x in archive.header.files.files], [u'successs.txt']) 288 cf = archive.getmember('successs.txt') 289 self.assertNotEqual(cf, None) 290 data = cf.read() 291 self.assertEqual(len(data), cf.uncompressed) 292 self.assertEqual(len(data), 0) 293 self._test_decode_all(archive) 294 295 def test_github_43(self): 296 # test loading of lzma2 compressed files 297 self._test_archive('github_43.7z') 298 299 def test_github_43_provided(self): 300 # test loading file submitted by @mikenye 301 fp = self._open_file(os.path.join(ROOT, 'data', 'test-issue-43.7z'), 'rb') 302 archive = Archive7z(fp) 303 self.assertEqual(sorted(archive.getnames()), ['blah.txt'] + ['blah%d.txt' % x for x in xrange(2, 10)]) 304 self._test_decode_all(archive) 305 306 def test_github_53(self): 307 # test loading file submitted by @VinylChloride 308 fp = self._open_file(os.path.join(ROOT, 'data', 'github_53.7z'), 'rb') 309 archive = Archive7z(fp, password='1234') 310 self.assertEqual(sorted(archive.getnames()), ['test/test/archive7zip.py', 'test/test/mainPrg.py']) 311 self._test_decode_all(archive) 312 313def suite(): 314 suite = unittest.TestSuite() 315 316 test_cases = [ 317 Test7ZipFiles, 318 ] 319 320 for tc in test_cases: 321 suite.addTest(unittest.makeSuite(tc)) 322 323 return suite 324 325if __name__ == '__main__': 326 unittest.main(defaultTest='suite') 327