test_zipfile.py revision c3ad68c88a5c7eab2c70fa3a58aab547f60f61e0
1# We can test part of the module without zlib.
2try:
3    import zlib
4except ImportError:
5    zlib = None
6
7import zipfile, os, unittest, sys, shutil, struct
8
9from StringIO import StringIO
10from tempfile import TemporaryFile
11from random import randint, random
12
13import test.test_support as support
14from test.test_support import TESTFN, run_unittest
15
16TESTFN2 = TESTFN + "2"
17FIXEDTEST_SIZE = 1000
18
19SMALL_TEST_DATA = [('_ziptest1', '1q2w3e4r5t'),
20                   ('ziptest2dir/_ziptest2', 'qawsedrftg'),
21                   ('/ziptest2dir/ziptest3dir/_ziptest3', 'azsxdcfvgb'),
22                   ('ziptest2dir/ziptest3dir/ziptest4dir/_ziptest3', '6y7u8i9o0p')]
23
24class TestsWithSourceFile(unittest.TestCase):
25    def setUp(self):
26        self.line_gen = ["Zipfile test line %d. random float: %f" % (i, random())
27                          for i in xrange(FIXEDTEST_SIZE)]
28        self.data = '\n'.join(self.line_gen) + '\n'
29
30        # Make a source file with some lines
31        fp = open(TESTFN, "wb")
32        fp.write(self.data)
33        fp.close()
34
35    def makeTestArchive(self, f, compression):
36        # Create the ZIP archive
37        zipfp = zipfile.ZipFile(f, "w", compression)
38        zipfp.write(TESTFN, "another"+os.extsep+"name")
39        zipfp.write(TESTFN, TESTFN)
40        zipfp.writestr("strfile", self.data)
41        zipfp.close()
42
43    def zipTest(self, f, compression):
44        self.makeTestArchive(f, compression)
45
46        # Read the ZIP archive
47        zipfp = zipfile.ZipFile(f, "r", compression)
48        self.assertEqual(zipfp.read(TESTFN), self.data)
49        self.assertEqual(zipfp.read("another"+os.extsep+"name"), self.data)
50        self.assertEqual(zipfp.read("strfile"), self.data)
51
52        # Print the ZIP directory
53        fp = StringIO()
54        stdout = sys.stdout
55        try:
56            sys.stdout = fp
57
58            zipfp.printdir()
59        finally:
60            sys.stdout = stdout
61
62        directory = fp.getvalue()
63        lines = directory.splitlines()
64        self.assertEquals(len(lines), 4) # Number of files + header
65
66        self.assert_('File Name' in lines[0])
67        self.assert_('Modified' in lines[0])
68        self.assert_('Size' in lines[0])
69
70        fn, date, time, size = lines[1].split()
71        self.assertEquals(fn, 'another.name')
72        # XXX: timestamp is not tested
73        self.assertEquals(size, str(len(self.data)))
74
75        # Check the namelist
76        names = zipfp.namelist()
77        self.assertEquals(len(names), 3)
78        self.assert_(TESTFN in names)
79        self.assert_("another"+os.extsep+"name" in names)
80        self.assert_("strfile" in names)
81
82        # Check infolist
83        infos = zipfp.infolist()
84        names = [ i.filename for i in infos ]
85        self.assertEquals(len(names), 3)
86        self.assert_(TESTFN in names)
87        self.assert_("another"+os.extsep+"name" in names)
88        self.assert_("strfile" in names)
89        for i in infos:
90            self.assertEquals(i.file_size, len(self.data))
91
92        # check getinfo
93        for nm in (TESTFN, "another"+os.extsep+"name", "strfile"):
94            info = zipfp.getinfo(nm)
95            self.assertEquals(info.filename, nm)
96            self.assertEquals(info.file_size, len(self.data))
97
98        # Check that testzip doesn't raise an exception
99        zipfp.testzip()
100        zipfp.close()
101
102    def testStored(self):
103        for f in (TESTFN2, TemporaryFile(), StringIO()):
104            self.zipTest(f, zipfile.ZIP_STORED)
105
106    def zipOpenTest(self, f, compression):
107        self.makeTestArchive(f, compression)
108
109        # Read the ZIP archive
110        zipfp = zipfile.ZipFile(f, "r", compression)
111        zipdata1 = []
112        zipopen1 = zipfp.open(TESTFN)
113        while 1:
114            read_data = zipopen1.read(256)
115            if not read_data:
116                break
117            zipdata1.append(read_data)
118
119        zipdata2 = []
120        zipopen2 = zipfp.open("another"+os.extsep+"name")
121        while 1:
122            read_data = zipopen2.read(256)
123            if not read_data:
124                break
125            zipdata2.append(read_data)
126
127        self.assertEqual(''.join(zipdata1), self.data)
128        self.assertEqual(''.join(zipdata2), self.data)
129        zipfp.close()
130
131    def testOpenStored(self):
132        for f in (TESTFN2, TemporaryFile(), StringIO()):
133            self.zipOpenTest(f, zipfile.ZIP_STORED)
134
135    def zipRandomOpenTest(self, f, compression):
136        self.makeTestArchive(f, compression)
137
138        # Read the ZIP archive
139        zipfp = zipfile.ZipFile(f, "r", compression)
140        zipdata1 = []
141        zipopen1 = zipfp.open(TESTFN)
142        while 1:
143            read_data = zipopen1.read(randint(1, 1024))
144            if not read_data:
145                break
146            zipdata1.append(read_data)
147
148        self.assertEqual(''.join(zipdata1), self.data)
149        zipfp.close()
150
151    def testRandomOpenStored(self):
152        for f in (TESTFN2, TemporaryFile(), StringIO()):
153            self.zipRandomOpenTest(f, zipfile.ZIP_STORED)
154
155    def zipReadlineTest(self, f, compression):
156        self.makeTestArchive(f, compression)
157
158        # Read the ZIP archive
159        zipfp = zipfile.ZipFile(f, "r")
160        zipopen = zipfp.open(TESTFN)
161        for line in self.line_gen:
162            linedata = zipopen.readline()
163            self.assertEqual(linedata, line + '\n')
164
165        zipfp.close()
166
167    def zipReadlinesTest(self, f, compression):
168        self.makeTestArchive(f, compression)
169
170        # Read the ZIP archive
171        zipfp = zipfile.ZipFile(f, "r")
172        ziplines = zipfp.open(TESTFN).readlines()
173        for line, zipline in zip(self.line_gen, ziplines):
174            self.assertEqual(zipline, line + '\n')
175
176        zipfp.close()
177
178    def zipIterlinesTest(self, f, compression):
179        self.makeTestArchive(f, compression)
180
181        # Read the ZIP archive
182        zipfp = zipfile.ZipFile(f, "r")
183        for line, zipline in zip(self.line_gen, zipfp.open(TESTFN)):
184            self.assertEqual(zipline, line + '\n')
185
186        zipfp.close()
187
188    def testReadlineStored(self):
189        for f in (TESTFN2, TemporaryFile(), StringIO()):
190            self.zipReadlineTest(f, zipfile.ZIP_STORED)
191
192    def testReadlinesStored(self):
193        for f in (TESTFN2, TemporaryFile(), StringIO()):
194            self.zipReadlinesTest(f, zipfile.ZIP_STORED)
195
196    def testIterlinesStored(self):
197        for f in (TESTFN2, TemporaryFile(), StringIO()):
198            self.zipIterlinesTest(f, zipfile.ZIP_STORED)
199
200    if zlib:
201        def testDeflated(self):
202            for f in (TESTFN2, TemporaryFile(), StringIO()):
203                self.zipTest(f, zipfile.ZIP_DEFLATED)
204
205        def testOpenDeflated(self):
206            for f in (TESTFN2, TemporaryFile(), StringIO()):
207                self.zipOpenTest(f, zipfile.ZIP_DEFLATED)
208
209        def testRandomOpenDeflated(self):
210            for f in (TESTFN2, TemporaryFile(), StringIO()):
211                self.zipRandomOpenTest(f, zipfile.ZIP_DEFLATED)
212
213        def testReadlineDeflated(self):
214            for f in (TESTFN2, TemporaryFile(), StringIO()):
215                self.zipReadlineTest(f, zipfile.ZIP_DEFLATED)
216
217        def testReadlinesDeflated(self):
218            for f in (TESTFN2, TemporaryFile(), StringIO()):
219                self.zipReadlinesTest(f, zipfile.ZIP_DEFLATED)
220
221        def testIterlinesDeflated(self):
222            for f in (TESTFN2, TemporaryFile(), StringIO()):
223                self.zipIterlinesTest(f, zipfile.ZIP_DEFLATED)
224
225        def testLowCompression(self):
226            # Checks for cases where compressed data is larger than original
227            # Create the ZIP archive
228            zipfp = zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_DEFLATED)
229            zipfp.writestr("strfile", '12')
230            zipfp.close()
231
232            # Get an open object for strfile
233            zipfp = zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_DEFLATED)
234            openobj = zipfp.open("strfile")
235            self.assertEqual(openobj.read(1), '1')
236            self.assertEqual(openobj.read(1), '2')
237
238    def testAbsoluteArcnames(self):
239        zipfp = zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED)
240        zipfp.write(TESTFN, "/absolute")
241        zipfp.close()
242
243        zipfp = zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED)
244        self.assertEqual(zipfp.namelist(), ["absolute"])
245        zipfp.close()
246
247    def testAppendToZipFile(self):
248        # Test appending to an existing zipfile
249        zipfp = zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED)
250        zipfp.write(TESTFN, TESTFN)
251        zipfp.close()
252        zipfp = zipfile.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED)
253        zipfp.writestr("strfile", self.data)
254        self.assertEqual(zipfp.namelist(), [TESTFN, "strfile"])
255        zipfp.close()
256
257    def testAppendToNonZipFile(self):
258        # Test appending to an existing file that is not a zipfile
259        # NOTE: this test fails if len(d) < 22 because of the first
260        # line "fpin.seek(-22, 2)" in _EndRecData
261        d = 'I am not a ZipFile!'*10
262        f = file(TESTFN2, 'wb')
263        f.write(d)
264        f.close()
265        zipfp = zipfile.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED)
266        zipfp.write(TESTFN, TESTFN)
267        zipfp.close()
268
269        f = file(TESTFN2, 'rb')
270        f.seek(len(d))
271        zipfp = zipfile.ZipFile(f, "r")
272        self.assertEqual(zipfp.namelist(), [TESTFN])
273        zipfp.close()
274        f.close()
275
276    def test_WriteDefaultName(self):
277        # Check that calling ZipFile.write without arcname specified produces the expected result
278        zipfp = zipfile.ZipFile(TESTFN2, "w")
279        zipfp.write(TESTFN)
280        self.assertEqual(zipfp.read(TESTFN), file(TESTFN).read())
281        zipfp.close()
282
283    def test_PerFileCompression(self):
284        # Check that files within a Zip archive can have different compression options
285        zipfp = zipfile.ZipFile(TESTFN2, "w")
286        zipfp.write(TESTFN, 'storeme', zipfile.ZIP_STORED)
287        zipfp.write(TESTFN, 'deflateme', zipfile.ZIP_DEFLATED)
288        sinfo = zipfp.getinfo('storeme')
289        dinfo = zipfp.getinfo('deflateme')
290        self.assertEqual(sinfo.compress_type, zipfile.ZIP_STORED)
291        self.assertEqual(dinfo.compress_type, zipfile.ZIP_DEFLATED)
292        zipfp.close()
293
294    def test_WriteToReadonly(self):
295        # Check that trying to call write() on a readonly ZipFile object
296        # raises a RuntimeError
297        zipf = zipfile.ZipFile(TESTFN2, mode="w")
298        zipf.writestr("somefile.txt", "bogus")
299        zipf.close()
300        zipf = zipfile.ZipFile(TESTFN2, mode="r")
301        self.assertRaises(RuntimeError, zipf.write, TESTFN)
302        zipf.close()
303
304    def testExtract(self):
305        zipfp = zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED)
306        for fpath, fdata in SMALL_TEST_DATA:
307            zipfp.writestr(fpath, fdata)
308        zipfp.close()
309
310        zipfp = zipfile.ZipFile(TESTFN2, "r")
311        for fpath, fdata in SMALL_TEST_DATA:
312            writtenfile = zipfp.extract(fpath)
313
314            # make sure it was written to the right place
315            if os.path.isabs(fpath):
316                correctfile = os.path.join(os.getcwd(), fpath[1:])
317            else:
318                correctfile = os.path.join(os.getcwd(), fpath)
319            correctfile = os.path.normpath(correctfile)
320
321            self.assertEqual(writtenfile, correctfile)
322
323            # make sure correct data is in correct file
324            self.assertEqual(fdata, file(writtenfile, "rb").read())
325
326            os.remove(writtenfile)
327
328        zipfp.close()
329
330        # remove the test file subdirectories
331        shutil.rmtree(os.path.join(os.getcwd(), 'ziptest2dir'))
332
333    def testExtractAll(self):
334        zipfp = zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED)
335        for fpath, fdata in SMALL_TEST_DATA:
336            zipfp.writestr(fpath, fdata)
337        zipfp.close()
338
339        zipfp = zipfile.ZipFile(TESTFN2, "r")
340        zipfp.extractall()
341        for fpath, fdata in SMALL_TEST_DATA:
342            if os.path.isabs(fpath):
343                outfile = os.path.join(os.getcwd(), fpath[1:])
344            else:
345                outfile = os.path.join(os.getcwd(), fpath)
346
347            self.assertEqual(fdata, file(outfile, "rb").read())
348
349            os.remove(outfile)
350
351        zipfp.close()
352
353        # remove the test file subdirectories
354        shutil.rmtree(os.path.join(os.getcwd(), 'ziptest2dir'))
355
356    def tearDown(self):
357        os.remove(TESTFN)
358        os.remove(TESTFN2)
359
360class TestZip64InSmallFiles(unittest.TestCase):
361    # These tests test the ZIP64 functionality without using large files,
362    # see test_zipfile64 for proper tests.
363
364    def setUp(self):
365        self._limit = zipfile.ZIP64_LIMIT
366        zipfile.ZIP64_LIMIT = 5
367
368        line_gen = ("Test of zipfile line %d." % i for i in range(0, FIXEDTEST_SIZE))
369        self.data = '\n'.join(line_gen)
370
371        # Make a source file with some lines
372        fp = open(TESTFN, "wb")
373        fp.write(self.data)
374        fp.close()
375
376    def largeFileExceptionTest(self, f, compression):
377        zipfp = zipfile.ZipFile(f, "w", compression)
378        self.assertRaises(zipfile.LargeZipFile,
379                zipfp.write, TESTFN, "another"+os.extsep+"name")
380        zipfp.close()
381
382    def largeFileExceptionTest2(self, f, compression):
383        zipfp = zipfile.ZipFile(f, "w", compression)
384        self.assertRaises(zipfile.LargeZipFile,
385                zipfp.writestr, "another"+os.extsep+"name", self.data)
386        zipfp.close()
387
388    def testLargeFileException(self):
389        for f in (TESTFN2, TemporaryFile(), StringIO()):
390            self.largeFileExceptionTest(f, zipfile.ZIP_STORED)
391            self.largeFileExceptionTest2(f, zipfile.ZIP_STORED)
392
393    def zipTest(self, f, compression):
394        # Create the ZIP archive
395        zipfp = zipfile.ZipFile(f, "w", compression, allowZip64=True)
396        zipfp.write(TESTFN, "another"+os.extsep+"name")
397        zipfp.write(TESTFN, TESTFN)
398        zipfp.writestr("strfile", self.data)
399        zipfp.close()
400
401        # Read the ZIP archive
402        zipfp = zipfile.ZipFile(f, "r", compression)
403        self.assertEqual(zipfp.read(TESTFN), self.data)
404        self.assertEqual(zipfp.read("another"+os.extsep+"name"), self.data)
405        self.assertEqual(zipfp.read("strfile"), self.data)
406
407        # Print the ZIP directory
408        fp = StringIO()
409        stdout = sys.stdout
410        try:
411            sys.stdout = fp
412
413            zipfp.printdir()
414        finally:
415            sys.stdout = stdout
416
417        directory = fp.getvalue()
418        lines = directory.splitlines()
419        self.assertEquals(len(lines), 4) # Number of files + header
420
421        self.assert_('File Name' in lines[0])
422        self.assert_('Modified' in lines[0])
423        self.assert_('Size' in lines[0])
424
425        fn, date, time, size = lines[1].split()
426        self.assertEquals(fn, 'another.name')
427        # XXX: timestamp is not tested
428        self.assertEquals(size, str(len(self.data)))
429
430        # Check the namelist
431        names = zipfp.namelist()
432        self.assertEquals(len(names), 3)
433        self.assert_(TESTFN in names)
434        self.assert_("another"+os.extsep+"name" in names)
435        self.assert_("strfile" in names)
436
437        # Check infolist
438        infos = zipfp.infolist()
439        names = [ i.filename for i in infos ]
440        self.assertEquals(len(names), 3)
441        self.assert_(TESTFN in names)
442        self.assert_("another"+os.extsep+"name" in names)
443        self.assert_("strfile" in names)
444        for i in infos:
445            self.assertEquals(i.file_size, len(self.data))
446
447        # check getinfo
448        for nm in (TESTFN, "another"+os.extsep+"name", "strfile"):
449            info = zipfp.getinfo(nm)
450            self.assertEquals(info.filename, nm)
451            self.assertEquals(info.file_size, len(self.data))
452
453        # Check that testzip doesn't raise an exception
454        zipfp.testzip()
455
456
457        zipfp.close()
458
459    def testStored(self):
460        for f in (TESTFN2, TemporaryFile(), StringIO()):
461            self.zipTest(f, zipfile.ZIP_STORED)
462
463
464    if zlib:
465        def testDeflated(self):
466            for f in (TESTFN2, TemporaryFile(), StringIO()):
467                self.zipTest(f, zipfile.ZIP_DEFLATED)
468
469    def testAbsoluteArcnames(self):
470        zipfp = zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED, allowZip64=True)
471        zipfp.write(TESTFN, "/absolute")
472        zipfp.close()
473
474        zipfp = zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED)
475        self.assertEqual(zipfp.namelist(), ["absolute"])
476        zipfp.close()
477
478    def tearDown(self):
479        zipfile.ZIP64_LIMIT = self._limit
480        os.remove(TESTFN)
481        os.remove(TESTFN2)
482
483class PyZipFileTests(unittest.TestCase):
484    def testWritePyfile(self):
485        zipfp  = zipfile.PyZipFile(TemporaryFile(), "w")
486        fn = __file__
487        if fn.endswith('.pyc') or fn.endswith('.pyo'):
488            fn = fn[:-1]
489
490        zipfp.writepy(fn)
491
492        bn = os.path.basename(fn)
493        self.assert_(bn not in zipfp.namelist())
494        self.assert_(bn + 'o' in zipfp.namelist() or bn + 'c' in zipfp.namelist())
495        zipfp.close()
496
497
498        zipfp  = zipfile.PyZipFile(TemporaryFile(), "w")
499        fn = __file__
500        if fn.endswith('.pyc') or fn.endswith('.pyo'):
501            fn = fn[:-1]
502
503        zipfp.writepy(fn, "testpackage")
504
505        bn = "%s/%s"%("testpackage", os.path.basename(fn))
506        self.assert_(bn not in zipfp.namelist())
507        self.assert_(bn + 'o' in zipfp.namelist() or bn + 'c' in zipfp.namelist())
508        zipfp.close()
509
510    def testWritePythonPackage(self):
511        import email
512        packagedir = os.path.dirname(email.__file__)
513
514        zipfp  = zipfile.PyZipFile(TemporaryFile(), "w")
515        zipfp.writepy(packagedir)
516
517        # Check for a couple of modules at different levels of the hieararchy
518        names = zipfp.namelist()
519        self.assert_('email/__init__.pyo' in names or 'email/__init__.pyc' in names)
520        self.assert_('email/mime/text.pyo' in names or 'email/mime/text.pyc' in names)
521
522    def testWritePythonDirectory(self):
523        os.mkdir(TESTFN2)
524        try:
525            fp = open(os.path.join(TESTFN2, "mod1.py"), "w")
526            fp.write("print 42\n")
527            fp.close()
528
529            fp = open(os.path.join(TESTFN2, "mod2.py"), "w")
530            fp.write("print 42 * 42\n")
531            fp.close()
532
533            fp = open(os.path.join(TESTFN2, "mod2.txt"), "w")
534            fp.write("bla bla bla\n")
535            fp.close()
536
537            zipfp  = zipfile.PyZipFile(TemporaryFile(), "w")
538            zipfp.writepy(TESTFN2)
539
540            names = zipfp.namelist()
541            self.assert_('mod1.pyc' in names or 'mod1.pyo' in names)
542            self.assert_('mod2.pyc' in names or 'mod2.pyo' in names)
543            self.assert_('mod2.txt' not in names)
544
545        finally:
546            shutil.rmtree(TESTFN2)
547
548    def testWriteNonPyfile(self):
549        zipfp  = zipfile.PyZipFile(TemporaryFile(), "w")
550        file(TESTFN, 'w').write('most definitely not a python file')
551        self.assertRaises(RuntimeError, zipfp.writepy, TESTFN)
552        os.remove(TESTFN)
553
554
555class OtherTests(unittest.TestCase):
556    def testUnicodeFilenames(self):
557        zf = zipfile.ZipFile(TESTFN, "w")
558        zf.writestr(u"foo.txt", "Test for unicode filename")
559        zf.writestr(u"\xf6.txt", "Test for unicode filename")
560        self.assertTrue(isinstance(zf.infolist()[0].filename, unicode))
561        zf.close()
562        zf = zipfile.ZipFile(TESTFN, "r")
563        self.assertEqual(zf.filelist[0].filename, "foo.txt")
564        self.assertEqual(zf.filelist[1].filename, u"\xf6.txt")
565        zf.close()
566
567    def testCreateNonExistentFileForAppend(self):
568        if os.path.exists(TESTFN):
569            os.unlink(TESTFN)
570
571        filename = 'testfile.txt'
572        content = 'hello, world. this is some content.'
573
574        try:
575            zf = zipfile.ZipFile(TESTFN, 'a')
576            zf.writestr(filename, content)
577            zf.close()
578        except IOError, (errno, errmsg):
579            self.fail('Could not append data to a non-existent zip file.')
580
581        self.assert_(os.path.exists(TESTFN))
582
583        zf = zipfile.ZipFile(TESTFN, 'r')
584        self.assertEqual(zf.read(filename), content)
585        zf.close()
586
587    def testCloseErroneousFile(self):
588        # This test checks that the ZipFile constructor closes the file object
589        # it opens if there's an error in the file.  If it doesn't, the traceback
590        # holds a reference to the ZipFile object and, indirectly, the file object.
591        # On Windows, this causes the os.unlink() call to fail because the
592        # underlying file is still open.  This is SF bug #412214.
593        #
594        fp = open(TESTFN, "w")
595        fp.write("this is not a legal zip file\n")
596        fp.close()
597        try:
598            zf = zipfile.ZipFile(TESTFN)
599        except zipfile.BadZipfile:
600            pass
601
602    def testIsZipErroneousFile(self):
603        # This test checks that the is_zipfile function correctly identifies
604        # a file that is not a zip file
605        fp = open(TESTFN, "w")
606        fp.write("this is not a legal zip file\n")
607        fp.close()
608        chk = zipfile.is_zipfile(TESTFN)
609        self.assert_(chk is False)
610
611    def testIsZipValidFile(self):
612        # This test checks that the is_zipfile function correctly identifies
613        # a file that is a zip file
614        zipf = zipfile.ZipFile(TESTFN, mode="w")
615        zipf.writestr("foo.txt", "O, for a Muse of Fire!")
616        zipf.close()
617        chk = zipfile.is_zipfile(TESTFN)
618        self.assert_(chk is True)
619
620    def testNonExistentFileRaisesIOError(self):
621        # make sure we don't raise an AttributeError when a partially-constructed
622        # ZipFile instance is finalized; this tests for regression on SF tracker
623        # bug #403871.
624
625        # The bug we're testing for caused an AttributeError to be raised
626        # when a ZipFile instance was created for a file that did not
627        # exist; the .fp member was not initialized but was needed by the
628        # __del__() method.  Since the AttributeError is in the __del__(),
629        # it is ignored, but the user should be sufficiently annoyed by
630        # the message on the output that regression will be noticed
631        # quickly.
632        self.assertRaises(IOError, zipfile.ZipFile, TESTFN)
633
634    def testClosedZipRaisesRuntimeError(self):
635        # Verify that testzip() doesn't swallow inappropriate exceptions.
636        data = StringIO()
637        zipf = zipfile.ZipFile(data, mode="w")
638        zipf.writestr("foo.txt", "O, for a Muse of Fire!")
639        zipf.close()
640
641        # This is correct; calling .read on a closed ZipFile should throw
642        # a RuntimeError, and so should calling .testzip.  An earlier
643        # version of .testzip would swallow this exception (and any other)
644        # and report that the first file in the archive was corrupt.
645        self.assertRaises(RuntimeError, zipf.read, "foo.txt")
646        self.assertRaises(RuntimeError, zipf.open, "foo.txt")
647        self.assertRaises(RuntimeError, zipf.testzip)
648        self.assertRaises(RuntimeError, zipf.writestr, "bogus.txt", "bogus")
649        file(TESTFN, 'w').write('zipfile test data')
650        self.assertRaises(RuntimeError, zipf.write, TESTFN)
651
652    def test_BadConstructorMode(self):
653        # Check that bad modes passed to ZipFile constructor are caught
654        self.assertRaises(RuntimeError, zipfile.ZipFile, TESTFN, "q")
655
656    def test_BadOpenMode(self):
657        # Check that bad modes passed to ZipFile.open are caught
658        zipf = zipfile.ZipFile(TESTFN, mode="w")
659        zipf.writestr("foo.txt", "O, for a Muse of Fire!")
660        zipf.close()
661        zipf = zipfile.ZipFile(TESTFN, mode="r")
662        # read the data to make sure the file is there
663        zipf.read("foo.txt")
664        self.assertRaises(RuntimeError, zipf.open, "foo.txt", "q")
665        zipf.close()
666
667    def test_Read0(self):
668        # Check that calling read(0) on a ZipExtFile object returns an empty
669        # string and doesn't advance file pointer
670        zipf = zipfile.ZipFile(TESTFN, mode="w")
671        zipf.writestr("foo.txt", "O, for a Muse of Fire!")
672        # read the data to make sure the file is there
673        f = zipf.open("foo.txt")
674        for i in xrange(FIXEDTEST_SIZE):
675            self.assertEqual(f.read(0), '')
676
677        self.assertEqual(f.read(), "O, for a Muse of Fire!")
678        zipf.close()
679
680    def test_OpenNonexistentItem(self):
681        # Check that attempting to call open() for an item that doesn't
682        # exist in the archive raises a RuntimeError
683        zipf = zipfile.ZipFile(TESTFN, mode="w")
684        self.assertRaises(KeyError, zipf.open, "foo.txt", "r")
685
686    def test_BadCompressionMode(self):
687        # Check that bad compression methods passed to ZipFile.open are caught
688        self.assertRaises(RuntimeError, zipfile.ZipFile, TESTFN, "w", -1)
689
690    def test_NullByteInFilename(self):
691        # Check that a filename containing a null byte is properly terminated
692        zipf = zipfile.ZipFile(TESTFN, mode="w")
693        zipf.writestr("foo.txt\x00qqq", "O, for a Muse of Fire!")
694        self.assertEqual(zipf.namelist(), ['foo.txt'])
695
696    def tearDown(self):
697        support.unlink(TESTFN)
698        support.unlink(TESTFN2)
699
700class DecryptionTests(unittest.TestCase):
701    # This test checks that ZIP decryption works. Since the library does not
702    # support encryption at the moment, we use a pre-generated encrypted
703    # ZIP file
704
705    data = (
706    'PK\x03\x04\x14\x00\x01\x00\x00\x00n\x92i.#y\xef?&\x00\x00\x00\x1a\x00'
707    '\x00\x00\x08\x00\x00\x00test.txt\xfa\x10\xa0gly|\xfa-\xc5\xc0=\xf9y'
708    '\x18\xe0\xa8r\xb3Z}Lg\xbc\xae\xf9|\x9b\x19\xe4\x8b\xba\xbb)\x8c\xb0\xdbl'
709    'PK\x01\x02\x14\x00\x14\x00\x01\x00\x00\x00n\x92i.#y\xef?&\x00\x00\x00'
710    '\x1a\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x01\x00 \x00\xb6\x81'
711    '\x00\x00\x00\x00test.txtPK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x006\x00'
712    '\x00\x00L\x00\x00\x00\x00\x00' )
713    data2 = (
714    'PK\x03\x04\x14\x00\t\x00\x08\x00\xcf}38xu\xaa\xb2\x14\x00\x00\x00\x00\x02'
715    '\x00\x00\x04\x00\x15\x00zeroUT\t\x00\x03\xd6\x8b\x92G\xda\x8b\x92GUx\x04'
716    '\x00\xe8\x03\xe8\x03\xc7<M\xb5a\xceX\xa3Y&\x8b{oE\xd7\x9d\x8c\x98\x02\xc0'
717    'PK\x07\x08xu\xaa\xb2\x14\x00\x00\x00\x00\x02\x00\x00PK\x01\x02\x17\x03'
718    '\x14\x00\t\x00\x08\x00\xcf}38xu\xaa\xb2\x14\x00\x00\x00\x00\x02\x00\x00'
719    '\x04\x00\r\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x00ze'
720    'roUT\x05\x00\x03\xd6\x8b\x92GUx\x00\x00PK\x05\x06\x00\x00\x00\x00\x01'
721    '\x00\x01\x00?\x00\x00\x00[\x00\x00\x00\x00\x00' )
722
723    plain = 'zipfile.py encryption test'
724    plain2 = '\x00'*512
725
726    def setUp(self):
727        fp = open(TESTFN, "wb")
728        fp.write(self.data)
729        fp.close()
730        self.zip = zipfile.ZipFile(TESTFN, "r")
731        fp = open(TESTFN2, "wb")
732        fp.write(self.data2)
733        fp.close()
734        self.zip2 = zipfile.ZipFile(TESTFN2, "r")
735
736    def tearDown(self):
737        self.zip.close()
738        os.unlink(TESTFN)
739        self.zip2.close()
740        os.unlink(TESTFN2)
741
742    def testNoPassword(self):
743        # Reading the encrypted file without password
744        # must generate a RunTime exception
745        self.assertRaises(RuntimeError, self.zip.read, "test.txt")
746        self.assertRaises(RuntimeError, self.zip2.read, "zero")
747
748    def testBadPassword(self):
749        self.zip.setpassword("perl")
750        self.assertRaises(RuntimeError, self.zip.read, "test.txt")
751        self.zip2.setpassword("perl")
752        self.assertRaises(RuntimeError, self.zip2.read, "zero")
753
754    def testGoodPassword(self):
755        self.zip.setpassword("python")
756        self.assertEquals(self.zip.read("test.txt"), self.plain)
757        self.zip2.setpassword("12345")
758        self.assertEquals(self.zip2.read("zero"), self.plain2)
759
760
761class TestsWithRandomBinaryFiles(unittest.TestCase):
762    def setUp(self):
763        datacount = randint(16, 64)*1024 + randint(1, 1024)
764        self.data = ''.join((struct.pack('<f', random()*randint(-1000, 1000)) for i in xrange(datacount)))
765
766        # Make a source file with some lines
767        fp = open(TESTFN, "wb")
768        fp.write(self.data)
769        fp.close()
770
771    def tearDown(self):
772        support.unlink(TESTFN)
773        support.unlink(TESTFN2)
774
775    def makeTestArchive(self, f, compression):
776        # Create the ZIP archive
777        zipfp = zipfile.ZipFile(f, "w", compression)
778        zipfp.write(TESTFN, "another"+os.extsep+"name")
779        zipfp.write(TESTFN, TESTFN)
780        zipfp.close()
781
782    def zipTest(self, f, compression):
783        self.makeTestArchive(f, compression)
784
785        # Read the ZIP archive
786        zipfp = zipfile.ZipFile(f, "r", compression)
787        testdata = zipfp.read(TESTFN)
788        self.assertEqual(len(testdata), len(self.data))
789        self.assertEqual(testdata, self.data)
790        self.assertEqual(zipfp.read("another"+os.extsep+"name"), self.data)
791        zipfp.close()
792
793    def testStored(self):
794        for f in (TESTFN2, TemporaryFile(), StringIO()):
795            self.zipTest(f, zipfile.ZIP_STORED)
796
797    def zipOpenTest(self, f, compression):
798        self.makeTestArchive(f, compression)
799
800        # Read the ZIP archive
801        zipfp = zipfile.ZipFile(f, "r", compression)
802        zipdata1 = []
803        zipopen1 = zipfp.open(TESTFN)
804        while 1:
805            read_data = zipopen1.read(256)
806            if not read_data:
807                break
808            zipdata1.append(read_data)
809
810        zipdata2 = []
811        zipopen2 = zipfp.open("another"+os.extsep+"name")
812        while 1:
813            read_data = zipopen2.read(256)
814            if not read_data:
815                break
816            zipdata2.append(read_data)
817
818        testdata1 = ''.join(zipdata1)
819        self.assertEqual(len(testdata1), len(self.data))
820        self.assertEqual(testdata1, self.data)
821
822        testdata2 = ''.join(zipdata2)
823        self.assertEqual(len(testdata1), len(self.data))
824        self.assertEqual(testdata1, self.data)
825        zipfp.close()
826
827    def testOpenStored(self):
828        for f in (TESTFN2, TemporaryFile(), StringIO()):
829            self.zipOpenTest(f, zipfile.ZIP_STORED)
830
831    def zipRandomOpenTest(self, f, compression):
832        self.makeTestArchive(f, compression)
833
834        # Read the ZIP archive
835        zipfp = zipfile.ZipFile(f, "r", compression)
836        zipdata1 = []
837        zipopen1 = zipfp.open(TESTFN)
838        while 1:
839            read_data = zipopen1.read(randint(1, 1024))
840            if not read_data:
841                break
842            zipdata1.append(read_data)
843
844        testdata = ''.join(zipdata1)
845        self.assertEqual(len(testdata), len(self.data))
846        self.assertEqual(testdata, self.data)
847        zipfp.close()
848
849    def testRandomOpenStored(self):
850        for f in (TESTFN2, TemporaryFile(), StringIO()):
851            self.zipRandomOpenTest(f, zipfile.ZIP_STORED)
852
853class TestsWithMultipleOpens(unittest.TestCase):
854    def setUp(self):
855        # Create the ZIP archive
856        zipfp = zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_DEFLATED)
857        zipfp.writestr('ones', '1'*FIXEDTEST_SIZE)
858        zipfp.writestr('twos', '2'*FIXEDTEST_SIZE)
859        zipfp.close()
860
861    def testSameFile(self):
862        # Verify that (when the ZipFile is in control of creating file objects)
863        # multiple open() calls can be made without interfering with each other.
864        zipf = zipfile.ZipFile(TESTFN2, mode="r")
865        zopen1 = zipf.open('ones')
866        zopen2 = zipf.open('ones')
867        data1 = zopen1.read(500)
868        data2 = zopen2.read(500)
869        data1 += zopen1.read(500)
870        data2 += zopen2.read(500)
871        self.assertEqual(data1, data2)
872        zipf.close()
873
874    def testDifferentFile(self):
875        # Verify that (when the ZipFile is in control of creating file objects)
876        # multiple open() calls can be made without interfering with each other.
877        zipf = zipfile.ZipFile(TESTFN2, mode="r")
878        zopen1 = zipf.open('ones')
879        zopen2 = zipf.open('twos')
880        data1 = zopen1.read(500)
881        data2 = zopen2.read(500)
882        data1 += zopen1.read(500)
883        data2 += zopen2.read(500)
884        self.assertEqual(data1, '1'*FIXEDTEST_SIZE)
885        self.assertEqual(data2, '2'*FIXEDTEST_SIZE)
886        zipf.close()
887
888    def testInterleaved(self):
889        # Verify that (when the ZipFile is in control of creating file objects)
890        # multiple open() calls can be made without interfering with each other.
891        zipf = zipfile.ZipFile(TESTFN2, mode="r")
892        zopen1 = zipf.open('ones')
893        data1 = zopen1.read(500)
894        zopen2 = zipf.open('twos')
895        data2 = zopen2.read(500)
896        data1 += zopen1.read(500)
897        data2 += zopen2.read(500)
898        self.assertEqual(data1, '1'*FIXEDTEST_SIZE)
899        self.assertEqual(data2, '2'*FIXEDTEST_SIZE)
900        zipf.close()
901
902    def tearDown(self):
903        os.remove(TESTFN2)
904
905
906class UniversalNewlineTests(unittest.TestCase):
907    def setUp(self):
908        self.line_gen = ["Test of zipfile line %d." % i for i in xrange(FIXEDTEST_SIZE)]
909        self.seps = ('\r', '\r\n', '\n')
910        self.arcdata, self.arcfiles = {}, {}
911        for n, s in enumerate(self.seps):
912            self.arcdata[s] = s.join(self.line_gen) + s
913            self.arcfiles[s] = '%s-%d' % (TESTFN, n)
914            open(self.arcfiles[s], "wb").write(self.arcdata[s])
915
916    def makeTestArchive(self, f, compression):
917        # Create the ZIP archive
918        zipfp = zipfile.ZipFile(f, "w", compression)
919        for fn in self.arcfiles.values():
920            zipfp.write(fn, fn)
921        zipfp.close()
922
923    def readTest(self, f, compression):
924        self.makeTestArchive(f, compression)
925
926        # Read the ZIP archive
927        zipfp = zipfile.ZipFile(f, "r")
928        for sep, fn in self.arcfiles.items():
929            zipdata = zipfp.open(fn, "rU").read()
930            self.assertEqual(self.arcdata[sep], zipdata)
931
932        zipfp.close()
933
934    def readlineTest(self, f, compression):
935        self.makeTestArchive(f, compression)
936
937        # Read the ZIP archive
938        zipfp = zipfile.ZipFile(f, "r")
939        for sep, fn in self.arcfiles.items():
940            zipopen = zipfp.open(fn, "rU")
941            for line in self.line_gen:
942                linedata = zipopen.readline()
943                self.assertEqual(linedata, line + '\n')
944
945        zipfp.close()
946
947    def readlinesTest(self, f, compression):
948        self.makeTestArchive(f, compression)
949
950        # Read the ZIP archive
951        zipfp = zipfile.ZipFile(f, "r")
952        for sep, fn in self.arcfiles.items():
953            ziplines = zipfp.open(fn, "rU").readlines()
954            for line, zipline in zip(self.line_gen, ziplines):
955                self.assertEqual(zipline, line + '\n')
956
957        zipfp.close()
958
959    def iterlinesTest(self, f, compression):
960        self.makeTestArchive(f, compression)
961
962        # Read the ZIP archive
963        zipfp = zipfile.ZipFile(f, "r")
964        for sep, fn in self.arcfiles.items():
965            for line, zipline in zip(self.line_gen, zipfp.open(fn, "rU")):
966                self.assertEqual(zipline, line + '\n')
967
968        zipfp.close()
969
970    def testReadStored(self):
971        for f in (TESTFN2, TemporaryFile(), StringIO()):
972            self.readTest(f, zipfile.ZIP_STORED)
973
974    def testReadlineStored(self):
975        for f in (TESTFN2, TemporaryFile(), StringIO()):
976            self.readlineTest(f, zipfile.ZIP_STORED)
977
978    def testReadlinesStored(self):
979        for f in (TESTFN2, TemporaryFile(), StringIO()):
980            self.readlinesTest(f, zipfile.ZIP_STORED)
981
982    def testIterlinesStored(self):
983        for f in (TESTFN2, TemporaryFile(), StringIO()):
984            self.iterlinesTest(f, zipfile.ZIP_STORED)
985
986    if zlib:
987        def testReadDeflated(self):
988            for f in (TESTFN2, TemporaryFile(), StringIO()):
989                self.readTest(f, zipfile.ZIP_DEFLATED)
990
991        def testReadlineDeflated(self):
992            for f in (TESTFN2, TemporaryFile(), StringIO()):
993                self.readlineTest(f, zipfile.ZIP_DEFLATED)
994
995        def testReadlinesDeflated(self):
996            for f in (TESTFN2, TemporaryFile(), StringIO()):
997                self.readlinesTest(f, zipfile.ZIP_DEFLATED)
998
999        def testIterlinesDeflated(self):
1000            for f in (TESTFN2, TemporaryFile(), StringIO()):
1001                self.iterlinesTest(f, zipfile.ZIP_DEFLATED)
1002
1003    def tearDown(self):
1004        for sep, fn in self.arcfiles.items():
1005            os.remove(fn)
1006        support.unlink(TESTFN)
1007        support.unlink(TESTFN2)
1008
1009
1010def test_main():
1011    run_unittest(TestsWithSourceFile, TestZip64InSmallFiles, OtherTests,
1012                 PyZipFileTests, DecryptionTests, TestsWithMultipleOpens,
1013                 UniversalNewlineTests, TestsWithRandomBinaryFiles)
1014
1015if __name__ == "__main__":
1016    test_main()
1017