|
1 # Copyright (C) 2003 Python Software Foundation |
|
2 |
|
3 import unittest |
|
4 import shutil |
|
5 import tempfile |
|
6 import sys |
|
7 import stat |
|
8 import os |
|
9 import os.path |
|
10 from test import test_support |
|
11 from test.test_support import TESTFN |
|
12 |
|
13 class TestShutil(unittest.TestCase): |
|
14 def test_rmtree_errors(self): |
|
15 # filename is guaranteed not to exist |
|
16 filename = tempfile.mktemp() |
|
17 self.assertRaises(OSError, shutil.rmtree, filename) |
|
18 |
|
19 # See bug #1071513 for why we don't run this on cygwin |
|
20 # and bug #1076467 for why we don't run this as root. |
|
21 if (hasattr(os, 'chmod') and sys.platform[:6] != 'cygwin' |
|
22 and not (hasattr(os, 'geteuid') and os.geteuid() == 0)): |
|
23 def test_on_error(self): |
|
24 self.errorState = 0 |
|
25 os.mkdir(TESTFN) |
|
26 self.childpath = os.path.join(TESTFN, 'a') |
|
27 f = open(self.childpath, 'w') |
|
28 f.close() |
|
29 old_dir_mode = os.stat(TESTFN).st_mode |
|
30 old_child_mode = os.stat(self.childpath).st_mode |
|
31 # Make unwritable. |
|
32 os.chmod(self.childpath, stat.S_IREAD) |
|
33 os.chmod(TESTFN, stat.S_IREAD) |
|
34 |
|
35 shutil.rmtree(TESTFN, onerror=self.check_args_to_onerror) |
|
36 # Test whether onerror has actually been called. |
|
37 self.assertEqual(self.errorState, 2, |
|
38 "Expected call to onerror function did not happen.") |
|
39 |
|
40 # Make writable again. |
|
41 os.chmod(TESTFN, old_dir_mode) |
|
42 os.chmod(self.childpath, old_child_mode) |
|
43 |
|
44 # Clean up. |
|
45 shutil.rmtree(TESTFN) |
|
46 |
|
47 def check_args_to_onerror(self, func, arg, exc): |
|
48 if self.errorState == 0: |
|
49 self.assertEqual(func, os.remove) |
|
50 self.assertEqual(arg, self.childpath) |
|
51 self.failUnless(issubclass(exc[0], OSError)) |
|
52 self.errorState = 1 |
|
53 else: |
|
54 self.assertEqual(func, os.rmdir) |
|
55 self.assertEqual(arg, TESTFN) |
|
56 self.failUnless(issubclass(exc[0], OSError)) |
|
57 self.errorState = 2 |
|
58 |
|
59 def test_rmtree_dont_delete_file(self): |
|
60 # When called on a file instead of a directory, don't delete it. |
|
61 handle, path = tempfile.mkstemp() |
|
62 os.fdopen(handle).close() |
|
63 self.assertRaises(OSError, shutil.rmtree, path) |
|
64 os.remove(path) |
|
65 |
|
66 def test_copytree_simple(self): |
|
67 def write_data(path, data): |
|
68 f = open(path, "w") |
|
69 f.write(data) |
|
70 f.close() |
|
71 |
|
72 def read_data(path): |
|
73 f = open(path) |
|
74 data = f.read() |
|
75 f.close() |
|
76 return data |
|
77 |
|
78 src_dir = tempfile.mkdtemp() |
|
79 dst_dir = os.path.join(tempfile.mkdtemp(), 'destination') |
|
80 |
|
81 write_data(os.path.join(src_dir, 'test.txt'), '123') |
|
82 |
|
83 os.mkdir(os.path.join(src_dir, 'test_dir')) |
|
84 write_data(os.path.join(src_dir, 'test_dir', 'test.txt'), '456') |
|
85 |
|
86 try: |
|
87 shutil.copytree(src_dir, dst_dir) |
|
88 self.assertTrue(os.path.isfile(os.path.join(dst_dir, 'test.txt'))) |
|
89 self.assertTrue(os.path.isdir(os.path.join(dst_dir, 'test_dir'))) |
|
90 self.assertTrue(os.path.isfile(os.path.join(dst_dir, 'test_dir', |
|
91 'test.txt'))) |
|
92 actual = read_data(os.path.join(dst_dir, 'test.txt')) |
|
93 self.assertEqual(actual, '123') |
|
94 actual = read_data(os.path.join(dst_dir, 'test_dir', 'test.txt')) |
|
95 self.assertEqual(actual, '456') |
|
96 finally: |
|
97 for path in ( |
|
98 os.path.join(src_dir, 'test.txt'), |
|
99 os.path.join(dst_dir, 'test.txt'), |
|
100 os.path.join(src_dir, 'test_dir', 'test.txt'), |
|
101 os.path.join(dst_dir, 'test_dir', 'test.txt'), |
|
102 ): |
|
103 if os.path.exists(path): |
|
104 os.remove(path) |
|
105 for path in (src_dir, |
|
106 os.path.abspath(os.path.join(dst_dir, os.path.pardir)) |
|
107 ): |
|
108 if os.path.exists(path): |
|
109 shutil.rmtree(path) |
|
110 |
|
111 def test_copytree_with_exclude(self): |
|
112 |
|
113 def write_data(path, data): |
|
114 f = open(path, "w") |
|
115 f.write(data) |
|
116 f.close() |
|
117 |
|
118 def read_data(path): |
|
119 f = open(path) |
|
120 data = f.read() |
|
121 f.close() |
|
122 return data |
|
123 |
|
124 # creating data |
|
125 join = os.path.join |
|
126 exists = os.path.exists |
|
127 src_dir = tempfile.mkdtemp() |
|
128 dst_dir = join(tempfile.mkdtemp(), 'destination') |
|
129 write_data(join(src_dir, 'test.txt'), '123') |
|
130 write_data(join(src_dir, 'test.tmp'), '123') |
|
131 os.mkdir(join(src_dir, 'test_dir')) |
|
132 write_data(join(src_dir, 'test_dir', 'test.txt'), '456') |
|
133 os.mkdir(join(src_dir, 'test_dir2')) |
|
134 write_data(join(src_dir, 'test_dir2', 'test.txt'), '456') |
|
135 os.mkdir(join(src_dir, 'test_dir2', 'subdir')) |
|
136 os.mkdir(join(src_dir, 'test_dir2', 'subdir2')) |
|
137 write_data(join(src_dir, 'test_dir2', 'subdir', 'test.txt'), '456') |
|
138 write_data(join(src_dir, 'test_dir2', 'subdir2', 'test.py'), '456') |
|
139 |
|
140 |
|
141 # testing glob-like patterns |
|
142 try: |
|
143 patterns = shutil.ignore_patterns('*.tmp', 'test_dir2') |
|
144 shutil.copytree(src_dir, dst_dir, ignore=patterns) |
|
145 # checking the result: some elements should not be copied |
|
146 self.assert_(exists(join(dst_dir, 'test.txt'))) |
|
147 self.assert_(not exists(join(dst_dir, 'test.tmp'))) |
|
148 self.assert_(not exists(join(dst_dir, 'test_dir2'))) |
|
149 finally: |
|
150 if os.path.exists(dst_dir): |
|
151 shutil.rmtree(dst_dir) |
|
152 try: |
|
153 patterns = shutil.ignore_patterns('*.tmp', 'subdir*') |
|
154 shutil.copytree(src_dir, dst_dir, ignore=patterns) |
|
155 # checking the result: some elements should not be copied |
|
156 self.assert_(not exists(join(dst_dir, 'test.tmp'))) |
|
157 self.assert_(not exists(join(dst_dir, 'test_dir2', 'subdir2'))) |
|
158 self.assert_(not exists(join(dst_dir, 'test_dir2', 'subdir'))) |
|
159 finally: |
|
160 if os.path.exists(dst_dir): |
|
161 shutil.rmtree(dst_dir) |
|
162 |
|
163 # testing callable-style |
|
164 try: |
|
165 def _filter(src, names): |
|
166 res = [] |
|
167 for name in names: |
|
168 path = os.path.join(src, name) |
|
169 |
|
170 if (os.path.isdir(path) and |
|
171 path.split()[-1] == 'subdir'): |
|
172 res.append(name) |
|
173 elif os.path.splitext(path)[-1] in ('.py'): |
|
174 res.append(name) |
|
175 return res |
|
176 |
|
177 shutil.copytree(src_dir, dst_dir, ignore=_filter) |
|
178 |
|
179 # checking the result: some elements should not be copied |
|
180 self.assert_(not exists(join(dst_dir, 'test_dir2', 'subdir2', |
|
181 'test.py'))) |
|
182 self.assert_(not exists(join(dst_dir, 'test_dir2', 'subdir'))) |
|
183 |
|
184 finally: |
|
185 if os.path.exists(dst_dir): |
|
186 shutil.rmtree(dst_dir) |
|
187 |
|
188 if hasattr(os, "symlink"): |
|
189 def test_dont_copy_file_onto_link_to_itself(self): |
|
190 # bug 851123. |
|
191 os.mkdir(TESTFN) |
|
192 src = os.path.join(TESTFN, 'cheese') |
|
193 dst = os.path.join(TESTFN, 'shop') |
|
194 try: |
|
195 f = open(src, 'w') |
|
196 f.write('cheddar') |
|
197 f.close() |
|
198 |
|
199 os.link(src, dst) |
|
200 self.assertRaises(shutil.Error, shutil.copyfile, src, dst) |
|
201 self.assertEqual(open(src,'r').read(), 'cheddar') |
|
202 os.remove(dst) |
|
203 |
|
204 # Using `src` here would mean we end up with a symlink pointing |
|
205 # to TESTFN/TESTFN/cheese, while it should point at |
|
206 # TESTFN/cheese. |
|
207 os.symlink('cheese', dst) |
|
208 self.assertRaises(shutil.Error, shutil.copyfile, src, dst) |
|
209 self.assertEqual(open(src,'r').read(), 'cheddar') |
|
210 os.remove(dst) |
|
211 finally: |
|
212 try: |
|
213 shutil.rmtree(TESTFN) |
|
214 except OSError: |
|
215 pass |
|
216 |
|
217 def test_rmtree_on_symlink(self): |
|
218 # bug 1669. |
|
219 os.mkdir(TESTFN) |
|
220 try: |
|
221 src = os.path.join(TESTFN, 'cheese') |
|
222 dst = os.path.join(TESTFN, 'shop') |
|
223 os.mkdir(src) |
|
224 os.symlink(src, dst) |
|
225 self.assertRaises(OSError, shutil.rmtree, dst) |
|
226 finally: |
|
227 shutil.rmtree(TESTFN, ignore_errors=True) |
|
228 |
|
229 |
|
230 class TestMove(unittest.TestCase): |
|
231 |
|
232 def setUp(self): |
|
233 filename = "foo" |
|
234 self.src_dir = tempfile.mkdtemp() |
|
235 self.dst_dir = tempfile.mkdtemp() |
|
236 self.src_file = os.path.join(self.src_dir, filename) |
|
237 self.dst_file = os.path.join(self.dst_dir, filename) |
|
238 # Try to create a dir in the current directory, hoping that it is |
|
239 # not located on the same filesystem as the system tmp dir. |
|
240 try: |
|
241 self.dir_other_fs = tempfile.mkdtemp( |
|
242 dir=os.path.dirname(__file__)) |
|
243 self.file_other_fs = os.path.join(self.dir_other_fs, |
|
244 filename) |
|
245 except OSError: |
|
246 self.dir_other_fs = None |
|
247 with open(self.src_file, "wb") as f: |
|
248 f.write("spam") |
|
249 |
|
250 def tearDown(self): |
|
251 for d in (self.src_dir, self.dst_dir, self.dir_other_fs): |
|
252 try: |
|
253 if d: |
|
254 shutil.rmtree(d) |
|
255 except: |
|
256 pass |
|
257 |
|
258 def _check_move_file(self, src, dst, real_dst): |
|
259 contents = open(src, "rb").read() |
|
260 shutil.move(src, dst) |
|
261 self.assertEqual(contents, open(real_dst, "rb").read()) |
|
262 self.assertFalse(os.path.exists(src)) |
|
263 |
|
264 def _check_move_dir(self, src, dst, real_dst): |
|
265 contents = sorted(os.listdir(src)) |
|
266 shutil.move(src, dst) |
|
267 self.assertEqual(contents, sorted(os.listdir(real_dst))) |
|
268 self.assertFalse(os.path.exists(src)) |
|
269 |
|
270 def test_move_file(self): |
|
271 # Move a file to another location on the same filesystem. |
|
272 self._check_move_file(self.src_file, self.dst_file, self.dst_file) |
|
273 |
|
274 def test_move_file_to_dir(self): |
|
275 # Move a file inside an existing dir on the same filesystem. |
|
276 self._check_move_file(self.src_file, self.dst_dir, self.dst_file) |
|
277 |
|
278 def test_move_file_other_fs(self): |
|
279 # Move a file to an existing dir on another filesystem. |
|
280 if not self.dir_other_fs: |
|
281 # skip |
|
282 return |
|
283 self._check_move_file(self.src_file, self.file_other_fs, |
|
284 self.file_other_fs) |
|
285 |
|
286 def test_move_file_to_dir_other_fs(self): |
|
287 # Move a file to another location on another filesystem. |
|
288 if not self.dir_other_fs: |
|
289 # skip |
|
290 return |
|
291 self._check_move_file(self.src_file, self.dir_other_fs, |
|
292 self.file_other_fs) |
|
293 |
|
294 def test_move_dir(self): |
|
295 # Move a dir to another location on the same filesystem. |
|
296 dst_dir = tempfile.mktemp() |
|
297 try: |
|
298 self._check_move_dir(self.src_dir, dst_dir, dst_dir) |
|
299 finally: |
|
300 try: |
|
301 shutil.rmtree(dst_dir) |
|
302 except: |
|
303 pass |
|
304 |
|
305 def test_move_dir_other_fs(self): |
|
306 # Move a dir to another location on another filesystem. |
|
307 if not self.dir_other_fs: |
|
308 # skip |
|
309 return |
|
310 dst_dir = tempfile.mktemp(dir=self.dir_other_fs) |
|
311 try: |
|
312 self._check_move_dir(self.src_dir, dst_dir, dst_dir) |
|
313 finally: |
|
314 try: |
|
315 shutil.rmtree(dst_dir) |
|
316 except: |
|
317 pass |
|
318 |
|
319 def test_move_dir_to_dir(self): |
|
320 # Move a dir inside an existing dir on the same filesystem. |
|
321 self._check_move_dir(self.src_dir, self.dst_dir, |
|
322 os.path.join(self.dst_dir, os.path.basename(self.src_dir))) |
|
323 |
|
324 def test_move_dir_to_dir_other_fs(self): |
|
325 # Move a dir inside an existing dir on another filesystem. |
|
326 if not self.dir_other_fs: |
|
327 # skip |
|
328 return |
|
329 self._check_move_dir(self.src_dir, self.dir_other_fs, |
|
330 os.path.join(self.dir_other_fs, os.path.basename(self.src_dir))) |
|
331 |
|
332 def test_existing_file_inside_dest_dir(self): |
|
333 # A file with the same name inside the destination dir already exists. |
|
334 with open(self.dst_file, "wb"): |
|
335 pass |
|
336 self.assertRaises(shutil.Error, shutil.move, self.src_file, self.dst_dir) |
|
337 |
|
338 def test_dont_move_dir_in_itself(self): |
|
339 # Moving a dir inside itself raises an Error. |
|
340 dst = os.path.join(self.src_dir, "bar") |
|
341 self.assertRaises(shutil.Error, shutil.move, self.src_dir, dst) |
|
342 |
|
343 |
|
344 |
|
345 def test_main(): |
|
346 test_support.run_unittest(TestShutil, TestMove) |
|
347 |
|
348 if __name__ == '__main__': |
|
349 test_main() |