(self)
| 684 | @os_helper.skip_if_dac_override |
| 685 | @os_helper.skip_unless_working_chmod |
| 686 | def test_rmtree_deleted_race_condition(self): |
| 687 | # bpo-37260 |
| 688 | # |
| 689 | # Test that a file or a directory deleted after it is enumerated |
| 690 | # by scandir() but before unlink() or rmdr() is called doesn't |
| 691 | # generate any errors. |
| 692 | def _onexc(fn, path, exc): |
| 693 | assert fn in (os.rmdir, os.unlink) |
| 694 | if not isinstance(exc, PermissionError): |
| 695 | raise |
| 696 | # Make the parent and the children writeable. |
| 697 | for p, mode in zip(paths, old_modes): |
| 698 | os.chmod(p, mode) |
| 699 | # Remove other dirs except one. |
| 700 | keep = next(p for p in dirs if p != path) |
| 701 | for p in dirs: |
| 702 | if p != keep: |
| 703 | os.rmdir(p) |
| 704 | # Remove other files except one. |
| 705 | keep = next(p for p in files if p != path) |
| 706 | for p in files: |
| 707 | if p != keep: |
| 708 | os.unlink(p) |
| 709 | |
| 710 | os.mkdir(TESTFN) |
| 711 | paths = [TESTFN] + [os.path.join(TESTFN, f'child{i}') |
| 712 | for i in range(6)] |
| 713 | dirs = paths[1::2] |
| 714 | files = paths[2::2] |
| 715 | for path in dirs: |
| 716 | os.mkdir(path) |
| 717 | for path in files: |
| 718 | create_file(path) |
| 719 | |
| 720 | old_modes = [os.stat(path).st_mode for path in paths] |
| 721 | |
| 722 | # Make the parent and the children non-writeable. |
| 723 | new_mode = stat.S_IREAD|stat.S_IEXEC |
| 724 | for path in reversed(paths): |
| 725 | os.chmod(path, new_mode) |
| 726 | |
| 727 | try: |
| 728 | shutil.rmtree(TESTFN, onexc=_onexc) |
| 729 | except: |
| 730 | # Test failed, so cleanup artifacts. |
| 731 | for path, mode in zip(paths, old_modes): |
| 732 | try: |
| 733 | os.chmod(path, mode) |
| 734 | except OSError: |
| 735 | pass |
| 736 | shutil.rmtree(TESTFN) |
| 737 | raise |
| 738 | |
| 739 | def test_rmtree_above_recursion_limit(self): |
| 740 | recursion_limit = 40 |
nothing calls this directly
no test coverage detected