]> git.madduck.net Git - etc/vim.git/blob - tests/test_format.py

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

Support 3.11 / PEP 654 syntax (#3016)
[etc/vim.git] / tests / test_format.py
1 from dataclasses import replace
2 from typing import Any, Iterator, List
3 from unittest.mock import patch
4
5 import pytest
6
7 import black
8 from tests.util import (
9     DEFAULT_MODE,
10     PY36_VERSIONS,
11     THIS_DIR,
12     assert_format,
13     dump_to_stderr,
14     read_data,
15 )
16
17 SIMPLE_CASES: List[str] = [
18     "attribute_access_on_number_literals",
19     "beginning_backslash",
20     "bracketmatch",
21     "class_blank_parentheses",
22     "class_methods_new_line",
23     "collections",
24     "comments",
25     "comments2",
26     "comments3",
27     "comments4",
28     "comments5",
29     "comments6",
30     "comments_non_breaking_space",
31     "comment_after_escaped_newline",
32     "composition",
33     "composition_no_trailing_comma",
34     "docstring",
35     "empty_lines",
36     "expression",
37     "fmtonoff",
38     "fmtonoff2",
39     "fmtonoff3",
40     "fmtonoff4",
41     "fmtskip",
42     "fmtskip2",
43     "fmtskip3",
44     "fmtskip4",
45     "fmtskip5",
46     "fmtskip6",
47     "fmtskip7",
48     "fstring",
49     "function",
50     "function2",
51     "function_trailing_comma",
52     "import_spacing",
53     "power_op_spacing",
54     "remove_parens",
55     "slices",
56     "string_prefixes",
57     "torture",
58     "trailing_comma_optional_parens1",
59     "trailing_comma_optional_parens2",
60     "trailing_comma_optional_parens3",
61     "tricky_unicode_symbols",
62     "tupleassign",
63 ]
64
65 PY310_CASES: List[str] = [
66     "starred_for_target",
67     "pattern_matching_simple",
68     "pattern_matching_complex",
69     "pattern_matching_extras",
70     "pattern_matching_style",
71     "pattern_matching_generic",
72     "parenthesized_context_managers",
73 ]
74
75 PY311_CASES: List[str] = [
76     "pep_654",
77     "pep_654_style",
78 ]
79
80 PREVIEW_CASES: List[str] = [
81     # string processing
82     "cantfit",
83     "comments7",
84     "comments8",
85     "long_strings",
86     "long_strings__edge_case",
87     "long_strings__regression",
88     "percent_precedence",
89     "remove_except_parens",
90     "remove_for_brackets",
91     "one_element_subscript",
92     "remove_await_parens",
93     "return_annotation_brackets",
94 ]
95
96 SOURCES: List[str] = [
97     "src/black/__init__.py",
98     "src/black/__main__.py",
99     "src/black/brackets.py",
100     "src/black/cache.py",
101     "src/black/comments.py",
102     "src/black/concurrency.py",
103     "src/black/const.py",
104     "src/black/debug.py",
105     "src/black/files.py",
106     "src/black/linegen.py",
107     "src/black/lines.py",
108     "src/black/mode.py",
109     "src/black/nodes.py",
110     "src/black/numerics.py",
111     "src/black/output.py",
112     "src/black/parsing.py",
113     "src/black/report.py",
114     "src/black/rusty.py",
115     "src/black/strings.py",
116     "src/black/trans.py",
117     "src/blackd/__init__.py",
118     "src/blib2to3/pygram.py",
119     "src/blib2to3/pytree.py",
120     "src/blib2to3/pgen2/conv.py",
121     "src/blib2to3/pgen2/driver.py",
122     "src/blib2to3/pgen2/grammar.py",
123     "src/blib2to3/pgen2/literals.py",
124     "src/blib2to3/pgen2/parse.py",
125     "src/blib2to3/pgen2/pgen.py",
126     "src/blib2to3/pgen2/tokenize.py",
127     "src/blib2to3/pgen2/token.py",
128     "setup.py",
129     "tests/test_black.py",
130     "tests/test_blackd.py",
131     "tests/test_format.py",
132     "tests/optional.py",
133     "tests/util.py",
134     "tests/conftest.py",
135 ]
136
137
138 @pytest.fixture(autouse=True)
139 def patch_dump_to_file(request: Any) -> Iterator[None]:
140     with patch("black.dump_to_file", dump_to_stderr):
141         yield
142
143
144 def check_file(filename: str, mode: black.Mode, *, data: bool = True) -> None:
145     source, expected = read_data(filename, data=data)
146     assert_format(source, expected, mode, fast=False)
147
148
149 @pytest.mark.parametrize("filename", SIMPLE_CASES)
150 def test_simple_format(filename: str) -> None:
151     check_file(filename, DEFAULT_MODE)
152
153
154 @pytest.mark.parametrize("filename", PREVIEW_CASES)
155 def test_preview_format(filename: str) -> None:
156     check_file(filename, black.Mode(preview=True))
157
158
159 @pytest.mark.parametrize("filename", SOURCES)
160 def test_source_is_formatted(filename: str) -> None:
161     path = THIS_DIR.parent / filename
162     check_file(str(path), DEFAULT_MODE, data=False)
163
164
165 # =============== #
166 # Complex cases
167 # ============= #
168
169
170 def test_empty() -> None:
171     source = expected = ""
172     assert_format(source, expected)
173
174
175 def test_pep_572() -> None:
176     source, expected = read_data("pep_572")
177     assert_format(source, expected, minimum_version=(3, 8))
178
179
180 def test_pep_572_remove_parens() -> None:
181     source, expected = read_data("pep_572_remove_parens")
182     assert_format(source, expected, minimum_version=(3, 8))
183
184
185 def test_pep_572_do_not_remove_parens() -> None:
186     source, expected = read_data("pep_572_do_not_remove_parens")
187     # the AST safety checks will fail, but that's expected, just make sure no
188     # parentheses are touched
189     assert_format(source, expected, fast=True)
190
191
192 @pytest.mark.parametrize("major, minor", [(3, 9), (3, 10)])
193 def test_pep_572_newer_syntax(major: int, minor: int) -> None:
194     source, expected = read_data(f"pep_572_py{major}{minor}")
195     assert_format(source, expected, minimum_version=(major, minor))
196
197
198 def test_pep_570() -> None:
199     source, expected = read_data("pep_570")
200     assert_format(source, expected, minimum_version=(3, 8))
201
202
203 def test_remove_with_brackets() -> None:
204     source, expected = read_data("remove_with_brackets")
205     assert_format(
206         source,
207         expected,
208         black.Mode(preview=True),
209         minimum_version=(3, 9),
210     )
211
212
213 @pytest.mark.parametrize("filename", PY310_CASES)
214 def test_python_310(filename: str) -> None:
215     source, expected = read_data(filename)
216     mode = black.Mode(target_versions={black.TargetVersion.PY310})
217     assert_format(source, expected, mode, minimum_version=(3, 10))
218
219
220 def test_python_310_without_target_version() -> None:
221     source, expected = read_data("pattern_matching_simple")
222     mode = black.Mode()
223     assert_format(source, expected, mode, minimum_version=(3, 10))
224
225
226 def test_patma_invalid() -> None:
227     source, expected = read_data("pattern_matching_invalid")
228     mode = black.Mode(target_versions={black.TargetVersion.PY310})
229     with pytest.raises(black.parsing.InvalidInput) as exc_info:
230         assert_format(source, expected, mode, minimum_version=(3, 10))
231
232     exc_info.match("Cannot parse: 10:11")
233
234
235 @pytest.mark.parametrize("filename", PY311_CASES)
236 def test_python_311(filename: str) -> None:
237     source, expected = read_data(filename)
238     mode = black.Mode(target_versions={black.TargetVersion.PY311})
239     assert_format(source, expected, mode, minimum_version=(3, 11))
240
241
242 def test_python_2_hint() -> None:
243     with pytest.raises(black.parsing.InvalidInput) as exc_info:
244         assert_format("print 'daylily'", "print 'daylily'")
245     exc_info.match(black.parsing.PY2_HINT)
246
247
248 def test_docstring_no_string_normalization() -> None:
249     """Like test_docstring but with string normalization off."""
250     source, expected = read_data("docstring_no_string_normalization")
251     mode = replace(DEFAULT_MODE, string_normalization=False)
252     assert_format(source, expected, mode)
253
254
255 def test_long_strings_flag_disabled() -> None:
256     """Tests for turning off the string processing logic."""
257     source, expected = read_data("long_strings_flag_disabled")
258     mode = replace(DEFAULT_MODE, experimental_string_processing=False)
259     assert_format(source, expected, mode)
260
261
262 def test_numeric_literals() -> None:
263     source, expected = read_data("numeric_literals")
264     mode = replace(DEFAULT_MODE, target_versions=PY36_VERSIONS)
265     assert_format(source, expected, mode)
266
267
268 def test_numeric_literals_ignoring_underscores() -> None:
269     source, expected = read_data("numeric_literals_skip_underscores")
270     mode = replace(DEFAULT_MODE, target_versions=PY36_VERSIONS)
271     assert_format(source, expected, mode)
272
273
274 def test_stub() -> None:
275     mode = replace(DEFAULT_MODE, is_pyi=True)
276     source, expected = read_data("stub.pyi")
277     assert_format(source, expected, mode)
278
279
280 def test_python38() -> None:
281     source, expected = read_data("python38")
282     assert_format(source, expected, minimum_version=(3, 8))
283
284
285 def test_python39() -> None:
286     source, expected = read_data("python39")
287     assert_format(source, expected, minimum_version=(3, 9))
288
289
290 def test_power_op_newline() -> None:
291     # requires line_length=0
292     source, expected = read_data("power_op_newline")
293     assert_format(source, expected, mode=black.Mode(line_length=0))