• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2009 Google Inc. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Tests for `fake_filesystem_shutil` if used in
16`fake_filesystem_unittest.TestCase`.
17Note that almost all of the functionality is delegated to the real `shutil`
18and works correctly with the fake filesystem because of the faked `os` module.
19"""
20
21import os
22import shutil
23import sys
24import tempfile
25import unittest
26from pathlib import Path
27
28from pyfakefs import fake_filesystem_unittest
29from pyfakefs.helpers import get_uid, set_uid, is_root, IS_PYPY
30from pyfakefs.tests.test_utils import RealFsTestMixin
31
32is_windows = sys.platform == "win32"
33
34
35class RealFsTestCase(fake_filesystem_unittest.TestCase, RealFsTestMixin):
36    def __init__(self, methodName="runTest"):
37        fake_filesystem_unittest.TestCase.__init__(self, methodName)
38        RealFsTestMixin.__init__(self)
39
40    def setUp(self):
41        RealFsTestMixin.setUp(self)
42        self.cwd = os.getcwd()
43        self.uid = get_uid()
44        set_uid(1000)
45        if not self.use_real_fs():
46            self.setUpPyfakefs()
47            self.filesystem = self.fs
48            self.os = os
49            self.open = open
50            self.create_basepath()
51            self.fs.set_disk_usage(1000, self.base_path)
52
53    def tearDown(self):
54        set_uid(self.uid)
55        RealFsTestMixin.tearDown(self)
56
57    @property
58    def is_windows_fs(self):
59        if self.use_real_fs():
60            return sys.platform == "win32"
61        return self.filesystem.is_windows_fs
62
63
64class FakeShutilModuleTest(RealFsTestCase):
65    @unittest.skipIf(is_windows, "Posix specific behavior")
66    def test_catch_permission_error(self):
67        root_path = self.make_path("rootpath")
68        self.create_dir(root_path)
69        dir1_path = self.os.path.join(root_path, "dir1")
70        dir2_path = self.os.path.join(root_path, "dir2")
71        self.create_dir(dir1_path)
72        self.os.chmod(dir1_path, 0o555)  # remove write permissions
73        self.create_dir(dir2_path)
74        old_file_path = self.os.path.join(dir2_path, "f1.txt")
75        new_file_path = self.os.path.join(dir1_path, "f1.txt")
76        self.create_file(old_file_path)
77
78        with self.assertRaises(PermissionError):
79            shutil.move(old_file_path, new_file_path)
80
81    def test_rmtree(self):
82        directory = self.make_path("xyzzy")
83        dir_path = os.path.join(directory, "subdir")
84        self.create_dir(dir_path)
85        file_path = os.path.join(directory, "subfile")
86        self.create_file(file_path)
87        self.assertTrue(os.path.exists(directory))
88        shutil.rmtree(directory)
89        self.assertFalse(os.path.exists(directory))
90        self.assertFalse(os.path.exists(dir_path))
91        self.assertFalse(os.path.exists(file_path))
92
93    def test_rmtree_with_trailing_slash(self):
94        directory = self.make_path("xyzzy")
95        dir_path = os.path.join(directory, "subdir")
96        self.create_dir(dir_path)
97        file_path = os.path.join(directory, "subfile")
98        self.create_file(file_path)
99        shutil.rmtree(directory + "/")
100        self.assertFalse(os.path.exists(directory))
101        self.assertFalse(os.path.exists(dir_path))
102        self.assertFalse(os.path.exists(file_path))
103
104    @unittest.skipIf(not is_windows, "Windows specific behavior")
105    def test_rmtree_without_permission_for_a_file_in_windows(self):
106        self.check_windows_only()
107        dir_path = self.make_path("foo")
108        self.create_file(os.path.join(dir_path, "bar"))
109        file_path = os.path.join(dir_path, "baz")
110        self.create_file(file_path)
111        self.os.chmod(file_path, 0o444)
112        with self.assertRaises(OSError):
113            shutil.rmtree(dir_path)
114        self.assertTrue(os.path.exists(file_path))
115        self.os.chmod(file_path, 0o666)
116
117    @unittest.skipIf(is_windows, "Posix specific behavior")
118    def test_rmtree_without_permission_for_a_dir_in_posix(self):
119        self.check_posix_only()
120        dir_path = self.make_path("foo")
121        self.create_file(os.path.join(dir_path, "bar"))
122        file_path = os.path.join(dir_path, "baz")
123        self.create_file(file_path)
124        self.os.chmod(dir_path, 0o555)
125        if not is_root():
126            with self.assertRaises(OSError):
127                shutil.rmtree(dir_path)
128            self.assertTrue(os.path.exists(file_path))
129            self.os.chmod(dir_path, 0o777)
130        else:
131            shutil.rmtree(dir_path)
132            self.assertFalse(os.path.exists(file_path))
133
134    @unittest.skipIf(is_windows, "Posix specific behavior")
135    def test_rmtree_with_open_file_posix(self):
136        self.check_posix_only()
137        dir_path = self.make_path("foo")
138        self.create_file(os.path.join(dir_path, "bar"))
139        file_path = os.path.join(dir_path, "baz")
140        self.create_file(file_path)
141        with open(file_path, encoding="utf8"):
142            shutil.rmtree(dir_path)
143        self.assertFalse(os.path.exists(file_path))
144
145    @unittest.skipIf(not is_windows, "Windows specific behavior")
146    def test_rmtree_with_open_file_fails_under_windows(self):
147        self.check_windows_only()
148        dir_path = self.make_path("foo")
149        self.create_file(os.path.join(dir_path, "bar"))
150        file_path = os.path.join(dir_path, "baz")
151        self.create_file(file_path)
152        with open(file_path, encoding="utf8"):
153            with self.assertRaises(OSError):
154                shutil.rmtree(dir_path)
155        self.assertTrue(os.path.exists(dir_path))
156
157    def test_rmtree_non_existing_dir(self):
158        directory = "nonexisting"
159        with self.assertRaises(OSError):
160            shutil.rmtree(directory)
161        try:
162            shutil.rmtree(directory, ignore_errors=True)
163        except OSError:
164            self.fail("rmtree raised despite ignore_errors True")
165
166    def test_rmtree_non_existing_dir_with_handler(self):
167        class NonLocal:
168            pass
169
170        def error_handler(_, path, _error_info):
171            NonLocal.errorHandled = True
172            NonLocal.errorPath = path
173
174        directory = self.make_path("nonexisting")
175        NonLocal.errorHandled = False
176        NonLocal.errorPath = ""
177        try:
178            shutil.rmtree(directory, onerror=error_handler)
179        except OSError:
180            self.fail("rmtree raised exception despite onerror defined")
181        self.assertTrue(NonLocal.errorHandled)
182        self.assertEqual(NonLocal.errorPath, directory)
183
184        NonLocal.errorHandled = False
185        NonLocal.errorPath = ""
186        try:
187            shutil.rmtree(directory, ignore_errors=True, onerror=error_handler)
188        except OSError:
189            self.fail("rmtree raised exception despite ignore_errors True")
190        # ignore_errors is True, so the onerror() error handler was
191        # not executed
192        self.assertFalse(NonLocal.errorHandled)
193        self.assertEqual(NonLocal.errorPath, "")
194
195    def test_rmtree_in_windows(self):
196        # regression test for #979
197        self.check_windows_only()
198        base_path = self.make_path("foo", "bar")
199        self.os.makedirs(self.os.path.join(base_path, "res"))
200        self.assertTrue(self.os.path.exists(base_path))
201        shutil.rmtree(base_path)
202        self.assertFalse(self.os.path.exists(base_path))
203
204    def test_copy(self):
205        src_file = self.make_path("xyzzy")
206        dst_file = self.make_path("xyzzy_copy")
207        self.create_file(src_file)
208        os.chmod(src_file, 0o750)
209        self.assertTrue(os.path.exists(src_file))
210        self.assertFalse(os.path.exists(dst_file))
211        shutil.copy(src_file, dst_file)
212        self.assertTrue(os.path.exists(dst_file))
213        self.assertEqual(os.stat(src_file).st_mode, os.stat(dst_file).st_mode)
214
215    def test_copy_directory(self):
216        src_file = self.make_path("xyzzy")
217        parent_directory = self.make_path("parent")
218        dst_file = os.path.join(parent_directory, "xyzzy")
219        self.create_file(src_file)
220        self.create_dir(parent_directory)
221        os.chmod(src_file, 0o750)
222        self.assertTrue(os.path.exists(src_file))
223        self.assertTrue(os.path.exists(parent_directory))
224        self.assertFalse(os.path.exists(dst_file))
225        shutil.copy(src_file, parent_directory)
226        self.assertTrue(os.path.exists(dst_file))
227        self.assertEqual(os.stat(src_file).st_mode, os.stat(dst_file).st_mode)
228
229    def test_copystat(self):
230        src_file = self.make_path("xyzzy")
231        self.create_file(src_file)
232        os.chmod(src_file, 0o750)
233        dst_file = self.make_path("xyzzy_copy")
234        self.create_file(dst_file)
235        self.assertTrue(os.path.exists(src_file))
236        self.assertTrue(os.path.exists(dst_file))
237        shutil.copystat(src_file, dst_file)
238        src_stat = os.stat(src_file)
239        dst_stat = os.stat(dst_file)
240        self.assertEqual(src_stat.st_mode, dst_stat.st_mode)
241        self.assertAlmostEqual(src_stat.st_atime, dst_stat.st_atime, places=0)
242        self.assertAlmostEqual(src_stat.st_mtime, dst_stat.st_mtime, places=2)
243
244    @unittest.skipIf(IS_PYPY, "Functionality not supported in PyPy")
245    def test_copystat_symlinks(self):
246        """Regression test for #799"""
247        self.skip_if_symlink_not_supported()
248        f = self.make_path("xyzzy")
249        self.create_file(f)
250        sym1 = self.make_path("sym1")
251        sym2 = self.make_path("sym2")
252        self.create_symlink(sym1, f)
253        self.create_symlink(sym2, f)
254        shutil.copystat(sym1, sym2, follow_symlinks=False)
255
256    def test_copy2(self):
257        src_file = self.make_path("xyzzy")
258        self.create_file(src_file)
259        os.chmod(src_file, 0o750)
260        dst_file = self.make_path("xyzzy_copy")
261        self.assertTrue(os.path.exists(src_file))
262        self.assertFalse(os.path.exists(dst_file))
263        shutil.copy2(src_file, dst_file)
264        self.assertTrue(os.path.exists(dst_file))
265        src_stat = os.stat(src_file)
266        dst_stat = os.stat(dst_file)
267        self.assertEqual(src_stat.st_mode, dst_stat.st_mode)
268        self.assertAlmostEqual(src_stat.st_atime, dst_stat.st_atime, places=0)
269        self.assertAlmostEqual(src_stat.st_mtime, dst_stat.st_mtime, places=2)
270
271    def test_copy2_directory(self):
272        src_file = self.make_path("xyzzy")
273        parent_directory = self.make_path("parent")
274        dst_file = os.path.join(parent_directory, "xyzzy")
275        self.create_file(src_file)
276        self.create_dir(parent_directory)
277        os.chmod(src_file, 0o750)
278        self.assertTrue(os.path.exists(src_file))
279        self.assertTrue(os.path.exists(parent_directory))
280        self.assertFalse(os.path.exists(dst_file))
281        shutil.copy2(src_file, parent_directory)
282        self.assertTrue(os.path.exists(dst_file))
283        src_stat = os.stat(src_file)
284        dst_stat = os.stat(dst_file)
285        self.assertEqual(src_stat.st_mode, dst_stat.st_mode)
286        self.assertAlmostEqual(src_stat.st_atime, dst_stat.st_atime, places=0)
287        self.assertAlmostEqual(src_stat.st_mtime, dst_stat.st_mtime, places=2)
288
289    def test_copytree(self):
290        src_directory = self.make_path("xyzzy")
291        dst_directory = self.make_path("xyzzy_copy")
292        self.create_dir(src_directory)
293        self.create_dir("%s/subdir" % src_directory)
294        self.create_file(os.path.join(src_directory, "subfile"))
295        self.assertTrue(os.path.exists(src_directory))
296        self.assertFalse(os.path.exists(dst_directory))
297        shutil.copytree(src_directory, dst_directory)
298        self.assertTrue(os.path.exists(dst_directory))
299        self.assertTrue(os.path.exists(os.path.join(dst_directory, "subdir")))
300        self.assertTrue(os.path.exists(os.path.join(dst_directory, "subfile")))
301
302    def test_copytree_src_is_file(self):
303        src_file = self.make_path("xyzzy")
304        dst_directory = self.make_path("xyzzy_copy")
305        self.create_file(src_file)
306        self.assertTrue(os.path.exists(src_file))
307        self.assertFalse(os.path.exists(dst_directory))
308        with self.assertRaises(OSError):
309            shutil.copytree(src_file, dst_directory)
310
311    def test_move_file_in_same_filesystem(self):
312        self.skip_real_fs()
313        src_file = "/original_xyzzy"
314        dst_file = "/moved_xyzzy"
315        src_object = self.fs.create_file(src_file)
316        src_ino = src_object.st_ino
317        src_dev = src_object.st_dev
318
319        self.assertTrue(os.path.exists(src_file))
320        self.assertFalse(os.path.exists(dst_file))
321        shutil.move(src_file, dst_file)
322        self.assertTrue(os.path.exists(dst_file))
323        self.assertFalse(os.path.exists(src_file))
324
325        dst_object = self.fs.get_object(dst_file)
326        self.assertEqual(src_ino, dst_object.st_ino)
327        self.assertEqual(src_dev, dst_object.st_dev)
328
329    def test_move_file_into_other_filesystem(self):
330        self.skip_real_fs()
331        mount_point = self.create_mount_point()
332
333        src_file = self.make_path("original_xyzzy")
334        dst_file = self.os.path.join(mount_point, "moved_xyzzy")
335        src_object = self.fs.create_file(src_file)
336        src_ino = src_object.st_ino
337        src_dev = src_object.st_dev
338
339        shutil.move(src_file, dst_file)
340        self.assertTrue(os.path.exists(dst_file))
341        self.assertFalse(os.path.exists(src_file))
342
343        dst_object = self.fs.get_object(dst_file)
344        self.assertNotEqual(src_ino, dst_object.st_ino)
345        self.assertNotEqual(src_dev, dst_object.st_dev)
346
347    def test_move_file_into_directory(self):
348        src_file = self.make_path("xyzzy")
349        dst_directory = self.make_path("directory")
350        dst_file = os.path.join(dst_directory, "xyzzy")
351        self.create_file(src_file)
352        self.create_dir(dst_directory)
353        self.assertTrue(os.path.exists(src_file))
354        self.assertFalse(os.path.exists(dst_file))
355        shutil.move(src_file, dst_directory)
356        self.assertTrue(os.path.exists(dst_file))
357        self.assertFalse(os.path.exists(src_file))
358
359    def test_move_directory(self):
360        src_directory = self.make_path("original_xyzzy")
361        dst_directory = self.make_path("moved_xyzzy")
362        self.create_dir(src_directory)
363        self.create_file(os.path.join(src_directory, "subfile"))
364        self.create_dir(os.path.join(src_directory, "subdir"))
365        self.assertTrue(os.path.exists(src_directory))
366        self.assertFalse(os.path.exists(dst_directory))
367        shutil.move(src_directory, dst_directory)
368        self.assertTrue(os.path.exists(dst_directory))
369        self.assertTrue(os.path.exists(os.path.join(dst_directory, "subfile")))
370        self.assertTrue(os.path.exists(os.path.join(dst_directory, "subdir")))
371        self.assertFalse(os.path.exists(src_directory))
372
373    def test_disk_usage(self):
374        self.skip_real_fs()
375        file_path = self.make_path("foo", "bar")
376        self.fs.create_file(file_path, st_size=400)
377        disk_usage = shutil.disk_usage(file_path)
378        self.assertEqual(1000, disk_usage.total)
379        self.assertEqual(400, disk_usage.used)
380        self.assertEqual(600, disk_usage.free)
381        self.assertEqual((1000, 400, 600), disk_usage)
382
383        mount_point = self.create_mount_point()
384        dir_path = self.os.path.join(mount_point, "foo")
385        file_path = self.os.path.join(dir_path, "bar")
386        self.fs.create_file(file_path, st_size=400)
387        disk_usage = shutil.disk_usage(dir_path)
388        self.assertEqual((500, 400, 100), disk_usage)
389
390    def test_disk_usage_with_path(self):
391        self.skip_real_fs()
392        file_path = self.make_path("foo", "bar")
393        self.fs.create_file(file_path, st_size=400)
394        path = Path(file_path)
395        disk_usage = shutil.disk_usage(path)
396        self.assertEqual(1000, disk_usage.total)
397        self.assertEqual(400, disk_usage.used)
398        self.assertEqual(600, disk_usage.free)
399        self.assertEqual((1000, 400, 600), disk_usage)
400
401    def create_mount_point(self):
402        mount_point = "M:" if self.is_windows_fs else "/mount"
403        self.fs.add_mount_point(mount_point, total_size=500)
404        return mount_point
405
406
407class RealShutilModuleTest(FakeShutilModuleTest):
408    def use_real_fs(self):
409        return True
410
411
412class FakeCopyFileTest(RealFsTestCase):
413    def tearDown(self):
414        super().tearDown()
415
416    def test_common_case(self):
417        src_file = self.make_path("xyzzy")
418        dst_file = self.make_path("xyzzy_copy")
419        contents = "contents of file"
420        self.create_file(src_file, contents=contents)
421        self.assertTrue(os.path.exists(src_file))
422        self.assertFalse(os.path.exists(dst_file))
423        shutil.copyfile(src_file, dst_file)
424        self.assertTrue(os.path.exists(dst_file))
425        self.check_contents(dst_file, contents)
426
427    def test_raises_if_source_and_dest_are_the_same_file(self):
428        src_file = self.make_path("xyzzy")
429        dst_file = src_file
430        contents = "contents of file"
431        self.create_file(src_file, contents=contents)
432        self.assertTrue(os.path.exists(src_file))
433        with self.assertRaises(shutil.Error):
434            shutil.copyfile(src_file, dst_file)
435
436    def test_raises_if_dest_is_a_symlink_to_src(self):
437        self.skip_if_symlink_not_supported()
438        src_file = self.make_path("foo")
439        dst_file = self.make_path("bar")
440        contents = "contents of file"
441        self.create_file(src_file, contents=contents)
442        self.create_symlink(dst_file, src_file)
443        self.assertTrue(os.path.exists(src_file))
444        with self.assertRaises(shutil.Error):
445            shutil.copyfile(src_file, dst_file)
446
447    def test_succeeds_if_dest_exists_and_is_writable(self):
448        src_file = self.make_path("xyzzy")
449        dst_file = self.make_path("xyzzy_copy")
450        src_contents = "contents of source file"
451        dst_contents = "contents of dest file"
452        self.create_file(src_file, contents=src_contents)
453        self.create_file(dst_file, contents=dst_contents)
454        self.assertTrue(os.path.exists(src_file))
455        self.assertTrue(os.path.exists(dst_file))
456        shutil.copyfile(src_file, dst_file)
457        self.assertTrue(os.path.exists(dst_file))
458        self.check_contents(dst_file, src_contents)
459
460    def test_raises_if_dest_exists_and_is_not_writable(self):
461        src_file = self.make_path("xyzzy")
462        dst_file = self.make_path("xyzzy_copy")
463        src_contents = "contents of source file"
464        dst_contents = "contents of dest file"
465        self.create_file(src_file, contents=src_contents)
466        self.create_file(dst_file, contents=dst_contents)
467        os.chmod(dst_file, 0o400)
468        self.assertTrue(os.path.exists(src_file))
469        self.assertTrue(os.path.exists(dst_file))
470
471        if is_root():
472            shutil.copyfile(src_file, dst_file)
473            self.assertTrue(self.os.path.exists(dst_file))
474            with self.open(dst_file) as f:
475                self.assertEqual("contents of source file", f.read())
476        else:
477            with self.assertRaises(OSError):
478                shutil.copyfile(src_file, dst_file)
479
480        os.chmod(dst_file, 0o666)
481
482    @unittest.skipIf(is_windows, "Posix specific behavior")
483    def test_raises_if_dest_dir_is_not_writable_under_posix(self):
484        self.check_posix_only()
485        src_file = self.make_path("xyzzy")
486        dst_dir = self.make_path("tmp", "foo")
487        dst_file = os.path.join(dst_dir, "xyzzy")
488        src_contents = "contents of source file"
489        self.create_file(src_file, contents=src_contents)
490        self.create_dir(dst_dir)
491        os.chmod(dst_dir, 0o555)
492        self.assertTrue(os.path.exists(src_file))
493        self.assertTrue(os.path.exists(dst_dir))
494        if not is_root():
495            with self.assertRaises(OSError):
496                shutil.copyfile(src_file, dst_file)
497        else:
498            shutil.copyfile(src_file, dst_file)
499            self.assertTrue(os.path.exists(dst_file))
500            self.check_contents(dst_file, src_contents)
501
502    def test_raises_if_src_doesnt_exist(self):
503        src_file = self.make_path("xyzzy")
504        dst_file = self.make_path("xyzzy_copy")
505        self.assertFalse(os.path.exists(src_file))
506        with self.assertRaises(OSError):
507            shutil.copyfile(src_file, dst_file)
508
509    @unittest.skipIf(is_windows, "Posix specific behavior")
510    def test_raises_if_src_not_readable(self):
511        self.check_posix_only()
512        src_file = self.make_path("xyzzy")
513        dst_file = self.make_path("xyzzy_copy")
514        src_contents = "contents of source file"
515        self.create_file(src_file, contents=src_contents)
516        os.chmod(src_file, 0o000)
517        self.assertTrue(os.path.exists(src_file))
518        if not is_root():
519            with self.assertRaises(OSError):
520                shutil.copyfile(src_file, dst_file)
521        else:
522            shutil.copyfile(src_file, dst_file)
523            self.assertTrue(os.path.exists(dst_file))
524            self.check_contents(dst_file, src_contents)
525
526    def test_raises_if_src_is_a_directory(self):
527        src_file = self.make_path("xyzzy")
528        dst_file = self.make_path("xyzzy_copy")
529        self.create_dir(src_file)
530        self.assertTrue(os.path.exists(src_file))
531        with self.assertRaises(OSError):
532            shutil.copyfile(src_file, dst_file)
533
534    def test_raises_if_dest_is_a_directory(self):
535        src_file = self.make_path("xyzzy")
536        dst_dir = self.make_path("tmp", "foo")
537        src_contents = "contents of source file"
538        self.create_file(src_file, contents=src_contents)
539        self.create_dir(dst_dir)
540        self.assertTrue(os.path.exists(src_file))
541        self.assertTrue(os.path.exists(dst_dir))
542        with self.assertRaises(OSError):
543            shutil.copyfile(src_file, dst_dir)
544
545    def test_moving_dir_into_dir(self):
546        # regression test for #515
547        source_dir = tempfile.mkdtemp()
548        target_dir = tempfile.mkdtemp()
549        filename = "foo.pdf"
550        with open(os.path.join(source_dir, filename), "wb") as fp:
551            fp.write(b"stub")
552
553        shutil.move(source_dir, target_dir)
554        shutil.rmtree(target_dir)
555
556
557class RealCopyFileTest(FakeCopyFileTest):
558    def use_real_fs(self):
559        return True
560
561
562if __name__ == "__main__":
563    unittest.main()
564