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

1"""Integration tests for Black tool definition. 

2 

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""" 

6 

7from __future__ import annotations 

8 

9import shutil 

10from collections.abc import Callable 

11from pathlib import Path 

12from typing import TYPE_CHECKING 

13 

14import pytest 

15from assertpy import assert_that 

16 

17if TYPE_CHECKING: 

18 from lintro.plugins.base import BaseToolPlugin 

19 

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) 

25 

26 

27@pytest.fixture 

28def temp_python_file_unformatted(tmp_path: Path) -> str: 

29 """Create a temporary Python file with formatting issues. 

30 

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 

36 

37 Args: 

38 tmp_path: Pytest fixture providing a temporary directory. 

39 

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 

48 

49class MyClass: 

50 def __init__(self,name,value): 

51 self.name=name 

52 self.value=value 

53 

54x = {"a":1,"b":2,"c":3} 

55y = [1,2,3,4,5] 

56""", 

57 ) 

58 return str(file_path) 

59 

60 

61@pytest.fixture 

62def temp_python_file_formatted(tmp_path: Path) -> str: 

63 """Create a temporary Python file that is already formatted. 

64 

65 Creates a file containing properly formatted Python code that 

66 Black should leave unchanged. 

67 

68 Args: 

69 tmp_path: Pytest fixture providing a temporary directory. 

70 

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.""" 

78 

79 

80def foo(x, y, z): 

81 """Add three numbers.""" 

82 return x + y + z 

83 

84 

85class MyClass: 

86 """A simple class.""" 

87 

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) 

95 

96 

97# --- Tests for BlackPlugin definition --- 

98 

99 

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. 

114 

115 Tests that the plugin definition exposes the expected values for 

116 name and can_fix attributes. 

117 

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) 

125 

126 

127def test_definition_file_patterns(get_plugin: Callable[[str], BaseToolPlugin]) -> None: 

128 """Verify BlackPlugin definition includes Python file patterns. 

129 

130 Tests that the plugin is configured to handle Python files (*.py). 

131 

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") 

137 

138 

139# --- Integration tests for black check command --- 

140 

141 

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. 

157 

158 Runs Black in check mode on files with different formatting states 

159 and verifies the expected issue count. 

160 

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], {}) 

170 

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) 

177 

178 

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. 

184 

185 Runs Black on an empty directory and verifies a result is returned 

186 without errors. 

187 

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)], {}) 

194 

195 assert_that(result).is_not_none() 

196 

197 

198# --- Integration tests for black fix command --- 

199 

200 

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. 

206 

207 Runs Black fix on a file with formatting issues and verifies 

208 the file content changes. 

209 

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() 

216 

217 result = black_plugin.fix([temp_python_file_unformatted], {}) 

218 

219 assert_that(result).is_not_none() 

220 assert_that(result.name).is_equal_to("black") 

221 assert_that(result.success).is_true() 

222 

223 new_content = Path(temp_python_file_unformatted).read_text() 

224 assert_that(new_content).is_not_equal_to(original) 

225 

226 

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. 

232 

233 Runs Black fix on a properly formatted file and verifies 

234 the file content remains unchanged. 

235 

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() 

242 

243 result = black_plugin.fix([temp_python_file_formatted], {}) 

244 

245 assert_that(result).is_not_none() 

246 assert_that(result.success).is_true() 

247 

248 new_content = Path(temp_python_file_formatted).read_text() 

249 assert_that(new_content).is_equal_to(original) 

250 

251 

252# --- Tests for BlackPlugin.set_options method --- 

253 

254 

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. 

270 

271 Tests that plugin options can be set and retrieved correctly. 

272 

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)