| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 09826f4 commit e2fa5e2
3 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3,10 +3,11 @@ requires = ["setuptools"] | |||
| 3 | 3 | build-backend = "setuptools.build_meta" | |
| 4 | 4 | ||
| 5 | 5 | [tool.pytest.ini_options] | |
| 6 | - python_files = 'test_*.py' | ||
| 7 | - testpaths = 'test' # space separated list of paths from root e.g test tests doc/testing | ||
| 8 | - addopts = '--cov=git --cov-report=term --disable-warnings' | ||
| 9 | - filterwarnings = 'ignore::DeprecationWarning' | ||
| 6 | + addopts = "--cov=git --cov-report=term --disable-warnings" | ||
| 7 | + filterwarnings = "ignore::DeprecationWarning" | ||
| 8 | + python_files = "test_*.py" | ||
| 9 | + tmp_path_retention_policy = "failed" | ||
| 10 | + testpaths = "test" # Space separated list of paths from root e.g test tests doc/testing. | ||
| 10 | 11 | # --cov coverage | |
| 11 | 12 | # --cov-report term # send report to terminal term-missing -> terminal with line numbers html xml | |
| 12 | 13 | # --cov-report term-missing # to terminal with line numbers | |
@@ -29,7 +30,7 @@ show_error_codes = true | |||
| 29 | 30 | implicit_reexport = true | |
| 30 | 31 | # strict = true | |
| 31 | 32 | ||
| 32 | - # TODO: remove when 'gitdb' is fully annotated | ||
| 33 | + # TODO: Remove when 'gitdb' is fully annotated. | ||
| 33 | 34 | exclude = ["^git/ext/gitdb"] | |
| 34 | 35 | [[tool.mypy.overrides]] | |
| 35 | 36 | module = "gitdb.*" | |
@@ -44,5 +45,5 @@ omit = ["*/git/ext/*"] | |||
| 44 | 45 | ||
| 45 | 46 | [tool.black] | |
| 46 | 47 | line-length = 120 | |
| 47 | - target-version = ['py37'] | ||
| 48 | + target-version = ["py37"] | ||
| 48 | 49 | extend-exclude = "git/ext/gitdb" | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4,8 +4,9 @@ ddt >= 1.1.1, != 1.4.3 | |||
| 4 | 4 | mock ; python_version < "3.8" | |
| 5 | 5 | mypy | |
| 6 | 6 | pre-commit | |
| 7 | - pytest | ||
| 7 | + pytest >= 7.3.1 | ||
| 8 | 8 | pytest-cov | |
| 9 | 9 | pytest-instafail | |
| 10 | + pytest-mock | ||
| 10 | 11 | pytest-subtests | |
| 11 | 12 | pytest-sugar | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -5,7 +5,6 @@ | |||
| 5 | 5 | # the BSD License: https://opensource.org/license/bsd-3-clause/ | |
| 6 | 6 | ||
| 7 | 7 | import ast | |
| 8 | - import contextlib | ||
| 9 | 8 | from datetime import datetime | |
| 10 | 9 | import os | |
| 11 | 10 | import pathlib | |
@@ -15,7 +14,7 @@ | |||
| 15 | 14 | import sys | |
| 16 | 15 | import tempfile | |
| 17 | 16 | import time | |
| 18 | - from unittest import SkipTest, mock, skipIf, skipUnless | ||
| 17 | + from unittest import SkipTest, mock, skipUnless | ||
| 19 | 18 | ||
| 20 | 19 | import ddt | |
| 21 | 20 | import pytest | |
@@ -44,123 +43,133 @@ | |||
| 44 | 43 | from test.lib import TestBase, 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 | - return f"{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 | + def permission_error_tmpdir(tmp_path): | ||
| 48 | + """Fixture to test permissions errors situations where they are not overcome.""" | ||
| 62 | 49 | if sys.platform == "cygwin": | |
| 63 | 50 | raise SkipTest("Cygwin can't set the permissions that make the test meaningful.") | |
| 64 | 51 | if sys.version_info < (3, 8): | |
| 65 | 52 | raise SkipTest("In 3.7, TemporaryDirectory doesn't clean up after weird permissions.") | |
| 66 | 53 | ||
| 67 | - with tempfile.TemporaryDirectory() as parent: | ||
| 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 | - yield td | ||
| 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 | + yield td | ||
| 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 | - with tempfile.TemporaryDirectory() as parent: | ||
| 80 | - yield pathlib.Path(parent, "testdir") # It is deliberately never created. | ||
| 62 | + @pytest.fixture | ||
| 63 | + def file_not_found_tmpdir(tmp_path): | ||
| 64 | + """Fixture to test errors deleting a directory that are not due to permissions.""" | ||
| 65 | + yield tmp_path / "testdir" # It is deliberately never created. | ||
| 81 | 66 | ||
| 82 | 67 | ||
| 83 | - @ddt.ddt | ||
| 84 | - class TestUtils(TestBase): | ||
| 85 | - def test_rmtree_deletes_nested_dir_with_files(self): | ||
| 86 | - with tempfile.TemporaryDirectory() as parent: | ||
| 87 | - td = pathlib.Path(parent, "testdir") | ||
| 88 | - for d in td, td / "q", td / "s": | ||
| 89 | - d.mkdir() | ||
| 90 | - for f in ( | ||
| 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 | + class TestRmtree: | ||
| 69 | + """Tests for :func:`git.util.rmtree`.""" | ||
| 99 | 70 | ||
| 100 | - try: | ||
| 101 | - rmtree(td) | ||
| 102 | - except SkipTest as ex: | ||
| 103 | - self.fail(f"rmtree unexpectedly attempts skip: {ex!r}") | ||
| 71 | + def test_deletes_nested_dir_with_files(self, tmp_path): | ||
| 72 | + td = tmp_path / "testdir" | ||
| 104 | 73 | ||
| 105 | - self.assertFalse(td.exists()) | ||
| 74 | + for d in td, td / "q", td / "s": | ||
| 75 | + d.mkdir() | ||
| 76 | + for f in ( | ||
| 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 | + except SkipTest as ex: | ||
| 89 | + pytest.fail(f"rmtree unexpectedly attempts skip: {ex!r}") | ||
| 90 | + | ||
| 91 | + assert not td.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 | - def test_rmtree_deletes_dir_with_readonly_files(self): | ||
| 97 | + def test_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 | - with tempfile.TemporaryDirectory() as parent: | ||
| 115 | - td = pathlib.Path(parent, "testdir") | ||
| 116 | - for d in td, td / "sub": | ||
| 117 | - d.mkdir() | ||
| 118 | - for f in td / "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 | + for d in td, td / "sub": | ||
| 104 | + d.mkdir() | ||
| 105 | + for f in td / "x", td / "sub" / "y": | ||
| 106 | + f.write_bytes(b"") | ||
| 107 | + f.chmod(0) | ||
| 108 | + | ||
| 109 | + try: | ||
| 110 | + rmtree(td) | ||
| 111 | + except SkipTest as ex: | ||
| 112 | + self.fail(f"rmtree unexpectedly attempts skip: {ex!r}") | ||
| 121 | 113 | ||
| 114 | + assert not td.exists() | ||
| 115 | + | ||
| 116 | + def test_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 | + with pytest.raises(SkipTest): | ||
| 129 | + rmtree(permission_error_tmpdir) | ||
| 130 | + | ||
| 131 | + def test_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 | + with pytest.raises(PermissionError): | ||
| 139 | + try: | ||
| 140 | + rmtree(permission_error_tmpdir) | ||
| 141 | + except SkipTest as ex: | ||
| 142 | + pytest.fail(f"rmtree unexpectedly attempts skip: {ex!r}") | ||
| 143 | + | ||
| 144 | + @pytest.mark.parametrize("hide_windows_known_errors", [False, True]) | ||
| 145 | + def test_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 | + with pytest.raises(FileNotFoundError): | ||
| 122 | 152 | try: | |
| 123 | - rmtree(td) | ||
| 153 | + rmtree(file_not_found_tmpdir) | ||
| 124 | 154 | except SkipTest as ex: | |
| 125 | 155 | self.fail(f"rmtree unexpectedly attempts skip: {ex!r}") | |
| 126 | 156 | ||
| 127 | - self.assertFalse(td.exists()) | ||
| 128 | 157 | ||
| 129 | - def test_rmtree_can_wrap_exceptions(self): | ||
| 130 | - """rmtree wraps PermissionError when HIDE_WINDOWS_KNOWN_ERRORS is true.""" | ||
| 131 | - with _tmpdir_to_force_permission_error() as td: | ||
| 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 | - with mock.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 | - with mock.patch.object(os, "chmod"), mock.patch.object(pathlib.Path, "chmod"): | ||
| 138 | - # Now we can see how an intractable PermissionError is treated. | ||
| 139 | - with self.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 | - def test_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 | - with tmpdir_context_factory() as td: | ||
| 152 | - # See comments in test_rmtree_can_wrap_exceptions regarding the patching done here. | ||
| 153 | - with mock.patch.object( | ||
| 154 | - sys.modules["git.util"], | ||
| 155 | - "HIDE_WINDOWS_KNOWN_ERRORS", | ||
| 156 | - hide_windows_known_errors, | ||
| 157 | - ): | ||
| 158 | - with mock.patch.object(os, "chmod"), mock.patch.object(pathlib.Path, "chmod"): | ||
| 159 | - with self.assertRaises(exception_type): | ||
| 160 | - try: | ||
| 161 | - rmtree(td) | ||
| 162 | - except SkipTest as ex: | ||
| 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 | + return f"{type(self).__name__}({self.name!r})" | ||
| 168 | + | ||
| 169 | + | ||
| 170 | + @ddt.ddt | ||
| 171 | + class TestUtils(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 | def test_env_vars_for_windows_tests(self, name): | |
| Back | FazBrowse Home | New Git URL |
0 commit comments