Coverage for tests / unit / tools / rustfmt / test_execution.py: 100%

82 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2026-04-03 18:53 +0000

1"""Unit tests for rustfmt plugin execution.""" 

2 

3from __future__ import annotations 

4 

5from pathlib import Path 

6from unittest.mock import patch 

7 

8from assertpy import assert_that 

9 

10from lintro.tools.definitions.rustfmt import RustfmtPlugin 

11 

12# ============================================================================= 

13# Tests for RustfmtPlugin.check method 

14# ============================================================================= 

15 

16 

17def test_check_no_cargo_toml( 

18 rustfmt_plugin: RustfmtPlugin, 

19 tmp_path: Path, 

20) -> None: 

21 """Check skips gracefully when no Cargo.toml found. 

22 

23 Args: 

24 rustfmt_plugin: The RustfmtPlugin instance to test. 

25 tmp_path: Temporary directory path for test files. 

26 """ 

27 test_file = tmp_path / "test.rs" 

28 test_file.write_text("fn main() {}") 

29 

30 with patch( 

31 "lintro.plugins.execution_preparation.verify_tool_version", 

32 return_value=None, 

33 ): 

34 result = rustfmt_plugin.check([str(test_file)], {}) 

35 

36 assert_that(result.success).is_true() 

37 assert_that(result.output).contains("No Cargo.toml found") 

38 

39 

40def test_check_with_mocked_subprocess_success( 

41 rustfmt_plugin: RustfmtPlugin, 

42 tmp_path: Path, 

43) -> None: 

44 """Check returns success when no issues found. 

45 

46 Args: 

47 rustfmt_plugin: The RustfmtPlugin instance to test. 

48 tmp_path: Temporary directory path for test files. 

49 """ 

50 # Create Cargo.toml to enable rustfmt check 

51 cargo_toml = tmp_path / "Cargo.toml" 

52 cargo_toml.write_text('[package]\nname = "test"\nversion = "0.1.0"') 

53 

54 test_file = tmp_path / "src" / "main.rs" 

55 test_file.parent.mkdir(parents=True, exist_ok=True) 

56 test_file.write_text("fn main() {}\n") 

57 

58 with patch( 

59 "lintro.plugins.execution_preparation.verify_tool_version", 

60 return_value=None, 

61 ): 

62 with patch.object( 

63 rustfmt_plugin, 

64 "_run_subprocess", 

65 return_value=(True, ""), 

66 ): 

67 result = rustfmt_plugin.check([str(test_file)], {}) 

68 

69 assert_that(result.success).is_true() 

70 assert_that(result.issues_count).is_equal_to(0) 

71 

72 

73def test_check_with_mocked_subprocess_issues( 

74 rustfmt_plugin: RustfmtPlugin, 

75 tmp_path: Path, 

76) -> None: 

77 """Check returns issues when formatting problems found. 

78 

79 Args: 

80 rustfmt_plugin: The RustfmtPlugin instance to test. 

81 tmp_path: Temporary directory path for test files. 

82 """ 

83 cargo_toml = tmp_path / "Cargo.toml" 

84 cargo_toml.write_text('[package]\nname = "test"\nversion = "0.1.0"') 

85 

86 test_file = tmp_path / "src" / "main.rs" 

87 test_file.parent.mkdir(parents=True, exist_ok=True) 

88 test_file.write_text("fn main(){let x=1;}") 

89 

90 mock_output = ( 

91 "Diff in src/main.rs:1:\n" 

92 "-fn main(){let x=1;}\n" 

93 "+fn main() {\n" 

94 "+ let x = 1;\n" 

95 "+}" 

96 ) 

97 

98 with patch( 

99 "lintro.plugins.execution_preparation.verify_tool_version", 

100 return_value=None, 

101 ): 

102 with patch.object( 

103 rustfmt_plugin, 

104 "_run_subprocess", 

105 return_value=(False, mock_output), 

106 ): 

107 result = rustfmt_plugin.check([str(test_file)], {}) 

108 

109 assert_that(result.success).is_false() 

110 assert_that(result.issues_count).is_greater_than(0) 

111 

112 

113def test_check_with_no_rust_files( 

114 rustfmt_plugin: RustfmtPlugin, 

115 tmp_path: Path, 

116) -> None: 

117 """Check returns success when no Rust files found. 

118 

119 Args: 

120 rustfmt_plugin: The RustfmtPlugin instance to test. 

121 tmp_path: Temporary directory path for test files. 

122 """ 

123 non_rs_file = tmp_path / "test.txt" 

124 non_rs_file.write_text("Not a Rust file") 

125 

