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