blob: 03fb55f935338cdce51de6269a138665dfd93eb7 [file] [log] [blame]
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +00001import sys
2import os
3import shutil
Brett Cannon455ea532003-06-12 08:01:06 +00004import tempfile
Georg Brandl38c6a222006-05-10 16:26:03 +00005import StringIO
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +00006
7import unittest
8import tarfile
9
10from test import test_support
11
12# Check for our compression modules.
13try:
14 import gzip
Neal Norwitzae323192003-04-14 01:18:32 +000015 gzip.GzipFile
16except (ImportError, AttributeError):
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +000017 gzip = None
18try:
19 import bz2
20except ImportError:
21 bz2 = None
22
23def path(path):
24 return test_support.findfile(path)
25
Brett Cannon455ea532003-06-12 08:01:06 +000026testtar = path("testtar.tar")
27tempdir = os.path.join(tempfile.gettempdir(), "testtar" + os.extsep + "dir")
28tempname = test_support.TESTFN
Georg Brandl38c6a222006-05-10 16:26:03 +000029membercount = 12
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +000030
31def tarname(comp=""):
32 if not comp:
33 return testtar
Brett Cannon43e559a2003-06-12 19:16:58 +000034 return os.path.join(tempdir, "%s%s%s" % (testtar, os.extsep, comp))
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +000035
36def dirname():
37 if not os.path.exists(tempdir):
38 os.mkdir(tempdir)
39 return tempdir
40
41def tmpname():
42 return tempname
43
44
45class BaseTest(unittest.TestCase):
46 comp = ''
47 mode = 'r'
48 sep = ':'
49
50 def setUp(self):
51 mode = self.mode + self.sep + self.comp
52 self.tar = tarfile.open(tarname(self.comp), mode)
53
54 def tearDown(self):
55 self.tar.close()
56
57class ReadTest(BaseTest):
58
59 def test(self):
60 """Test member extraction.
61 """
62 members = 0
63 for tarinfo in self.tar:
64 members += 1
65 if not tarinfo.isreg():
66 continue
67 f = self.tar.extractfile(tarinfo)
68 self.assert_(len(f.read()) == tarinfo.size,
69 "size read does not match expected size")
70 f.close()
71
72 self.assert_(members == membercount,
73 "could not find all members")
74
75 def test_sparse(self):
76 """Test sparse member extraction.
77 """
78 if self.sep != "|":
79 f1 = self.tar.extractfile("S-SPARSE")
Jack Jansen149a8992003-03-07 13:27:53 +000080 f2 = self.tar.extractfile("S-SPARSE-WITH-NULLS")
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +000081 self.assert_(f1.read() == f2.read(),
82 "_FileObject failed on sparse file member")
83
84 def test_readlines(self):
85 """Test readlines() method of _FileObject.
86 """
87 if self.sep != "|":
Jack Jansen149a8992003-03-07 13:27:53 +000088 filename = "0-REGTYPE-TEXT"
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +000089 self.tar.extract(filename, dirname())
Jack Jansenc7fcc2d2003-03-07 12:50:45 +000090 lines1 = file(os.path.join(dirname(), filename), "rU").readlines()
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +000091 lines2 = self.tar.extractfile(filename).readlines()
92 self.assert_(lines1 == lines2,
93 "_FileObject.readline() does not work correctly")
94
Martin v. Löwisdf241532005-03-03 08:17:42 +000095 def test_iter(self):
96 # Test iteration over ExFileObject.
97 if self.sep != "|":
98 filename = "0-REGTYPE-TEXT"
99 self.tar.extract(filename, dirname())
100 lines1 = file(os.path.join(dirname(), filename), "rU").readlines()
101 lines2 = [line for line in self.tar.extractfile(filename)]
102 self.assert_(lines1 == lines2,
103 "ExFileObject iteration does not work correctly")
104
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000105 def test_seek(self):
106 """Test seek() method of _FileObject, incl. random reading.
107 """
108 if self.sep != "|":
Jack Jansen149a8992003-03-07 13:27:53 +0000109 filename = "0-REGTYPE"
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000110 self.tar.extract(filename, dirname())
111 data = file(os.path.join(dirname(), filename), "rb").read()
112
113 tarinfo = self.tar.getmember(filename)
114 fobj = self.tar.extractfile(tarinfo)
115
116 text = fobj.read()
117 fobj.seek(0)
118 self.assert_(0 == fobj.tell(),
119 "seek() to file's start failed")
120 fobj.seek(2048, 0)
121 self.assert_(2048 == fobj.tell(),
122 "seek() to absolute position failed")
123 fobj.seek(-1024, 1)
124 self.assert_(1024 == fobj.tell(),
125 "seek() to negative relative position failed")
126 fobj.seek(1024, 1)
127 self.assert_(2048 == fobj.tell(),
128 "seek() to positive relative position failed")
129 s = fobj.read(10)
130 self.assert_(s == data[2048:2058],
131 "read() after seek failed")
132 fobj.seek(0, 2)
133 self.assert_(tarinfo.size == fobj.tell(),
134 "seek() to file's end failed")
135 self.assert_(fobj.read() == "",
136 "read() at file's end did not return empty string")
137 fobj.seek(-tarinfo.size, 2)
138 self.assert_(0 == fobj.tell(),
139 "relative seek() to file's start failed")
140 fobj.seek(512)
141 s1 = fobj.readlines()
142 fobj.seek(512)
143 s2 = fobj.readlines()
144 self.assert_(s1 == s2,
145 "readlines() after seek failed")
146 fobj.close()
147
Neal Norwitzf3396542005-10-28 05:52:22 +0000148 def test_old_dirtype(self):
149 """Test old style dirtype member (bug #1336623).
150 """
151 # Old tars create directory members using a REGTYPE
152 # header with a "/" appended to the filename field.
153
154 # Create an old tar style directory entry.
155 filename = tmpname()
156 tarinfo = tarfile.TarInfo("directory/")
157 tarinfo.type = tarfile.REGTYPE
158
159 fobj = file(filename, "w")
160 fobj.write(tarinfo.tobuf())
161 fobj.close()
162
163 try:
164 # Test if it is still a directory entry when
165 # read back.
166 tar = tarfile.open(filename)
167 tarinfo = tar.getmembers()[0]
168 tar.close()
169
170 self.assert_(tarinfo.type == tarfile.DIRTYPE)
171 self.assert_(tarinfo.name.endswith("/"))
172 finally:
173 try:
174 os.unlink(filename)
175 except:
176 pass
177
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000178class ReadStreamTest(ReadTest):
179 sep = "|"
180
181 def test(self):
182 """Test member extraction, and for StreamError when
183 seeking backwards.
184 """
185 ReadTest.test(self)
186 tarinfo = self.tar.getmembers()[0]
187 f = self.tar.extractfile(tarinfo)
188 self.assertRaises(tarfile.StreamError, f.read)
189
190 def test_stream(self):
191 """Compare the normal tar and the stream tar.
192 """
193 stream = self.tar
194 tar = tarfile.open(tarname(), 'r')
195
196 while 1:
197 t1 = tar.next()
198 t2 = stream.next()
199 if t1 is None:
200 break
201 self.assert_(t2 is not None, "stream.next() failed.")
202
203 if t2.islnk() or t2.issym():
204 self.assertRaises(tarfile.StreamError, stream.extractfile, t2)
205 continue
206 v1 = tar.extractfile(t1)
207 v2 = stream.extractfile(t2)
208 if v1 is None:
209 continue
210 self.assert_(v2 is not None, "stream.extractfile() failed")
211 self.assert_(v1.read() == v2.read(), "stream extraction failed")
212
213 stream.close()
214
Martin v. Löwis78be7df2005-03-05 12:47:42 +0000215class ReadAsteriskTest(ReadTest):
216
217 def setUp(self):
218 mode = self.mode + self.sep + "*"
219 self.tar = tarfile.open(tarname(self.comp), mode)
220
221class ReadStreamAsteriskTest(ReadStreamTest):
222
223 def setUp(self):
224 mode = self.mode + self.sep + "*"
225 self.tar = tarfile.open(tarname(self.comp), mode)
226
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000227class WriteTest(BaseTest):
228 mode = 'w'
229
230 def setUp(self):
231 mode = self.mode + self.sep + self.comp
232 self.src = tarfile.open(tarname(self.comp), 'r')
Martin v. Löwisc234a522004-08-22 21:28:33 +0000233 self.dstname = tmpname()
234 self.dst = tarfile.open(self.dstname, mode)
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000235
236 def tearDown(self):
237 self.src.close()
238 self.dst.close()
239
240 def test_posix(self):
241 self.dst.posix = 1
242 self._test()
243
244 def test_nonposix(self):
245 self.dst.posix = 0
246 self._test()
247
Martin v. Löwisc234a522004-08-22 21:28:33 +0000248 def test_small(self):
249 self.dst.add(os.path.join(os.path.dirname(__file__),"cfgparser.1"))
250 self.dst.close()
251 self.assertNotEqual(os.stat(self.dstname).st_size, 0)
252
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000253 def _test(self):
254 for tarinfo in self.src:
255 if not tarinfo.isreg():
256 continue
257 f = self.src.extractfile(tarinfo)
Georg Brandl38c6a222006-05-10 16:26:03 +0000258 if self.dst.posix and len(tarinfo.name) > tarfile.LENGTH_NAME and "/" not in tarinfo.name:
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000259 self.assertRaises(ValueError, self.dst.addfile,
260 tarinfo, f)
261 else:
262 self.dst.addfile(tarinfo, f)
263
Martin v. Löwis5dbdc592005-08-27 10:07:56 +0000264class WriteSize0Test(BaseTest):
265 mode = 'w'
266
267 def setUp(self):
268 self.tmpdir = dirname()
269 self.dstname = tmpname()
270 self.dst = tarfile.open(self.dstname, "w")
271
272 def tearDown(self):
273 self.dst.close()
274
275 def test_file(self):
276 path = os.path.join(self.tmpdir, "file")
277 file(path, "w")
278 tarinfo = self.dst.gettarinfo(path)
279 self.assertEqual(tarinfo.size, 0)
280 file(path, "w").write("aaa")
281 tarinfo = self.dst.gettarinfo(path)
282 self.assertEqual(tarinfo.size, 3)
283
284 def test_directory(self):
285 path = os.path.join(self.tmpdir, "directory")
286 os.mkdir(path)
287 tarinfo = self.dst.gettarinfo(path)
288 self.assertEqual(tarinfo.size, 0)
289
290 def test_symlink(self):
291 if hasattr(os, "symlink"):
292 path = os.path.join(self.tmpdir, "symlink")
293 os.symlink("link_target", path)
294 tarinfo = self.dst.gettarinfo(path)
295 self.assertEqual(tarinfo.size, 0)
296
297
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000298class WriteStreamTest(WriteTest):
299 sep = '|'
300
Neal Norwitz0662f8a2004-07-20 21:54:18 +0000301class WriteGNULongTest(unittest.TestCase):
302 """This testcase checks for correct creation of GNU Longname
303 and Longlink extensions.
304
305 It creates a tarfile and adds empty members with either
306 long names, long linknames or both and compares the size
307 of the tarfile with the expected size.
308
309 It checks for SF bug #812325 in TarFile._create_gnulong().
310
311 While I was writing this testcase, I noticed a second bug
312 in the same method:
313 Long{names,links} weren't null-terminated which lead to
314 bad tarfiles when their length was a multiple of 512. This
315 is tested as well.
316 """
317
318 def setUp(self):
319 self.tar = tarfile.open(tmpname(), "w")
320 self.tar.posix = False
321
322 def tearDown(self):
323 self.tar.close()
324
325 def _length(self, s):
326 blocks, remainder = divmod(len(s) + 1, 512)
327 if remainder:
328 blocks += 1
329 return blocks * 512
330
331 def _calc_size(self, name, link=None):
332 # initial tar header
333 count = 512
334
335 if len(name) > tarfile.LENGTH_NAME:
336 # gnu longname extended header + longname
337 count += 512
338 count += self._length(name)
339
340 if link is not None and len(link) > tarfile.LENGTH_LINK:
341 # gnu longlink extended header + longlink
342 count += 512
343 count += self._length(link)
344
345 return count
346
347 def _test(self, name, link=None):
348 tarinfo = tarfile.TarInfo(name)
349 if link:
350 tarinfo.linkname = link
351 tarinfo.type = tarfile.LNKTYPE
352
353 self.tar.addfile(tarinfo)
354
355 v1 = self._calc_size(name, link)
356 v2 = self.tar.offset
357 self.assertEqual(v1, v2, "GNU longname/longlink creation failed")
358
359 def test_longname_1023(self):
360 self._test(("longnam/" * 127) + "longnam")
361
362 def test_longname_1024(self):
363 self._test(("longnam/" * 127) + "longname")
364
365 def test_longname_1025(self):
366 self._test(("longnam/" * 127) + "longname_")
367
368 def test_longlink_1023(self):
369 self._test("name", ("longlnk/" * 127) + "longlnk")
370
371 def test_longlink_1024(self):
372 self._test("name", ("longlnk/" * 127) + "longlink")
373
374 def test_longlink_1025(self):
375 self._test("name", ("longlnk/" * 127) + "longlink_")
376
377 def test_longnamelink_1023(self):
378 self._test(("longnam/" * 127) + "longnam",
379 ("longlnk/" * 127) + "longlnk")
380
381 def test_longnamelink_1024(self):
382 self._test(("longnam/" * 127) + "longname",
383 ("longlnk/" * 127) + "longlink")
384
385 def test_longnamelink_1025(self):
386 self._test(("longnam/" * 127) + "longname_",
387 ("longlnk/" * 127) + "longlink_")
388
Georg Brandl38c6a222006-05-10 16:26:03 +0000389class ReadGNULongTest(unittest.TestCase):
390
391 def setUp(self):
392 self.tar = tarfile.open(tarname())
393
394 def tearDown(self):
395 self.tar.close()
396
397 def test_1471427(self):
398 """Test reading of longname (bug #1471427).
399 """
400 name = "test/" * 20 + "0-REGTYPE"
401 try:
402 tarinfo = self.tar.getmember(name)
403 except KeyError:
404 tarinfo = None
405 self.assert_(tarinfo is not None, "longname not found")
406 self.assert_(tarinfo.type != tarfile.DIRTYPE, "read longname as dirtype")
407
408 def test_read_name(self):
409 name = ("0-LONGNAME-" * 10)[:101]
410 try:
411 tarinfo = self.tar.getmember(name)
412 except KeyError:
413 tarinfo = None
414 self.assert_(tarinfo is not None, "longname not found")
415
416 def test_read_link(self):
417 link = ("1-LONGLINK-" * 10)[:101]
418 name = ("0-LONGNAME-" * 10)[:101]
419 try:
420 tarinfo = self.tar.getmember(link)
421 except KeyError:
422 tarinfo = None
423 self.assert_(tarinfo is not None, "longlink not found")
424 self.assert_(tarinfo.linkname == name, "linkname wrong")
425
426 def test_truncated_longname(self):
427 fobj = StringIO.StringIO(file(tarname()).read(1024))
428 tar = tarfile.open(name="foo.tar", fileobj=fobj)
429 self.assert_(len(tar.getmembers()) == 0, "")
430
431
Neal Norwitza4f651a2004-07-20 22:07:44 +0000432class ExtractHardlinkTest(BaseTest):
433
434 def test_hardlink(self):
435 """Test hardlink extraction (bug #857297)
436 """
437 # Prevent errors from being caught
438 self.tar.errorlevel = 1
439
440 self.tar.extract("0-REGTYPE", dirname())
441 try:
442 # Extract 1-LNKTYPE which is a hardlink to 0-REGTYPE
443 self.tar.extract("1-LNKTYPE", dirname())
444 except EnvironmentError, e:
445 import errno
446 if e.errno == errno.ENOENT:
447 self.fail("hardlink not extracted properly")
448
Neal Norwitzb0e32e22005-10-20 04:50:13 +0000449class CreateHardlinkTest(BaseTest):
450 """Test the creation of LNKTYPE (hardlink) members in an archive.
451 In this respect tarfile.py mimics the behaviour of GNU tar: If
452 a file has a st_nlink > 1, it will be added a REGTYPE member
453 only the first time.
454 """
455
456 def setUp(self):
457 self.tar = tarfile.open(tmpname(), "w")
458
459 self.foo = os.path.join(dirname(), "foo")
460 self.bar = os.path.join(dirname(), "bar")
461
462 if os.path.exists(self.foo):
463 os.remove(self.foo)
464 if os.path.exists(self.bar):
465 os.remove(self.bar)
466
467 file(self.foo, "w").write("foo")
468 self.tar.add(self.foo)
469
470 def test_add_twice(self):
471 # If st_nlink == 1 then the same file will be added as
472 # REGTYPE every time.
473 tarinfo = self.tar.gettarinfo(self.foo)
474 self.assertEqual(tarinfo.type, tarfile.REGTYPE,
475 "add file as regular failed")
476
477 def test_add_hardlink(self):
478 # If st_nlink > 1 then the same file will be added as
479 # LNKTYPE.
480 os.link(self.foo, self.bar)
481 tarinfo = self.tar.gettarinfo(self.foo)
482 self.assertEqual(tarinfo.type, tarfile.LNKTYPE,
483 "add file as hardlink failed")
484
485 tarinfo = self.tar.gettarinfo(self.bar)
486 self.assertEqual(tarinfo.type, tarfile.LNKTYPE,
487 "add file as hardlink failed")
488
489 def test_dereference_hardlink(self):
490 self.tar.dereference = True
491 os.link(self.foo, self.bar)
492 tarinfo = self.tar.gettarinfo(self.bar)
493 self.assertEqual(tarinfo.type, tarfile.REGTYPE,
494 "dereferencing hardlink failed")
495
Neal Norwitza4f651a2004-07-20 22:07:44 +0000496
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000497# Gzip TestCases
498class ReadTestGzip(ReadTest):
499 comp = "gz"
500class ReadStreamTestGzip(ReadStreamTest):
501 comp = "gz"
502class WriteTestGzip(WriteTest):
503 comp = "gz"
504class WriteStreamTestGzip(WriteStreamTest):
505 comp = "gz"
Martin v. Löwis78be7df2005-03-05 12:47:42 +0000506class ReadAsteriskTestGzip(ReadAsteriskTest):
507 comp = "gz"
508class ReadStreamAsteriskTestGzip(ReadStreamAsteriskTest):
509 comp = "gz"
510
Andrew M. Kuchlingd4f25522004-10-20 11:47:01 +0000511# Filemode test cases
512
513class FileModeTest(unittest.TestCase):
514 def test_modes(self):
515 self.assertEqual(tarfile.filemode(0755), '-rwxr-xr-x')
516 self.assertEqual(tarfile.filemode(07111), '---s--s--t')
517
Tim Peters8ceefc52004-10-25 03:19:41 +0000518
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000519if bz2:
520 # Bzip2 TestCases
521 class ReadTestBzip2(ReadTestGzip):
522 comp = "bz2"
523 class ReadStreamTestBzip2(ReadStreamTestGzip):
524 comp = "bz2"
525 class WriteTestBzip2(WriteTest):
526 comp = "bz2"
527 class WriteStreamTestBzip2(WriteStreamTestGzip):
528 comp = "bz2"
Martin v. Löwis78be7df2005-03-05 12:47:42 +0000529 class ReadAsteriskTestBzip2(ReadAsteriskTest):
530 comp = "bz2"
531 class ReadStreamAsteriskTestBzip2(ReadStreamAsteriskTest):
532 comp = "bz2"
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000533
534# If importing gzip failed, discard the Gzip TestCases.
535if not gzip:
536 del ReadTestGzip
537 del ReadStreamTestGzip
538 del WriteTestGzip
539 del WriteStreamTestGzip
540
Neal Norwitz996acf12003-02-17 14:51:41 +0000541def test_main():
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000542 if gzip:
543 # create testtar.tar.gz
544 gzip.open(tarname("gz"), "wb").write(file(tarname(), "rb").read())
545 if bz2:
546 # create testtar.tar.bz2
547 bz2.BZ2File(tarname("bz2"), "wb").write(file(tarname(), "rb").read())
548
Walter Dörwald21d3a322003-05-01 17:45:56 +0000549 tests = [
Andrew M. Kuchlingd4f25522004-10-20 11:47:01 +0000550 FileModeTest,
Walter Dörwald21d3a322003-05-01 17:45:56 +0000551 ReadTest,
552 ReadStreamTest,
Martin v. Löwis78be7df2005-03-05 12:47:42 +0000553 ReadAsteriskTest,
554 ReadStreamAsteriskTest,
Walter Dörwald21d3a322003-05-01 17:45:56 +0000555 WriteTest,
Martin v. Löwis5dbdc592005-08-27 10:07:56 +0000556 WriteSize0Test,
Neal Norwitz0662f8a2004-07-20 21:54:18 +0000557 WriteStreamTest,
558 WriteGNULongTest,
Georg Brandl38c6a222006-05-10 16:26:03 +0000559 ReadGNULongTest,
Walter Dörwald21d3a322003-05-01 17:45:56 +0000560 ]
561
Neal Norwitza4f651a2004-07-20 22:07:44 +0000562 if hasattr(os, "link"):
563 tests.append(ExtractHardlinkTest)
Neal Norwitzb0e32e22005-10-20 04:50:13 +0000564 tests.append(CreateHardlinkTest)
Neal Norwitza4f651a2004-07-20 22:07:44 +0000565
Walter Dörwald21d3a322003-05-01 17:45:56 +0000566 if gzip:
567 tests.extend([
568 ReadTestGzip, ReadStreamTestGzip,
Martin v. Löwis78be7df2005-03-05 12:47:42 +0000569 WriteTestGzip, WriteStreamTestGzip,
570 ReadAsteriskTestGzip, ReadStreamAsteriskTestGzip
Walter Dörwald21d3a322003-05-01 17:45:56 +0000571 ])
572
573 if bz2:
574 tests.extend([
575 ReadTestBzip2, ReadStreamTestBzip2,
Martin v. Löwis78be7df2005-03-05 12:47:42 +0000576 WriteTestBzip2, WriteStreamTestBzip2,
577 ReadAsteriskTestBzip2, ReadStreamAsteriskTestBzip2
Walter Dörwald21d3a322003-05-01 17:45:56 +0000578 ])
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000579 try:
Walter Dörwald21d3a322003-05-01 17:45:56 +0000580 test_support.run_unittest(*tests)
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000581 finally:
582 if gzip:
583 os.remove(tarname("gz"))
584 if bz2:
585 os.remove(tarname("bz2"))
Brett Cannon455ea532003-06-12 08:01:06 +0000586 if os.path.exists(dirname()):
587 shutil.rmtree(dirname())
588 if os.path.exists(tmpname()):
589 os.remove(tmpname())
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000590
Neal Norwitz996acf12003-02-17 14:51:41 +0000591if __name__ == "__main__":
592 test_main()