126 with patch( 

127 "lintro.plugins.execution_preparation.verify_tool_version", 

128 return_value=None, 

129 ): 

130 result = rustfmt_plugin.check([str(non_rs_file)], {}) 

131 

132 assert_that(result.success).is_true() 

133 assert_that(result.output).contains("No") 

134 

135 

136# ============================================================================= 

137# Tests for RustfmtPlugin.fix method 

138# ============================================================================= 

139 

140 

141def test_fix_no_cargo_toml( 

142 rustfmt_plugin: RustfmtPlugin, 

143 tmp_path: Path, 

144) -> None: 

145 """Fix skips gracefully when no Cargo.toml found. 

146 

147 Args: 

148 rustfmt_plugin: The RustfmtPlugin instance to test. 

149 tmp_path: Temporary directory path for test files. 

150 """ 

151 test_file = tmp_path / "test.rs" 

152 test_file.write_text("fn main() {}") 

153 

154 with patch( 

155 "lintro.plugins.execution_preparation.verify_tool_version", 

156 return_value=None, 

157 ): 

158 result = rustfmt_plugin.fix([str(test_file)], {}) 

159 

160 assert_that(result.success).is_true() 

161 assert_that(result.output).contains("No Cargo.toml found") 

162 assert_that(result.fixed_issues_count).is_equal_to(0) 

163 

164 

165def test_fix_with_mocked_subprocess_success( 

166 rustfmt_plugin: RustfmtPlugin, 

167 tmp_path: Path, 

168) -> None: 

169 """Fix returns success when fixes are applied. 

170 

171 Args: 

172 rustfmt_plugin: The RustfmtPlugin instance to test. 

173 tmp_path: Temporary directory path for test files. 

174 """ 

175 cargo_toml = tmp_path / "Cargo.toml" 

176 cargo_toml.write_text('[package]\nname = "test"\nversion = "0.1.0"') 

177 

178 test_file = tmp_path / "src" / "main.rs" 

179 test_file.parent.mkdir(parents=True, exist_ok=True) 

180 test_file.write_text("fn main(){}") 

181 

182 call_count = 0 

183 

184 def mock_run( 

185 cmd: list[str], 

186 timeout: int, 

187 cwd: str | None = None, 

188 ) -> tuple[bool, str]: 

189 """Mock subprocess that returns diff on check, success on fix. 

190 

191 Args: 

192 cmd: Command list. 

193 timeout: Timeout in seconds. 

194 cwd: Working directory. 

195 

196 Returns: 

197 Tuple of (success, output). 

198 """ 

199 nonlocal call_count 

200 call_count += 1 

201 if call_count == 1: 

202 # First check - issues found 

203 return (False, "Diff in src/main.rs:1:") 

204 elif call_count == 2: 

205 # Fix command 

206 return (True, "") 

207 else: 

208 # Verification - no issues 

209 return (True, "") 

210 

211 with patch( 

212 "lintro.plugins.execution_preparation.verify_tool_version", 

213 return_value=None, 

214 ): 

215 with patch.object(rustfmt_plugin, "_run_subprocess", side_effect=mock_run): 

216 result = rustfmt_plugin.fix([str(test_file)], {}) 

217 

218 assert_that(result.success).is_true() 

219 assert_that(result.fixed_issues_count).is_equal_to(1) 

220 # Verify the mock was called expected number of times (check + fix + verify) 

221 assert_that(call_count).is_equal_to(3) 

222 

223 

224def test_fix_with_nothing_to_fix( 

225 rustfmt_plugin: RustfmtPlugin, 

226 tmp_path: Path, 

227) -> None: 

228 """Fix returns success when no fixes needed. 

229 

230 Args: 

231 rustfmt_plugin: The RustfmtPlugin instance to test. 

232 tmp_path: Temporary directory path for test files. 

233 """ 

234 cargo_toml = tmp_path / "Cargo.toml" 

235 cargo_toml.write_text('[package]\nname = "test"\nversion = "0.1.0"') 

236 

237 test_file = tmp_path / "src" / "main.rs" 

238 test_file.parent.mkdir(parents=True, exist_ok=True) 

239 test_file.write_text("fn main() {}\n") 

240 

241 with patch( 

242 "lintro.plugins.execution_preparation.verify_tool_version", 

243 return_value=None, 

244 ): 

245 with patch.object( 

246 rustfmt_plugin, 

247 "_run_subprocess", 

248 return_value=(True, ""), 

249 ): 

250 result = rustfmt_plugin.fix([str(test_file)], {}) 

251 

252 assert_that(result.success).is_true() 

253 assert_that(result.issues_count).is_equal_to(0) 

254 assert_that(result.fixed_issues_count).is_equal_to(0)