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