Skip to content

Commit

Permalink
Merge pull request #761 from akaihola/git-test-cache
Browse files Browse the repository at this point in the history
Speed up parameterized tests which use Git
  • Loading branch information
akaihola authored Jan 1, 2025
2 parents 573e0aa + 91742fb commit 6f5b4cf
Show file tree
Hide file tree
Showing 14 changed files with 560 additions and 305 deletions.
2 changes: 1 addition & 1 deletion constraints-oldest.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# interpreter and Python ependencies. Keep this up-to-date with minimum
# versions in `setup.cfg`.
black==22.3.0
darkgraylib==2.0.1
darkgraylib==2.1.0
defusedxml==0.7.1
flake8-2020==1.6.1
flake8-bugbear==22.1.11
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ ignore = [
"ANN001", # Missing type annotation for function argument
"ANN201", # Missing return type annotation for public function
"ANN204", # Missing return type annotation for special method `__init__`
"ARG001", # Unused function argument
"C408", # Unnecessary `dict` call (rewrite as a literal)
"PLR0913", # Too many arguments in function definition (n > 5)
"S101", # Use of `assert` detected
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ packages = find:
install_requires =
# NOTE: remember to keep `constraints-oldest.txt` in sync with these
black>=22.3.0
darkgraylib~=2.0.1
darkgraylib~=2.1.0
toml>=0.10.0
typing_extensions>=4.0.1
# NOTE: remember to keep `.github/workflows/python-package.yml` in sync
Expand Down
23 changes: 23 additions & 0 deletions src/darker/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""Configuration and fixtures for the Pytest based test suite."""

import pytest

try:
from black.files import _load_toml
except ImportError:
# Black 24.1.1 and earlier don't have `_load_toml`.
_load_toml = None # type: ignore[assignment]


@pytest.fixture
def load_toml_cache_clear() -> None:
"""Clear LRU caching in `black.files._load_toml` before each test.
To use this on all test cases in a test module, add this to the top::
pytestmark = pytest.mark.usefixtures("load_toml_cache_clear")
"""
if _load_toml:
# Black 24.1.1 and earlier don't have `_load_toml`, so no LRU cache to clear.
_load_toml.cache_clear()
11 changes: 11 additions & 0 deletions src/darker/tests/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from typing import Generator, Optional
from unittest.mock import patch

from darkgraylib.testtools.git_repo_plugin import GitRepoFixture


@contextmanager
def _package_present(
Expand Down Expand Up @@ -43,3 +45,12 @@ def flynt_present(present: bool) -> Generator[None, None, None]:
fake_flynt_module.code_editor = ModuleType("process") # type: ignore
fake_flynt_module.code_editor.fstringify_code_by_line = None # type: ignore
yield


@contextmanager
def unix_and_windows_newline_repos(request, tmp_path_factory):
"""Create temporary repositories for Unix and windows newlines separately."""
with GitRepoFixture.context(
request, tmp_path_factory
) as repo_unix, GitRepoFixture.context(request, tmp_path_factory) as repo_windows:
yield {"\n": repo_unix, "\r\n": repo_windows}
114 changes: 77 additions & 37 deletions src/darker/tests/test_command_line.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# pylint: disable=too-many-arguments,too-many-locals,use-dict-literal
"""Unit tests for `darker.command_line` and `darker.__main__`."""

"""Unit tests for :mod:`darker.command_line` and :mod:`darker.__main__`"""
# pylint: disable=too-many-arguments,too-many-locals
# pylint: disable=no-member,redefined-outer-name,unused-argument,use-dict-literal

from __future__ import annotations

import os
import re
Expand All @@ -22,10 +25,14 @@
from darker.tests.helpers import flynt_present, isort_present
from darkgraylib.config import ConfigurationError
from darkgraylib.git import RevisionRange
from darkgraylib.testtools.git_repo_plugin import GitRepoFixture
from darkgraylib.testtools.helpers import raises_if_exception
from darkgraylib.utils import TextDocument, joinlines

pytestmark = pytest.mark.usefixtures("find_project_root_cache_clear")
# Clear LRU caches for `find_project_root()` and `_load_toml()` before each test
pytestmark = pytest.mark.usefixtures(
"find_project_root_cache_clear", "load_toml_cache_clear"
)


@pytest.mark.kwparametrize(
Expand Down Expand Up @@ -463,6 +470,24 @@ def test_help_with_flynt_package(capsys):
)


@pytest.fixture(scope="module")
def black_options_files(request, tmp_path_factory):
"""Fixture for the `test_black_options` test."""
with GitRepoFixture.context(request, tmp_path_factory) as repo:
(repo.root / "pyproject.toml").write_bytes(b"[tool.black]\n")
(repo.root / "black.cfg").write_text(
dedent(
"""
[tool.black]
line-length = 81
skip-string-normalization = false
target-version = 'py38'
"""
)
)
yield repo.add({"main.py": 'print("Hello World!")\n'}, commit="Initial commit")


@pytest.mark.kwparametrize(
dict(options=[], expect=call()),
dict(
Expand Down Expand Up @@ -547,34 +572,35 @@ def test_help_with_flynt_package(capsys):
),
),
)
def test_black_options(monkeypatch, tmpdir, git_repo, options, expect):
"""Black options from the command line are passed correctly to Black"""
monkeypatch.chdir(tmpdir)
(tmpdir / "pyproject.toml").write("[tool.black]\n")
(tmpdir / "black.cfg").write(
dedent(
"""
[tool.black]
line-length = 81
skip-string-normalization = false
target-version = 'py38'
"""
)
)
added_files = git_repo.add(
{"main.py": 'print("Hello World!")\n'}, commit="Initial commit"
)
added_files["main.py"].write_bytes(b'print ("Hello World!")\n')
def test_black_options(black_options_files, options, expect):
"""Black options from the command line are passed correctly to Black."""
# The Git repository set up by the module-scope `black_options_repo` fixture is
# shared by all test cases. The "main.py" file modified by the test run needs to be
# reset to its original content before the next test case.
black_options_files["main.py"].write_bytes(b'print ("Hello World!")\n')
with patch.object(
black_formatter, "Mode", wraps=black_formatter.Mode
) as file_mode_class:

main(options + [str(path) for path in added_files.values()])
main(options + [str(path) for path in black_options_files.values()])

assert black_options_files["main.py"].read_bytes() == b'print("Hello World!")\n'
_, expect_args, expect_kwargs = expect
file_mode_class.assert_called_once_with(*expect_args, **expect_kwargs)


@pytest.fixture(scope="module")
def black_config_file_and_options_files(request, tmp_path_factory):
"""Git repository fixture for the `test_black_config_file_and_options` test."""
with GitRepoFixture.context(request, tmp_path_factory) as repo:
repo_files = repo.add(
{"main.py": "foo", "pyproject.toml": "* placeholder, will be overwritten"},
commit="Initial commit",
)
repo_files["main.py"].write_bytes(b"a = [1, 2,]")
yield repo_files


@pytest.mark.kwparametrize(
dict(config=[], options=[], expect=call()),
dict(
Expand Down Expand Up @@ -673,23 +699,33 @@ def test_black_options(monkeypatch, tmpdir, git_repo, options, expect):
expect=call(preview=True),
),
)
def test_black_config_file_and_options(git_repo, config, options, expect):
def test_black_config_file_and_options(
black_config_file_and_options_files, config, options, expect
):
"""Black configuration file and command line options are combined correctly"""
added_files = git_repo.add(
{"main.py": "foo", "pyproject.toml": joinlines(["[tool.black]"] + config)},
commit="Initial commit",
)
added_files["main.py"].write_bytes(b"a = [1, 2,]")
repo_files = black_config_file_and_options_files
repo_files["pyproject.toml"].write_text(joinlines(["[tool.black]", *config]))
mode_class_mock = Mock(wraps=black_formatter.Mode)
# Speed up tests by mocking `format_str` to skip running Black
format_str = Mock(return_value="a = [1, 2,]")
with patch.multiple(black_formatter, Mode=mode_class_mock, format_str=format_str):

main(options + [str(path) for path in added_files.values()])
main(options + [str(path) for path in repo_files.values()])

assert mode_class_mock.call_args_list == [expect]


@pytest.fixture(scope="module")
def options_repo(request, tmp_path_factory):
"""Git repository fixture for the `test_options` test."""
with GitRepoFixture.context(request, tmp_path_factory) as repo:
paths = repo.add(
{"a.py": "1\n", "b.py": "2\n", "my.cfg": ""}, commit="Initial commit"
)
paths["a.py"].write_bytes(b"one\n")
yield repo


@pytest.mark.kwparametrize(
dict(
options=["a.py"],
Expand Down Expand Up @@ -784,41 +820,45 @@ def test_black_config_file_and_options(git_repo, config, options, expect):
),
),
)
def test_options(git_repo, options, expect):
def test_options(options_repo, monkeypatch, options, expect):
"""The main engine is called with correct parameters based on the command line
Executed in a clean directory so Darker's own ``pyproject.toml`` doesn't interfere.
"""
paths = git_repo.add(
{"a.py": "1\n", "b.py": "2\n", "my.cfg": ""}, commit="Initial commit"
)
paths["a.py"].write_bytes(b"one\n")
with patch('darker.__main__.format_edited_parts') as format_edited_parts:
monkeypatch.chdir(options_repo.root)

retval = main(options)

expect_formatter = BlackFormatter()
expect_formatter.config = expect[4]
actual_formatter = format_edited_parts.call_args.args[4]
assert actual_formatter.config == expect_formatter.config
expect = (Path(git_repo.root), expect[1]) + expect[2:4] + (expect_formatter,)
expect = (Path(options_repo.root), expect[1]) + expect[2:4] + (expect_formatter,)
format_edited_parts.assert_called_once_with(
*expect, report_unmodified=False, workers=1
)
assert retval == 0


@pytest.fixture(scope="module")
def main_retval_repo(request, tmp_path_factory):
"""Git repository fixture for the `test_main_retval` test."""
with GitRepoFixture.context(request, tmp_path_factory) as repo:
repo.add({"a.py": ""}, commit="Initial commit")
yield


@pytest.mark.kwparametrize(
dict(arguments=["a.py"], changes=False),
dict(arguments=["a.py"], changes=True),
dict(arguments=["--check", "a.py"], changes=False),
dict(arguments=["--check", "a.py"], changes=True, expect_retval=1),
expect_retval=0,
)
def test_main_retval(git_repo, arguments, changes, expect_retval):
def test_main_retval(main_retval_repo, arguments, changes, expect_retval):
"""``main()`` return value is correct based on ``--check`` and reformatting."""
git_repo.add({"a.py": ""}, commit="Initial commit")
format_edited_parts = Mock()
format_edited_parts.return_value = (
[
Expand Down
Loading

0 comments on commit 6f5b4cf

Please sign in to comment.