Coverage for tests / integration / tools / test_black_integration.py: 100%
61 statements
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
1"""Integration tests for Black tool definition.
3These tests require black to be installed and available in PATH.
4They verify the BlackPlugin definition, check command, fix command, and set_options method.
5"""
7from __future__ import annotations
9import shutil
10from collections.abc import Callable
11from pathlib import Path
12from typing import TYPE_CHECKING
14import pytest
15from assertpy import assert_that
17if TYPE_CHECKING:
18 from lintro.plugins.base import BaseToolPlugin
20# Skip all tests if black is not installed
21pytestmark = pytest.mark.skipif(
22 shutil.which("black") is None,
23 reason="black not installed",
24)
27@pytest.fixture
28def temp_python_file_unformatted(tmp_path: Path) -> str:
29 """Create a temporary Python file with formatting issues.
31 Creates a file containing code with formatting issues that Black
32 should fix, including:
33 - Missing spaces around operators
34 - Missing spaces after commas
35 - Compact dictionary and list formatting
37 Args:
38 tmp_path: Pytest fixture providing a temporary directory.
40 Returns:
41 Path to the created file as a string.
42 """
43 file_path = tmp_path / "unformatted.py"
44 file_path.write_text(
45 """\
46def foo(x,y,z):
47 return x+y+z
49class MyClass:
50 def __init__(self,name,value):
51 self.name=name
52 self.value=value
54x = {"a":1,"b":2,"c":3}
55y = [1,2,3,4,5]
56""",
57 )
58 return str(file_path)
61@pytest.fixture
62def temp_python_file_formatted(tmp_path: Path) -> str:
63 """Create a temporary Python file that is already formatted.
65 Creates a file containing properly formatted Python code that
66 Black should leave unchanged.
68 Args:
69 tmp_path: Pytest fixture providing a temporary directory.
71 Returns:
72 Path to the created file as a string.
73 """
74 file_path = tmp_path / "formatted.py"
75 file_path.write_text(
76 '''\
77"""A properly formatted module."""
80def foo(x, y, z):
81 """Add three numbers."""
82 return x + y + z
85class MyClass:
86 """A simple class."""
88 def __init__(self, name, value):
89 """Initialize the class."""
90 self.name = name
91 self.value = value
92''',
93 )
94 return str(file_path)
97# --- Tests for BlackPlugin definition ---
100@pytest.mark.parametrize(
101 ("attr", "expected"),
102 [
103 ("name", "black"),
104 ("can_fix", True),
105 ],
106 ids=["name", "can_fix"],
107)
108def test_definition_attributes(
109 get_plugin: Callable[[str], BaseToolPlugin],
110 attr: str,
111 expected: object,
112) -> None:
113 """Verify BlackPlugin definition has correct attribute values.
115 Tests that the plugin definition exposes the expected values for
116 name and can_fix attributes.
118 Args:
119 get_plugin: Fixture factory to get plugin instances.
120 attr: The attribute name to check on the definition.
121 expected: The expected value of the attribute.
122 """
123 black_plugin = get_plugin("black")
124 assert_that(getattr(black_plugin.definition, attr)).is_equal_to(expected)
127def test_definition_file_patterns(get_plugin: Callable[[str], BaseToolPlugin]) -> None:
128 """Verify BlackPlugin definition includes Python file patterns.
130 Tests that the plugin is configured to handle Python files (*.py).
132 Args:
133 get_plugin: Fixture factory to get plugin instances.
134 """
135 black_plugin = get_plugin("black")
136 assert_that(black_plugin.definition.file_patterns).contains("*.py")
139# --- Integration tests for black check command ---
142@pytest.mark.parametrize(
143 ("file_fixture", "expect_issues"),
144 [
145 ("temp_python_file_unformatted", True),
146 ("temp_python_file_formatted", False),
147 ],
148 ids=["unformatted_file", "formatted_file"],
149)
150def test_check_file_formatting_state(
151 get_plugin: Callable[[str], BaseToolPlugin],
152 file_fixture: str,
153 expect_issues: bool,
154 request: pytest.FixtureRequest,
155) -> None:
156 """Verify Black check correctly detects formatting state.
158 Runs Black in check mode on files with different formatting states
159 and verifies the expected issue count.
161 Args:
162 get_plugin: Fixture factory to get plugin instances.
163 file_fixture: Name of the fixture providing the file path.
164 expect_issues: Whether issues are expected (True for unformatted).
165 request: Pytest request fixture for dynamic fixture access.
166 """
167 file_path = request.getfixturevalue(file_fixture)
168 black_plugin = get_plugin("black")
169 result = black_plugin.check([file_path], {})
171 assert_that(result).is_not_none()
172 assert_that(result.name).is_equal_to("black")
173 if expect_issues:
174 assert_that(result.issues_count).is_greater_than(0)
175 else:
176 assert_that(result.issues_count).is_equal_to(0)
179def test_check_empty_directory(
180 get_plugin: Callable[[str], BaseToolPlugin],
181 tmp_path: Path,
182) -> None:
183 """Verify Black check handles empty directories gracefully.
185 Runs Black on an empty directory and verifies a result is returned
186 without errors.
188 Args:
189 get_plugin: Fixture factory to get plugin instances.
190 tmp_path: Pytest fixture providing a temporary directory.
191 """
192 black_plugin = get_plugin("black")
193 result = black_plugin.check([str(tmp_path)], {})
195 assert_that(result).is_not_none()
198# --- Integration tests for black fix command ---
201def test_fix_formats_file(
202 get_plugin: Callable[[str], BaseToolPlugin],
203 temp_python_file_unformatted: str,
204) -> None:
205 """Verify Black fix reformats unformatted files.
207 Runs Black fix on a file with formatting issues and verifies
208 the file content changes.
210 Args:
211 get_plugin: Fixture factory to get plugin instances.
212 temp_python_file_unformatted: Path to file with formatting issues.
213 """
214 black_plugin = get_plugin("black")
215 original = Path(temp_python_file_unformatted).read_text()
217 result = black_plugin.fix([temp_python_file_unformatted], {})
219 assert_that(result).is_not_none()
220 assert_that(result.name).is_equal_to("black")
221 assert_that(result.success).is_true()
223 new_content = Path(temp_python_file_unformatted).read_text()
224 assert_that(new_content).is_not_equal_to(original)
227def test_fix_preserves_formatted_file(
228 get_plugin: Callable[[str], BaseToolPlugin],
229 temp_python_file_formatted: str,
230) -> None:
231 """Verify Black fix does not change already formatted files.
233 Runs Black fix on a properly formatted file and verifies
234 the file content remains unchanged.
236 Args:
237 get_plugin: Fixture factory to get plugin instances.
238 temp_python_file_formatted: Path to properly formatted file.
239 """
240 black_plugin = get_plugin("black")
241 original = Path(temp_python_file_formatted).read_text()
243 result = black_plugin.fix([temp_python_file_formatted], {})
245 assert_that(result).is_not_none()
246 assert_that(result.success).is_true()
248 new_content = Path(temp_python_file_formatted).read_text()
249 assert_that(new_content).is_equal_to(original)
252# --- Tests for BlackPlugin.set_options method ---
255@pytest.mark.parametrize(
256 ("option_name", "option_value", "expected"),
257 [
258 ("line_length", 100, 100),
259 ("target_version", "py311", "py311"),
260 ],
261 ids=["line_length", "target_version"],
262)
263def test_set_options(
264 get_plugin: Callable[[str], BaseToolPlugin],
265 option_name: str,
266 option_value: object,
267 expected: object,
268) -> None:
269 """Verify BlackPlugin.set_options correctly sets various options.
271 Tests that plugin options can be set and retrieved correctly.
273 Args:
274 get_plugin: Fixture factory to get plugin instances.
275 option_name: Name of the option to set.
276 option_value: Value to set for the option.
277 expected: Expected value when retrieving the option.
278 """
279 black_plugin = get_plugin("black")
280 black_plugin.set_options(**{option_name: option_value})
281 assert_that(black_plugin.options.get(option_name)).is_equal_to(expected)