|
5 | 5 | # the BSD License: https://opensource.org/license/bsd-3-clause/
|
6 | 6 |
|
7 | 7 | importast
|
8 |
| -importcontextlib |
9 | 8 | fromdatetimeimportdatetime
|
10 | 9 | importos
|
11 | 10 | importpathlib
|
|
15 | 14 | importsys
|
16 | 15 | importtempfile
|
17 | 16 | importtime
|
18 |
| -fromunittestimportSkipTest,mock,skipIf,skipUnless |
| 17 | +fromunittestimportSkipTest,mock,skipUnless |
19 | 18 |
|
20 | 19 | importddt
|
21 | 20 | importpytest
|
|
44 | 43 | fromtest.libimportTestBase,with_rw_repo
|
45 | 44 |
|
46 | 45 |
|
47 |
| -class_Member: |
48 |
| -"""A member of an IterableList.""" |
49 |
| - |
50 |
| -__slots__= ("name",) |
51 |
| - |
52 |
| -def__init__(self,name): |
53 |
| -self.name=name |
54 |
| - |
55 |
| -def__repr__(self): |
56 |
| -returnf"{type(self).__name__}({self.name!r})" |
57 |
| - |
58 |
| - |
59 |
| -@contextlib.contextmanager |
60 |
| -def_tmpdir_to_force_permission_error(): |
61 |
| -"""Context manager to test permission errors in situations where they are not overcome.""" |
| 46 | +@pytest.fixture |
| 47 | +defpermission_error_tmpdir(tmp_path): |
| 48 | +"""Fixture to test permissions errors situations where they are not overcome.""" |
62 | 49 | ifsys.platform=="cygwin":
|
63 | 50 | raiseSkipTest("Cygwin can't set the permissions that make the test meaningful.")
|
64 | 51 | ifsys.version_info< (3,8):
|
65 | 52 | raiseSkipTest("In 3.7, TemporaryDirectory doesn't clean up after weird permissions.")
|
66 | 53 |
|
67 |
| -withtempfile.TemporaryDirectory()asparent: |
68 |
| -td=pathlib.Path(parent,"testdir") |
69 |
| -td.mkdir() |
70 |
| - (td/"x").write_bytes(b"") |
71 |
| - (td/"x").chmod(stat.S_IRUSR)# Set up PermissionError on Windows. |
72 |
| -td.chmod(stat.S_IRUSR|stat.S_IXUSR)# Set up PermissionError on Unix. |
73 |
| -yieldtd |
| 54 | +td=tmp_path/"testdir" |
| 55 | +td.mkdir() |
| 56 | + (td/"x").write_bytes(b"") |
| 57 | + (td/"x").chmod(stat.S_IRUSR)# Set up PermissionError on Windows. |
| 58 | +td.chmod(stat.S_IRUSR|stat.S_IXUSR)# Set up PermissionError on Unix. |
| 59 | +yieldtd |
74 | 60 |
|
75 | 61 |
|
76 |
| -@contextlib.contextmanager |
77 |
| -def_tmpdir_for_file_not_found(): |
78 |
| -"""Context manager to test errors deleting a directory that are not due to permissions.""" |
79 |
| -withtempfile.TemporaryDirectory()asparent: |
80 |
| -yieldpathlib.Path(parent,"testdir")# It is deliberately never created. |
| 62 | +@pytest.fixture |
| 63 | +deffile_not_found_tmpdir(tmp_path): |
| 64 | +"""Fixture to test errors deleting a directory that are not due to permissions.""" |
| 65 | +yieldtmp_path/"testdir"# It is deliberately never created. |
81 | 66 |
|
82 | 67 |
|
83 |
| -@ddt.ddt |
84 |
| -classTestUtils(TestBase): |
85 |
| -deftest_rmtree_deletes_nested_dir_with_files(self): |
86 |
| -withtempfile.TemporaryDirectory()asparent: |
87 |
| -td=pathlib.Path(parent,"testdir") |
88 |
| -fordintd,td/"q",td/"s": |
89 |
| -d.mkdir() |
90 |
| -forfin ( |
91 |
| -td/"p", |
92 |
| -td/"q"/"w", |
93 |
| -td/"q"/"x", |
94 |
| -td/"r", |
95 |
| -td/"s"/"y", |
96 |
| -td/"s"/"z", |
97 |
| - ): |
98 |
| -f.write_bytes(b"") |
| 68 | +classTestRmtree: |
| 69 | +"""Tests for :func:`git.util.rmtree`.""" |
99 | 70 |
|
100 |
| -try: |
101 |
| -rmtree(td) |
102 |
| -exceptSkipTestasex: |
103 |
| -self.fail(f"rmtree unexpectedly attempts skip:{ex!r}") |
| 71 | +deftest_deletes_nested_dir_with_files(self,tmp_path): |
| 72 | +td=tmp_path/"testdir" |
104 | 73 |
|
105 |
| -self.assertFalse(td.exists()) |
| 74 | +fordintd,td/"q",td/"s": |
| 75 | +d.mkdir() |
| 76 | +forfin ( |
| 77 | +td/"p", |
| 78 | +td/"q"/"w", |
| 79 | +td/"q"/"x", |
| 80 | +td/"r", |
| 81 | +td/"s"/"y", |
| 82 | +td/"s"/"z", |
| 83 | + ): |
| 84 | +f.write_bytes(b"") |
106 | 85 |
|
107 |
| -@skipIf( |
| 86 | +try: |
| 87 | +rmtree(td) |
| 88 | +exceptSkipTestasex: |
| 89 | +pytest.fail(f"rmtree unexpectedly attempts skip:{ex!r}") |
| 90 | + |
| 91 | +assertnottd.exists() |
| 92 | + |
| 93 | +@pytest.mark.skipif( |
108 | 94 | sys.platform=="cygwin",
|
109 |
| -"Cygwin can't set the permissions that make the test meaningful.", |
| 95 | +reason="Cygwin can't set the permissions that make the test meaningful.", |
110 | 96 | )
|
111 |
| -deftest_rmtree_deletes_dir_with_readonly_files(self): |
| 97 | +deftest_deletes_dir_with_readonly_files(self,tmp_path): |
112 | 98 | # Automatically works on Unix, but requires special handling on Windows.
|
113 |
| -# Not to be confused with what _tmpdir_to_force_permission_error sets up (see below). |
114 |
| -withtempfile.TemporaryDirectory()asparent: |
115 |
| -td=pathlib.Path(parent,"testdir") |
116 |
| -fordintd,td/"sub": |
117 |
| -d.mkdir() |
118 |
| -forfintd/"x",td/"sub"/"y": |
119 |
| -f.write_bytes(b"") |
120 |
| -f.chmod(0) |
| 99 | +# Not to be confused with what permission_error_tmpdir sets up (see below). |
| 100 | + |
| 101 | +td=tmp_path/"testdir" |
| 102 | + |
| 103 | +fordintd,td/"sub": |
| 104 | +d.mkdir() |
| 105 | +forfintd/"x",td/"sub"/"y": |
| 106 | +f.write_bytes(b"") |
| 107 | +f.chmod(0) |
| 108 | + |
| 109 | +try: |
| 110 | +rmtree(td) |
| 111 | +exceptSkipTestasex: |
| 112 | +self.fail(f"rmtree unexpectedly attempts skip:{ex!r}") |
121 | 113 |
|
| 114 | +assertnottd.exists() |
| 115 | + |
| 116 | +deftest_wraps_perm_error_if_enabled(self,mocker,permission_error_tmpdir): |
| 117 | +"""rmtree wraps PermissionError when HIDE_WINDOWS_KNOWN_ERRORS is true.""" |
| 118 | +# Access the module through sys.modules so it is unambiguous which module's |
| 119 | +# attribute we patch: the original git.util, not git.index.util even though |
| 120 | +# git.index.util "replaces" git.util and is what "import git.util" gives us. |
| 121 | +mocker.patch.object(sys.modules["git.util"],"HIDE_WINDOWS_KNOWN_ERRORS",True) |
| 122 | + |
| 123 | +# Disable common chmod functions so the callback can't fix the problem. |
| 124 | +mocker.patch.object(os,"chmod") |
| 125 | +mocker.patch.object(pathlib.Path,"chmod") |
| 126 | + |
| 127 | +# Now we can see how an intractable PermissionError is treated. |
| 128 | +withpytest.raises(SkipTest): |
| 129 | +rmtree(permission_error_tmpdir) |
| 130 | + |
| 131 | +deftest_does_not_wrap_perm_error_unless_enabled(self,mocker,permission_error_tmpdir): |
| 132 | +"""rmtree does not wrap PermissionError when HIDE_WINDOWS_KNOWN_ERRORS is false.""" |
| 133 | +# See comments in test_wraps_perm_error_if_enabled for details about patching. |
| 134 | +mocker.patch.object(sys.modules["git.util"],"HIDE_WINDOWS_KNOWN_ERRORS",False) |
| 135 | +mocker.patch.object(os,"chmod") |
| 136 | +mocker.patch.object(pathlib.Path,"chmod") |
| 137 | + |
| 138 | +withpytest.raises(PermissionError): |
| 139 | +try: |
| 140 | +rmtree(permission_error_tmpdir) |
| 141 | +exceptSkipTestasex: |
| 142 | +pytest.fail(f"rmtree unexpectedly attempts skip:{ex!r}") |
| 143 | + |
| 144 | +@pytest.mark.parametrize("hide_windows_known_errors", [False,True]) |
| 145 | +deftest_does_not_wrap_other_errors(self,mocker,file_not_found_tmpdir,hide_windows_known_errors): |
| 146 | +# See comments in test_wraps_perm_error_if_enabled for details about patching. |
| 147 | +mocker.patch.object(sys.modules["git.util"],"HIDE_WINDOWS_KNOWN_ERRORS",hide_windows_known_errors) |
| 148 | +mocker.patch.object(os,"chmod") |
| 149 | +mocker.patch.object(pathlib.Path,"chmod") |
| 150 | + |
| 151 | +withpytest.raises(FileNotFoundError): |
122 | 152 | try:
|
123 |
| -rmtree(td) |
| 153 | +rmtree(file_not_found_tmpdir) |
124 | 154 | exceptSkipTestasex:
|
125 | 155 | self.fail(f"rmtree unexpectedly attempts skip:{ex!r}")
|
126 | 156 |
|
127 |
| -self.assertFalse(td.exists()) |
128 | 157 |
|
129 |
| -deftest_rmtree_can_wrap_exceptions(self): |
130 |
| -"""rmtree wraps PermissionError when HIDE_WINDOWS_KNOWN_ERRORS is true.""" |
131 |
| -with_tmpdir_to_force_permission_error()astd: |
132 |
| -# Access the module through sys.modules so it is unambiguous which module's |
133 |
| -# attribute we patch: the original git.util, not git.index.util even though |
134 |
| -# git.index.util "replaces" git.util and is what "import git.util" gives us. |
135 |
| -withmock.patch.object(sys.modules["git.util"],"HIDE_WINDOWS_KNOWN_ERRORS",True): |
136 |
| -# Disable common chmod functions so the callback can't fix the problem. |
137 |
| -withmock.patch.object(os,"chmod"),mock.patch.object(pathlib.Path,"chmod"): |
138 |
| -# Now we can see how an intractable PermissionError is treated. |
139 |
| -withself.assertRaises(SkipTest): |
140 |
| -rmtree(td) |
| 158 | +class_Member: |
| 159 | +"""A member of an IterableList.""" |
141 | 160 |
|
142 |
| -@ddt.data( |
143 |
| - (False,PermissionError,_tmpdir_to_force_permission_error), |
144 |
| - (False,FileNotFoundError,_tmpdir_for_file_not_found), |
145 |
| - (True,FileNotFoundError,_tmpdir_for_file_not_found), |
146 |
| - ) |
147 |
| -deftest_rmtree_does_not_wrap_unless_called_for(self,case): |
148 |
| -"""rmtree doesn't wrap non-PermissionError, nor if HIDE_WINDOWS_KNOWN_ERRORS is false.""" |
149 |
| -hide_windows_known_errors,exception_type,tmpdir_context_factory=case |
150 |
| - |
151 |
| -withtmpdir_context_factory()astd: |
152 |
| -# See comments in test_rmtree_can_wrap_exceptions regarding the patching done here. |
153 |
| -withmock.patch.object( |
154 |
| -sys.modules["git.util"], |
155 |
| -"HIDE_WINDOWS_KNOWN_ERRORS", |
156 |
| -hide_windows_known_errors, |
157 |
| - ): |
158 |
| -withmock.patch.object(os,"chmod"),mock.patch.object(pathlib.Path,"chmod"): |
159 |
| -withself.assertRaises(exception_type): |
160 |
| -try: |
161 |
| -rmtree(td) |
162 |
| -exceptSkipTestasex: |
163 |
| -self.fail(f"rmtree unexpectedly attempts skip:{ex!r}") |
| 161 | +__slots__= ("name",) |
| 162 | + |
| 163 | +def__init__(self,name): |
| 164 | +self.name=name |
| 165 | + |
| 166 | +def__repr__(self): |
| 167 | +returnf"{type(self).__name__}({self.name!r})" |
| 168 | + |
| 169 | + |
| 170 | +@ddt.ddt |
| 171 | +classTestUtils(TestBase): |
| 172 | +"""Tests for utilities in :mod:`git.util` other than :func:`git.util.rmtree`.""" |
164 | 173 |
|
165 | 174 | @ddt.data("HIDE_WINDOWS_KNOWN_ERRORS","HIDE_WINDOWS_FREEZE_ERRORS")
|
166 | 175 | deftest_env_vars_for_windows_tests(self,name):
|
|