]> git.madduck.net Git - etc/vim.git/blob - tests/test_black.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:

Graceful shutdown in case of cancellation
[etc/vim.git] / tests / test_black.py
1 #!/usr/bin/env python3
2 from functools import partial
3 from io import StringIO
4 import os
5 from pathlib import Path
6 import sys
7 from typing import Any, List, Tuple
8 import unittest
9 from unittest.mock import patch
10
11 from click import unstyle
12
13 import black
14
15 ll = 88
16 ff = partial(black.format_file_in_place, line_length=ll, fast=True)
17 fs = partial(black.format_str, line_length=ll)
18 THIS_FILE = Path(__file__)
19 THIS_DIR = THIS_FILE.parent
20 EMPTY_LINE = '# EMPTY LINE WITH WHITESPACE' + ' (this comment will be removed)'
21
22
23 def dump_to_stderr(*output: str) -> str:
24     return '\n' + '\n'.join(output) + '\n'
25
26
27 def read_data(name: str) -> Tuple[str, str]:
28     """read_data('test_name') -> 'input', 'output'"""
29     if not name.endswith(('.py', '.out')):
30         name += '.py'
31     _input: List[str] = []
32     _output: List[str] = []
33     with open(THIS_DIR / name, 'r', encoding='utf8') as test:
34         lines = test.readlines()
35     result = _input
36     for line in lines:
37         line = line.replace(EMPTY_LINE, '')
38         if line.rstrip() == '# output':
39             result = _output
40             continue
41
42         result.append(line)
43     if _input and not _output:
44         # If there's no output marker, treat the entire file as already pre-formatted.
45         _output = _input[:]
46     return ''.join(_input).strip() + '\n', ''.join(_output).strip() + '\n'
47
48
49 class BlackTestCase(unittest.TestCase):
50     maxDiff = None
51
52     def assertFormatEqual(self, expected: str, actual: str) -> None:
53         if actual != expected and not os.environ.get('SKIP_AST_PRINT'):
54             bdv: black.DebugVisitor[Any]
55             black.out('Expected tree:', fg='green')
56             try:
57                 exp_node = black.lib2to3_parse(expected)
58                 bdv = black.DebugVisitor()
59                 list(bdv.visit(exp_node))
60             except Exception as ve:
61                 black.err(str(ve))
62             black.out('Actual tree:', fg='red')
63             try:
64                 exp_node = black.lib2to3_parse(actual)
65                 bdv = black.DebugVisitor()
66                 list(bdv.visit(exp_node))
67             except Exception as ve:
68                 black.err(str(ve))
69         self.assertEqual(expected, actual)
70
71     @patch("black.dump_to_file", dump_to_stderr)
72     def test_self(self) -> None:
73         source, expected = read_data('test_black')
74         actual = fs(source)
75         self.assertFormatEqual(expected, actual)
76         black.assert_equivalent(source, actual)
77         black.assert_stable(source, actual, line_length=ll)
78         self.assertFalse(ff(THIS_FILE))
79
80     @patch("black.dump_to_file", dump_to_stderr)
81     def test_black(self) -> None:
82         source, expected = read_data('../black')
83         actual = fs(source)
84         self.assertFormatEqual(expected, actual)
85         black.assert_equivalent(source, actual)
86         black.assert_stable(source, actual, line_length=ll)
87         self.assertFalse(ff(THIS_DIR / '..' / 'black.py'))
88
89     def test_piping(self) -> None:
90         source, expected = read_data('../black')
91         hold_stdin, hold_stdout = sys.stdin, sys.stdout
92         try:
93             sys.stdin, sys.stdout = StringIO(source), StringIO()
94             sys.stdin.name = '<stdin>'
95             black.format_stdin_to_stdout(line_length=ll, fast=True, write_back=True)
96             sys.stdout.seek(0)
97             actual = sys.stdout.read()
98         finally:
99             sys.stdin, sys.stdout = hold_stdin, hold_stdout
100         self.assertFormatEqual(expected, actual)
101         black.assert_equivalent(source, actual)
102         black.assert_stable(source, actual, line_length=ll)
103
104     @patch("black.dump_to_file", dump_to_stderr)
105     def test_setup(self) -> None:
106         source, expected = read_data('../setup')
107         actual = fs(source)
108         self.assertFormatEqual(expected, actual)
109         black.assert_equivalent(source, actual)
110         black.assert_stable(source, actual, line_length=ll)
111         self.assertFalse(ff(THIS_DIR / '..' / 'setup.py'))
112
113     @patch("black.dump_to_file", dump_to_stderr)
114     def test_function(self) -> None:
115         source, expected = read_data('function')
116         actual = fs(source)
117         self.assertFormatEqual(expected, actual)
118         black.assert_equivalent(source, actual)
119         black.assert_stable(source, actual, line_length=ll)
120
121     @patch("black.dump_to_file", dump_to_stderr)
122     def test_expression(self) -> None:
123         source, expected = read_data('expression')
124         actual = fs(source)
125         self.assertFormatEqual(expected, actual)
126         black.assert_equivalent(source, actual)
127         black.assert_stable(source, actual, line_length=ll)
128
129     @patch("black.dump_to_file", dump_to_stderr)
130     def test_fstring(self) -> None:
131         source, expected = read_data('fstring')
132         actual = fs(source)
133         self.assertFormatEqual(expected, actual)
134         black.assert_equivalent(source, actual)
135         black.assert_stable(source, actual, line_length=ll)
136
137     @patch("black.dump_to_file", dump_to_stderr)
138     def test_comments(self) -> None:
139         source, expected = read_data('comments')
140         actual = fs(source)
141         self.assertFormatEqual(expected, actual)
142         black.assert_equivalent(source, actual)
143         black.assert_stable(source, actual, line_length=ll)
144
145     @patch("black.dump_to_file", dump_to_stderr)
146     def test_comments2(self) -> None:
147         source, expected = read_data('comments2')
148         actual = fs(source)
149         self.assertFormatEqual(expected, actual)
150         black.assert_equivalent(source, actual)
151         black.assert_stable(source, actual, line_length=ll)
152
153     @patch("black.dump_to_file", dump_to_stderr)
154     def test_comments3(self) -> None:
155         source, expected = read_data('comments3')
156         actual = fs(source)
157         self.assertFormatEqual(expected, actual)
158         black.assert_equivalent(source, actual)
159         black.assert_stable(source, actual, line_length=ll)
160
161     @patch("black.dump_to_file", dump_to_stderr)
162     def test_comments4(self) -> None:
163         source, expected = read_data('comments4')
164         actual = fs(source)
165         self.assertFormatEqual(expected, actual)
166         black.assert_equivalent(source, actual)
167         black.assert_stable(source, actual, line_length=ll)
168
169     @patch("black.dump_to_file", dump_to_stderr)
170     def test_cantfit(self) -> None:
171         source, expected = read_data('cantfit')
172         actual = fs(source)
173         self.assertFormatEqual(expected, actual)
174         black.assert_equivalent(source, actual)
175         black.assert_stable(source, actual, line_length=ll)
176
177     @patch("black.dump_to_file", dump_to_stderr)
178     def test_import_spacing(self) -> None:
179         source, expected = read_data('import_spacing')
180         actual = fs(source)
181         self.assertFormatEqual(expected, actual)
182         black.assert_equivalent(source, actual)
183         black.assert_stable(source, actual, line_length=ll)
184
185     @patch("black.dump_to_file", dump_to_stderr)
186     def test_composition(self) -> None:
187         source, expected = read_data('composition')
188         actual = fs(source)
189         self.assertFormatEqual(expected, actual)
190         black.assert_equivalent(source, actual)
191         black.assert_stable(source, actual, line_length=ll)
192
193     @patch("black.dump_to_file", dump_to_stderr)
194     def test_empty_lines(self) -> None:
195         source, expected = read_data('empty_lines')
196         actual = fs(source)
197         self.assertFormatEqual(expected, actual)
198         black.assert_equivalent(source, actual)
199         black.assert_stable(source, actual, line_length=ll)
200
201     @patch("black.dump_to_file", dump_to_stderr)
202     def test_python2(self) -> None:
203         source, expected = read_data('python2')
204         actual = fs(source)
205         self.assertFormatEqual(expected, actual)
206         # black.assert_equivalent(source, actual)
207         black.assert_stable(source, actual, line_length=ll)
208
209     @patch("black.dump_to_file", dump_to_stderr)
210     def test_fmtonoff(self) -> None:
211         source, expected = read_data('fmtonoff')
212         actual = fs(source)
213         self.assertFormatEqual(expected, actual)
214         black.assert_equivalent(source, actual)
215         black.assert_stable(source, actual, line_length=ll)
216
217     def test_report(self) -> None:
218         report = black.Report()
219         out_lines = []
220         err_lines = []
221
222         def out(msg: str, **kwargs: Any) -> None:
223             out_lines.append(msg)
224
225         def err(msg: str, **kwargs: Any) -> None:
226             err_lines.append(msg)
227
228         with patch("black.out", out), patch("black.err", err):
229             report.done(Path('f1'), changed=False)
230             self.assertEqual(len(out_lines), 1)
231             self.assertEqual(len(err_lines), 0)
232             self.assertEqual(out_lines[-1], 'f1 already well formatted, good job.')
233             self.assertEqual(unstyle(str(report)), '1 file left unchanged.')
234             self.assertEqual(report.return_code, 0)
235             report.done(Path('f2'), changed=True)
236             self.assertEqual(len(out_lines), 2)
237             self.assertEqual(len(err_lines), 0)
238             self.assertEqual(out_lines[-1], 'reformatted f2')
239             self.assertEqual(
240                 unstyle(str(report)), '1 file reformatted, 1 file left unchanged.'
241             )
242             self.assertEqual(report.return_code, 0)
243             report.check = True
244             self.assertEqual(report.return_code, 1)
245             report.check = False
246             report.failed(Path('e1'), 'boom')
247             self.assertEqual(len(out_lines), 2)
248             self.assertEqual(len(err_lines), 1)
249             self.assertEqual(err_lines[-1], 'error: cannot format e1: boom')
250             self.assertEqual(
251                 unstyle(str(report)),
252                 '1 file reformatted, 1 file left unchanged, '
253                 '1 file failed to reformat.',
254             )
255             self.assertEqual(report.return_code, 123)
256             report.done(Path('f3'), changed=True)
257             self.assertEqual(len(out_lines), 3)
258             self.assertEqual(len(err_lines), 1)
259             self.assertEqual(out_lines[-1], 'reformatted f3')
260             self.assertEqual(
261                 unstyle(str(report)),
262                 '2 files reformatted, 1 file left unchanged, '
263                 '1 file failed to reformat.',
264             )
265             self.assertEqual(report.return_code, 123)
266             report.failed(Path('e2'), 'boom')
267             self.assertEqual(len(out_lines), 3)
268             self.assertEqual(len(err_lines), 2)
269             self.assertEqual(err_lines[-1], 'error: cannot format e2: boom')
270             self.assertEqual(
271                 unstyle(str(report)),
272                 '2 files reformatted, 1 file left unchanged, '
273                 '2 files failed to reformat.',
274             )
275             self.assertEqual(report.return_code, 123)
276             report.done(Path('f4'), changed=False)
277             self.assertEqual(len(out_lines), 4)
278             self.assertEqual(len(err_lines), 2)
279             self.assertEqual(out_lines[-1], 'f4 already well formatted, good job.')
280             self.assertEqual(
281                 unstyle(str(report)),
282                 '2 files reformatted, 2 files left unchanged, '
283                 '2 files failed to reformat.',
284             )
285             self.assertEqual(report.return_code, 123)
286             report.check = True
287             self.assertEqual(
288                 unstyle(str(report)),
289                 '2 files would be reformatted, 2 files would be left unchanged, '
290                 '2 files would fail to reformat.',
291             )
292
293     def test_is_python36(self) -> None:
294         node = black.lib2to3_parse("def f(*, arg): ...\n")
295         self.assertFalse(black.is_python36(node))
296         node = black.lib2to3_parse("def f(*, arg,): ...\n")
297         self.assertTrue(black.is_python36(node))
298         node = black.lib2to3_parse("def f(*, arg): f'string'\n")
299         self.assertTrue(black.is_python36(node))
300         source, expected = read_data('function')
301         node = black.lib2to3_parse(source)
302         self.assertTrue(black.is_python36(node))
303         node = black.lib2to3_parse(expected)
304         self.assertTrue(black.is_python36(node))
305         source, expected = read_data('expression')
306         node = black.lib2to3_parse(source)
307         self.assertFalse(black.is_python36(node))
308         node = black.lib2to3_parse(expected)
309         self.assertFalse(black.is_python36(node))
310
311     def test_debug_visitor(self) -> None:
312         source, _ = read_data('debug_visitor.py')
313         expected, _ = read_data('debug_visitor.out')
314         out_lines = []
315         err_lines = []
316
317         def out(msg: str, **kwargs: Any) -> None:
318             out_lines.append(msg)
319
320         def err(msg: str, **kwargs: Any) -> None:
321             err_lines.append(msg)
322
323         with patch("black.out", out), patch("black.err", err):
324             black.DebugVisitor.show(source)
325         actual = '\n'.join(out_lines) + '\n'
326         log_name = ''
327         if expected != actual:
328             log_name = black.dump_to_file(*out_lines)
329         self.assertEqual(
330             expected,
331             actual,
332             f"AST print out is different. Actual version dumped to {log_name}",
333         )
334
335
336 if __name__ == '__main__':
337     unittest.main()