blob: b202ea586873fd58385d20055c4c65c736ad1d8d [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
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +00005
6import unittest
7import tarfile
8
9from test import test_support
10
11# Check for our compression modules.
12try:
13 import gzip
Neal Norwitzae323192003-04-14 01:18:32 +000014 gzip.GzipFile
15except (ImportError, AttributeError):
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +000016 gzip = None
17try:
18 import bz2
19except ImportError:
20 bz2 = None
21
22def path(path):
23 return test_support.findfile(path)
24
Brett Cannon455ea532003-06-12 08:01:06 +000025testtar = path("testtar.tar")
26tempdir = os.path.join(tempfile.gettempdir(), "testtar" + os.extsep + "dir")
27tempname = test_support.TESTFN
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +000028membercount = 10
29
30def tarname(comp=""):
31 if not comp:
32 return testtar
Brett Cannon43e559a2003-06-12 19:16:58 +000033 return os.path.join(tempdir, "%s%s%s" % (testtar, os.extsep, comp))
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +000034
35def dirname():
36 if not os.path.exists(tempdir):
37 os.mkdir(tempdir)
38 return tempdir
39
40def tmpname():
41 return tempname
42
43
44class BaseTest(unittest.TestCase):
45 comp = ''
46 mode = 'r'
47 sep = ':'
48
49 def setUp(self):
50 mode = self.mode + self.sep + self.comp
51 self.tar = tarfile.open(tarname(self.comp), mode)
52
53 def tearDown(self):
54 self.tar.close()
55
56class ReadTest(BaseTest):
57
58 def test(self):
59 """Test member extraction.
60 """
61 members = 0
62 for tarinfo in self.tar:
63 members += 1
64 if not tarinfo.isreg():
65 continue
66 f = self.tar.extractfile(tarinfo)
67 self.assert_(len(f.read()) == tarinfo.size,
68 "size read does not match expected size")
69 f.close()
70
71 self.assert_(members == membercount,
72 "could not find all members")
73
74 def test_sparse(self):
75 """Test sparse member extraction.
76 """
77 if self.sep != "|":
78 f1 = self.tar.extractfile("S-SPARSE")
Jack Jansen149a8992003-03-07 13:27:53 +000079 f2 = self.tar.extractfile("S-SPARSE-WITH-NULLS")
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +000080 self.assert_(f1.read() == f2.read(),
81 "_FileObject failed on sparse file member")
82
83 def test_readlines(self):
84 """Test readlines() method of _FileObject.
85 """
86 if self.sep != "|":
Jack Jansen149a8992003-03-07 13:27:53 +000087 filename = "0-REGTYPE-TEXT"
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +000088 self.tar.extract(filename, dirname())
Jack Jansenc7fcc2d2003-03-07 12:50:45 +000089 lines1 = file(os.path.join(dirname(), filename), "rU").readlines()
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +000090 lines2 = self.tar.extractfile(filename).readlines()
91 self.assert_(lines1 == lines2,
92 "_FileObject.readline() does not work correctly")
93
Martin v. Löwisdf241532005-03-03 08:17:42 +000094 def test_iter(self):
95 # Test iteration over ExFileObject.
96 if self.sep != "|":
97 filename = "0-REGTYPE-TEXT"
98 self.tar.extract(filename, dirname())
99 lines1 = file(os.path.join(dirname(), filename), "rU").readlines()
100 lines2 = [line for line in self.tar.extractfile(filename)]
101 self.assert_(lines1 == lines2,
102 "ExFileObject iteration does not work correctly")
103
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000104 def test_seek(self):
105 """Test seek() method of _FileObject, incl. random reading.
106 """
107 if self.sep != "|":
Jack Jansen149a8992003-03-07 13:27:53 +0000108 filename = "0-REGTYPE"
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000109 self.tar.extract(filename, dirname())
110 data = file(os.path.join(dirname(), filename), "rb").read()
111
112 tarinfo = self.tar.getmember(filename)
113 fobj = self.tar.extractfile(tarinfo)
114
115 text = fobj.read()
116 fobj.seek(0)
117 self.assert_(0 == fobj.tell(),
118 "seek() to file's start failed")
119 fobj.seek(2048, 0)
120 self.assert_(2048 == fobj.tell(),
121 "seek() to absolute position failed")
122 fobj.seek(-1024, 1)
123 self.assert_(1024 == fobj.tell(),
124 "seek() to negative relative position failed")
125 fobj.seek(1024, 1)
126 self.assert_(2048 == fobj.tell(),
127 "seek() to positive relative position failed")
128 s = fobj.read(10)
129 self.assert_(s == data[2048:2058],
130 "read() after seek failed")
131 fobj.seek(0, 2)
132 self.assert_(tarinfo.size == fobj.tell(),
133 "seek() to file's end failed")
134 self.assert_(fobj.read() == "",
135 "read() at file's end did not return empty string")
136 fobj.seek(-tarinfo.size, 2)
137 self.assert_(0 == fobj.tell(),
138 "relative seek() to file's start failed")
139 fobj.seek(512)
140 s1 = fobj.readlines()
141 fobj.seek(512)
142 s2 = fobj.readlines()
143 self.assert_(s1 == s2,
144 "readlines() after seek failed")
145 fobj.close()
146
147class ReadStreamTest(ReadTest):
148 sep = "|"
149
150 def test(self):
151 """Test member extraction, and for StreamError when
152 seeking backwards.
153 """
154 ReadTest.test(self)
155 tarinfo = self.tar.getmembers()[0]
156 f = self.tar.extractfile(tarinfo)
157 self.assertRaises(tarfile.StreamError, f.read)
158
159 def test_stream(self):
160 """Compare the normal tar and the stream tar.
161 """
162 stream = self.tar
163 tar = tarfile.open(tarname(), 'r')
164
165 while 1:
166 t1 = tar.next()
167 t2 = stream.next()
168 if t1 is None:
169 break
170 self.assert_(t2 is not None, "stream.next() failed.")
171
172 if t2.islnk() or t2.issym():
173 self.assertRaises(tarfile.StreamError, stream.extractfile, t2)
174 continue
175 v1 = tar.extractfile(t1)
176 v2 = stream.extractfile(t2)
177 if v1 is None:
178 continue
179 self.assert_(v2 is not None, "stream.extractfile() failed")
180 self.assert_(v1.read() == v2.read(), "stream extraction failed")
181
182 stream.close()
183
Martin v. Löwis78be7df2005-03-05 12:47:42 +0000184class ReadAsteriskTest(ReadTest):
185
186 def setUp(self):
187 mode = self.mode + self.sep + "*"
188 self.tar = tarfile.open(tarname(self.comp), mode)
189
190class ReadStreamAsteriskTest(ReadStreamTest):
191
192 def setUp(self):
193 mode = self.mode + self.sep + "*"
194 self.tar = tarfile.open(tarname(self.comp), mode)
195
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000196class WriteTest(BaseTest):
197 mode = 'w'
198
199 def setUp(self):
200 mode = self.mode + self.sep + self.comp
201 self.src = tarfile.open(tarname(self.comp), 'r')
Martin v. Löwisc234a522004-08-22 21:28:33 +0000202 self.dstname = tmpname()
203 self.dst = tarfile.open(self.dstname, mode)
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000204
205 def tearDown(self):
206 self.src.close()
207 self.dst.close()
208
209 def test_posix(self):
210 self.dst.posix = 1
211 self._test()
212
213 def test_nonposix(self):
214 self.dst.posix = 0
215 self._test()
216
Martin v. Löwisc234a522004-08-22 21:28:33 +0000217 def test_small(self):
218 self.dst.add(os.path.join(os.path.dirname(__file__),"cfgparser.1"))
219 self.dst.close()
220 self.assertNotEqual(os.stat(self.dstname).st_size, 0)
221
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000222 def _test(self):
223 for tarinfo in self.src:
224 if not tarinfo.isreg():
225 continue
226 f = self.src.extractfile(tarinfo)
227 if self.dst.posix and len(tarinfo.name) > tarfile.LENGTH_NAME:
228 self.assertRaises(ValueError, self.dst.addfile,
229 tarinfo, f)
230 else:
231 self.dst.addfile(tarinfo, f)
232
Martin v. Löwis5dbdc592005-08-27 10:07:56 +0000233class WriteSize0Test(BaseTest):
234 mode = 'w'
235
236 def setUp(self):
237 self.tmpdir = dirname()
238 self.dstname = tmpname()
239 self.dst = tarfile.open(self.dstname, "w")
240
241 def tearDown(self):
242 self.dst.close()
243
244 def test_file(self):
245 path = os.path.join(self.tmpdir, "file")
246 file(path, "w")
247 tarinfo = self.dst.gettarinfo(path)
248 self.assertEqual(tarinfo.size, 0)
249 file(path, "w").write("aaa")
250 tarinfo = self.dst.gettarinfo(path)
251 self.assertEqual(tarinfo.size, 3)
252
253 def test_directory(self):
254 path = os.path.join(self.tmpdir, "directory")
255 os.mkdir(path)
256 tarinfo = self.dst.gettarinfo(path)
257 self.assertEqual(tarinfo.size, 0)
258
259 def test_symlink(self):
260 if hasattr(os, "symlink"):
261 path = os.path.join(self.tmpdir, "symlink")
262 os.symlink("link_target", path)
263 tarinfo = self.dst.gettarinfo(path)
264 self.assertEqual(tarinfo.size, 0)
265
266
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000267class WriteStreamTest(WriteTest):
268 sep = '|'
269
Neal Norwitz0662f8a2004-07-20 21:54:18 +0000270class WriteGNULongTest(unittest.TestCase):
271 """This testcase checks for correct creation of GNU Longname
272 and Longlink extensions.
273
274 It creates a tarfile and adds empty members with either
275 long names, long linknames or both and compares the size
276 of the tarfile with the expected size.
277
278 It checks for SF bug #812325 in TarFile._create_gnulong().
279
280 While I was writing this testcase, I noticed a second bug
281 in the same method:
282 Long{names,links} weren't null-terminated which lead to
283 bad tarfiles when their length was a multiple of 512. This
284 is tested as well.
285 """
286
287 def setUp(self):
288 self.tar = tarfile.open(tmpname(), "w")
289 self.tar.posix = False
290
291 def tearDown(self):
292 self.tar.close()
293
294 def _length(self, s):
295 blocks, remainder = divmod(len(s) + 1, 512)
296 if remainder:
297 blocks += 1
298 return blocks * 512
299
300 def _calc_size(self, name, link=None):
301 # initial tar header
302 count = 512
303
304 if len(name) > tarfile.LENGTH_NAME:
305 # gnu longname extended header + longname
306 count += 512
307 count += self._length(name)
308
309 if link is not None and len(link) > tarfile.LENGTH_LINK:
310 # gnu longlink extended header + longlink
311 count += 512
312 count += self._length(link)
313
314 return count
315
316 def _test(self, name, link=None):
317 tarinfo = tarfile.TarInfo(name)
318 if link:
319 tarinfo.linkname = link
320 tarinfo.type = tarfile.LNKTYPE
321
322 self.tar.addfile(tarinfo)
323
324 v1 = self._calc_size(name, link)
325 v2 = self.tar.offset
326 self.assertEqual(v1, v2, "GNU longname/longlink creation failed")
327
328 def test_longname_1023(self):
329 self._test(("longnam/" * 127) + "longnam")
330
331 def test_longname_1024(self):
332 self._test(("longnam/" * 127) + "longname")
333
334 def test_longname_1025(self):
335 self._test(("longnam/" * 127) + "longname_")
336
337 def test_longlink_1023(self):
338 self._test("name", ("longlnk/" * 127) + "longlnk")
339
340 def test_longlink_1024(self):
341 self._test("name", ("longlnk/" * 127) + "longlink")
342
343 def test_longlink_1025(self):
344 self._test("name", ("longlnk/" * 127) + "longlink_")
345
346 def test_longnamelink_1023(self):
347 self._test(("longnam/" * 127) + "longnam",
348 ("longlnk/" * 127) + "longlnk")
349
350 def test_longnamelink_1024(self):
351 self._test(("longnam/" * 127) + "longname",
352 ("longlnk/" * 127) + "longlink")
353
354 def test_longnamelink_1025(self):
355 self._test(("longnam/" * 127) + "longname_",
356 ("longlnk/" * 127) + "longlink_")
357
Neal Norwitza4f651a2004-07-20 22:07:44 +0000358class ExtractHardlinkTest(BaseTest):
359
360 def test_hardlink(self):
361 """Test hardlink extraction (bug #857297)
362 """
363 # Prevent errors from being caught
364 self.tar.errorlevel = 1
365
366 self.tar.extract("0-REGTYPE", dirname())
367 try:
368 # Extract 1-LNKTYPE which is a hardlink to 0-REGTYPE
369 self.tar.extract("1-LNKTYPE", dirname())
370 except EnvironmentError, e:
371 import errno
372 if e.errno == errno.ENOENT:
373 self.fail("hardlink not extracted properly")
374
Neal Norwitzb0e32e22005-10-20 04:50:13 +0000375class CreateHardlinkTest(BaseTest):
376 """Test the creation of LNKTYPE (hardlink) members in an archive.
377 In this respect tarfile.py mimics the behaviour of GNU tar: If
378 a file has a st_nlink > 1, it will be added a REGTYPE member
379 only the first time.
380 """
381
382 def setUp(self):
383 self.tar = tarfile.open(tmpname(), "w")
384
385 self.foo = os.path.join(dirname(), "foo")
386 self.bar = os.path.join(dirname(), "bar")
387
388 if os.path.exists(self.foo):
389 os.remove(self.foo)
390 if os.path.exists(self.bar):
391 os.remove(self.bar)
392
393 file(self.foo, "w").write("foo")
394 self.tar.add(self.foo)
395
396 def test_add_twice(self):
397 # If st_nlink == 1 then the same file will be added as
398 # REGTYPE every time.
399 tarinfo = self.tar.gettarinfo(self.foo)
400 self.assertEqual(tarinfo.type, tarfile.REGTYPE,
401 "add file as regular failed")
402
403 def test_add_hardlink(self):
404 # If st_nlink > 1 then the same file will be added as
405 # LNKTYPE.
406 os.link(self.foo, self.bar)
407 tarinfo = self.tar.gettarinfo(self.foo)
408 self.assertEqual(tarinfo.type, tarfile.LNKTYPE,
409 "add file as hardlink failed")
410
411 tarinfo = self.tar.gettarinfo(self.bar)
412 self.assertEqual(tarinfo.type, tarfile.LNKTYPE,
413 "add file as hardlink failed")
414
415 def test_dereference_hardlink(self):
416 self.tar.dereference = True
417 os.link(self.foo, self.bar)
418 tarinfo = self.tar.gettarinfo(self.bar)
419 self.assertEqual(tarinfo.type, tarfile.REGTYPE,
420 "dereferencing hardlink failed")
421
Neal Norwitza4f651a2004-07-20 22:07:44 +0000422
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000423# Gzip TestCases
424class ReadTestGzip(ReadTest):
425 comp = "gz"
426class ReadStreamTestGzip(ReadStreamTest):
427 comp = "gz"
428class WriteTestGzip(WriteTest):
429 comp = "gz"
430class WriteStreamTestGzip(WriteStreamTest):
431 comp = "gz"
Martin v. Löwis78be7df2005-03-05 12:47:42 +0000432class ReadAsteriskTestGzip(ReadAsteriskTest):
433 comp = "gz"
434class ReadStreamAsteriskTestGzip(ReadStreamAsteriskTest):
435 comp = "gz"
436
Andrew M. Kuchlingd4f25522004-10-20 11:47:01 +0000437# Filemode test cases
438
439class FileModeTest(unittest.TestCase):
440 def test_modes(self):
441 self.assertEqual(tarfile.filemode(0755), '-rwxr-xr-x')
442 self.assertEqual(tarfile.filemode(07111), '---s--s--t')
443
Tim Peters8ceefc52004-10-25 03:19:41 +0000444
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000445if bz2:
446 # Bzip2 TestCases
447 class ReadTestBzip2(ReadTestGzip):
448 comp = "bz2"
449 class ReadStreamTestBzip2(ReadStreamTestGzip):
450 comp = "bz2"
451 class WriteTestBzip2(WriteTest):
452 comp = "bz2"
453 class WriteStreamTestBzip2(WriteStreamTestGzip):
454 comp = "bz2"
Martin v. Löwis78be7df2005-03-05 12:47:42 +0000455 class ReadAsteriskTestBzip2(ReadAsteriskTest):
456 comp = "bz2"
457 class ReadStreamAsteriskTestBzip2(ReadStreamAsteriskTest):
458 comp = "bz2"
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000459
460# If importing gzip failed, discard the Gzip TestCases.
461if not gzip:
462 del ReadTestGzip
463 del ReadStreamTestGzip
464 del WriteTestGzip
465 del WriteStreamTestGzip
466
Neal Norwitz996acf12003-02-17 14:51:41 +0000467def test_main():
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000468 if gzip:
469 # create testtar.tar.gz
470 gzip.open(tarname("gz"), "wb").write(file(tarname(), "rb").read())
471 if bz2:
472 # create testtar.tar.bz2
473 bz2.BZ2File(tarname("bz2"), "wb").write(file(tarname(), "rb").read())
474
Walter Dörwald21d3a322003-05-01 17:45:56 +0000475 tests = [
Andrew M. Kuchlingd4f25522004-10-20 11:47:01 +0000476 FileModeTest,
Walter Dörwald21d3a322003-05-01 17:45:56 +0000477 ReadTest,
478 ReadStreamTest,
Martin v. Löwis78be7df2005-03-05 12:47:42 +0000479 ReadAsteriskTest,
480 ReadStreamAsteriskTest,
Walter Dörwald21d3a322003-05-01 17:45:56 +0000481 WriteTest,
Martin v. Löwis5dbdc592005-08-27 10:07:56 +0000482 WriteSize0Test,
Neal Norwitz0662f8a2004-07-20 21:54:18 +0000483 WriteStreamTest,
484 WriteGNULongTest,
Walter Dörwald21d3a322003-05-01 17:45:56 +0000485 ]
486
Neal Norwitza4f651a2004-07-20 22:07:44 +0000487 if hasattr(os, "link"):
488 tests.append(ExtractHardlinkTest)
Neal Norwitzb0e32e22005-10-20 04:50:13 +0000489 tests.append(CreateHardlinkTest)
Neal Norwitza4f651a2004-07-20 22:07:44 +0000490
Walter Dörwald21d3a322003-05-01 17:45:56 +0000491 if gzip:
492 tests.extend([
493 ReadTestGzip, ReadStreamTestGzip,
Martin v. Löwis78be7df2005-03-05 12:47:42 +0000494 WriteTestGzip, WriteStreamTestGzip,
495 ReadAsteriskTestGzip, ReadStreamAsteriskTestGzip
Walter Dörwald21d3a322003-05-01 17:45:56 +0000496 ])
497
498 if bz2:
499 tests.extend([
500 ReadTestBzip2, ReadStreamTestBzip2,
Martin v. Löwis78be7df2005-03-05 12:47:42 +0000501 WriteTestBzip2, WriteStreamTestBzip2,
502 ReadAsteriskTestBzip2, ReadStreamAsteriskTestBzip2
Walter Dörwald21d3a322003-05-01 17:45:56 +0000503 ])
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000504 try:
Walter Dörwald21d3a322003-05-01 17:45:56 +0000505 test_support.run_unittest(*tests)
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000506 finally:
507 if gzip:
508 os.remove(tarname("gz"))
509 if bz2:
510 os.remove(tarname("bz2"))
Brett Cannon455ea532003-06-12 08:01:06 +0000511 if os.path.exists(dirname()):
512 shutil.rmtree(dirname())
513 if os.path.exists(tmpname()):
514 os.remove(tmpname())
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000515
Neal Norwitz996acf12003-02-17 14:51:41 +0000516if __name__ == "__main__":
517 test_main()