1# -*- coding: utf-8 -*- 2"""Tests for distutils.archive_util.""" 3import unittest 4import os 5import sys 6import tarfile 7from os.path import splitdrive 8import warnings 9 10from distutils import archive_util 11from distutils.archive_util import (check_archive_formats, make_tarball, 12 make_zipfile, make_archive, 13 ARCHIVE_FORMATS) 14from distutils.spawn import find_executable, spawn 15from distutils.tests import support 16from test.support import check_warnings, run_unittest, patch, change_cwd 17 18try: 19 import grp 20 import pwd 21 UID_GID_SUPPORT = True 22except ImportError: 23 UID_GID_SUPPORT = False 24 25try: 26 import zipfile 27 ZIP_SUPPORT = True 28except ImportError: 29 ZIP_SUPPORT = find_executable('zip') 30 31try: 32 import zlib 33 ZLIB_SUPPORT = True 34except ImportError: 35 ZLIB_SUPPORT = False 36 37try: 38 import bz2 39except ImportError: 40 bz2 = None 41 42try: 43 import lzma 44except ImportError: 45 lzma = None 46 47def can_fs_encode(filename): 48 """ 49 Return True if the filename can be saved in the file system. 50 """ 51 if os.path.supports_unicode_filenames: 52 return True 53 try: 54 filename.encode(sys.getfilesystemencoding()) 55 except UnicodeEncodeError: 56 return False 57 return True 58 59 60class ArchiveUtilTestCase(support.TempdirManager, 61 support.LoggingSilencer, 62 unittest.TestCase): 63 64 @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') 65 def test_make_tarball(self, name='archive'): 66 # creating something to tar 67 tmpdir = self._create_files() 68 self._make_tarball(tmpdir, name, '.tar.gz') 69 # trying an uncompressed one 70 self._make_tarball(tmpdir, name, '.tar', compress=None) 71 72 @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') 73 def test_make_tarball_gzip(self): 74 tmpdir = self._create_files() 75 self._make_tarball(tmpdir, 'archive', '.tar.gz', compress='gzip') 76 77 @unittest.skipUnless(bz2, 'Need bz2 support to run') 78 def test_make_tarball_bzip2(self): 79 tmpdir = self._create_files() 80 self._make_tarball(tmpdir, 'archive', '.tar.bz2', compress='bzip2') 81 82 @unittest.skipUnless(lzma, 'Need lzma support to run') 83 def test_make_tarball_xz(self): 84 tmpdir = self._create_files() 85 self._make_tarball(tmpdir, 'archive', '.tar.xz', compress='xz') 86 87 @unittest.skipUnless(can_fs_encode('årchiv'), 88 'File system cannot handle this filename') 89 def test_make_tarball_latin1(self): 90 """ 91 Mirror test_make_tarball, except filename contains latin characters. 92 """ 93 self.test_make_tarball('årchiv') # note this isn't a real word 94 95 @unittest.skipUnless(can_fs_encode('のアーカイブ'), 96 'File system cannot handle this filename') 97 def test_make_tarball_extended(self): 98 """ 99 Mirror test_make_tarball, except filename contains extended 100 characters outside the latin charset. 101 """ 102 self.test_make_tarball('のアーカイブ') # japanese for archive 103 104 def _make_tarball(self, tmpdir, target_name, suffix, **kwargs): 105 tmpdir2 = self.mkdtemp() 106 unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0], 107 "source and target should be on same drive") 108 109 base_name = os.path.join(tmpdir2, target_name) 110 111 # working with relative paths to avoid tar warnings 112 with change_cwd(tmpdir): 113 make_tarball(splitdrive(base_name)[1], 'dist', **kwargs) 114 115 # check if the compressed tarball was created 116 tarball = base_name + suffix 117 self.assertTrue(os.path.exists(tarball)) 118 self.assertEqual(self._tarinfo(tarball), self._created_files) 119 120 def _tarinfo(self, path): 121 tar = tarfile.open(path) 122 try: 123 names = tar.getnames() 124 names.sort() 125 return names 126 finally: 127 tar.close() 128 129 _zip_created_files = ['dist/', 'dist/file1', 'dist/file2', 130 'dist/sub/', 'dist/sub/file3', 'dist/sub2/'] 131 _created_files = [p.rstrip('/') for p in _zip_created_files] 132 133 def _create_files(self): 134 # creating something to tar 135 tmpdir = self.mkdtemp() 136 dist = os.path.join(tmpdir, 'dist') 137 os.mkdir(dist) 138 self.write_file([dist, 'file1'], 'xxx') 139 self.write_file([dist, 'file2'], 'xxx') 140 os.mkdir(os.path.join(dist, 'sub')) 141 self.write_file([dist, 'sub', 'file3'], 'xxx') 142 os.mkdir(os.path.join(dist, 'sub2')) 143 return tmpdir 144 145 @unittest.skipUnless(find_executable('tar') and find_executable('gzip') 146 and ZLIB_SUPPORT, 147 'Need the tar, gzip and zlib command to run') 148 def test_tarfile_vs_tar(self): 149 tmpdir = self._create_files() 150 tmpdir2 = self.mkdtemp() 151 base_name = os.path.join(tmpdir2, 'archive') 152 old_dir = os.getcwd() 153 os.chdir(tmpdir) 154 try: 155 make_tarball(base_name, 'dist') 156 finally: 157 os.chdir(old_dir) 158 159 # check if the compressed tarball was created 160 tarball = base_name + '.tar.gz' 161 self.assertTrue(os.path.exists(tarball)) 162 163 # now create another tarball using `tar` 164 tarball2 = os.path.join(tmpdir, 'archive2.tar.gz') 165 tar_cmd = ['tar', '-cf', 'archive2.tar', 'dist'] 166 gzip_cmd = ['gzip', '-f', '-9', 'archive2.tar'] 167 old_dir = os.getcwd() 168 os.chdir(tmpdir) 169 try: 170 spawn(tar_cmd) 171 spawn(gzip_cmd) 172 finally: 173 os.chdir(old_dir) 174 175 self.assertTrue(os.path.exists(tarball2)) 176 # let's compare both tarballs 177 self.assertEqual(self._tarinfo(tarball), self._created_files) 178 self.assertEqual(self._tarinfo(tarball2), self._created_files) 179 180 # trying an uncompressed one 181 base_name = os.path.join(tmpdir2, 'archive') 182 old_dir = os.getcwd() 183 os.chdir(tmpdir) 184 try: 185 make_tarball(base_name, 'dist', compress=None) 186 finally: 187 os.chdir(old_dir) 188 tarball = base_name + '.tar' 189 self.assertTrue(os.path.exists(tarball)) 190 191 # now for a dry_run 192 base_name = os.path.join(tmpdir2, 'archive') 193 old_dir = os.getcwd() 194 os.chdir(tmpdir) 195 try: 196 make_tarball(base_name, 'dist', compress=None, dry_run=True) 197 finally: 198 os.chdir(old_dir) 199 tarball = base_name + '.tar' 200 self.assertTrue(os.path.exists(tarball)) 201 202 @unittest.skipUnless(find_executable('compress'), 203 'The compress program is required') 204 def test_compress_deprecated(self): 205 tmpdir = self._create_files() 206 base_name = os.path.join(self.mkdtemp(), 'archive') 207 208 # using compress and testing the PendingDeprecationWarning 209 old_dir = os.getcwd() 210 os.chdir(tmpdir) 211 try: 212 with check_warnings() as w: 213 warnings.simplefilter("always") 214 make_tarball(base_name, 'dist', compress='compress') 215 finally: 216 os.chdir(old_dir) 217 tarball = base_name + '.tar.Z' 218 self.assertTrue(os.path.exists(tarball)) 219 self.assertEqual(len(w.warnings), 1) 220 221 # same test with dry_run 222 os.remove(tarball) 223 old_dir = os.getcwd() 224 os.chdir(tmpdir) 225 try: 226 with check_warnings() as w: 227 warnings.simplefilter("always") 228 make_tarball(base_name, 'dist', compress='compress', 229 dry_run=True) 230 finally: 231 os.chdir(old_dir) 232 self.assertFalse(os.path.exists(tarball)) 233 self.assertEqual(len(w.warnings), 1) 234 235 @unittest.skipUnless(ZIP_SUPPORT and ZLIB_SUPPORT, 236 'Need zip and zlib support to run') 237 def test_make_zipfile(self): 238 # creating something to tar 239 tmpdir = self._create_files() 240 base_name = os.path.join(self.mkdtemp(), 'archive') 241 with change_cwd(tmpdir): 242 make_zipfile(base_name, 'dist') 243 244 # check if the compressed tarball was created 245 tarball = base_name + '.zip' 246 self.assertTrue(os.path.exists(tarball)) 247 with zipfile.ZipFile(tarball) as zf: 248 self.assertEqual(sorted(zf.namelist()), self._zip_created_files) 249 250 @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') 251 def test_make_zipfile_no_zlib(self): 252 patch(self, archive_util.zipfile, 'zlib', None) # force zlib ImportError 253 254 called = [] 255 zipfile_class = zipfile.ZipFile 256 def fake_zipfile(*a, **kw): 257 if kw.get('compression', None) == zipfile.ZIP_STORED: 258 called.append((a, kw)) 259 return zipfile_class(*a, **kw) 260 261 patch(self, archive_util.zipfile, 'ZipFile', fake_zipfile) 262 263 # create something to tar and compress 264 tmpdir = self._create_files() 265 base_name = os.path.join(self.mkdtemp(), 'archive') 266 with change_cwd(tmpdir): 267 make_zipfile(base_name, 'dist') 268 269 tarball = base_name + '.zip' 270 self.assertEqual(called, 271 [((tarball, "w"), {'compression': zipfile.ZIP_STORED})]) 272 self.assertTrue(os.path.exists(tarball)) 273 with zipfile.ZipFile(tarball) as zf: 274 self.assertEqual(sorted(zf.namelist()), self._zip_created_files) 275 276 def test_check_archive_formats(self): 277 self.assertEqual(check_archive_formats(['gztar', 'xxx', 'zip']), 278 'xxx') 279 self.assertIsNone(check_archive_formats(['gztar', 'bztar', 'xztar', 280 'ztar', 'tar', 'zip'])) 281 282 def test_make_archive(self): 283 tmpdir = self.mkdtemp() 284 base_name = os.path.join(tmpdir, 'archive') 285 self.assertRaises(ValueError, make_archive, base_name, 'xxx') 286 287 def test_make_archive_cwd(self): 288 current_dir = os.getcwd() 289 def _breaks(*args, **kw): 290 raise RuntimeError() 291 ARCHIVE_FORMATS['xxx'] = (_breaks, [], 'xxx file') 292 try: 293 try: 294 make_archive('xxx', 'xxx', root_dir=self.mkdtemp()) 295 except: 296 pass 297 self.assertEqual(os.getcwd(), current_dir) 298 finally: 299 del ARCHIVE_FORMATS['xxx'] 300 301 def test_make_archive_tar(self): 302 base_dir = self._create_files() 303 base_name = os.path.join(self.mkdtemp() , 'archive') 304 res = make_archive(base_name, 'tar', base_dir, 'dist') 305 self.assertTrue(os.path.exists(res)) 306 self.assertEqual(os.path.basename(res), 'archive.tar') 307 self.assertEqual(self._tarinfo(res), self._created_files) 308 309 @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') 310 def test_make_archive_gztar(self): 311 base_dir = self._create_files() 312 base_name = os.path.join(self.mkdtemp() , 'archive') 313 res = make_archive(base_name, 'gztar', base_dir, 'dist') 314 self.assertTrue(os.path.exists(res)) 315 self.assertEqual(os.path.basename(res), 'archive.tar.gz') 316 self.assertEqual(self._tarinfo(res), self._created_files) 317 318 @unittest.skipUnless(bz2, 'Need bz2 support to run') 319 def test_make_archive_bztar(self): 320 base_dir = self._create_files() 321 base_name = os.path.join(self.mkdtemp() , 'archive') 322 res = make_archive(base_name, 'bztar', base_dir, 'dist') 323 self.assertTrue(os.path.exists(res)) 324 self.assertEqual(os.path.basename(res), 'archive.tar.bz2') 325 self.assertEqual(self._tarinfo(res), self._created_files) 326 327 @unittest.skipUnless(lzma, 'Need xz support to run') 328 def test_make_archive_xztar(self): 329 base_dir = self._create_files() 330 base_name = os.path.join(self.mkdtemp() , 'archive') 331 res = make_archive(base_name, 'xztar', base_dir, 'dist') 332 self.assertTrue(os.path.exists(res)) 333 self.assertEqual(os.path.basename(res), 'archive.tar.xz') 334 self.assertEqual(self._tarinfo(res), self._created_files) 335 336 def test_make_archive_owner_group(self): 337 # testing make_archive with owner and group, with various combinations 338 # this works even if there's not gid/uid support 339 if UID_GID_SUPPORT: 340 group = grp.getgrgid(0)[0] 341 owner = pwd.getpwuid(0)[0] 342 else: 343 group = owner = 'root' 344 345 base_dir = self._create_files() 346 root_dir = self.mkdtemp() 347 base_name = os.path.join(self.mkdtemp() , 'archive') 348 res = make_archive(base_name, 'zip', root_dir, base_dir, owner=owner, 349 group=group) 350 self.assertTrue(os.path.exists(res)) 351 352 res = make_archive(base_name, 'zip', root_dir, base_dir) 353 self.assertTrue(os.path.exists(res)) 354 355 res = make_archive(base_name, 'tar', root_dir, base_dir, 356 owner=owner, group=group) 357 self.assertTrue(os.path.exists(res)) 358 359 res = make_archive(base_name, 'tar', root_dir, base_dir, 360 owner='kjhkjhkjg', group='oihohoh') 361 self.assertTrue(os.path.exists(res)) 362 363 @unittest.skipUnless(ZLIB_SUPPORT, "Requires zlib") 364 @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support") 365 def test_tarfile_root_owner(self): 366 tmpdir = self._create_files() 367 base_name = os.path.join(self.mkdtemp(), 'archive') 368 old_dir = os.getcwd() 369 os.chdir(tmpdir) 370 group = grp.getgrgid(0)[0] 371 owner = pwd.getpwuid(0)[0] 372 try: 373 archive_name = make_tarball(base_name, 'dist', compress=None, 374 owner=owner, group=group) 375 finally: 376 os.chdir(old_dir) 377 378 # check if the compressed tarball was created 379 self.assertTrue(os.path.exists(archive_name)) 380 381 # now checks the rights 382 archive = tarfile.open(archive_name) 383 try: 384 for member in archive.getmembers(): 385 self.assertEqual(member.uid, 0) 386 self.assertEqual(member.gid, 0) 387 finally: 388 archive.close() 389 390def test_suite(): 391 return unittest.makeSuite(ArchiveUtilTestCase) 392 393if __name__ == "__main__": 394 run_unittest(test_suite()) 395