Option C Tools
Loading...
Searching...
No Matches
test_func_pattern_fixing.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# tests/tests_formatter/test_func_pattern_fixing.py
4
5"""
6Tests for func pattern fixing.
7
8Purpose
9-------
10Verify that the formatter correctly fixes missing func = "..." patterns.
11
12Responsibilities
13----------------
14- Test addition of func variable to functions with _dbg() calls.
15- Test skipping of functions without _dbg() calls.
16- Test skipping of functions that already have the pattern.
17- Test proper indentation.
18
19Diagnostics
20-----------
21Domain: TESTS.FORMATTER
22Levels:
23 L2 — test lifecycle
24 L3 — test case setup
25 L4 — AST parsing and pattern insertion
26
27Contracts
28---------
29- func variable must be first statement in function.
30- Pattern is skipped if already present.
31- Functions without _dbg() are never modified.
32"""
33
34import ast
35import pytest
36from pathlib import Path
37
38from oct.formatter.oct_format import (
39 fix_func_pattern,
40 FormatterContext,
41 _detect_indent_style,
42)
43
44
45def _make_nontrivial(content: str) -> str:
46 """Pad content to be > 20 lines."""
47 lines = content.split('\n')
48 while len(lines) < 22:
49 lines.append("# padding")
50 return '\n'.join(lines)
51
52
53def test_add_func_pattern_single_function(temp_project_root: Path, formatter_ctx_fix: FormatterContext):
54 """Test adding func pattern to a single function."""
55 test_file = temp_project_root / "test_single_func.py"
56 content = _make_nontrivial(
57 "#!/usr/bin/env python3\n"
58 "# -*- coding: utf-8 -*-\n"
59 "# test.py\n"
60 "\n"
61 "from oc_diagnostics import _dbg\n"
62 "\n"
63 "def my_func():\n"
64 " _dbg('TEST', 'start')\n"
65 " return 42\n"
66 )
67 test_file.write_text(content, encoding="utf-8")
68
69 changed, new_content, messages = fix_func_pattern(test_file, content, formatter_ctx_fix)
70
71 assert changed is True
72 assert 'func = "my_func"' in new_content
73
74
75def test_add_func_pattern_multiple_functions(temp_project_root: Path, formatter_ctx_fix: FormatterContext):
76 """Test adding func pattern to multiple functions."""
77 test_file = temp_project_root / "test_multi_func.py"
78 content = _make_nontrivial(
79 "#!/usr/bin/env python3\n"
80 "# -*- coding: utf-8 -*-\n"
81 "# test.py\n"
82 "\n"
83 "from oc_diagnostics import _dbg\n"
84 "\n"
85 "def foo():\n"
86 " _dbg('TEST', 'foo')\n"
87 "\n"
88 "def bar():\n"
89 " _dbg('TEST', 'bar')\n"
90 )
91 test_file.write_text(content, encoding="utf-8")
92
93 changed, new_content, messages = fix_func_pattern(test_file, content, formatter_ctx_fix)
94
95 assert changed is True
96 assert 'func = "foo"' in new_content
97 assert 'func = "bar"' in new_content
98
99
100def test_skip_function_without_dbg(temp_project_root: Path, formatter_ctx_fix: FormatterContext):
101 """Test that functions without _dbg() are not modified."""
102 test_file = temp_project_root / "test_no_dbg.py"
103 content = _make_nontrivial(
104 "#!/usr/bin/env python3\n"
105 "# -*- coding: utf-8 -*-\n"
106 "# test.py\n"
107 "\n"
108 "from oc_diagnostics import _dbg\n"
109 "\n"
110 "def no_dbg():\n"
111 " '''No diagnostics.'''\n"
112 " return 42\n"
113 "\n"
114 "def has_dbg():\n"
115 " _dbg('TEST', 'test')\n"
116 )
117 test_file.write_text(content, encoding="utf-8")
118
119 changed, new_content, messages = fix_func_pattern(test_file, content, formatter_ctx_fix)
120
121 assert changed is True
122 assert 'func = "has_dbg"' in new_content
123 assert 'func = "no_dbg"' not in new_content
124
125
126def test_skip_existing_pattern(temp_project_root: Path, formatter_ctx_fix: FormatterContext):
127 """Test that existing func patterns are not duplicated."""
128 test_file = temp_project_root / "test_existing_pattern.py"
129 content = _make_nontrivial(
130 "#!/usr/bin/env python3\n"
131 "# -*- coding: utf-8 -*-\n"
132 "# test.py\n"
133 "\n"
134 "from oc_diagnostics import _dbg\n"
135 "\n"
136 "def my_func():\n"
137 " func = 'my_func'\n"
138 " _dbg('TEST', func, 'start', 2)\n"
139 )
140 test_file.write_text(content, encoding="utf-8")
141
142 changed, _, messages = fix_func_pattern(test_file, content, formatter_ctx_fix)
143
144 assert changed is False
145
146
147def test_skip_trivial_files(temp_project_root: Path, formatter_ctx_fix: FormatterContext):
148 """Test that trivial files are skipped."""
149 test_file = temp_project_root / "test_trivial_func.py"
150 content = (
151 "def foo():\n"
152 " _dbg('TEST', 'test')\n"
153 )
154 test_file.write_text(content, encoding="utf-8")
155
156 changed, _, messages = fix_func_pattern(test_file, content, formatter_ctx_fix)
157
158 assert changed is False
159
160
161# ============================================================
162# OI-425: indentation style detection and tab preservation
163# ============================================================
164
165
167 """``_detect_indent_style`` returns '\\t' when tabs dominate."""
168 lines = [
169 "def foo():",
170 "\treturn 1",
171 "\tfor i in range(3):",
172 "\t\tprint(i)",
173 " stray = 1", # one space-indented line — still minority
174 ]
175 assert _detect_indent_style(lines) == "\t"
176
177
179 """``_detect_indent_style`` returns ' ' when spaces dominate."""
180 lines = [
181 "def foo():",
182 " return 1",
183 " for i in range(3):",
184 " print(i)",
185 ]
186 assert _detect_indent_style(lines) == " "
187
188
190 """Blank lines must not affect the tally."""
191 lines = ["", "\n", " ", "\tcode", "\tcode2"]
192 assert _detect_indent_style(lines) == "\t"
193
194
196 temp_project_root: Path, formatter_ctx_fix: FormatterContext,
197) -> None:
198 """A tab-indented file receives a tab-indented ``func = "..."`` line."""
199 test_file = temp_project_root / "test_tabs.py"
200 content = _make_nontrivial(
201 "#!/usr/bin/env python3\n"
202 "# -*- coding: utf-8 -*-\n"
203 "# test.py\n"
204 "\n"
205 "from oc_diagnostics import _dbg\n"
206 "\n"
207 "def my_func():\n"
208 "\t_dbg('TEST', 'start')\n"
209 "\treturn 42\n"
210 )
211 test_file.write_text(content, encoding="utf-8")
212
213 changed, new_content, _ = fix_func_pattern(
214 test_file, content, formatter_ctx_fix,
215 )
216
217 assert changed is True
218 assert '\tfunc = "my_func"' in new_content
219 # No space-indented injection mixed into the tab file.
220 assert ' func = "my_func"' not in new_content
221 # Output must remain valid Python.
222 ast.parse(new_content)
223
224
226 temp_project_root: Path, formatter_ctx_fix: FormatterContext,
227) -> None:
228 """Baseline: a space-indented file still receives space injection."""
229 test_file = temp_project_root / "test_spaces.py"
230 content = _make_nontrivial(
231 "#!/usr/bin/env python3\n"
232 "# -*- coding: utf-8 -*-\n"
233 "# test.py\n"
234 "\n"
235 "from oc_diagnostics import _dbg\n"
236 "\n"
237 "def my_func():\n"
238 " _dbg('TEST', 'start')\n"
239 " return 42\n"
240 )
241 test_file.write_text(content, encoding="utf-8")
242
243 changed, new_content, _ = fix_func_pattern(
244 test_file, content, formatter_ctx_fix,
245 )
246
247 assert changed is True
248 assert ' func = "my_func"' in new_content
249 ast.parse(new_content)
250
251
253 temp_project_root: Path, formatter_ctx_fix: FormatterContext,
254) -> None:
255 """A class method in a tab-indented file gets a ``\\t\\t`` injection."""
256 test_file = temp_project_root / "test_tabs_class.py"
257 content = _make_nontrivial(
258 "#!/usr/bin/env python3\n"
259 "# -*- coding: utf-8 -*-\n"
260 "# test.py\n"
261 "\n"
262 "from oc_diagnostics import _dbg\n"
263 "\n"
264 "class Thing:\n"
265 "\tdef run(self):\n"
266 "\t\t_dbg('TEST', 'run')\n"
267 "\t\treturn 1\n"
268 )
269 test_file.write_text(content, encoding="utf-8")
270
271 changed, new_content, _ = fix_func_pattern(
272 test_file, content, formatter_ctx_fix,
273 )
274
275 assert changed is True
276 assert '\t\tfunc = "run"' in new_content
277 ast.parse(new_content)
None test_fix_func_pattern_tab_indented_class_method(Path temp_project_root, FormatterContext formatter_ctx_fix)
None test_fix_func_pattern_preserves_spaces(Path temp_project_root, FormatterContext formatter_ctx_fix)
test_skip_existing_pattern(Path temp_project_root, FormatterContext formatter_ctx_fix)
None test_fix_func_pattern_preserves_tabs(Path temp_project_root, FormatterContext formatter_ctx_fix)
test_skip_function_without_dbg(Path temp_project_root, FormatterContext formatter_ctx_fix)
test_skip_trivial_files(Path temp_project_root, FormatterContext formatter_ctx_fix)
test_add_func_pattern_multiple_functions(Path temp_project_root, FormatterContext formatter_ctx_fix)
test_add_func_pattern_single_function(Path temp_project_root, FormatterContext formatter_ctx_fix)