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