]> 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:

Use tomllib on Python 3.11 (#2903)
[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     "fstring",
48     "function",
49     "function2",
50     "function_trailing_comma",
51     "import_spacing",
52     "power_op_spacing",
53     "remove_parens",
54     "slices",
55     "string_prefixes",
56     "torture",
57     "trailing_comma_optional_parens1",
58     "trailing_comma_optional_parens2",
59     "trailing_comma_optional_parens3",
60     "tricky_unicode_symbols",
61     "tupleassign",
62 ]
63
64 PY310_CASES: List[str] = [
65     "starred_for_target",
66     "pattern_matching_simple",
67     "pattern_matching_complex",
68     "pattern_matching_extras",
69     "pattern_matching_style",
70     "pattern_matching_generic",
71     "parenthesized_context_managers",
72 ]
73
74 PREVIEW_CASES: List[str] = [
75     # string processing
76     "cantfit",
77     "comments7",
78     "long_strings",
79     "long_strings__edge_case",
80     "long_strings__regression",
81     "percent_precedence",
82 ]
83
84 SOURCES: List[str] = [
85     "src/black/__init__.py",
86     "src/black/__main__.py",
87     "src/black/brackets.py",
88     "src/black/cache.py",
89     "src/black/comments.py",
90     "src/black/concurrency.py",
91     "src/black/const.py",
92     "src/black/debug.py",
93     "src/black/files.py",
94     "src/black/linegen.py",
95     "src/black/lines.py",
96     "src/black/mode.py",
97     "src/black/nodes.py",
98     "src/black/numerics.py",
99     "src/black/output.py",
100     "src/black/parsing.py",
101     "src/black/report.py",
102     "src/black/rusty.py",
103     "src/black/strings.py",
104     "src/black/trans.py",
105     "src/blackd/__init__.py",
106     "src/black_primer/cli.py",
107     "src/black_primer/lib.py",
108     "src/blib2to3/pygram.py",
109     "src/blib2to3/pytree.py",
110     "src/blib2to3/pgen2/conv.py",
111     "src/blib2to3/pgen2/driver.py",
112     "src/blib2to3/pgen2/grammar.py",
113     "src/blib2to3/pgen2/literals.py",
114     "src/blib2to3/pgen2/parse.py",
115     "src/blib2to3/pgen2/pgen.py",
116     "src/blib2to3/pgen2/tokenize.py",
117     "src/blib2to3/pgen2/token.py",
118     "setup.py",
119     "tests/test_black.py",
120     "tests/test_blackd.py",
121     "tests/test_format.py",
122     "tests/test_primer.py",
123     "tests/optional.py",
124     "tests/util.py",
125     "tests/conftest.py",
126 ]
127
128
129 @pytest.fixture(autouse=True)
130 def patch_dump_to_file(request: Any) -> Iterator[None]:
131     with patch("black.dump_to_file", dump_to_stderr):
132         yield
133
134
135 def check_file(filename: str, mode: black.Mode, *, data: bool = True) -> None:
136     source, expected = read_data(filename, data=data)
137     assert_format(source, expected, mode, fast=False)
138
139
140 @pytest.mark.parametrize("filename", SIMPLE_CASES)
141 def test_simple_format(filename: str) -> None:
142     check_file(filename, DEFAULT_MODE)
143
144
145 @pytest.mark.parametrize("filename", PREVIEW_CASES)
146 def test_preview_format(filename: str) -> None:
147     check_file(filename, black.Mode(preview=True))
148
149
150 @pytest.mark.parametrize("filename", SOURCES)
151 def test_source_is_formatted(filename: str) -> None:
152     path = THIS_DIR.parent / filename
153     check_file(str(path), DEFAULT_MODE, data=False)
154
155
156 # =============== #
157 # Complex cases
158 # ============= #
159
160
161 def test_empty() -> None:
162     source = expected = ""
163     assert_format(source, expected)
164
165
166 def test_pep_572() -> None:
167     source, expected = read_data("pep_572")
168     assert_format(source, expected, minimum_version=(3, 8))
169
170
171 def test_pep_572_remove_parens() -> None:
172     source, expected = read_data("pep_572_remove_parens")
173     assert_format(source, expected, minimum_version=(3, 8))
174
175
176 def test_pep_572_do_not_remove_parens() -> None:
177     source, expected = read_data("pep_572_do_not_remove_parens")
178     # the AST safety checks will fail, but that's expected, just make sure no
179     # parentheses are touched
180     assert_format(source, expected, fast=True)
181
182
183 @pytest.mark.parametrize("major, minor", [(3, 9), (3, 10)])
184 def test_pep_572_newer_syntax(major: int, minor: int) -> None:
185     source, expected = read_data(f"pep_572_py{major}{minor}")
186     assert_format(source, expected, minimum_version=(major, minor))
187
188
189 def test_pep_570() -> None:
190     source, expected = read_data("pep_570")
191     assert_format(source, expected, minimum_version=(3, 8))
192
193
194 @pytest.mark.parametrize("filename", PY310_CASES)
195 def test_python_310(filename: str) -> None:
196     source, expected = read_data(filename)
197     mode = black.Mode(target_versions={black.TargetVersion.PY310})
198     assert_format(source, expected, mode, minimum_version=(3, 10))
199
200
201 def test_python_310_without_target_version() -> None:
202     source, expected = read_data("pattern_matching_simple")
203     mode = black.Mode()
204     assert_format(source, expected, mode, minimum_version=(3, 10))
205
206
207 def test_patma_invalid() -> None:
208     source, expected = read_data("pattern_matching_invalid")
209     mode = black.Mode(target_versions={black.TargetVersion.PY310})
210     with pytest.raises(black.parsing.InvalidInput) as exc_info:
211         assert_format(source, expected, mode, minimum_version=(3, 10))
212
213     exc_info.match("Cannot parse: 10:11")
214
215
216 def test_python_2_hint() -> None:
217     with pytest.raises(black.parsing.InvalidInput) as exc_info:
218         assert_format("print 'daylily'", "print 'daylily'")
219     exc_info.match(black.parsing.PY2_HINT)
220
221
222 def test_docstring_no_string_normalization() -> None:
223     """Like test_docstring but with string normalization off."""
224     source, expected = read_data("docstring_no_string_normalization")
225     mode = replace(DEFAULT_MODE, string_normalization=False)
226     assert_format(source, expected, mode)
227
228
229 def test_long_strings_flag_disabled() -> None:
230     """Tests for turning off the string processing logic."""
231     source, expected = read_data("long_strings_flag_disabled")
232     mode = replace(DEFAULT_MODE, experimental_string_processing=False)
233     assert_format(source, expected, mode)
234
235
236 def test_numeric_literals() -> None:
237     source, expected = read_data("numeric_literals")
238     mode = replace(DEFAULT_MODE, target_versions=PY36_VERSIONS)
239     assert_format(source, expected, mode)
240
241
242 def test_numeric_literals_ignoring_underscores() -> None:
243     source, expected = read_data("numeric_literals_skip_underscores")
244     mode = replace(DEFAULT_MODE, target_versions=PY36_VERSIONS)
245     assert_format(source, expected, mode)
246
247
248 def test_stub() -> None:
249     mode = replace(DEFAULT_MODE, is_pyi=True)
250     source, expected = read_data("stub.pyi")
251     assert_format(source, expected, mode)
252
253
254 def test_python38() -> None:
255     source, expected = read_data("python38")
256     assert_format(source, expected, minimum_version=(3, 8))
257
258
259 def test_python39() -> None:
260     source, expected = read_data("python39")
261     assert_format(source, expected, minimum_version=(3, 9))
262
263
264 def test_power_op_newline() -> None:
265     # requires line_length=0
266     source, expected = read_data("power_op_newline")
267     assert_format(source, expected, mode=black.Mode(line_length=0))