blob: df95bd9a5c72ac3702f7ee0e0f2892b82d440855 [file] [log] [blame]
Barry Warsaw7fc2cca2003-01-24 17:34:13 +00001# Copyright (C) 2003 Python Software Foundation
2
3import unittest
4import shutil
5import tempfile
Johannes Gijsbers8e6f2de2004-11-23 09:27:27 +00006import sys
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +00007import stat
Brett Cannon1c3fa182004-06-19 21:11:35 +00008import os
9import os.path
Antoine Pitrouc041ab62012-01-02 19:18:02 +010010import errno
Nick Coghlan8ed3cf32011-03-16 14:05:35 -040011import functools
Antoine Pitroubcf2b592012-02-08 23:28:36 +010012import subprocess
Benjamin Petersonee8712c2008-05-20 21:35:26 +000013from test import support
14from test.support import TESTFN
Tarek Ziadé396fad72010-02-23 05:30:31 +000015from os.path import splitdrive
16from distutils.spawn import find_executable, spawn
17from shutil import (_make_tarball, _make_zipfile, make_archive,
18 register_archive_format, unregister_archive_format,
Tarek Ziadé6ac91722010-04-28 17:51:36 +000019 get_archive_formats, Error, unpack_archive,
20 register_unpack_format, RegistryError,
Hynek Schlawack26fe37d2012-07-19 21:41:02 +020021 unregister_unpack_format, get_unpack_formats)
Tarek Ziadé396fad72010-02-23 05:30:31 +000022import tarfile
23import warnings
24
25from test import support
Ezio Melotti975077a2011-05-19 22:03:22 +030026from test.support import TESTFN, check_warnings, captured_stdout, requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +000027
Tarek Ziadéffa155a2010-04-29 13:34:35 +000028try:
29 import bz2
30 BZ2_SUPPORTED = True
31except ImportError:
32 BZ2_SUPPORTED = False
33
Antoine Pitrou7fff0962009-05-01 21:09:44 +000034TESTFN2 = TESTFN + "2"
Barry Warsaw7fc2cca2003-01-24 17:34:13 +000035
Tarek Ziadé396fad72010-02-23 05:30:31 +000036try:
37 import grp
38 import pwd
39 UID_GID_SUPPORT = True
40except ImportError:
41 UID_GID_SUPPORT = False
42
43try:
Tarek Ziadé396fad72010-02-23 05:30:31 +000044 import zipfile
45 ZIP_SUPPORT = True
46except ImportError:
47 ZIP_SUPPORT = find_executable('zip')
48
Nick Coghlan8ed3cf32011-03-16 14:05:35 -040049def _fake_rename(*args, **kwargs):
50 # Pretend the destination path is on a different filesystem.
Antoine Pitrouc041ab62012-01-02 19:18:02 +010051 raise OSError(getattr(errno, 'EXDEV', 18), "Invalid cross-device link")
Nick Coghlan8ed3cf32011-03-16 14:05:35 -040052
53def mock_rename(func):
54 @functools.wraps(func)
55 def wrap(*args, **kwargs):
56 try:
57 builtin_rename = os.rename
58 os.rename = _fake_rename
59 return func(*args, **kwargs)
60 finally:
61 os.rename = builtin_rename
62 return wrap
63
Éric Araujoa7e33a12011-08-12 19:51:35 +020064def write_file(path, content, binary=False):
65 """Write *content* to a file located at *path*.
66
67 If *path* is a tuple instead of a string, os.path.join will be used to
68 make a path. If *binary* is true, the file will be opened in binary
69 mode.
70 """
71 if isinstance(path, tuple):
72 path = os.path.join(*path)
73 with open(path, 'wb' if binary else 'w') as fp:
74 fp.write(content)
75
76def read_file(path, binary=False):
77 """Return contents from a file located at *path*.
78
79 If *path* is a tuple instead of a string, os.path.join will be used to
80 make a path. If *binary* is true, the file will be opened in binary
81 mode.
82 """
83 if isinstance(path, tuple):
84 path = os.path.join(*path)
85 with open(path, 'rb' if binary else 'r') as fp:
86 return fp.read()
87
88
Barry Warsaw7fc2cca2003-01-24 17:34:13 +000089class TestShutil(unittest.TestCase):
Tarek Ziadé396fad72010-02-23 05:30:31 +000090
91 def setUp(self):
92 super(TestShutil, self).setUp()
93 self.tempdirs = []
94
95 def tearDown(self):
96 super(TestShutil, self).tearDown()
97 while self.tempdirs:
98 d = self.tempdirs.pop()
99 shutil.rmtree(d, os.name in ('nt', 'cygwin'))
100
Tarek Ziadé396fad72010-02-23 05:30:31 +0000101
102 def mkdtemp(self):
103 """Create a temporary directory that will be cleaned up.
104
105 Returns the path of the directory.
106 """
107 d = tempfile.mkdtemp()
108 self.tempdirs.append(d)
109 return d
Tarek Ziadé5340db32010-04-19 22:30:51 +0000110
Hynek Schlawack3b527782012-06-25 13:27:31 +0200111 def test_rmtree_works_on_bytes(self):
112 tmp = self.mkdtemp()
113 victim = os.path.join(tmp, 'killme')
114 os.mkdir(victim)
115 write_file(os.path.join(victim, 'somefile'), 'foo')
116 victim = os.fsencode(victim)
117 self.assertIsInstance(victim, bytes)
118 shutil.rmtree(victim)
119
Hynek Schlawacka75cd1c2012-06-28 12:07:29 +0200120 @support.skip_unless_symlink
121 def test_rmtree_fails_on_symlink(self):
122 tmp = self.mkdtemp()
123 dir_ = os.path.join(tmp, 'dir')
124 os.mkdir(dir_)
125 link = os.path.join(tmp, 'link')
126 os.symlink(dir_, link)
127 self.assertRaises(OSError, shutil.rmtree, link)
128 self.assertTrue(os.path.exists(dir_))
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100129 self.assertTrue(os.path.lexists(link))
130 errors = []
131 def onerror(*args):
132 errors.append(args)
133 shutil.rmtree(link, onerror=onerror)
134 self.assertEqual(len(errors), 1)
135 self.assertIs(errors[0][0], os.path.islink)
136 self.assertEqual(errors[0][1], link)
137 self.assertIsInstance(errors[0][2][1], OSError)
Hynek Schlawacka75cd1c2012-06-28 12:07:29 +0200138
139 @support.skip_unless_symlink
140 def test_rmtree_works_on_symlinks(self):
141 tmp = self.mkdtemp()
142 dir1 = os.path.join(tmp, 'dir1')
143 dir2 = os.path.join(dir1, 'dir2')
144 dir3 = os.path.join(tmp, 'dir3')
145 for d in dir1, dir2, dir3:
146 os.mkdir(d)
147 file1 = os.path.join(tmp, 'file1')
148 write_file(file1, 'foo')
149 link1 = os.path.join(dir1, 'link1')
150 os.symlink(dir2, link1)
151 link2 = os.path.join(dir1, 'link2')
152 os.symlink(dir3, link2)
153 link3 = os.path.join(dir1, 'link3')
154 os.symlink(file1, link3)
155 # make sure symlinks are removed but not followed
156 shutil.rmtree(dir1)
157 self.assertFalse(os.path.exists(dir1))
158 self.assertTrue(os.path.exists(dir3))
159 self.assertTrue(os.path.exists(file1))
160
Barry Warsaw7fc2cca2003-01-24 17:34:13 +0000161 def test_rmtree_errors(self):
162 # filename is guaranteed not to exist
163 filename = tempfile.mktemp()
Hynek Schlawackb5501102012-12-10 09:11:25 +0100164 self.assertRaises(FileNotFoundError, shutil.rmtree, filename)
165 # test that ignore_errors option is honored
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100166 shutil.rmtree(filename, ignore_errors=True)
167
168 # existing file
169 tmpdir = self.mkdtemp()
Hynek Schlawackb5501102012-12-10 09:11:25 +0100170 write_file((tmpdir, "tstfile"), "")
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100171 filename = os.path.join(tmpdir, "tstfile")
Hynek Schlawackb5501102012-12-10 09:11:25 +0100172 with self.assertRaises(NotADirectoryError) as cm:
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100173 shutil.rmtree(filename)
Hynek Schlawack87f9b462012-12-10 16:29:57 +0100174 # The reason for this rather odd construct is that Windows sprinkles
175 # a \*.* at the end of file names. But only sometimes on some buildbots
176 possible_args = [filename, os.path.join(filename, '*.*')]
177 self.assertIn(cm.exception.filename, possible_args)
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100178 self.assertTrue(os.path.exists(filename))
Hynek Schlawackb5501102012-12-10 09:11:25 +0100179 # test that ignore_errors option is honored
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100180 shutil.rmtree(filename, ignore_errors=True)
181 self.assertTrue(os.path.exists(filename))
182 errors = []
183 def onerror(*args):
184 errors.append(args)
185 shutil.rmtree(filename, onerror=onerror)
186 self.assertEqual(len(errors), 2)
187 self.assertIs(errors[0][0], os.listdir)
188 self.assertEqual(errors[0][1], filename)
Hynek Schlawackb5501102012-12-10 09:11:25 +0100189 self.assertIsInstance(errors[0][2][1], NotADirectoryError)
Hynek Schlawack87f9b462012-12-10 16:29:57 +0100190 self.assertIn(errors[0][2][1].filename, possible_args)
Hynek Schlawackd16eacb2012-12-10 09:00:09 +0100191 self.assertIs(errors[1][0], os.rmdir)
192 self.assertEqual(errors[1][1], filename)
Hynek Schlawackb5501102012-12-10 09:11:25 +0100193 self.assertIsInstance(errors[1][2][1], NotADirectoryError)
Hynek Schlawack87f9b462012-12-10 16:29:57 +0100194 self.assertIn(errors[1][2][1].filename, possible_args)
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000195
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000196
Serhiy Storchaka79080682013-11-03 21:31:18 +0200197 @unittest.skipUnless(hasattr(os, 'chmod'), 'requires os.chmod()')
198 @unittest.skipIf(sys.platform[:6] == 'cygwin',
199 "This test can't be run on Cygwin (issue #1071513).")
200 @unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0,
201 "This test can't be run reliably as root (issue #1076467).")
202 def test_on_error(self):
203 self.errorState = 0
204 os.mkdir(TESTFN)
205 self.addCleanup(shutil.rmtree, TESTFN)
Antoine Pitrou2f8a75c2012-06-23 21:28:15 +0200206
Serhiy Storchaka79080682013-11-03 21:31:18 +0200207 self.child_file_path = os.path.join(TESTFN, 'a')
208 self.child_dir_path = os.path.join(TESTFN, 'b')
209 support.create_empty_file(self.child_file_path)
210 os.mkdir(self.child_dir_path)
211 old_dir_mode = os.stat(TESTFN).st_mode
212 old_child_file_mode = os.stat(self.child_file_path).st_mode
213 old_child_dir_mode = os.stat(self.child_dir_path).st_mode
214 # Make unwritable.
215 new_mode = stat.S_IREAD|stat.S_IEXEC
216 os.chmod(self.child_file_path, new_mode)
217 os.chmod(self.child_dir_path, new_mode)
218 os.chmod(TESTFN, new_mode)
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000219
Serhiy Storchaka79080682013-11-03 21:31:18 +0200220 self.addCleanup(os.chmod, TESTFN, old_dir_mode)
221 self.addCleanup(os.chmod, self.child_file_path, old_child_file_mode)
222 self.addCleanup(os.chmod, self.child_dir_path, old_child_dir_mode)
Antoine Pitrou2f8a75c2012-06-23 21:28:15 +0200223
Serhiy Storchaka79080682013-11-03 21:31:18 +0200224 shutil.rmtree(TESTFN, onerror=self.check_args_to_onerror)
225 # Test whether onerror has actually been called.
226 self.assertEqual(self.errorState, 3,
227 "Expected call to onerror function did not happen.")
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000228
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000229 def check_args_to_onerror(self, func, arg, exc):
Benjamin Peterson25c95f12009-05-08 20:42:26 +0000230 # test_rmtree_errors deliberately runs rmtree
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200231 # on a directory that is chmod 500, which will fail.
Benjamin Peterson25c95f12009-05-08 20:42:26 +0000232 # This function is run when shutil.rmtree fails.
233 # 99.9% of the time it initially fails to remove
234 # a file in the directory, so the first time through
235 # func is os.remove.
236 # However, some Linux machines running ZFS on
237 # FUSE experienced a failure earlier in the process
238 # at os.listdir. The first failure may legally
239 # be either.
Antoine Pitrou4f6e3f72012-06-23 22:05:11 +0200240 if self.errorState < 2:
Hynek Schlawack2100b422012-06-23 20:28:32 +0200241 if func is os.unlink:
Antoine Pitrou4f6e3f72012-06-23 22:05:11 +0200242 self.assertEqual(arg, self.child_file_path)
243 elif func is os.rmdir:
244 self.assertEqual(arg, self.child_dir_path)
Benjamin Peterson25c95f12009-05-08 20:42:26 +0000245 else:
Antoine Pitrou4f6e3f72012-06-23 22:05:11 +0200246 self.assertIs(func, os.listdir)
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200247 self.assertIn(arg, [TESTFN, self.child_dir_path])
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000248 self.assertTrue(issubclass(exc[0], OSError))
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200249 self.errorState += 1
Johannes Gijsbersef5ffc42004-10-31 12:05:31 +0000250 else:
251 self.assertEqual(func, os.rmdir)
252 self.assertEqual(arg, TESTFN)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000253 self.assertTrue(issubclass(exc[0], OSError))
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200254 self.errorState = 3
255
256 def test_rmtree_does_not_choke_on_failing_lstat(self):
257 try:
258 orig_lstat = os.lstat
Hynek Schlawacka75cd1c2012-06-28 12:07:29 +0200259 def raiser(fn, *args, **kwargs):
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200260 if fn != TESTFN:
261 raise OSError()
262 else:
263 return orig_lstat(fn)
264 os.lstat = raiser
265
266 os.mkdir(TESTFN)
267 write_file((TESTFN, 'foo'), 'foo')
268 shutil.rmtree(TESTFN)
269 finally:
270 os.lstat = orig_lstat
Barry Warsaw7fc2cca2003-01-24 17:34:13 +0000271
Antoine Pitrou78091e62011-12-29 18:54:15 +0100272 @unittest.skipUnless(hasattr(os, 'chmod'), 'requires os.chmod')
273 @support.skip_unless_symlink
274 def test_copymode_follow_symlinks(self):
275 tmp_dir = self.mkdtemp()
276 src = os.path.join(tmp_dir, 'foo')
277 dst = os.path.join(tmp_dir, 'bar')
278 src_link = os.path.join(tmp_dir, 'baz')
279 dst_link = os.path.join(tmp_dir, 'quux')
280 write_file(src, 'foo')
281 write_file(dst, 'foo')
282 os.symlink(src, src_link)
283 os.symlink(dst, dst_link)
284 os.chmod(src, stat.S_IRWXU|stat.S_IRWXG)
285 # file to file
286 os.chmod(dst, stat.S_IRWXO)
287 self.assertNotEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
288 shutil.copymode(src, dst)
289 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
290 # follow src link
291 os.chmod(dst, stat.S_IRWXO)
292 shutil.copymode(src_link, dst)
293 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
294 # follow dst link
295 os.chmod(dst, stat.S_IRWXO)
296 shutil.copymode(src, dst_link)
297 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
298 # follow both links
299 os.chmod(dst, stat.S_IRWXO)
300 shutil.copymode(src_link, dst)
301 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
302
303 @unittest.skipUnless(hasattr(os, 'lchmod'), 'requires os.lchmod')
304 @support.skip_unless_symlink
305 def test_copymode_symlink_to_symlink(self):
306 tmp_dir = self.mkdtemp()
307 src = os.path.join(tmp_dir, 'foo')
308 dst = os.path.join(tmp_dir, 'bar')
309 src_link = os.path.join(tmp_dir, 'baz')
310 dst_link = os.path.join(tmp_dir, 'quux')
311 write_file(src, 'foo')
312 write_file(dst, 'foo')
313 os.symlink(src, src_link)
314 os.symlink(dst, dst_link)
315 os.chmod(src, stat.S_IRWXU|stat.S_IRWXG)
316 os.chmod(dst, stat.S_IRWXU)
317 os.lchmod(src_link, stat.S_IRWXO|stat.S_IRWXG)
318 # link to link
319 os.lchmod(dst_link, stat.S_IRWXO)
Larry Hastingsb4038062012-07-15 10:57:38 -0700320 shutil.copymode(src_link, dst_link, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100321 self.assertEqual(os.lstat(src_link).st_mode,
322 os.lstat(dst_link).st_mode)
323 self.assertNotEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
324 # src link - use chmod
325 os.lchmod(dst_link, stat.S_IRWXO)
Larry Hastingsb4038062012-07-15 10:57:38 -0700326 shutil.copymode(src_link, dst, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100327 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
328 # dst link - use chmod
329 os.lchmod(dst_link, stat.S_IRWXO)
Larry Hastingsb4038062012-07-15 10:57:38 -0700330 shutil.copymode(src, dst_link, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100331 self.assertEqual(os.stat(src).st_mode, os.stat(dst).st_mode)
332
333 @unittest.skipIf(hasattr(os, 'lchmod'), 'requires os.lchmod to be missing')
334 @support.skip_unless_symlink
335 def test_copymode_symlink_to_symlink_wo_lchmod(self):
336 tmp_dir = self.mkdtemp()
337 src = os.path.join(tmp_dir, 'foo')
338 dst = os.path.join(tmp_dir, 'bar')
339 src_link = os.path.join(tmp_dir, 'baz')
340 dst_link = os.path.join(tmp_dir, 'quux')
341 write_file(src, 'foo')
342 write_file(dst, 'foo')
343 os.symlink(src, src_link)
344 os.symlink(dst, dst_link)
Larry Hastingsb4038062012-07-15 10:57:38 -0700345 shutil.copymode(src_link, dst_link, follow_symlinks=False) # silent fail
Antoine Pitrou78091e62011-12-29 18:54:15 +0100346
347 @support.skip_unless_symlink
348 def test_copystat_symlinks(self):
349 tmp_dir = self.mkdtemp()
350 src = os.path.join(tmp_dir, 'foo')
351 dst = os.path.join(tmp_dir, 'bar')
352 src_link = os.path.join(tmp_dir, 'baz')
353 dst_link = os.path.join(tmp_dir, 'qux')
354 write_file(src, 'foo')
355 src_stat = os.stat(src)
356 os.utime(src, (src_stat.st_atime,
357 src_stat.st_mtime - 42.0)) # ensure different mtimes
358 write_file(dst, 'bar')
359 self.assertNotEqual(os.stat(src).st_mtime, os.stat(dst).st_mtime)
360 os.symlink(src, src_link)
361 os.symlink(dst, dst_link)
362 if hasattr(os, 'lchmod'):
363 os.lchmod(src_link, stat.S_IRWXO)
364 if hasattr(os, 'lchflags') and hasattr(stat, 'UF_NODUMP'):
365 os.lchflags(src_link, stat.UF_NODUMP)
366 src_link_stat = os.lstat(src_link)
367 # follow
368 if hasattr(os, 'lchmod'):
Larry Hastingsb4038062012-07-15 10:57:38 -0700369 shutil.copystat(src_link, dst_link, follow_symlinks=True)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100370 self.assertNotEqual(src_link_stat.st_mode, os.stat(dst).st_mode)
371 # don't follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700372 shutil.copystat(src_link, dst_link, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100373 dst_link_stat = os.lstat(dst_link)
Larry Hastings9cf065c2012-06-22 16:30:09 -0700374 if os.utime in os.supports_follow_symlinks:
Antoine Pitrou78091e62011-12-29 18:54:15 +0100375 for attr in 'st_atime', 'st_mtime':
376 # The modification times may be truncated in the new file.
377 self.assertLessEqual(getattr(src_link_stat, attr),
378 getattr(dst_link_stat, attr) + 1)
379 if hasattr(os, 'lchmod'):
380 self.assertEqual(src_link_stat.st_mode, dst_link_stat.st_mode)
381 if hasattr(os, 'lchflags') and hasattr(src_link_stat, 'st_flags'):
382 self.assertEqual(src_link_stat.st_flags, dst_link_stat.st_flags)
383 # tell to follow but dst is not a link
Larry Hastingsb4038062012-07-15 10:57:38 -0700384 shutil.copystat(src_link, dst, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100385 self.assertTrue(abs(os.stat(src).st_mtime - os.stat(dst).st_mtime) <
386 00000.1)
387
Ned Deilybaf75712012-05-10 17:05:19 -0700388 @unittest.skipUnless(hasattr(os, 'chflags') and
389 hasattr(errno, 'EOPNOTSUPP') and
390 hasattr(errno, 'ENOTSUP'),
391 "requires os.chflags, EOPNOTSUPP & ENOTSUP")
392 def test_copystat_handles_harmless_chflags_errors(self):
393 tmpdir = self.mkdtemp()
394 file1 = os.path.join(tmpdir, 'file1')
395 file2 = os.path.join(tmpdir, 'file2')
396 write_file(file1, 'xxx')
397 write_file(file2, 'xxx')
398
399 def make_chflags_raiser(err):
400 ex = OSError()
401
Larry Hastings90867a52012-06-22 17:01:41 -0700402 def _chflags_raiser(path, flags, *, follow_symlinks=True):
Ned Deilybaf75712012-05-10 17:05:19 -0700403 ex.errno = err
404 raise ex
405 return _chflags_raiser
406 old_chflags = os.chflags
407 try:
408 for err in errno.EOPNOTSUPP, errno.ENOTSUP:
409 os.chflags = make_chflags_raiser(err)
410 shutil.copystat(file1, file2)
411 # assert others errors break it
412 os.chflags = make_chflags_raiser(errno.EOPNOTSUPP + errno.ENOTSUP)
413 self.assertRaises(OSError, shutil.copystat, file1, file2)
414 finally:
415 os.chflags = old_chflags
416
Antoine Pitrou424246f2012-05-12 19:02:01 +0200417 @support.skip_unless_xattr
418 def test_copyxattr(self):
419 tmp_dir = self.mkdtemp()
420 src = os.path.join(tmp_dir, 'foo')
421 write_file(src, 'foo')
422 dst = os.path.join(tmp_dir, 'bar')
423 write_file(dst, 'bar')
424
425 # no xattr == no problem
426 shutil._copyxattr(src, dst)
427 # common case
428 os.setxattr(src, 'user.foo', b'42')
429 os.setxattr(src, 'user.bar', b'43')
430 shutil._copyxattr(src, dst)
431 self.assertEqual(os.listxattr(src), os.listxattr(dst))
432 self.assertEqual(
433 os.getxattr(src, 'user.foo'),
434 os.getxattr(dst, 'user.foo'))
435 # check errors don't affect other attrs
436 os.remove(dst)
437 write_file(dst, 'bar')
438 os_error = OSError(errno.EPERM, 'EPERM')
439
Larry Hastings9cf065c2012-06-22 16:30:09 -0700440 def _raise_on_user_foo(fname, attr, val, **kwargs):
Antoine Pitrou424246f2012-05-12 19:02:01 +0200441 if attr == 'user.foo':
442 raise os_error
443 else:
Larry Hastings9cf065c2012-06-22 16:30:09 -0700444 orig_setxattr(fname, attr, val, **kwargs)
Antoine Pitrou424246f2012-05-12 19:02:01 +0200445 try:
446 orig_setxattr = os.setxattr
447 os.setxattr = _raise_on_user_foo
448 shutil._copyxattr(src, dst)
Antoine Pitrou61597d32012-05-12 23:37:35 +0200449 self.assertIn('user.bar', os.listxattr(dst))
Antoine Pitrou424246f2012-05-12 19:02:01 +0200450 finally:
451 os.setxattr = orig_setxattr
Hynek Schlawack0beab052013-02-05 08:22:44 +0100452 # the source filesystem not supporting xattrs should be ok, too.
453 def _raise_on_src(fname, *, follow_symlinks=True):
454 if fname == src:
455 raise OSError(errno.ENOTSUP, 'Operation not supported')
456 return orig_listxattr(fname, follow_symlinks=follow_symlinks)
457 try:
458 orig_listxattr = os.listxattr
459 os.listxattr = _raise_on_src
460 shutil._copyxattr(src, dst)
461 finally:
462 os.listxattr = orig_listxattr
Antoine Pitrou424246f2012-05-12 19:02:01 +0200463
Larry Hastingsad5ae042012-07-14 17:55:11 -0700464 # test that shutil.copystat copies xattrs
465 src = os.path.join(tmp_dir, 'the_original')
466 write_file(src, src)
467 os.setxattr(src, 'user.the_value', b'fiddly')
468 dst = os.path.join(tmp_dir, 'the_copy')
469 write_file(dst, dst)
470 shutil.copystat(src, dst)
Hynek Schlawackc2d481f2012-07-16 17:11:10 +0200471 self.assertEqual(os.getxattr(dst, 'user.the_value'), b'fiddly')
Larry Hastingsad5ae042012-07-14 17:55:11 -0700472
Antoine Pitrou424246f2012-05-12 19:02:01 +0200473 @support.skip_unless_symlink
474 @support.skip_unless_xattr
475 @unittest.skipUnless(hasattr(os, 'geteuid') and os.geteuid() == 0,
476 'root privileges required')
477 def test_copyxattr_symlinks(self):
478 # On Linux, it's only possible to access non-user xattr for symlinks;
479 # which in turn require root privileges. This test should be expanded
480 # as soon as other platforms gain support for extended attributes.
481 tmp_dir = self.mkdtemp()
482 src = os.path.join(tmp_dir, 'foo')
483 src_link = os.path.join(tmp_dir, 'baz')
484 write_file(src, 'foo')
485 os.symlink(src, src_link)
486 os.setxattr(src, 'trusted.foo', b'42')
Larry Hastings9cf065c2012-06-22 16:30:09 -0700487 os.setxattr(src_link, 'trusted.foo', b'43', follow_symlinks=False)
Antoine Pitrou424246f2012-05-12 19:02:01 +0200488 dst = os.path.join(tmp_dir, 'bar')
489 dst_link = os.path.join(tmp_dir, 'qux')
490 write_file(dst, 'bar')
491 os.symlink(dst, dst_link)
Larry Hastingsb4038062012-07-15 10:57:38 -0700492 shutil._copyxattr(src_link, dst_link, follow_symlinks=False)
Larry Hastings9cf065c2012-06-22 16:30:09 -0700493 self.assertEqual(os.getxattr(dst_link, 'trusted.foo', follow_symlinks=False), b'43')
Antoine Pitrou424246f2012-05-12 19:02:01 +0200494 self.assertRaises(OSError, os.getxattr, dst, 'trusted.foo')
Larry Hastingsb4038062012-07-15 10:57:38 -0700495 shutil._copyxattr(src_link, dst, follow_symlinks=False)
Antoine Pitrou424246f2012-05-12 19:02:01 +0200496 self.assertEqual(os.getxattr(dst, 'trusted.foo'), b'43')
497
Antoine Pitrou78091e62011-12-29 18:54:15 +0100498 @support.skip_unless_symlink
499 def test_copy_symlinks(self):
500 tmp_dir = self.mkdtemp()
501 src = os.path.join(tmp_dir, 'foo')
502 dst = os.path.join(tmp_dir, 'bar')
503 src_link = os.path.join(tmp_dir, 'baz')
504 write_file(src, 'foo')
505 os.symlink(src, src_link)
506 if hasattr(os, 'lchmod'):
507 os.lchmod(src_link, stat.S_IRWXU | stat.S_IRWXO)
508 # don't follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700509 shutil.copy(src_link, dst, follow_symlinks=True)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100510 self.assertFalse(os.path.islink(dst))
511 self.assertEqual(read_file(src), read_file(dst))
512 os.remove(dst)
513 # follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700514 shutil.copy(src_link, dst, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100515 self.assertTrue(os.path.islink(dst))
516 self.assertEqual(os.readlink(dst), os.readlink(src_link))
517 if hasattr(os, 'lchmod'):
518 self.assertEqual(os.lstat(src_link).st_mode,
519 os.lstat(dst).st_mode)
520
521 @support.skip_unless_symlink
522 def test_copy2_symlinks(self):
523 tmp_dir = self.mkdtemp()
524 src = os.path.join(tmp_dir, 'foo')
525 dst = os.path.join(tmp_dir, 'bar')
526 src_link = os.path.join(tmp_dir, 'baz')
527 write_file(src, 'foo')
528 os.symlink(src, src_link)
529 if hasattr(os, 'lchmod'):
530 os.lchmod(src_link, stat.S_IRWXU | stat.S_IRWXO)
531 if hasattr(os, 'lchflags') and hasattr(stat, 'UF_NODUMP'):
532 os.lchflags(src_link, stat.UF_NODUMP)
533 src_stat = os.stat(src)
534 src_link_stat = os.lstat(src_link)
535 # follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700536 shutil.copy2(src_link, dst, follow_symlinks=True)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100537 self.assertFalse(os.path.islink(dst))
538 self.assertEqual(read_file(src), read_file(dst))
539 os.remove(dst)
540 # don't follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700541 shutil.copy2(src_link, dst, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100542 self.assertTrue(os.path.islink(dst))
543 self.assertEqual(os.readlink(dst), os.readlink(src_link))
544 dst_stat = os.lstat(dst)
Larry Hastings9cf065c2012-06-22 16:30:09 -0700545 if os.utime in os.supports_follow_symlinks:
Antoine Pitrou78091e62011-12-29 18:54:15 +0100546 for attr in 'st_atime', 'st_mtime':
547 # The modification times may be truncated in the new file.
548 self.assertLessEqual(getattr(src_link_stat, attr),
549 getattr(dst_stat, attr) + 1)
550 if hasattr(os, 'lchmod'):
551 self.assertEqual(src_link_stat.st_mode, dst_stat.st_mode)
552 self.assertNotEqual(src_stat.st_mode, dst_stat.st_mode)
553 if hasattr(os, 'lchflags') and hasattr(src_link_stat, 'st_flags'):
554 self.assertEqual(src_link_stat.st_flags, dst_stat.st_flags)
555
Antoine Pitrou424246f2012-05-12 19:02:01 +0200556 @support.skip_unless_xattr
557 def test_copy2_xattr(self):
558 tmp_dir = self.mkdtemp()
559 src = os.path.join(tmp_dir, 'foo')
560 dst = os.path.join(tmp_dir, 'bar')
561 write_file(src, 'foo')
562 os.setxattr(src, 'user.foo', b'42')
563 shutil.copy2(src, dst)
564 self.assertEqual(
565 os.getxattr(src, 'user.foo'),
566 os.getxattr(dst, 'user.foo'))
567 os.remove(dst)
568
Antoine Pitrou78091e62011-12-29 18:54:15 +0100569 @support.skip_unless_symlink
570 def test_copyfile_symlinks(self):
571 tmp_dir = self.mkdtemp()
572 src = os.path.join(tmp_dir, 'src')
573 dst = os.path.join(tmp_dir, 'dst')
574 dst_link = os.path.join(tmp_dir, 'dst_link')
575 link = os.path.join(tmp_dir, 'link')
576 write_file(src, 'foo')
577 os.symlink(src, link)
578 # don't follow
Larry Hastingsb4038062012-07-15 10:57:38 -0700579 shutil.copyfile(link, dst_link, follow_symlinks=False)
Antoine Pitrou78091e62011-12-29 18:54:15 +0100580 self.assertTrue(os.path.islink(dst_link))
581 self.assertEqual(os.readlink(link), os.readlink(dst_link))
582 # follow
583 shutil.copyfile(link, dst)
584 self.assertFalse(os.path.islink(dst))
585
Hynek Schlawack2100b422012-06-23 20:28:32 +0200586 def test_rmtree_uses_safe_fd_version_if_available(self):
Hynek Schlawackd0f6e0a2012-06-29 08:28:20 +0200587 _use_fd_functions = ({os.open, os.stat, os.unlink, os.rmdir} <=
588 os.supports_dir_fd and
589 os.listdir in os.supports_fd and
590 os.stat in os.supports_follow_symlinks)
591 if _use_fd_functions:
Hynek Schlawack2100b422012-06-23 20:28:32 +0200592 self.assertTrue(shutil._use_fd_functions)
Nick Coghlan5b0eca12012-06-24 16:43:06 +1000593 self.assertTrue(shutil.rmtree.avoids_symlink_attacks)
Hynek Schlawack2100b422012-06-23 20:28:32 +0200594 tmp_dir = self.mkdtemp()
595 d = os.path.join(tmp_dir, 'a')
596 os.mkdir(d)
597 try:
598 real_rmtree = shutil._rmtree_safe_fd
599 class Called(Exception): pass
600 def _raiser(*args, **kwargs):
601 raise Called
602 shutil._rmtree_safe_fd = _raiser
603 self.assertRaises(Called, shutil.rmtree, d)
604 finally:
605 shutil._rmtree_safe_fd = real_rmtree
606 else:
607 self.assertFalse(shutil._use_fd_functions)
Nick Coghlan5b0eca12012-06-24 16:43:06 +1000608 self.assertFalse(shutil.rmtree.avoids_symlink_attacks)
Hynek Schlawack2100b422012-06-23 20:28:32 +0200609
Johannes Gijsbersd60e92a2004-09-11 21:26:21 +0000610 def test_rmtree_dont_delete_file(self):
611 # When called on a file instead of a directory, don't delete it.
612 handle, path = tempfile.mkstemp()
Victor Stinnerbf816222011-06-30 23:25:47 +0200613 os.close(handle)
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200614 self.assertRaises(NotADirectoryError, shutil.rmtree, path)
Johannes Gijsbersd60e92a2004-09-11 21:26:21 +0000615 os.remove(path)
616
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000617 def test_copytree_simple(self):
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000618 src_dir = tempfile.mkdtemp()
619 dst_dir = os.path.join(tempfile.mkdtemp(), 'destination')
Éric Araujoa7e33a12011-08-12 19:51:35 +0200620 self.addCleanup(shutil.rmtree, src_dir)
621 self.addCleanup(shutil.rmtree, os.path.dirname(dst_dir))
622 write_file((src_dir, 'test.txt'), '123')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000623 os.mkdir(os.path.join(src_dir, 'test_dir'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200624 write_file((src_dir, 'test_dir', 'test.txt'), '456')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000625
Éric Araujoa7e33a12011-08-12 19:51:35 +0200626 shutil.copytree(src_dir, dst_dir)
627 self.assertTrue(os.path.isfile(os.path.join(dst_dir, 'test.txt')))
628 self.assertTrue(os.path.isdir(os.path.join(dst_dir, 'test_dir')))
629 self.assertTrue(os.path.isfile(os.path.join(dst_dir, 'test_dir',
630 'test.txt')))
631 actual = read_file((dst_dir, 'test.txt'))
632 self.assertEqual(actual, '123')
633 actual = read_file((dst_dir, 'test_dir', 'test.txt'))
634 self.assertEqual(actual, '456')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000635
Antoine Pitrou78091e62011-12-29 18:54:15 +0100636 @support.skip_unless_symlink
637 def test_copytree_symlinks(self):
638 tmp_dir = self.mkdtemp()
639 src_dir = os.path.join(tmp_dir, 'src')
640 dst_dir = os.path.join(tmp_dir, 'dst')
641 sub_dir = os.path.join(src_dir, 'sub')
642 os.mkdir(src_dir)
643 os.mkdir(sub_dir)
644 write_file((src_dir, 'file.txt'), 'foo')
645 src_link = os.path.join(sub_dir, 'link')
646 dst_link = os.path.join(dst_dir, 'sub/link')
647 os.symlink(os.path.join(src_dir, 'file.txt'),
648 src_link)
649 if hasattr(os, 'lchmod'):
650 os.lchmod(src_link, stat.S_IRWXU | stat.S_IRWXO)
651 if hasattr(os, 'lchflags') and hasattr(stat, 'UF_NODUMP'):
652 os.lchflags(src_link, stat.UF_NODUMP)
653 src_stat = os.lstat(src_link)
654 shutil.copytree(src_dir, dst_dir, symlinks=True)
655 self.assertTrue(os.path.islink(os.path.join(dst_dir, 'sub', 'link')))
656 self.assertEqual(os.readlink(os.path.join(dst_dir, 'sub', 'link')),
657 os.path.join(src_dir, 'file.txt'))
658 dst_stat = os.lstat(dst_link)
659 if hasattr(os, 'lchmod'):
660 self.assertEqual(dst_stat.st_mode, src_stat.st_mode)
661 if hasattr(os, 'lchflags'):
662 self.assertEqual(dst_stat.st_flags, src_stat.st_flags)
663
Georg Brandl2ee470f2008-07-16 12:55:28 +0000664 def test_copytree_with_exclude(self):
Georg Brandl2ee470f2008-07-16 12:55:28 +0000665 # creating data
666 join = os.path.join
667 exists = os.path.exists
668 src_dir = tempfile.mkdtemp()
Georg Brandl2ee470f2008-07-16 12:55:28 +0000669 try:
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000670 dst_dir = join(tempfile.mkdtemp(), 'destination')
Éric Araujoa7e33a12011-08-12 19:51:35 +0200671 write_file((src_dir, 'test.txt'), '123')
672 write_file((src_dir, 'test.tmp'), '123')
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000673 os.mkdir(join(src_dir, 'test_dir'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200674 write_file((src_dir, 'test_dir', 'test.txt'), '456')
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000675 os.mkdir(join(src_dir, 'test_dir2'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200676 write_file((src_dir, 'test_dir2', 'test.txt'), '456')
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000677 os.mkdir(join(src_dir, 'test_dir2', 'subdir'))
678 os.mkdir(join(src_dir, 'test_dir2', 'subdir2'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200679 write_file((src_dir, 'test_dir2', 'subdir', 'test.txt'), '456')
680 write_file((src_dir, 'test_dir2', 'subdir2', 'test.py'), '456')
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000681
682 # testing glob-like patterns
683 try:
684 patterns = shutil.ignore_patterns('*.tmp', 'test_dir2')
685 shutil.copytree(src_dir, dst_dir, ignore=patterns)
686 # checking the result: some elements should not be copied
687 self.assertTrue(exists(join(dst_dir, 'test.txt')))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200688 self.assertFalse(exists(join(dst_dir, 'test.tmp')))
689 self.assertFalse(exists(join(dst_dir, 'test_dir2')))
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000690 finally:
Éric Araujoa7e33a12011-08-12 19:51:35 +0200691 shutil.rmtree(dst_dir)
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000692 try:
693 patterns = shutil.ignore_patterns('*.tmp', 'subdir*')
694 shutil.copytree(src_dir, dst_dir, ignore=patterns)
695 # checking the result: some elements should not be copied
Éric Araujoa7e33a12011-08-12 19:51:35 +0200696 self.assertFalse(exists(join(dst_dir, 'test.tmp')))
697 self.assertFalse(exists(join(dst_dir, 'test_dir2', 'subdir2')))
698 self.assertFalse(exists(join(dst_dir, 'test_dir2', 'subdir')))
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000699 finally:
Éric Araujoa7e33a12011-08-12 19:51:35 +0200700 shutil.rmtree(dst_dir)
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000701
702 # testing callable-style
703 try:
704 def _filter(src, names):
705 res = []
706 for name in names:
707 path = os.path.join(src, name)
708
709 if (os.path.isdir(path) and
710 path.split()[-1] == 'subdir'):
711 res.append(name)
712 elif os.path.splitext(path)[-1] in ('.py'):
713 res.append(name)
714 return res
715
716 shutil.copytree(src_dir, dst_dir, ignore=_filter)
717
718 # checking the result: some elements should not be copied
Éric Araujoa7e33a12011-08-12 19:51:35 +0200719 self.assertFalse(exists(join(dst_dir, 'test_dir2', 'subdir2',
720 'test.py')))
721 self.assertFalse(exists(join(dst_dir, 'test_dir2', 'subdir')))
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000722
723 finally:
Éric Araujoa7e33a12011-08-12 19:51:35 +0200724 shutil.rmtree(dst_dir)
Georg Brandl2ee470f2008-07-16 12:55:28 +0000725 finally:
Antoine Pitrou97c81ef2009-11-04 00:57:15 +0000726 shutil.rmtree(src_dir)
727 shutil.rmtree(os.path.dirname(dst_dir))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000728
Antoine Pitrouac601602013-08-16 19:35:02 +0200729 def test_copytree_retains_permissions(self):
730 tmp_dir = tempfile.mkdtemp()
731 src_dir = os.path.join(tmp_dir, 'source')
732 os.mkdir(src_dir)
733 dst_dir = os.path.join(tmp_dir, 'destination')
734 self.addCleanup(shutil.rmtree, tmp_dir)
735
736 os.chmod(src_dir, 0o777)
737 write_file((src_dir, 'permissive.txt'), '123')
738 os.chmod(os.path.join(src_dir, 'permissive.txt'), 0o777)
739 write_file((src_dir, 'restrictive.txt'), '456')
740 os.chmod(os.path.join(src_dir, 'restrictive.txt'), 0o600)
741 restrictive_subdir = tempfile.mkdtemp(dir=src_dir)
742 os.chmod(restrictive_subdir, 0o600)
743
744 shutil.copytree(src_dir, dst_dir)
745 self.assertEquals(os.stat(src_dir).st_mode, os.stat(dst_dir).st_mode)
746 self.assertEquals(os.stat(os.path.join(src_dir, 'permissive.txt')).st_mode,
747 os.stat(os.path.join(dst_dir, 'permissive.txt')).st_mode)
748 self.assertEquals(os.stat(os.path.join(src_dir, 'restrictive.txt')).st_mode,
749 os.stat(os.path.join(dst_dir, 'restrictive.txt')).st_mode)
750 restrictive_subdir_dst = os.path.join(dst_dir,
751 os.path.split(restrictive_subdir)[1])
752 self.assertEquals(os.stat(restrictive_subdir).st_mode,
753 os.stat(restrictive_subdir_dst).st_mode)
754
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000755 @unittest.skipUnless(hasattr(os, 'link'), 'requires os.link')
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000756 def test_dont_copy_file_onto_link_to_itself(self):
Georg Brandl724d0892010-12-05 07:51:39 +0000757 # Temporarily disable test on Windows.
758 if os.name == 'nt':
759 return
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000760 # bug 851123.
761 os.mkdir(TESTFN)
762 src = os.path.join(TESTFN, 'cheese')
763 dst = os.path.join(TESTFN, 'shop')
764 try:
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000765 with open(src, 'w') as f:
766 f.write('cheddar')
767 os.link(src, dst)
Hynek Schlawack26fe37d2012-07-19 21:41:02 +0200768 self.assertRaises(shutil.Error, shutil.copyfile, src, dst)
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000769 with open(src, 'r') as f:
770 self.assertEqual(f.read(), 'cheddar')
771 os.remove(dst)
772 finally:
773 shutil.rmtree(TESTFN, ignore_errors=True)
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000774
Brian Curtin3b4499c2010-12-28 14:31:47 +0000775 @support.skip_unless_symlink
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000776 def test_dont_copy_file_onto_symlink_to_itself(self):
777 # bug 851123.
778 os.mkdir(TESTFN)
779 src = os.path.join(TESTFN, 'cheese')
780 dst = os.path.join(TESTFN, 'shop')
781 try:
782 with open(src, 'w') as f:
783 f.write('cheddar')
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000784 # Using `src` here would mean we end up with a symlink pointing
785 # to TESTFN/TESTFN/cheese, while it should point at
786 # TESTFN/cheese.
787 os.symlink('cheese', dst)
Hynek Schlawack26fe37d2012-07-19 21:41:02 +0200788 self.assertRaises(shutil.Error, shutil.copyfile, src, dst)
Antoine Pitrou92f60ed2010-10-14 22:11:44 +0000789 with open(src, 'r') as f:
790 self.assertEqual(f.read(), 'cheddar')
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000791 os.remove(dst)
792 finally:
Hirokazu Yamamoto26681452010-12-05 02:04:16 +0000793 shutil.rmtree(TESTFN, ignore_errors=True)
Johannes Gijsbers68128712004-08-14 13:57:08 +0000794
Brian Curtin3b4499c2010-12-28 14:31:47 +0000795 @support.skip_unless_symlink
Brian Curtind40e6f72010-07-08 21:39:08 +0000796 def test_rmtree_on_symlink(self):
797 # bug 1669.
798 os.mkdir(TESTFN)
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000799 try:
Brian Curtind40e6f72010-07-08 21:39:08 +0000800 src = os.path.join(TESTFN, 'cheese')
801 dst = os.path.join(TESTFN, 'shop')
802 os.mkdir(src)
803 os.symlink(src, dst)
804 self.assertRaises(OSError, shutil.rmtree, dst)
Hynek Schlawack67be92b2012-06-23 17:58:42 +0200805 shutil.rmtree(dst, ignore_errors=True)
Tarek Ziadé51a6f722010-04-23 13:03:09 +0000806 finally:
Brian Curtind40e6f72010-07-08 21:39:08 +0000807 shutil.rmtree(TESTFN, ignore_errors=True)
808
Serhiy Storchaka79080682013-11-03 21:31:18 +0200809 # Issue #3002: copyfile and copytree block indefinitely on named pipes
810 @unittest.skipUnless(hasattr(os, "mkfifo"), 'requires os.mkfifo()')
811 def test_copyfile_named_pipe(self):
812 os.mkfifo(TESTFN)
813 try:
814 self.assertRaises(shutil.SpecialFileError,
815 shutil.copyfile, TESTFN, TESTFN2)
816 self.assertRaises(shutil.SpecialFileError,
817 shutil.copyfile, __file__, TESTFN)
818 finally:
819 os.remove(TESTFN)
Antoine Pitrou7fff0962009-05-01 21:09:44 +0000820
Serhiy Storchaka79080682013-11-03 21:31:18 +0200821 @unittest.skipUnless(hasattr(os, "mkfifo"), 'requires os.mkfifo()')
822 @support.skip_unless_symlink
823 def test_copytree_named_pipe(self):
824 os.mkdir(TESTFN)
825 try:
826 subdir = os.path.join(TESTFN, "subdir")
827 os.mkdir(subdir)
828 pipe = os.path.join(subdir, "mypipe")
829 os.mkfifo(pipe)
Antoine Pitrou7fff0962009-05-01 21:09:44 +0000830 try:
Serhiy Storchaka79080682013-11-03 21:31:18 +0200831 shutil.copytree(TESTFN, TESTFN2)
832 except shutil.Error as e:
833 errors = e.args[0]
834 self.assertEqual(len(errors), 1)
835 src, dst, error_msg = errors[0]
836 self.assertEqual("`%s` is a named pipe" % pipe, error_msg)
837 else:
838 self.fail("shutil.Error should have been raised")
839 finally:
840 shutil.rmtree(TESTFN, ignore_errors=True)
841 shutil.rmtree(TESTFN2, ignore_errors=True)
Antoine Pitrou7fff0962009-05-01 21:09:44 +0000842
Tarek Ziadé5340db32010-04-19 22:30:51 +0000843 def test_copytree_special_func(self):
844
845 src_dir = self.mkdtemp()
846 dst_dir = os.path.join(self.mkdtemp(), 'destination')
Éric Araujoa7e33a12011-08-12 19:51:35 +0200847 write_file((src_dir, 'test.txt'), '123')
Tarek Ziadé5340db32010-04-19 22:30:51 +0000848 os.mkdir(os.path.join(src_dir, 'test_dir'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200849 write_file((src_dir, 'test_dir', 'test.txt'), '456')
Tarek Ziadé5340db32010-04-19 22:30:51 +0000850
851 copied = []
852 def _copy(src, dst):
853 copied.append((src, dst))
854
855 shutil.copytree(src_dir, dst_dir, copy_function=_copy)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000856 self.assertEqual(len(copied), 2)
Tarek Ziadé5340db32010-04-19 22:30:51 +0000857
Brian Curtin3b4499c2010-12-28 14:31:47 +0000858 @support.skip_unless_symlink
Tarek Ziadéfb437512010-04-20 08:57:33 +0000859 def test_copytree_dangling_symlinks(self):
860
861 # a dangling symlink raises an error at the end
862 src_dir = self.mkdtemp()
863 dst_dir = os.path.join(self.mkdtemp(), 'destination')
864 os.symlink('IDONTEXIST', os.path.join(src_dir, 'test.txt'))
865 os.mkdir(os.path.join(src_dir, 'test_dir'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200866 write_file((src_dir, 'test_dir', 'test.txt'), '456')
Tarek Ziadéfb437512010-04-20 08:57:33 +0000867 self.assertRaises(Error, shutil.copytree, src_dir, dst_dir)
868
869 # a dangling symlink is ignored with the proper flag
870 dst_dir = os.path.join(self.mkdtemp(), 'destination2')
871 shutil.copytree(src_dir, dst_dir, ignore_dangling_symlinks=True)
872 self.assertNotIn('test.txt', os.listdir(dst_dir))
873
874 # a dangling symlink is copied if symlinks=True
875 dst_dir = os.path.join(self.mkdtemp(), 'destination3')
876 shutil.copytree(src_dir, dst_dir, symlinks=True)
877 self.assertIn('test.txt', os.listdir(dst_dir))
878
Nick Coghlan8ed3cf32011-03-16 14:05:35 -0400879 def _copy_file(self, method):
880 fname = 'test.txt'
881 tmpdir = self.mkdtemp()
Éric Araujoa7e33a12011-08-12 19:51:35 +0200882 write_file((tmpdir, fname), 'xxx')
Nick Coghlan8ed3cf32011-03-16 14:05:35 -0400883 file1 = os.path.join(tmpdir, fname)
884 tmpdir2 = self.mkdtemp()
885 method(file1, tmpdir2)
886 file2 = os.path.join(tmpdir2, fname)
887 return (file1, file2)
888
889 @unittest.skipUnless(hasattr(os, 'chmod'), 'requires os.chmod')
890 def test_copy(self):
891 # Ensure that the copied file exists and has the same mode bits.
892 file1, file2 = self._copy_file(shutil.copy)
893 self.assertTrue(os.path.exists(file2))
894 self.assertEqual(os.stat(file1).st_mode, os.stat(file2).st_mode)
895
896 @unittest.skipUnless(hasattr(os, 'chmod'), 'requires os.chmod')
Senthil Kumaran0c2dba52011-07-03 18:21:38 -0700897 @unittest.skipUnless(hasattr(os, 'utime'), 'requires os.utime')
Nick Coghlan8ed3cf32011-03-16 14:05:35 -0400898 def test_copy2(self):
899 # Ensure that the copied file exists and has the same mode and
900 # modification time bits.
901 file1, file2 = self._copy_file(shutil.copy2)
902 self.assertTrue(os.path.exists(file2))
903 file1_stat = os.stat(file1)
904 file2_stat = os.stat(file2)
905 self.assertEqual(file1_stat.st_mode, file2_stat.st_mode)
906 for attr in 'st_atime', 'st_mtime':
907 # The modification times may be truncated in the new file.
908 self.assertLessEqual(getattr(file1_stat, attr),
909 getattr(file2_stat, attr) + 1)
910 if hasattr(os, 'chflags') and hasattr(file1_stat, 'st_flags'):
911 self.assertEqual(getattr(file1_stat, 'st_flags'),
912 getattr(file2_stat, 'st_flags'))
913
Ezio Melotti975077a2011-05-19 22:03:22 +0300914 @requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +0000915 def test_make_tarball(self):
916 # creating something to tar
917 tmpdir = self.mkdtemp()
Éric Araujoa7e33a12011-08-12 19:51:35 +0200918 write_file((tmpdir, 'file1'), 'xxx')
919 write_file((tmpdir, 'file2'), 'xxx')
Tarek Ziadé396fad72010-02-23 05:30:31 +0000920 os.mkdir(os.path.join(tmpdir, 'sub'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200921 write_file((tmpdir, 'sub', 'file3'), 'xxx')
Tarek Ziadé396fad72010-02-23 05:30:31 +0000922
923 tmpdir2 = self.mkdtemp()
Nick Coghlan8ed3cf32011-03-16 14:05:35 -0400924 # force shutil to create the directory
925 os.rmdir(tmpdir2)
Tarek Ziadé396fad72010-02-23 05:30:31 +0000926 unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0],
927 "source and target should be on same drive")
928
929 base_name = os.path.join(tmpdir2, 'archive')
930
931 # working with relative paths to avoid tar warnings
932 old_dir = os.getcwd()
933 os.chdir(tmpdir)
934 try:
935 _make_tarball(splitdrive(base_name)[1], '.')
936 finally:
937 os.chdir(old_dir)
938
939 # check if the compressed tarball was created
940 tarball = base_name + '.tar.gz'
941 self.assertTrue(os.path.exists(tarball))
942
943 # trying an uncompressed one
944 base_name = os.path.join(tmpdir2, 'archive')
945 old_dir = os.getcwd()
946 os.chdir(tmpdir)
947 try:
948 _make_tarball(splitdrive(base_name)[1], '.', compress=None)
949 finally:
950 os.chdir(old_dir)
951 tarball = base_name + '.tar'
952 self.assertTrue(os.path.exists(tarball))
953
954 def _tarinfo(self, path):
955 tar = tarfile.open(path)
956 try:
957 names = tar.getnames()
958 names.sort()
959 return tuple(names)
960 finally:
961 tar.close()
962
963 def _create_files(self):
964 # creating something to tar
965 tmpdir = self.mkdtemp()
966 dist = os.path.join(tmpdir, 'dist')
967 os.mkdir(dist)
Éric Araujoa7e33a12011-08-12 19:51:35 +0200968 write_file((dist, 'file1'), 'xxx')
969 write_file((dist, 'file2'), 'xxx')
Tarek Ziadé396fad72010-02-23 05:30:31 +0000970 os.mkdir(os.path.join(dist, 'sub'))
Éric Araujoa7e33a12011-08-12 19:51:35 +0200971 write_file((dist, 'sub', 'file3'), 'xxx')
Tarek Ziadé396fad72010-02-23 05:30:31 +0000972 os.mkdir(os.path.join(dist, 'sub2'))
973 tmpdir2 = self.mkdtemp()
974 base_name = os.path.join(tmpdir2, 'archive')
975 return tmpdir, tmpdir2, base_name
976
Ezio Melotti975077a2011-05-19 22:03:22 +0300977 @requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +0000978 @unittest.skipUnless(find_executable('tar') and find_executable('gzip'),
979 'Need the tar command to run')
980 def test_tarfile_vs_tar(self):
981 tmpdir, tmpdir2, base_name = self._create_files()
982 old_dir = os.getcwd()
983 os.chdir(tmpdir)
984 try:
985 _make_tarball(base_name, 'dist')
986 finally:
987 os.chdir(old_dir)
988
989 # check if the compressed tarball was created
990 tarball = base_name + '.tar.gz'
991 self.assertTrue(os.path.exists(tarball))
992
993 # now create another tarball using `tar`
994 tarball2 = os.path.join(tmpdir, 'archive2.tar.gz')
995 tar_cmd = ['tar', '-cf', 'archive2.tar', 'dist']
996 gzip_cmd = ['gzip', '-f9', 'archive2.tar']
997 old_dir = os.getcwd()
998 os.chdir(tmpdir)
999 try:
1000 with captured_stdout() as s:
1001 spawn(tar_cmd)
1002 spawn(gzip_cmd)
1003 finally:
1004 os.chdir(old_dir)
1005
1006 self.assertTrue(os.path.exists(tarball2))
1007 # let's compare both tarballs
Ezio Melottib3aedd42010-11-20 19:04:17 +00001008 self.assertEqual(self._tarinfo(tarball), self._tarinfo(tarball2))
Tarek Ziadé396fad72010-02-23 05:30:31 +00001009
1010 # trying an uncompressed one
1011 base_name = os.path.join(tmpdir2, 'archive')
1012 old_dir = os.getcwd()
1013 os.chdir(tmpdir)
1014 try:
1015 _make_tarball(base_name, 'dist', compress=None)
1016 finally:
1017 os.chdir(old_dir)
1018 tarball = base_name + '.tar'
1019 self.assertTrue(os.path.exists(tarball))
1020
1021 # now for a dry_run
1022 base_name = os.path.join(tmpdir2, 'archive')
1023 old_dir = os.getcwd()
1024 os.chdir(tmpdir)
1025 try:
1026 _make_tarball(base_name, 'dist', compress=None, dry_run=True)
1027 finally:
1028 os.chdir(old_dir)
1029 tarball = base_name + '.tar'
1030 self.assertTrue(os.path.exists(tarball))
1031
Ezio Melotti975077a2011-05-19 22:03:22 +03001032 @requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +00001033 @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run')
1034 def test_make_zipfile(self):
1035 # creating something to tar
1036 tmpdir = self.mkdtemp()
Éric Araujoa7e33a12011-08-12 19:51:35 +02001037 write_file((tmpdir, 'file1'), 'xxx')
1038 write_file((tmpdir, 'file2'), 'xxx')
Tarek Ziadé396fad72010-02-23 05:30:31 +00001039
1040 tmpdir2 = self.mkdtemp()
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001041 # force shutil to create the directory
1042 os.rmdir(tmpdir2)
Tarek Ziadé396fad72010-02-23 05:30:31 +00001043 base_name = os.path.join(tmpdir2, 'archive')
1044 _make_zipfile(base_name, tmpdir)
1045
1046 # check if the compressed tarball was created
1047 tarball = base_name + '.zip'
Éric Araujo1c505492010-11-06 02:12:51 +00001048 self.assertTrue(os.path.exists(tarball))
Tarek Ziadé396fad72010-02-23 05:30:31 +00001049
1050
1051 def test_make_archive(self):
1052 tmpdir = self.mkdtemp()
1053 base_name = os.path.join(tmpdir, 'archive')
1054 self.assertRaises(ValueError, make_archive, base_name, 'xxx')
1055
Ezio Melotti975077a2011-05-19 22:03:22 +03001056 @requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +00001057 def test_make_archive_owner_group(self):
1058 # testing make_archive with owner and group, with various combinations
1059 # this works even if there's not gid/uid support
1060 if UID_GID_SUPPORT:
1061 group = grp.getgrgid(0)[0]
1062 owner = pwd.getpwuid(0)[0]
1063 else:
1064 group = owner = 'root'
1065
1066 base_dir, root_dir, base_name = self._create_files()
1067 base_name = os.path.join(self.mkdtemp() , 'archive')
1068 res = make_archive(base_name, 'zip', root_dir, base_dir, owner=owner,
1069 group=group)
1070 self.assertTrue(os.path.exists(res))
1071
1072 res = make_archive(base_name, 'zip', root_dir, base_dir)
1073 self.assertTrue(os.path.exists(res))
1074
1075 res = make_archive(base_name, 'tar', root_dir, base_dir,
1076 owner=owner, group=group)
1077 self.assertTrue(os.path.exists(res))
1078
1079 res = make_archive(base_name, 'tar', root_dir, base_dir,
1080 owner='kjhkjhkjg', group='oihohoh')
1081 self.assertTrue(os.path.exists(res))
1082
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001083
Ezio Melotti975077a2011-05-19 22:03:22 +03001084 @requires_zlib
Tarek Ziadé396fad72010-02-23 05:30:31 +00001085 @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support")
1086 def test_tarfile_root_owner(self):
1087 tmpdir, tmpdir2, base_name = self._create_files()
1088 old_dir = os.getcwd()
1089 os.chdir(tmpdir)
1090 group = grp.getgrgid(0)[0]
1091 owner = pwd.getpwuid(0)[0]
1092 try:
1093 archive_name = _make_tarball(base_name, 'dist', compress=None,
1094 owner=owner, group=group)
1095 finally:
1096 os.chdir(old_dir)
1097
1098 # check if the compressed tarball was created
1099 self.assertTrue(os.path.exists(archive_name))
1100
1101 # now checks the rights
1102 archive = tarfile.open(archive_name)
1103 try:
1104 for member in archive.getmembers():
Ezio Melottib3aedd42010-11-20 19:04:17 +00001105 self.assertEqual(member.uid, 0)
1106 self.assertEqual(member.gid, 0)
Tarek Ziadé396fad72010-02-23 05:30:31 +00001107 finally:
1108 archive.close()
1109
1110 def test_make_archive_cwd(self):
1111 current_dir = os.getcwd()
1112 def _breaks(*args, **kw):
1113 raise RuntimeError()
1114
1115 register_archive_format('xxx', _breaks, [], 'xxx file')
1116 try:
1117 try:
1118 make_archive('xxx', 'xxx', root_dir=self.mkdtemp())
1119 except Exception:
1120 pass
Ezio Melottib3aedd42010-11-20 19:04:17 +00001121 self.assertEqual(os.getcwd(), current_dir)
Tarek Ziadé396fad72010-02-23 05:30:31 +00001122 finally:
1123 unregister_archive_format('xxx')
1124
1125 def test_register_archive_format(self):
1126
1127 self.assertRaises(TypeError, register_archive_format, 'xxx', 1)
1128 self.assertRaises(TypeError, register_archive_format, 'xxx', lambda: x,
1129 1)
1130 self.assertRaises(TypeError, register_archive_format, 'xxx', lambda: x,
1131 [(1, 2), (1, 2, 3)])
1132
1133 register_archive_format('xxx', lambda: x, [(1, 2)], 'xxx file')
1134 formats = [name for name, params in get_archive_formats()]
1135 self.assertIn('xxx', formats)
1136
1137 unregister_archive_format('xxx')
1138 formats = [name for name, params in get_archive_formats()]
1139 self.assertNotIn('xxx', formats)
1140
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001141 def _compare_dirs(self, dir1, dir2):
1142 # check that dir1 and dir2 are equivalent,
1143 # return the diff
1144 diff = []
1145 for root, dirs, files in os.walk(dir1):
1146 for file_ in files:
1147 path = os.path.join(root, file_)
1148 target_path = os.path.join(dir2, os.path.split(path)[-1])
1149 if not os.path.exists(target_path):
1150 diff.append(file_)
1151 return diff
1152
Ezio Melotti975077a2011-05-19 22:03:22 +03001153 @requires_zlib
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001154 def test_unpack_archive(self):
Tarek Ziadéffa155a2010-04-29 13:34:35 +00001155 formats = ['tar', 'gztar', 'zip']
1156 if BZ2_SUPPORTED:
1157 formats.append('bztar')
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001158
Tarek Ziadéffa155a2010-04-29 13:34:35 +00001159 for format in formats:
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001160 tmpdir = self.mkdtemp()
1161 base_dir, root_dir, base_name = self._create_files()
1162 tmpdir2 = self.mkdtemp()
1163 filename = make_archive(base_name, format, root_dir, base_dir)
1164
1165 # let's try to unpack it now
1166 unpack_archive(filename, tmpdir2)
1167 diff = self._compare_dirs(tmpdir, tmpdir2)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001168 self.assertEqual(diff, [])
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001169
Nick Coghlanabf202d2011-03-16 13:52:20 -04001170 # and again, this time with the format specified
1171 tmpdir3 = self.mkdtemp()
1172 unpack_archive(filename, tmpdir3, format=format)
1173 diff = self._compare_dirs(tmpdir, tmpdir3)
1174 self.assertEqual(diff, [])
1175 self.assertRaises(shutil.ReadError, unpack_archive, TESTFN)
1176 self.assertRaises(ValueError, unpack_archive, TESTFN, format='xxx')
1177
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001178 def test_unpack_registery(self):
1179
1180 formats = get_unpack_formats()
1181
1182 def _boo(filename, extract_dir, extra):
Ezio Melottib3aedd42010-11-20 19:04:17 +00001183 self.assertEqual(extra, 1)
1184 self.assertEqual(filename, 'stuff.boo')
1185 self.assertEqual(extract_dir, 'xx')
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001186
1187 register_unpack_format('Boo', ['.boo', '.b2'], _boo, [('extra', 1)])
1188 unpack_archive('stuff.boo', 'xx')
1189
1190 # trying to register a .boo unpacker again
1191 self.assertRaises(RegistryError, register_unpack_format, 'Boo2',
1192 ['.boo'], _boo)
1193
1194 # should work now
1195 unregister_unpack_format('Boo')
1196 register_unpack_format('Boo2', ['.boo'], _boo)
1197 self.assertIn(('Boo2', ['.boo'], ''), get_unpack_formats())
1198 self.assertNotIn(('Boo', ['.boo'], ''), get_unpack_formats())
1199
1200 # let's leave a clean state
1201 unregister_unpack_format('Boo2')
Ezio Melottib3aedd42010-11-20 19:04:17 +00001202 self.assertEqual(get_unpack_formats(), formats)
Tarek Ziadé6ac91722010-04-28 17:51:36 +00001203
Giampaolo Rodola'210e7ca2011-07-01 13:55:36 +02001204 @unittest.skipUnless(hasattr(shutil, 'disk_usage'),
1205 "disk_usage not available on this platform")
1206 def test_disk_usage(self):
1207 usage = shutil.disk_usage(os.getcwd())
Éric Araujo2ee61882011-07-02 16:45:45 +02001208 self.assertGreater(usage.total, 0)
1209 self.assertGreater(usage.used, 0)
1210 self.assertGreaterEqual(usage.free, 0)
1211 self.assertGreaterEqual(usage.total, usage.used)
1212 self.assertGreater(usage.total, usage.free)
Giampaolo Rodola'210e7ca2011-07-01 13:55:36 +02001213
Sandro Tosid902a142011-08-22 23:28:27 +02001214 @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support")
1215 @unittest.skipUnless(hasattr(os, 'chown'), 'requires os.chown')
1216 def test_chown(self):
1217
1218 # cleaned-up automatically by TestShutil.tearDown method
1219 dirname = self.mkdtemp()
1220 filename = tempfile.mktemp(dir=dirname)
1221 write_file(filename, 'testing chown function')
1222
1223 with self.assertRaises(ValueError):
1224 shutil.chown(filename)
1225
1226 with self.assertRaises(LookupError):
1227 shutil.chown(filename, user='non-exising username')
1228
1229 with self.assertRaises(LookupError):
1230 shutil.chown(filename, group='non-exising groupname')
1231
1232 with self.assertRaises(TypeError):
1233 shutil.chown(filename, b'spam')
1234
1235 with self.assertRaises(TypeError):
1236 shutil.chown(filename, 3.14)
1237
1238 uid = os.getuid()
1239 gid = os.getgid()
1240
1241 def check_chown(path, uid=None, gid=None):
1242 s = os.stat(filename)
1243 if uid is not None:
1244 self.assertEqual(uid, s.st_uid)
1245 if gid is not None:
1246 self.assertEqual(gid, s.st_gid)
1247
1248 shutil.chown(filename, uid, gid)
1249 check_chown(filename, uid, gid)
1250 shutil.chown(filename, uid)
1251 check_chown(filename, uid)
1252 shutil.chown(filename, user=uid)
1253 check_chown(filename, uid)
1254 shutil.chown(filename, group=gid)
Sandro Tosi91f948a2011-08-22 23:55:39 +02001255 check_chown(filename, gid=gid)
Sandro Tosid902a142011-08-22 23:28:27 +02001256
1257 shutil.chown(dirname, uid, gid)
1258 check_chown(dirname, uid, gid)
1259 shutil.chown(dirname, uid)
1260 check_chown(dirname, uid)
1261 shutil.chown(dirname, user=uid)
1262 check_chown(dirname, uid)
1263 shutil.chown(dirname, group=gid)
Sandro Tosi91f948a2011-08-22 23:55:39 +02001264 check_chown(dirname, gid=gid)
Sandro Tosid902a142011-08-22 23:28:27 +02001265
1266 user = pwd.getpwuid(uid)[0]
1267 group = grp.getgrgid(gid)[0]
1268 shutil.chown(filename, user, group)
1269 check_chown(filename, uid, gid)
1270 shutil.chown(dirname, user, group)
1271 check_chown(dirname, uid, gid)
1272
Brian Curtin0d0a1de2012-06-18 18:41:07 -05001273 def test_copy_return_value(self):
1274 # copy and copy2 both return their destination path.
1275 for fn in (shutil.copy, shutil.copy2):
1276 src_dir = self.mkdtemp()
1277 dst_dir = self.mkdtemp()
1278 src = os.path.join(src_dir, 'foo')
1279 write_file(src, 'foo')
1280 rv = fn(src, dst_dir)
1281 self.assertEqual(rv, os.path.join(dst_dir, 'foo'))
1282 rv = fn(src, os.path.join(dst_dir, 'bar'))
1283 self.assertEqual(rv, os.path.join(dst_dir, 'bar'))
1284
1285 def test_copyfile_return_value(self):
1286 # copytree returns its destination path.
1287 src_dir = self.mkdtemp()
1288 dst_dir = self.mkdtemp()
1289 dst_file = os.path.join(dst_dir, 'bar')
1290 src_file = os.path.join(src_dir, 'foo')
1291 write_file(src_file, 'foo')
1292 rv = shutil.copyfile(src_file, dst_file)
1293 self.assertTrue(os.path.exists(rv))
1294 self.assertEqual(read_file(src_file), read_file(dst_file))
1295
1296 def test_copytree_return_value(self):
1297 # copytree returns its destination path.
1298 src_dir = self.mkdtemp()
1299 dst_dir = src_dir + "dest"
Larry Hastings5b2f9c02012-06-25 23:50:01 -07001300 self.addCleanup(shutil.rmtree, dst_dir, True)
Brian Curtin0d0a1de2012-06-18 18:41:07 -05001301 src = os.path.join(src_dir, 'foo')
1302 write_file(src, 'foo')
1303 rv = shutil.copytree(src_dir, dst_dir)
1304 self.assertEqual(['foo'], os.listdir(rv))
1305
Christian Heimes9bd667a2008-01-20 15:14:11 +00001306
Brian Curtinc57a3452012-06-22 16:00:30 -05001307class TestWhich(unittest.TestCase):
1308
1309 def setUp(self):
Serhiy Storchaka014791f2013-01-21 15:00:27 +02001310 self.temp_dir = tempfile.mkdtemp(prefix="Tmp")
Larry Hastings5b2f9c02012-06-25 23:50:01 -07001311 self.addCleanup(shutil.rmtree, self.temp_dir, True)
Brian Curtinc57a3452012-06-22 16:00:30 -05001312 # Give the temp_file an ".exe" suffix for all.
1313 # It's needed on Windows and not harmful on other platforms.
1314 self.temp_file = tempfile.NamedTemporaryFile(dir=self.temp_dir,
Serhiy Storchaka014791f2013-01-21 15:00:27 +02001315 prefix="Tmp",
1316 suffix=".Exe")
Brian Curtinc57a3452012-06-22 16:00:30 -05001317 os.chmod(self.temp_file.name, stat.S_IXUSR)
1318 self.addCleanup(self.temp_file.close)
1319 self.dir, self.file = os.path.split(self.temp_file.name)
1320
1321 def test_basic(self):
1322 # Given an EXE in a directory, it should be returned.
1323 rv = shutil.which(self.file, path=self.dir)
1324 self.assertEqual(rv, self.temp_file.name)
1325
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001326 def test_absolute_cmd(self):
Brian Curtinc57a3452012-06-22 16:00:30 -05001327 # When given the fully qualified path to an executable that exists,
1328 # it should be returned.
1329 rv = shutil.which(self.temp_file.name, path=self.temp_dir)
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001330 self.assertEqual(rv, self.temp_file.name)
1331
1332 def test_relative_cmd(self):
1333 # When given the relative path with a directory part to an executable
1334 # that exists, it should be returned.
1335 base_dir, tail_dir = os.path.split(self.dir)
1336 relpath = os.path.join(tail_dir, self.file)
Nick Coghlan55175962013-07-28 22:11:50 +10001337 with support.change_cwd(path=base_dir):
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001338 rv = shutil.which(relpath, path=self.temp_dir)
1339 self.assertEqual(rv, relpath)
1340 # But it shouldn't be searched in PATH directories (issue #16957).
Nick Coghlan55175962013-07-28 22:11:50 +10001341 with support.change_cwd(path=self.dir):
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001342 rv = shutil.which(relpath, path=base_dir)
1343 self.assertIsNone(rv)
1344
1345 def test_cwd(self):
1346 # Issue #16957
1347 base_dir = os.path.dirname(self.dir)
Nick Coghlan55175962013-07-28 22:11:50 +10001348 with support.change_cwd(path=self.dir):
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001349 rv = shutil.which(self.file, path=base_dir)
1350 if sys.platform == "win32":
1351 # Windows: current directory implicitly on PATH
1352 self.assertEqual(rv, os.path.join(os.curdir, self.file))
1353 else:
1354 # Other platforms: shouldn't match in the current directory.
1355 self.assertIsNone(rv)
Brian Curtinc57a3452012-06-22 16:00:30 -05001356
Serhiy Storchaka12516e22013-05-28 15:50:15 +03001357 @unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0,
1358 'non-root user required')
Brian Curtinc57a3452012-06-22 16:00:30 -05001359 def test_non_matching_mode(self):
1360 # Set the file read-only and ask for writeable files.
1361 os.chmod(self.temp_file.name, stat.S_IREAD)
Serhiy Storchaka12516e22013-05-28 15:50:15 +03001362 if os.access(self.temp_file.name, os.W_OK):
1363 self.skipTest("can't set the file read-only")
Brian Curtinc57a3452012-06-22 16:00:30 -05001364 rv = shutil.which(self.file, path=self.dir, mode=os.W_OK)
1365 self.assertIsNone(rv)
1366
Serhiy Storchaka8bea2002013-01-23 10:44:21 +02001367 def test_relative_path(self):
Antoine Pitrou07c24d12012-06-22 23:33:05 +02001368 base_dir, tail_dir = os.path.split(self.dir)
Nick Coghlan55175962013-07-28 22:11:50 +10001369 with support.change_cwd(path=base_dir):
Antoine Pitrou07c24d12012-06-22 23:33:05 +02001370 rv = shutil.which(self.file, path=tail_dir)
1371 self.assertEqual(rv, os.path.join(tail_dir, self.file))
Antoine Pitrou07c24d12012-06-22 23:33:05 +02001372
Brian Curtinc57a3452012-06-22 16:00:30 -05001373 def test_nonexistent_file(self):
1374 # Return None when no matching executable file is found on the path.
1375 rv = shutil.which("foo.exe", path=self.dir)
1376 self.assertIsNone(rv)
1377
1378 @unittest.skipUnless(sys.platform == "win32",
1379 "pathext check is Windows-only")
1380 def test_pathext_checking(self):
1381 # Ask for the file without the ".exe" extension, then ensure that
1382 # it gets found properly with the extension.
Serhiy Storchakad70127a2013-01-24 20:03:49 +02001383 rv = shutil.which(self.file[:-4], path=self.dir)
Serhiy Storchaka80c88f42013-01-22 10:31:36 +02001384 self.assertEqual(rv, self.temp_file.name[:-4] + ".EXE")
Brian Curtinc57a3452012-06-22 16:00:30 -05001385
Barry Warsaw618738b2013-04-16 11:05:03 -04001386 def test_environ_path(self):
1387 with support.EnvironmentVarGuard() as env:
1388 env['PATH'] = self.dir
1389 rv = shutil.which(self.file)
1390 self.assertEqual(rv, self.temp_file.name)
1391
1392 def test_empty_path(self):
1393 base_dir = os.path.dirname(self.dir)
Nick Coghlan55175962013-07-28 22:11:50 +10001394 with support.change_cwd(path=self.dir), \
Barry Warsaw618738b2013-04-16 11:05:03 -04001395 support.EnvironmentVarGuard() as env:
1396 env['PATH'] = self.dir
1397 rv = shutil.which(self.file, path='')
1398 self.assertIsNone(rv)
1399
1400 def test_empty_path_no_PATH(self):
1401 with support.EnvironmentVarGuard() as env:
1402 env.pop('PATH', None)
1403 rv = shutil.which(self.file)
1404 self.assertIsNone(rv)
1405
Brian Curtinc57a3452012-06-22 16:00:30 -05001406
Christian Heimesada8c3b2008-03-18 18:26:33 +00001407class TestMove(unittest.TestCase):
1408
1409 def setUp(self):
1410 filename = "foo"
1411 self.src_dir = tempfile.mkdtemp()
1412 self.dst_dir = tempfile.mkdtemp()
1413 self.src_file = os.path.join(self.src_dir, filename)
1414 self.dst_file = os.path.join(self.dst_dir, filename)
Christian Heimesada8c3b2008-03-18 18:26:33 +00001415 with open(self.src_file, "wb") as f:
1416 f.write(b"spam")
1417
1418 def tearDown(self):
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001419 for d in (self.src_dir, self.dst_dir):
Christian Heimesada8c3b2008-03-18 18:26:33 +00001420 try:
1421 if d:
1422 shutil.rmtree(d)
1423 except:
1424 pass
1425
1426 def _check_move_file(self, src, dst, real_dst):
Antoine Pitrou92f60ed2010-10-14 22:11:44 +00001427 with open(src, "rb") as f:
1428 contents = f.read()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001429 shutil.move(src, dst)
Antoine Pitrou92f60ed2010-10-14 22:11:44 +00001430 with open(real_dst, "rb") as f:
1431 self.assertEqual(contents, f.read())
Christian Heimesada8c3b2008-03-18 18:26:33 +00001432 self.assertFalse(os.path.exists(src))
1433
1434 def _check_move_dir(self, src, dst, real_dst):
1435 contents = sorted(os.listdir(src))
1436 shutil.move(src, dst)
1437 self.assertEqual(contents, sorted(os.listdir(real_dst)))
1438 self.assertFalse(os.path.exists(src))
1439
1440 def test_move_file(self):
1441 # Move a file to another location on the same filesystem.
1442 self._check_move_file(self.src_file, self.dst_file, self.dst_file)
1443
1444 def test_move_file_to_dir(self):
1445 # Move a file inside an existing dir on the same filesystem.
1446 self._check_move_file(self.src_file, self.dst_dir, self.dst_file)
1447
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001448 @mock_rename
Christian Heimesada8c3b2008-03-18 18:26:33 +00001449 def test_move_file_other_fs(self):
1450 # Move a file to an existing dir on another filesystem.
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001451 self.test_move_file()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001452
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001453 @mock_rename
Christian Heimesada8c3b2008-03-18 18:26:33 +00001454 def test_move_file_to_dir_other_fs(self):
1455 # Move a file to another location on another filesystem.
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001456 self.test_move_file_to_dir()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001457
1458 def test_move_dir(self):
1459 # Move a dir to another location on the same filesystem.
1460 dst_dir = tempfile.mktemp()
1461 try:
1462 self._check_move_dir(self.src_dir, dst_dir, dst_dir)
1463 finally:
1464 try:
1465 shutil.rmtree(dst_dir)
1466 except:
1467 pass
1468
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001469 @mock_rename
Christian Heimesada8c3b2008-03-18 18:26:33 +00001470 def test_move_dir_other_fs(self):
1471 # Move a dir to another location on another filesystem.
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001472 self.test_move_dir()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001473
1474 def test_move_dir_to_dir(self):
1475 # Move a dir inside an existing dir on the same filesystem.
1476 self._check_move_dir(self.src_dir, self.dst_dir,
1477 os.path.join(self.dst_dir, os.path.basename(self.src_dir)))
1478
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001479 @mock_rename
Christian Heimesada8c3b2008-03-18 18:26:33 +00001480 def test_move_dir_to_dir_other_fs(self):
1481 # Move a dir inside an existing dir on another filesystem.
Nick Coghlan8ed3cf32011-03-16 14:05:35 -04001482 self.test_move_dir_to_dir()
Christian Heimesada8c3b2008-03-18 18:26:33 +00001483
1484 def test_existing_file_inside_dest_dir(self):
1485 # A file with the same name inside the destination dir already exists.
1486 with open(self.dst_file, "wb"):
1487 pass
1488 self.assertRaises(shutil.Error, shutil.move, self.src_file, self.dst_dir)
1489
1490 def test_dont_move_dir_in_itself(self):
1491 # Moving a dir inside itself raises an Error.
1492 dst = os.path.join(self.src_dir, "bar")
1493 self.assertRaises(shutil.Error, shutil.move, self.src_dir, dst)
1494
Antoine Pitrou0dcc3cd2009-01-29 20:26:59 +00001495 def test_destinsrc_false_negative(self):
1496 os.mkdir(TESTFN)
1497 try:
1498 for src, dst in [('srcdir', 'srcdir/dest')]:
1499 src = os.path.join(TESTFN, src)
1500 dst = os.path.join(TESTFN, dst)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001501 self.assertTrue(shutil._destinsrc(src, dst),
Benjamin Peterson247a9b82009-02-20 04:09:19 +00001502 msg='_destinsrc() wrongly concluded that '
Antoine Pitrou0dcc3cd2009-01-29 20:26:59 +00001503 'dst (%s) is not in src (%s)' % (dst, src))
1504 finally:
1505 shutil.rmtree(TESTFN, ignore_errors=True)
Christian Heimesada8c3b2008-03-18 18:26:33 +00001506
Antoine Pitrou0dcc3cd2009-01-29 20:26:59 +00001507 def test_destinsrc_false_positive(self):
1508 os.mkdir(TESTFN)
1509 try:
1510 for src, dst in [('srcdir', 'src/dest'), ('srcdir', 'srcdir.new')]:
1511 src = os.path.join(TESTFN, src)
1512 dst = os.path.join(TESTFN, dst)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001513 self.assertFalse(shutil._destinsrc(src, dst),
Benjamin Peterson247a9b82009-02-20 04:09:19 +00001514 msg='_destinsrc() wrongly concluded that '
Antoine Pitrou0dcc3cd2009-01-29 20:26:59 +00001515 'dst (%s) is in src (%s)' % (dst, src))
1516 finally:
1517 shutil.rmtree(TESTFN, ignore_errors=True)
Christian Heimes9bd667a2008-01-20 15:14:11 +00001518
Antoine Pitrou0a08d7a2012-01-06 20:16:19 +01001519 @support.skip_unless_symlink
1520 @mock_rename
1521 def test_move_file_symlink(self):
1522 dst = os.path.join(self.src_dir, 'bar')
1523 os.symlink(self.src_file, dst)
1524 shutil.move(dst, self.dst_file)
1525 self.assertTrue(os.path.islink(self.dst_file))
1526 self.assertTrue(os.path.samefile(self.src_file, self.dst_file))
1527
1528 @support.skip_unless_symlink
1529 @mock_rename
1530 def test_move_file_symlink_to_dir(self):
1531 filename = "bar"
1532 dst = os.path.join(self.src_dir, filename)
1533 os.symlink(self.src_file, dst)
1534 shutil.move(dst, self.dst_dir)
1535 final_link = os.path.join(self.dst_dir, filename)
1536 self.assertTrue(os.path.islink(final_link))
1537 self.assertTrue(os.path.samefile(self.src_file, final_link))
1538
1539 @support.skip_unless_symlink
1540 @mock_rename
1541 def test_move_dangling_symlink(self):
1542 src = os.path.join(self.src_dir, 'baz')
1543 dst = os.path.join(self.src_dir, 'bar')
1544 os.symlink(src, dst)
1545 dst_link = os.path.join(self.dst_dir, 'quux')
1546 shutil.move(dst, dst_link)
1547 self.assertTrue(os.path.islink(dst_link))
1548 self.assertEqual(os.path.realpath(src), os.path.realpath(dst_link))
1549
1550 @support.skip_unless_symlink
1551 @mock_rename
1552 def test_move_dir_symlink(self):
1553 src = os.path.join(self.src_dir, 'baz')
1554 dst = os.path.join(self.src_dir, 'bar')
1555 os.mkdir(src)
1556 os.symlink(src, dst)
1557 dst_link = os.path.join(self.dst_dir, 'quux')
1558 shutil.move(dst, dst_link)
1559 self.assertTrue(os.path.islink(dst_link))
1560 self.assertTrue(os.path.samefile(src, dst_link))
1561
Brian Curtin0d0a1de2012-06-18 18:41:07 -05001562 def test_move_return_value(self):
1563 rv = shutil.move(self.src_file, self.dst_dir)
1564 self.assertEqual(rv,
1565 os.path.join(self.dst_dir, os.path.basename(self.src_file)))
1566
1567 def test_move_as_rename_return_value(self):
1568 rv = shutil.move(self.src_file, os.path.join(self.dst_dir, 'bar'))
1569 self.assertEqual(rv, os.path.join(self.dst_dir, 'bar'))
1570
Tarek Ziadé5340db32010-04-19 22:30:51 +00001571
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00001572class TestCopyFile(unittest.TestCase):
1573
1574 _delete = False
1575
1576 class Faux(object):
1577 _entered = False
1578 _exited_with = None
1579 _raised = False
1580 def __init__(self, raise_in_exit=False, suppress_at_exit=True):
1581 self._raise_in_exit = raise_in_exit
1582 self._suppress_at_exit = suppress_at_exit
1583 def read(self, *args):
1584 return ''
1585 def __enter__(self):
1586 self._entered = True
1587 def __exit__(self, exc_type, exc_val, exc_tb):
1588 self._exited_with = exc_type, exc_val, exc_tb
1589 if self._raise_in_exit:
1590 self._raised = True
1591 raise IOError("Cannot close")
1592 return self._suppress_at_exit
1593
1594 def tearDown(self):
1595 if self._delete:
1596 del shutil.open
1597
1598 def _set_shutil_open(self, func):
1599 shutil.open = func
1600 self._delete = True
1601
1602 def test_w_source_open_fails(self):
1603 def _open(filename, mode='r'):
1604 if filename == 'srcfile':
1605 raise IOError('Cannot open "srcfile"')
1606 assert 0 # shouldn't reach here.
1607
1608 self._set_shutil_open(_open)
1609
1610 self.assertRaises(IOError, shutil.copyfile, 'srcfile', 'destfile')
1611
1612 def test_w_dest_open_fails(self):
1613
1614 srcfile = self.Faux()
1615
1616 def _open(filename, mode='r'):
1617 if filename == 'srcfile':
1618 return srcfile
1619 if filename == 'destfile':
1620 raise IOError('Cannot open "destfile"')
1621 assert 0 # shouldn't reach here.
1622
1623 self._set_shutil_open(_open)
1624
1625 shutil.copyfile('srcfile', 'destfile')
1626 self.assertTrue(srcfile._entered)
1627 self.assertTrue(srcfile._exited_with[0] is IOError)
1628 self.assertEqual(srcfile._exited_with[1].args,
1629 ('Cannot open "destfile"',))
1630
1631 def test_w_dest_close_fails(self):
1632
1633 srcfile = self.Faux()
1634 destfile = self.Faux(True)
1635
1636 def _open(filename, mode='r'):
1637 if filename == 'srcfile':
1638 return srcfile
1639 if filename == 'destfile':
1640 return destfile
1641 assert 0 # shouldn't reach here.
1642
1643 self._set_shutil_open(_open)
1644
1645 shutil.copyfile('srcfile', 'destfile')
1646 self.assertTrue(srcfile._entered)
1647 self.assertTrue(destfile._entered)
1648 self.assertTrue(destfile._raised)
1649 self.assertTrue(srcfile._exited_with[0] is IOError)
1650 self.assertEqual(srcfile._exited_with[1].args,
1651 ('Cannot close',))
1652
1653 def test_w_source_close_fails(self):
1654
1655 srcfile = self.Faux(True)
1656 destfile = self.Faux()
1657
1658 def _open(filename, mode='r'):
1659 if filename == 'srcfile':
1660 return srcfile
1661 if filename == 'destfile':
1662 return destfile
1663 assert 0 # shouldn't reach here.
1664
1665 self._set_shutil_open(_open)
1666
1667 self.assertRaises(IOError,
1668 shutil.copyfile, 'srcfile', 'destfile')
1669 self.assertTrue(srcfile._entered)
1670 self.assertTrue(destfile._entered)
1671 self.assertFalse(destfile._raised)
1672 self.assertTrue(srcfile._exited_with[0] is None)
1673 self.assertTrue(srcfile._raised)
1674
Ronald Oussorenf51738b2011-05-06 10:23:04 +02001675 def test_move_dir_caseinsensitive(self):
1676 # Renames a folder to the same name
1677 # but a different case.
1678
1679 self.src_dir = tempfile.mkdtemp()
Larry Hastings5b2f9c02012-06-25 23:50:01 -07001680 self.addCleanup(shutil.rmtree, self.src_dir, True)
Ronald Oussorenf51738b2011-05-06 10:23:04 +02001681 dst_dir = os.path.join(
1682 os.path.dirname(self.src_dir),
1683 os.path.basename(self.src_dir).upper())
1684 self.assertNotEqual(self.src_dir, dst_dir)
1685
1686 try:
1687 shutil.move(self.src_dir, dst_dir)
1688 self.assertTrue(os.path.isdir(dst_dir))
1689 finally:
Éric Araujoa7e33a12011-08-12 19:51:35 +02001690 os.rmdir(dst_dir)
Ronald Oussorenf51738b2011-05-06 10:23:04 +02001691
Antoine Pitroubcf2b592012-02-08 23:28:36 +01001692class TermsizeTests(unittest.TestCase):
1693 def test_does_not_crash(self):
1694 """Check if get_terminal_size() returns a meaningful value.
1695
1696 There's no easy portable way to actually check the size of the
1697 terminal, so let's check if it returns something sensible instead.
1698 """
1699 size = shutil.get_terminal_size()
Antoine Pitroucfade362012-02-08 23:48:59 +01001700 self.assertGreaterEqual(size.columns, 0)
1701 self.assertGreaterEqual(size.lines, 0)
Antoine Pitroubcf2b592012-02-08 23:28:36 +01001702
1703 def test_os_environ_first(self):
1704 "Check if environment variables have precedence"
1705
1706 with support.EnvironmentVarGuard() as env:
1707 env['COLUMNS'] = '777'
1708 size = shutil.get_terminal_size()
1709 self.assertEqual(size.columns, 777)
1710
1711 with support.EnvironmentVarGuard() as env:
1712 env['LINES'] = '888'
1713 size = shutil.get_terminal_size()
1714 self.assertEqual(size.lines, 888)
1715
1716 @unittest.skipUnless(os.isatty(sys.__stdout__.fileno()), "not on tty")
1717 def test_stty_match(self):
1718 """Check if stty returns the same results ignoring env
1719
1720 This test will fail if stdin and stdout are connected to
1721 different terminals with different sizes. Nevertheless, such
1722 situations should be pretty rare.
1723 """
1724 try:
1725 size = subprocess.check_output(['stty', 'size']).decode().split()
1726 except (FileNotFoundError, subprocess.CalledProcessError):
1727 self.skipTest("stty invocation failed")
1728 expected = (int(size[1]), int(size[0])) # reversed order
1729
1730 with support.EnvironmentVarGuard() as env:
1731 del env['LINES']
1732 del env['COLUMNS']
1733 actual = shutil.get_terminal_size()
1734
1735 self.assertEqual(expected, actual)
Ronald Oussorenf51738b2011-05-06 10:23:04 +02001736
Tarek Ziadéae4d5c62010-05-05 22:27:31 +00001737
Barry Warsaw7fc2cca2003-01-24 17:34:13 +00001738def test_main():
Antoine Pitroubcf2b592012-02-08 23:28:36 +01001739 support.run_unittest(TestShutil, TestMove, TestCopyFile,
Brian Curtinc57a3452012-06-22 16:00:30 -05001740 TermsizeTests, TestWhich)
Barry Warsaw7fc2cca2003-01-24 17:34:13 +00001741
Barry Warsaw7fc2cca2003-01-24 17:34:13 +00001742if __name__ == '__main__':
Walter Dörwald21d3a322003-05-01 17:45:56 +00001743 test_main()