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