44# This module is part of GitPython and is released under
55# the BSD License: https://opensource.org/license/bsd-3-clause/
66
7+ import contextlib
78import os
89import pathlib
910import pickle
@@ -121,16 +122,31 @@ def test_rmtree_deletes_dir_with_readonly_files(self):
121122
122123 self .assertFalse (td .exists ())
123124
124- @skipIf (sys .platform == "cygwin" , "Cygwin can't set the permissions that make the test meaningful." )
125- @skipIf (sys .version_info < (3 , 8 ), "In 3.7, TemporaryDirectory doesn't clean up after weird permissions." )
126- def test_rmtree_can_wrap_exceptions (self ):
125+ @staticmethod
126+ @contextlib .contextmanager
127+ def _tmpdir_to_force_permission_error ():
128+ if sys .platform == "cygwin" :
129+ raise SkipTest ("Cygwin can't set the permissions that make the test meaningful." )
130+ if sys .version_info < (3 , 8 ):
131+ raise SkipTest ("In 3.7, TemporaryDirectory doesn't clean up after weird permissions." )
132+
127133 with tempfile .TemporaryDirectory () as parent :
128134 td = pathlib .Path (parent , "testdir" )
129135 td .mkdir ()
130136 (td / "x" ).write_bytes (b"" )
131137 (td / "x" ).chmod (stat .S_IRUSR ) # Set up PermissionError on Windows.
132138 td .chmod (stat .S_IRUSR | stat .S_IXUSR ) # Set up PermissionError on Unix.
139+ yield td
133140
141+ @staticmethod
142+ @contextlib .contextmanager
143+ def _tmpdir_for_file_not_found ():
144+ with tempfile .TemporaryDirectory () as parent :
145+ yield pathlib .Path (parent , "testdir" ) # It is deliberately never created.
146+
147+ def test_rmtree_can_wrap_exceptions (self ):
148+ """Our rmtree wraps PermissionError when HIDE_WINDOWS_KNOWN_ERRORS is true."""
149+ with self ._tmpdir_to_force_permission_error () as td :
134150 # Access the module through sys.modules so it is unambiguous which module's
135151 # attribute we patch: the original git.util, not git.index.util even though
136152 # git.index.util "replaces" git.util and is what "import git.util" gives us.
@@ -141,6 +157,25 @@ def test_rmtree_can_wrap_exceptions(self):
141157 with self .assertRaises (SkipTest ):
142158 rmtree (td )
143159
160+ @ddt .data (
161+ (False , PermissionError , _tmpdir_to_force_permission_error ),
162+ (False , FileNotFoundError , _tmpdir_for_file_not_found ),
163+ (True , FileNotFoundError , _tmpdir_for_file_not_found ),
164+ )
165+ def test_rmtree_does_not_wrap_unless_called_for (self , case ):
166+ """Our rmtree doesn't wrap non-PermissionError, nor when HIDE_WINDOWS_KNOWN_ERRORS is false."""
167+ hide_windows_known_errors , exception_type , tmpdir_context_factory = case
168+
169+ with tmpdir_context_factory () as td :
170+ # See comments in test_rmtree_can_wrap_exceptions regarding the patching done here.
171+ with mock .patch .object (sys .modules ["git.util" ], "HIDE_WINDOWS_KNOWN_ERRORS" , hide_windows_known_errors ):
172+ with mock .patch .object (os , "chmod" ), mock .patch .object (pathlib .Path , "chmod" ):
173+ with self .assertRaises (exception_type ):
174+ try :
175+ rmtree (td )
176+ except SkipTest as ex :
177+ self .fail (f"rmtree unexpectedly attempts skip: { ex !r} " )
178+
144179 # FIXME: Mark only the /proc-prefixing cases xfail, somehow (or fix them).
145180 @pytest .mark .xfail (
146181 reason = "Many return paths prefixed /proc/cygdrive instead." ,
0 commit comments