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

Change tests with stdin/out to exercise black.main (#307)
[etc/vim.git] / tests / test_black.py
1 #!/usr/bin/env python3
2 import asyncio
3 from concurrent.futures import ThreadPoolExecutor
4 from contextlib import contextmanager
5 from functools import partial
6 from io import BytesIO, TextIOWrapper
7 import os
8 from pathlib import Path
9 import re
10 import sys
11 from tempfile import TemporaryDirectory
12 from typing import Any, BinaryIO, Generator, List, Tuple, Iterator
13 import unittest
14 from unittest.mock import patch
15
16 from click import unstyle
17 from click.testing import CliRunner
18
19 import black
20
21
22 ll = 88
23 ff = partial(black.format_file_in_place, line_length=ll, fast=True)
24 fs = partial(black.format_str, line_length=ll)
25 THIS_FILE = Path(__file__)
26 THIS_DIR = THIS_FILE.parent
27 EMPTY_LINE = "# EMPTY LINE WITH WHITESPACE" + " (this comment will be removed)"
28
29
30 def dump_to_stderr(*output: str) -> str:
31     return "\n" + "\n".join(output) + "\n"
32
33
34 def read_data(name: str) -> Tuple[str, str]:
35     """read_data('test_name') -> 'input', 'output'"""
36     if not name.endswith((".py", ".pyi", ".out", ".diff")):
37         name += ".py"
38     _input: List[str] = []
39     _output: List[str] = []
40     with open(THIS_DIR / name, "r", encoding="utf8") as test:
41         lines = test.readlines()
42     result = _input
43     for line in lines:
44         line = line.replace(EMPTY_LINE, "")
45         if line.rstrip() == "# output":
46             result = _output
47             continue
48
49         result.append(line)
50     if _input and not _output:
51         # If there's no output marker, treat the entire file as already pre-formatted.
52         _output = _input[:]
53     return "".join(_input).strip() + "\n", "".join(_output).strip() + "\n"
54
55
56 @contextmanager
57 def cache_dir(exists: bool = True) -> Iterator[Path]:
58     with TemporaryDirectory() as workspace:
59         cache_dir = Path(workspace)
60         if not exists:
61             cache_dir = cache_dir / "new"
62         with patch("black.CACHE_DIR", cache_dir):
63             yield cache_dir
64
65
66 @contextmanager
67 def event_loop(close: bool) -> Iterator[None]:
68     policy = asyncio.get_event_loop_policy()
69     old_loop = policy.get_event_loop()
70     loop = policy.new_event_loop()
71     asyncio.set_event_loop(loop)
72     try:
73         yield
74
75     finally:
76         policy.set_event_loop(old_loop)
77         if close:
78             loop.close()
79
80
81 class BlackRunner(CliRunner):
82     """Modify CliRunner so that stderr is not merged with stdout.
83
84     This is a hack that can be removed once we depend on Click 7.x"""
85
86     def __init__(self, stderrbuf: BinaryIO) -> None:
87         self.stderrbuf = stderrbuf
88         super().__init__()
89
90     @contextmanager
91     def isolation(self, *args: Any, **kwargs: Any) -> Generator[BinaryIO, None, None]:
92         with super().isolation(*args, **kwargs) as output:
93             try:
94                 hold_stderr = sys.stderr
95                 sys.stderr = TextIOWrapper(self.stderrbuf, encoding=self.charset)
96                 yield output
97             finally:
98                 sys.stderr = hold_stderr
99
100
101 class BlackTestCase(unittest.TestCase):
102     maxDiff = None
103
104     def assertFormatEqual(self, expected: str, actual: str) -> None:
105         if actual != expected and not os.environ.get("SKIP_AST_PRINT"):
106             bdv: black.DebugVisitor[Any]
107             black.out("Expected tree:", fg="green")
108             try:
109                 exp_node = black.lib2to3_parse(expected)
110                 bdv = black.DebugVisitor()
111                 list(bdv.visit(exp_node))
112             except Exception as ve:
113                 black.err(str(ve))
114             black.out("Actual tree:", fg="red")
115             try:
116                 exp_node = black.lib2to3_parse(actual)
117                 bdv = black.DebugVisitor()
118                 list(bdv.visit(exp_node))
119             except Exception as ve:
120                 black.err(str(ve))
121         self.assertEqual(expected, actual)
122
123     @patch("black.dump_to_file", dump_to_stderr)
124     def test_empty(self) -> None:
125         source = expected = ""
126         actual = fs(source)
127         self.assertFormatEqual(expected, actual)
128         black.assert_equivalent(source, actual)
129         black.assert_stable(source, actual, line_length=ll)
130
131     def test_empty_ff(self) -> None:
132         expected = ""
133         tmp_file = Path(black.dump_to_file())
134         try:
135             self.assertFalse(ff(tmp_file, write_back=black.WriteBack.YES))
136             with open(tmp_file, encoding="utf8") as f:
137                 actual = f.read()
138         finally:
139             os.unlink(tmp_file)
140         self.assertFormatEqual(expected, actual)
141
142     @patch("black.dump_to_file", dump_to_stderr)
143     def test_self(self) -> None:
144         source, expected = read_data("test_black")
145         actual = fs(source)
146         self.assertFormatEqual(expected, actual)
147         black.assert_equivalent(source, actual)
148         black.assert_stable(source, actual, line_length=ll)
149         self.assertFalse(ff(THIS_FILE))
150
151     @patch("black.dump_to_file", dump_to_stderr)
152     def test_black(self) -> None:
153         source, expected = read_data("../black")
154         actual = fs(source)
155         self.assertFormatEqual(expected, actual)
156         black.assert_equivalent(source, actual)
157         black.assert_stable(source, actual, line_length=ll)
158         self.assertFalse(ff(THIS_DIR / ".." / "black.py"))
159
160     def test_piping(self) -> None:
161         source, expected = read_data("../black")
162         stderrbuf = BytesIO()
163         result = BlackRunner(stderrbuf).invoke(
164             black.main, ["-", "--fast", f"--line-length={ll}"], input=source
165         )
166         self.assertEqual(result.exit_code, 0)
167         self.assertFormatEqual(expected, result.output)
168         black.assert_equivalent(source, result.output)
169         black.assert_stable(source, result.output, line_length=ll)
170
171     def test_piping_diff(self) -> None:
172         diff_header = re.compile(
173             rf"(STDIN|STDOUT)\t\d\d\d\d-\d\d-\d\d "
174             rf"\d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d"
175         )
176         source, _ = read_data("expression.py")
177         expected, _ = read_data("expression.diff")
178         stderrbuf = BytesIO()
179         result = BlackRunner(stderrbuf).invoke(
180             black.main, ["-", "--fast", f"--line-length={ll}", "--diff"], input=source
181         )
182         self.assertEqual(result.exit_code, 0)
183         actual = diff_header.sub("[Deterministic header]", result.output)
184         actual = actual.rstrip() + "\n"  # the diff output has a trailing space
185         self.assertEqual(expected, actual)
186
187     @patch("black.dump_to_file", dump_to_stderr)
188     def test_setup(self) -> None:
189         source, expected = read_data("../setup")
190         actual = fs(source)
191         self.assertFormatEqual(expected, actual)
192         black.assert_equivalent(source, actual)
193         black.assert_stable(source, actual, line_length=ll)
194         self.assertFalse(ff(THIS_DIR / ".." / "setup.py"))
195
196     @patch("black.dump_to_file", dump_to_stderr)
197     def test_function(self) -> None:
198         source, expected = read_data("function")
199         actual = fs(source)
200         self.assertFormatEqual(expected, actual)
201         black.assert_equivalent(source, actual)
202         black.assert_stable(source, actual, line_length=ll)
203
204     @patch("black.dump_to_file", dump_to_stderr)
205     def test_function2(self) -> None:
206         source, expected = read_data("function2")
207         actual = fs(source)
208         self.assertFormatEqual(expected, actual)
209         black.assert_equivalent(source, actual)
210         black.assert_stable(source, actual, line_length=ll)
211
212     @patch("black.dump_to_file", dump_to_stderr)
213     def test_expression(self) -> None:
214         source, expected = read_data("expression")
215         actual = fs(source)
216         self.assertFormatEqual(expected, actual)
217         black.assert_equivalent(source, actual)
218         black.assert_stable(source, actual, line_length=ll)
219
220     def test_expression_ff(self) -> None:
221         source, expected = read_data("expression")
222         tmp_file = Path(black.dump_to_file(source))
223         try:
224             self.assertTrue(ff(tmp_file, write_back=black.WriteBack.YES))
225             with open(tmp_file, encoding="utf8") as f:
226                 actual = f.read()
227         finally:
228             os.unlink(tmp_file)
229         self.assertFormatEqual(expected, actual)
230         with patch("black.dump_to_file", dump_to_stderr):
231             black.assert_equivalent(source, actual)
232             black.assert_stable(source, actual, line_length=ll)
233
234     def test_expression_diff(self) -> None:
235         source, _ = read_data("expression.py")
236         expected, _ = read_data("expression.diff")
237         tmp_file = Path(black.dump_to_file(source))
238         diff_header = re.compile(
239             rf"{re.escape(str(tmp_file))}\t\d\d\d\d-\d\d-\d\d "
240             rf"\d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d"
241         )
242         stderrbuf = BytesIO()
243         try:
244             result = BlackRunner(stderrbuf).invoke(
245                 black.main, ["--diff", str(tmp_file)]
246             )
247             self.assertEqual(result.exit_code, 0)
248         finally:
249             os.unlink(tmp_file)
250         actual = result.output
251         actual = diff_header.sub("[Deterministic header]", actual)
252         actual = actual.rstrip() + "\n"  # the diff output has a trailing space
253         if expected != actual:
254             dump = black.dump_to_file(actual)
255             msg = (
256                 f"Expected diff isn't equal to the actual. If you made changes "
257                 f"to expression.py and this is an anticipated difference, "
258                 f"overwrite tests/expression.diff with {dump}"
259             )
260             self.assertEqual(expected, actual, msg)
261
262     @patch("black.dump_to_file", dump_to_stderr)
263     def test_fstring(self) -> None:
264         source, expected = read_data("fstring")
265         actual = fs(source)
266         self.assertFormatEqual(expected, actual)
267         black.assert_equivalent(source, actual)
268         black.assert_stable(source, actual, line_length=ll)
269
270     @patch("black.dump_to_file", dump_to_stderr)
271     def test_string_quotes(self) -> None:
272         source, expected = read_data("string_quotes")
273         actual = fs(source)
274         self.assertFormatEqual(expected, actual)
275         black.assert_equivalent(source, actual)
276         black.assert_stable(source, actual, line_length=ll)
277         mode = black.FileMode.NO_STRING_NORMALIZATION
278         not_normalized = fs(source, mode=mode)
279         self.assertFormatEqual(source, not_normalized)
280         black.assert_equivalent(source, not_normalized)
281         black.assert_stable(source, not_normalized, line_length=ll, mode=mode)
282
283     @patch("black.dump_to_file", dump_to_stderr)
284     def test_slices(self) -> None:
285         source, expected = read_data("slices")
286         actual = fs(source)
287         self.assertFormatEqual(expected, actual)
288         black.assert_equivalent(source, actual)
289         black.assert_stable(source, actual, line_length=ll)
290
291     @patch("black.dump_to_file", dump_to_stderr)
292     def test_comments(self) -> None:
293         source, expected = read_data("comments")
294         actual = fs(source)
295         self.assertFormatEqual(expected, actual)
296         black.assert_equivalent(source, actual)
297         black.assert_stable(source, actual, line_length=ll)
298
299     @patch("black.dump_to_file", dump_to_stderr)
300     def test_comments2(self) -> None:
301         source, expected = read_data("comments2")
302         actual = fs(source)
303         self.assertFormatEqual(expected, actual)
304         black.assert_equivalent(source, actual)
305         black.assert_stable(source, actual, line_length=ll)
306
307     @patch("black.dump_to_file", dump_to_stderr)
308     def test_comments3(self) -> None:
309         source, expected = read_data("comments3")
310         actual = fs(source)
311         self.assertFormatEqual(expected, actual)
312         black.assert_equivalent(source, actual)
313         black.assert_stable(source, actual, line_length=ll)
314
315     @patch("black.dump_to_file", dump_to_stderr)
316     def test_comments4(self) -> None:
317         source, expected = read_data("comments4")
318         actual = fs(source)
319         self.assertFormatEqual(expected, actual)
320         black.assert_equivalent(source, actual)
321         black.assert_stable(source, actual, line_length=ll)
322
323     @patch("black.dump_to_file", dump_to_stderr)
324     def test_comments5(self) -> None:
325         source, expected = read_data("comments5")
326         actual = fs(source)
327         self.assertFormatEqual(expected, actual)
328         black.assert_equivalent(source, actual)
329         black.assert_stable(source, actual, line_length=ll)
330
331     @patch("black.dump_to_file", dump_to_stderr)
332     def test_cantfit(self) -> None:
333         source, expected = read_data("cantfit")
334         actual = fs(source)
335         self.assertFormatEqual(expected, actual)
336         black.assert_equivalent(source, actual)
337         black.assert_stable(source, actual, line_length=ll)
338
339     @patch("black.dump_to_file", dump_to_stderr)
340     def test_import_spacing(self) -> None:
341         source, expected = read_data("import_spacing")
342         actual = fs(source)
343         self.assertFormatEqual(expected, actual)
344         black.assert_equivalent(source, actual)
345         black.assert_stable(source, actual, line_length=ll)
346
347     @patch("black.dump_to_file", dump_to_stderr)
348     def test_composition(self) -> None:
349         source, expected = read_data("composition")
350         actual = fs(source)
351         self.assertFormatEqual(expected, actual)
352         black.assert_equivalent(source, actual)
353         black.assert_stable(source, actual, line_length=ll)
354
355     @patch("black.dump_to_file", dump_to_stderr)
356     def test_empty_lines(self) -> None:
357         source, expected = read_data("empty_lines")
358         actual = fs(source)
359         self.assertFormatEqual(expected, actual)
360         black.assert_equivalent(source, actual)
361         black.assert_stable(source, actual, line_length=ll)
362
363     @patch("black.dump_to_file", dump_to_stderr)
364     def test_string_prefixes(self) -> None:
365         source, expected = read_data("string_prefixes")
366         actual = fs(source)
367         self.assertFormatEqual(expected, actual)
368         black.assert_equivalent(source, actual)
369         black.assert_stable(source, actual, line_length=ll)
370
371     @patch("black.dump_to_file", dump_to_stderr)
372     def test_python2(self) -> None:
373         source, expected = read_data("python2")
374         actual = fs(source)
375         self.assertFormatEqual(expected, actual)
376         # black.assert_equivalent(source, actual)
377         black.assert_stable(source, actual, line_length=ll)
378
379     @patch("black.dump_to_file", dump_to_stderr)
380     def test_python2_unicode_literals(self) -> None:
381         source, expected = read_data("python2_unicode_literals")
382         actual = fs(source)
383         self.assertFormatEqual(expected, actual)
384         black.assert_stable(source, actual, line_length=ll)
385
386     @patch("black.dump_to_file", dump_to_stderr)
387     def test_stub(self) -> None:
388         mode = black.FileMode.PYI
389         source, expected = read_data("stub.pyi")
390         actual = fs(source, mode=mode)
391         self.assertFormatEqual(expected, actual)
392         black.assert_stable(source, actual, line_length=ll, mode=mode)
393
394     @patch("black.dump_to_file", dump_to_stderr)
395     def test_fmtonoff(self) -> None:
396         source, expected = read_data("fmtonoff")
397         actual = fs(source)
398         self.assertFormatEqual(expected, actual)
399         black.assert_equivalent(source, actual)
400         black.assert_stable(source, actual, line_length=ll)
401
402     @patch("black.dump_to_file", dump_to_stderr)
403     def test_remove_empty_parentheses_after_class(self) -> None:
404         source, expected = read_data("class_blank_parentheses")
405         actual = fs(source)
406         self.assertFormatEqual(expected, actual)
407         black.assert_equivalent(source, actual)
408         black.assert_stable(source, actual, line_length=ll)
409
410     @patch("black.dump_to_file", dump_to_stderr)
411     def test_new_line_between_class_and_code(self) -> None:
412         source, expected = read_data("class_methods_new_line")
413         actual = fs(source)
414         self.assertFormatEqual(expected, actual)
415         black.assert_equivalent(source, actual)
416         black.assert_stable(source, actual, line_length=ll)
417
418     def test_report_verbose(self) -> None:
419         report = black.Report(verbose=True)
420         out_lines = []
421         err_lines = []
422
423         def out(msg: str, **kwargs: Any) -> None:
424             out_lines.append(msg)
425
426         def err(msg: str, **kwargs: Any) -> None:
427             err_lines.append(msg)
428
429         with patch("black.out", out), patch("black.err", err):
430             report.done(Path("f1"), black.Changed.NO)
431             self.assertEqual(len(out_lines), 1)
432             self.assertEqual(len(err_lines), 0)
433             self.assertEqual(out_lines[-1], "f1 already well formatted, good job.")
434             self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
435             self.assertEqual(report.return_code, 0)
436             report.done(Path("f2"), black.Changed.YES)
437             self.assertEqual(len(out_lines), 2)
438             self.assertEqual(len(err_lines), 0)
439             self.assertEqual(out_lines[-1], "reformatted f2")
440             self.assertEqual(
441                 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
442             )
443             report.done(Path("f3"), black.Changed.CACHED)
444             self.assertEqual(len(out_lines), 3)
445             self.assertEqual(len(err_lines), 0)
446             self.assertEqual(
447                 out_lines[-1], "f3 wasn't modified on disk since last run."
448             )
449             self.assertEqual(
450                 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
451             )
452             self.assertEqual(report.return_code, 0)
453             report.check = True
454             self.assertEqual(report.return_code, 1)
455             report.check = False
456             report.failed(Path("e1"), "boom")
457             self.assertEqual(len(out_lines), 3)
458             self.assertEqual(len(err_lines), 1)
459             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
460             self.assertEqual(
461                 unstyle(str(report)),
462                 "1 file reformatted, 2 files left unchanged, "
463                 "1 file failed to reformat.",
464             )
465             self.assertEqual(report.return_code, 123)
466             report.done(Path("f3"), black.Changed.YES)
467             self.assertEqual(len(out_lines), 4)
468             self.assertEqual(len(err_lines), 1)
469             self.assertEqual(out_lines[-1], "reformatted f3")
470             self.assertEqual(
471                 unstyle(str(report)),
472                 "2 files reformatted, 2 files left unchanged, "
473                 "1 file failed to reformat.",
474             )
475             self.assertEqual(report.return_code, 123)
476             report.failed(Path("e2"), "boom")
477             self.assertEqual(len(out_lines), 4)
478             self.assertEqual(len(err_lines), 2)
479             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
480             self.assertEqual(
481                 unstyle(str(report)),
482                 "2 files reformatted, 2 files left unchanged, "
483                 "2 files failed to reformat.",
484             )
485             self.assertEqual(report.return_code, 123)
486             report.path_ignored(Path("wat"), "no match")
487             self.assertEqual(len(out_lines), 5)
488             self.assertEqual(len(err_lines), 2)
489             self.assertEqual(out_lines[-1], "wat ignored: no match")
490             self.assertEqual(
491                 unstyle(str(report)),
492                 "2 files reformatted, 2 files left unchanged, "
493                 "2 files failed to reformat.",
494             )
495             self.assertEqual(report.return_code, 123)
496             report.done(Path("f4"), black.Changed.NO)
497             self.assertEqual(len(out_lines), 6)
498             self.assertEqual(len(err_lines), 2)
499             self.assertEqual(out_lines[-1], "f4 already well formatted, good job.")
500             self.assertEqual(
501                 unstyle(str(report)),
502                 "2 files reformatted, 3 files left unchanged, "
503                 "2 files failed to reformat.",
504             )
505             self.assertEqual(report.return_code, 123)
506             report.check = True
507             self.assertEqual(
508                 unstyle(str(report)),
509                 "2 files would be reformatted, 3 files would be left unchanged, "
510                 "2 files would fail to reformat.",
511             )
512
513     def test_report_quiet(self) -> None:
514         report = black.Report(quiet=True)
515         out_lines = []
516         err_lines = []
517
518         def out(msg: str, **kwargs: Any) -> None:
519             out_lines.append(msg)
520
521         def err(msg: str, **kwargs: Any) -> None:
522             err_lines.append(msg)
523
524         with patch("black.out", out), patch("black.err", err):
525             report.done(Path("f1"), black.Changed.NO)
526             self.assertEqual(len(out_lines), 0)
527             self.assertEqual(len(err_lines), 0)
528             self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
529             self.assertEqual(report.return_code, 0)
530             report.done(Path("f2"), black.Changed.YES)
531             self.assertEqual(len(out_lines), 0)
532             self.assertEqual(len(err_lines), 0)
533             self.assertEqual(
534                 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
535             )
536             report.done(Path("f3"), black.Changed.CACHED)
537             self.assertEqual(len(out_lines), 0)
538             self.assertEqual(len(err_lines), 0)
539             self.assertEqual(
540                 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
541             )
542             self.assertEqual(report.return_code, 0)
543             report.check = True
544             self.assertEqual(report.return_code, 1)
545             report.check = False
546             report.failed(Path("e1"), "boom")
547             self.assertEqual(len(out_lines), 0)
548             self.assertEqual(len(err_lines), 1)
549             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
550             self.assertEqual(
551                 unstyle(str(report)),
552                 "1 file reformatted, 2 files left unchanged, "
553                 "1 file failed to reformat.",
554             )
555             self.assertEqual(report.return_code, 123)
556             report.done(Path("f3"), black.Changed.YES)
557             self.assertEqual(len(out_lines), 0)
558             self.assertEqual(len(err_lines), 1)
559             self.assertEqual(
560                 unstyle(str(report)),
561                 "2 files reformatted, 2 files left unchanged, "
562                 "1 file failed to reformat.",
563             )
564             self.assertEqual(report.return_code, 123)
565             report.failed(Path("e2"), "boom")
566             self.assertEqual(len(out_lines), 0)
567             self.assertEqual(len(err_lines), 2)
568             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
569             self.assertEqual(
570                 unstyle(str(report)),
571                 "2 files reformatted, 2 files left unchanged, "
572                 "2 files failed to reformat.",
573             )
574             self.assertEqual(report.return_code, 123)
575             report.path_ignored(Path("wat"), "no match")
576             self.assertEqual(len(out_lines), 0)
577             self.assertEqual(len(err_lines), 2)
578             self.assertEqual(
579                 unstyle(str(report)),
580                 "2 files reformatted, 2 files left unchanged, "
581                 "2 files failed to reformat.",
582             )
583             self.assertEqual(report.return_code, 123)
584             report.done(Path("f4"), black.Changed.NO)
585             self.assertEqual(len(out_lines), 0)
586             self.assertEqual(len(err_lines), 2)
587             self.assertEqual(
588                 unstyle(str(report)),
589                 "2 files reformatted, 3 files left unchanged, "
590                 "2 files failed to reformat.",
591             )
592             self.assertEqual(report.return_code, 123)
593             report.check = True
594             self.assertEqual(
595                 unstyle(str(report)),
596                 "2 files would be reformatted, 3 files would be left unchanged, "
597                 "2 files would fail to reformat.",
598             )
599
600     def test_report_normal(self) -> None:
601         report = black.Report()
602         out_lines = []
603         err_lines = []
604
605         def out(msg: str, **kwargs: Any) -> None:
606             out_lines.append(msg)
607
608         def err(msg: str, **kwargs: Any) -> None:
609             err_lines.append(msg)
610
611         with patch("black.out", out), patch("black.err", err):
612             report.done(Path("f1"), black.Changed.NO)
613             self.assertEqual(len(out_lines), 0)
614             self.assertEqual(len(err_lines), 0)
615             self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
616             self.assertEqual(report.return_code, 0)
617             report.done(Path("f2"), black.Changed.YES)
618             self.assertEqual(len(out_lines), 1)
619             self.assertEqual(len(err_lines), 0)
620             self.assertEqual(out_lines[-1], "reformatted f2")
621             self.assertEqual(
622                 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
623             )
624             report.done(Path("f3"), black.Changed.CACHED)
625             self.assertEqual(len(out_lines), 1)
626             self.assertEqual(len(err_lines), 0)
627             self.assertEqual(out_lines[-1], "reformatted f2")
628             self.assertEqual(
629                 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
630             )
631             self.assertEqual(report.return_code, 0)
632             report.check = True
633             self.assertEqual(report.return_code, 1)
634             report.check = False
635             report.failed(Path("e1"), "boom")
636             self.assertEqual(len(out_lines), 1)
637             self.assertEqual(len(err_lines), 1)
638             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
639             self.assertEqual(
640                 unstyle(str(report)),
641                 "1 file reformatted, 2 files left unchanged, "
642                 "1 file failed to reformat.",
643             )
644             self.assertEqual(report.return_code, 123)
645             report.done(Path("f3"), black.Changed.YES)
646             self.assertEqual(len(out_lines), 2)
647             self.assertEqual(len(err_lines), 1)
648             self.assertEqual(out_lines[-1], "reformatted f3")
649             self.assertEqual(
650                 unstyle(str(report)),
651                 "2 files reformatted, 2 files left unchanged, "
652                 "1 file failed to reformat.",
653             )
654             self.assertEqual(report.return_code, 123)
655             report.failed(Path("e2"), "boom")
656             self.assertEqual(len(out_lines), 2)
657             self.assertEqual(len(err_lines), 2)
658             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
659             self.assertEqual(
660                 unstyle(str(report)),
661                 "2 files reformatted, 2 files left unchanged, "
662                 "2 files failed to reformat.",
663             )
664             self.assertEqual(report.return_code, 123)
665             report.path_ignored(Path("wat"), "no match")
666             self.assertEqual(len(out_lines), 2)
667             self.assertEqual(len(err_lines), 2)
668             self.assertEqual(
669                 unstyle(str(report)),
670                 "2 files reformatted, 2 files left unchanged, "
671                 "2 files failed to reformat.",
672             )
673             self.assertEqual(report.return_code, 123)
674             report.done(Path("f4"), black.Changed.NO)
675             self.assertEqual(len(out_lines), 2)
676             self.assertEqual(len(err_lines), 2)
677             self.assertEqual(
678                 unstyle(str(report)),
679                 "2 files reformatted, 3 files left unchanged, "
680                 "2 files failed to reformat.",
681             )
682             self.assertEqual(report.return_code, 123)
683             report.check = True
684             self.assertEqual(
685                 unstyle(str(report)),
686                 "2 files would be reformatted, 3 files would be left unchanged, "
687                 "2 files would fail to reformat.",
688             )
689
690     def test_is_python36(self) -> None:
691         node = black.lib2to3_parse("def f(*, arg): ...\n")
692         self.assertFalse(black.is_python36(node))
693         node = black.lib2to3_parse("def f(*, arg,): ...\n")
694         self.assertTrue(black.is_python36(node))
695         node = black.lib2to3_parse("def f(*, arg): f'string'\n")
696         self.assertTrue(black.is_python36(node))
697         source, expected = read_data("function")
698         node = black.lib2to3_parse(source)
699         self.assertTrue(black.is_python36(node))
700         node = black.lib2to3_parse(expected)
701         self.assertTrue(black.is_python36(node))
702         source, expected = read_data("expression")
703         node = black.lib2to3_parse(source)
704         self.assertFalse(black.is_python36(node))
705         node = black.lib2to3_parse(expected)
706         self.assertFalse(black.is_python36(node))
707
708     def test_get_future_imports(self) -> None:
709         node = black.lib2to3_parse("\n")
710         self.assertEqual(set(), black.get_future_imports(node))
711         node = black.lib2to3_parse("from __future__ import black\n")
712         self.assertEqual({"black"}, black.get_future_imports(node))
713         node = black.lib2to3_parse("from __future__ import multiple, imports\n")
714         self.assertEqual({"multiple", "imports"}, black.get_future_imports(node))
715         node = black.lib2to3_parse("from __future__ import (parenthesized, imports)\n")
716         self.assertEqual({"parenthesized", "imports"}, black.get_future_imports(node))
717         node = black.lib2to3_parse(
718             "from __future__ import multiple\nfrom __future__ import imports\n"
719         )
720         self.assertEqual({"multiple", "imports"}, black.get_future_imports(node))
721         node = black.lib2to3_parse("# comment\nfrom __future__ import black\n")
722         self.assertEqual({"black"}, black.get_future_imports(node))
723         node = black.lib2to3_parse('"""docstring"""\nfrom __future__ import black\n')
724         self.assertEqual({"black"}, black.get_future_imports(node))
725         node = black.lib2to3_parse("some(other, code)\nfrom __future__ import black\n")
726         self.assertEqual(set(), black.get_future_imports(node))
727         node = black.lib2to3_parse("from some.module import black\n")
728         self.assertEqual(set(), black.get_future_imports(node))
729
730     def test_debug_visitor(self) -> None:
731         source, _ = read_data("debug_visitor.py")
732         expected, _ = read_data("debug_visitor.out")
733         out_lines = []
734         err_lines = []
735
736         def out(msg: str, **kwargs: Any) -> None:
737             out_lines.append(msg)
738
739         def err(msg: str, **kwargs: Any) -> None:
740             err_lines.append(msg)
741
742         with patch("black.out", out), patch("black.err", err):
743             black.DebugVisitor.show(source)
744         actual = "\n".join(out_lines) + "\n"
745         log_name = ""
746         if expected != actual:
747             log_name = black.dump_to_file(*out_lines)
748         self.assertEqual(
749             expected,
750             actual,
751             f"AST print out is different. Actual version dumped to {log_name}",
752         )
753
754     def test_format_file_contents(self) -> None:
755         empty = ""
756         with self.assertRaises(black.NothingChanged):
757             black.format_file_contents(empty, line_length=ll, fast=False)
758         just_nl = "\n"
759         with self.assertRaises(black.NothingChanged):
760             black.format_file_contents(just_nl, line_length=ll, fast=False)
761         same = "l = [1, 2, 3]\n"
762         with self.assertRaises(black.NothingChanged):
763             black.format_file_contents(same, line_length=ll, fast=False)
764         different = "l = [1,2,3]"
765         expected = same
766         actual = black.format_file_contents(different, line_length=ll, fast=False)
767         self.assertEqual(expected, actual)
768         invalid = "return if you can"
769         with self.assertRaises(ValueError) as e:
770             black.format_file_contents(invalid, line_length=ll, fast=False)
771         self.assertEqual(str(e.exception), "Cannot parse: 1:7: return if you can")
772
773     def test_endmarker(self) -> None:
774         n = black.lib2to3_parse("\n")
775         self.assertEqual(n.type, black.syms.file_input)
776         self.assertEqual(len(n.children), 1)
777         self.assertEqual(n.children[0].type, black.token.ENDMARKER)
778
779     @unittest.skipIf(os.environ.get("SKIP_AST_PRINT"), "user set SKIP_AST_PRINT")
780     def test_assertFormatEqual(self) -> None:
781         out_lines = []
782         err_lines = []
783
784         def out(msg: str, **kwargs: Any) -> None:
785             out_lines.append(msg)
786
787         def err(msg: str, **kwargs: Any) -> None:
788             err_lines.append(msg)
789
790         with patch("black.out", out), patch("black.err", err):
791             with self.assertRaises(AssertionError):
792                 self.assertFormatEqual("l = [1, 2, 3]", "l = [1, 2, 3,]")
793
794         out_str = "".join(out_lines)
795         self.assertTrue("Expected tree:" in out_str)
796         self.assertTrue("Actual tree:" in out_str)
797         self.assertEqual("".join(err_lines), "")
798
799     def test_cache_broken_file(self) -> None:
800         mode = black.FileMode.AUTO_DETECT
801         with cache_dir() as workspace:
802             cache_file = black.get_cache_file(black.DEFAULT_LINE_LENGTH, mode)
803             with cache_file.open("w") as fobj:
804                 fobj.write("this is not a pickle")
805             self.assertEqual(black.read_cache(black.DEFAULT_LINE_LENGTH, mode), {})
806             src = (workspace / "test.py").resolve()
807             with src.open("w") as fobj:
808                 fobj.write("print('hello')")
809             result = CliRunner().invoke(black.main, [str(src)])
810             self.assertEqual(result.exit_code, 0)
811             cache = black.read_cache(black.DEFAULT_LINE_LENGTH, mode)
812             self.assertIn(src, cache)
813
814     def test_cache_single_file_already_cached(self) -> None:
815         mode = black.FileMode.AUTO_DETECT
816         with cache_dir() as workspace:
817             src = (workspace / "test.py").resolve()
818             with src.open("w") as fobj:
819                 fobj.write("print('hello')")
820             black.write_cache({}, [src], black.DEFAULT_LINE_LENGTH, mode)
821             result = CliRunner().invoke(black.main, [str(src)])
822             self.assertEqual(result.exit_code, 0)
823             with src.open("r") as fobj:
824                 self.assertEqual(fobj.read(), "print('hello')")
825
826     @event_loop(close=False)
827     def test_cache_multiple_files(self) -> None:
828         mode = black.FileMode.AUTO_DETECT
829         with cache_dir() as workspace, patch(
830             "black.ProcessPoolExecutor", new=ThreadPoolExecutor
831         ):
832             one = (workspace / "one.py").resolve()
833             with one.open("w") as fobj:
834                 fobj.write("print('hello')")
835             two = (workspace / "two.py").resolve()
836             with two.open("w") as fobj:
837                 fobj.write("print('hello')")
838             black.write_cache({}, [one], black.DEFAULT_LINE_LENGTH, mode)
839             result = CliRunner().invoke(black.main, [str(workspace)])
840             self.assertEqual(result.exit_code, 0)
841             with one.open("r") as fobj:
842                 self.assertEqual(fobj.read(), "print('hello')")
843             with two.open("r") as fobj:
844                 self.assertEqual(fobj.read(), 'print("hello")\n')
845             cache = black.read_cache(black.DEFAULT_LINE_LENGTH, mode)
846             self.assertIn(one, cache)
847             self.assertIn(two, cache)
848
849     def test_no_cache_when_writeback_diff(self) -> None:
850         mode = black.FileMode.AUTO_DETECT
851         with cache_dir() as workspace:
852             src = (workspace / "test.py").resolve()
853             with src.open("w") as fobj:
854                 fobj.write("print('hello')")
855             result = CliRunner().invoke(black.main, [str(src), "--diff"])
856             self.assertEqual(result.exit_code, 0)
857             cache_file = black.get_cache_file(black.DEFAULT_LINE_LENGTH, mode)
858             self.assertFalse(cache_file.exists())
859
860     def test_no_cache_when_stdin(self) -> None:
861         mode = black.FileMode.AUTO_DETECT
862         with cache_dir():
863             result = CliRunner().invoke(black.main, ["-"], input="print('hello')")
864             self.assertEqual(result.exit_code, 0)
865             cache_file = black.get_cache_file(black.DEFAULT_LINE_LENGTH, mode)
866             self.assertFalse(cache_file.exists())
867
868     def test_read_cache_no_cachefile(self) -> None:
869         mode = black.FileMode.AUTO_DETECT
870         with cache_dir():
871             self.assertEqual(black.read_cache(black.DEFAULT_LINE_LENGTH, mode), {})
872
873     def test_write_cache_read_cache(self) -> None:
874         mode = black.FileMode.AUTO_DETECT
875         with cache_dir() as workspace:
876             src = (workspace / "test.py").resolve()
877             src.touch()
878             black.write_cache({}, [src], black.DEFAULT_LINE_LENGTH, mode)
879             cache = black.read_cache(black.DEFAULT_LINE_LENGTH, mode)
880             self.assertIn(src, cache)
881             self.assertEqual(cache[src], black.get_cache_info(src))
882
883     def test_filter_cached(self) -> None:
884         with TemporaryDirectory() as workspace:
885             path = Path(workspace)
886             uncached = (path / "uncached").resolve()
887             cached = (path / "cached").resolve()
888             cached_but_changed = (path / "changed").resolve()
889             uncached.touch()
890             cached.touch()
891             cached_but_changed.touch()
892             cache = {cached: black.get_cache_info(cached), cached_but_changed: (0.0, 0)}
893             todo, done = black.filter_cached(
894                 cache, {uncached, cached, cached_but_changed}
895             )
896             self.assertEqual(todo, {uncached, cached_but_changed})
897             self.assertEqual(done, {cached})
898
899     def test_write_cache_creates_directory_if_needed(self) -> None:
900         mode = black.FileMode.AUTO_DETECT
901         with cache_dir(exists=False) as workspace:
902             self.assertFalse(workspace.exists())
903             black.write_cache({}, [], black.DEFAULT_LINE_LENGTH, mode)
904             self.assertTrue(workspace.exists())
905
906     @event_loop(close=False)
907     def test_failed_formatting_does_not_get_cached(self) -> None:
908         mode = black.FileMode.AUTO_DETECT
909         with cache_dir() as workspace, patch(
910             "black.ProcessPoolExecutor", new=ThreadPoolExecutor
911         ):
912             failing = (workspace / "failing.py").resolve()
913             with failing.open("w") as fobj:
914                 fobj.write("not actually python")
915             clean = (workspace / "clean.py").resolve()
916             with clean.open("w") as fobj:
917                 fobj.write('print("hello")\n')
918             result = CliRunner().invoke(black.main, [str(workspace)])
919             self.assertEqual(result.exit_code, 123)
920             cache = black.read_cache(black.DEFAULT_LINE_LENGTH, mode)
921             self.assertNotIn(failing, cache)
922             self.assertIn(clean, cache)
923
924     def test_write_cache_write_fail(self) -> None:
925         mode = black.FileMode.AUTO_DETECT
926         with cache_dir(), patch.object(Path, "open") as mock:
927             mock.side_effect = OSError
928             black.write_cache({}, [], black.DEFAULT_LINE_LENGTH, mode)
929
930     @event_loop(close=False)
931     def test_check_diff_use_together(self) -> None:
932         with cache_dir():
933             # Files which will be reformatted.
934             src1 = (THIS_DIR / "string_quotes.py").resolve()
935             result = CliRunner().invoke(black.main, [str(src1), "--diff", "--check"])
936             self.assertEqual(result.exit_code, 1)
937
938             # Files which will not be reformatted.
939             src2 = (THIS_DIR / "composition.py").resolve()
940             result = CliRunner().invoke(black.main, [str(src2), "--diff", "--check"])
941             self.assertEqual(result.exit_code, 0)
942
943             # Multi file command.
944             result = CliRunner().invoke(
945                 black.main, [str(src1), str(src2), "--diff", "--check"]
946             )
947             self.assertEqual(result.exit_code, 1, result.output)
948
949     def test_no_files(self) -> None:
950         with cache_dir():
951             # Without an argument, black exits with error code 0.
952             result = CliRunner().invoke(black.main, [])
953             self.assertEqual(result.exit_code, 0)
954
955     def test_broken_symlink(self) -> None:
956         with cache_dir() as workspace:
957             symlink = workspace / "broken_link.py"
958             try:
959                 symlink.symlink_to("nonexistent.py")
960             except OSError as e:
961                 self.skipTest(f"Can't create symlinks: {e}")
962             result = CliRunner().invoke(black.main, [str(workspace.resolve())])
963             self.assertEqual(result.exit_code, 0)
964
965     def test_read_cache_line_lengths(self) -> None:
966         mode = black.FileMode.AUTO_DETECT
967         with cache_dir() as workspace:
968             path = (workspace / "file.py").resolve()
969             path.touch()
970             black.write_cache({}, [path], 1, mode)
971             one = black.read_cache(1, mode)
972             self.assertIn(path, one)
973             two = black.read_cache(2, mode)
974             self.assertNotIn(path, two)
975
976     def test_single_file_force_pyi(self) -> None:
977         reg_mode = black.FileMode.AUTO_DETECT
978         pyi_mode = black.FileMode.PYI
979         contents, expected = read_data("force_pyi")
980         with cache_dir() as workspace:
981             path = (workspace / "file.py").resolve()
982             with open(path, "w") as fh:
983                 fh.write(contents)
984             result = CliRunner().invoke(black.main, [str(path), "--pyi"])
985             self.assertEqual(result.exit_code, 0)
986             with open(path, "r") as fh:
987                 actual = fh.read()
988             # verify cache with --pyi is separate
989             pyi_cache = black.read_cache(black.DEFAULT_LINE_LENGTH, pyi_mode)
990             self.assertIn(path, pyi_cache)
991             normal_cache = black.read_cache(black.DEFAULT_LINE_LENGTH, reg_mode)
992             self.assertNotIn(path, normal_cache)
993         self.assertEqual(actual, expected)
994
995     @event_loop(close=False)
996     def test_multi_file_force_pyi(self) -> None:
997         reg_mode = black.FileMode.AUTO_DETECT
998         pyi_mode = black.FileMode.PYI
999         contents, expected = read_data("force_pyi")
1000         with cache_dir() as workspace:
1001             paths = [
1002                 (workspace / "file1.py").resolve(),
1003                 (workspace / "file2.py").resolve(),
1004             ]
1005             for path in paths:
1006                 with open(path, "w") as fh:
1007                     fh.write(contents)
1008             result = CliRunner().invoke(black.main, [str(p) for p in paths] + ["--pyi"])
1009             self.assertEqual(result.exit_code, 0)
1010             for path in paths:
1011                 with open(path, "r") as fh:
1012                     actual = fh.read()
1013                 self.assertEqual(actual, expected)
1014             # verify cache with --pyi is separate
1015             pyi_cache = black.read_cache(black.DEFAULT_LINE_LENGTH, pyi_mode)
1016             normal_cache = black.read_cache(black.DEFAULT_LINE_LENGTH, reg_mode)
1017             for path in paths:
1018                 self.assertIn(path, pyi_cache)
1019                 self.assertNotIn(path, normal_cache)
1020
1021     def test_pipe_force_pyi(self) -> None:
1022         source, expected = read_data("force_pyi")
1023         result = CliRunner().invoke(black.main, ["-", "-q", "--pyi"], input=source)
1024         self.assertEqual(result.exit_code, 0)
1025         actual = result.output
1026         self.assertFormatEqual(actual, expected)
1027
1028     def test_single_file_force_py36(self) -> None:
1029         reg_mode = black.FileMode.AUTO_DETECT
1030         py36_mode = black.FileMode.PYTHON36
1031         source, expected = read_data("force_py36")
1032         with cache_dir() as workspace:
1033             path = (workspace / "file.py").resolve()
1034             with open(path, "w") as fh:
1035                 fh.write(source)
1036             result = CliRunner().invoke(black.main, [str(path), "--py36"])
1037             self.assertEqual(result.exit_code, 0)
1038             with open(path, "r") as fh:
1039                 actual = fh.read()
1040             # verify cache with --py36 is separate
1041             py36_cache = black.read_cache(black.DEFAULT_LINE_LENGTH, py36_mode)
1042             self.assertIn(path, py36_cache)
1043             normal_cache = black.read_cache(black.DEFAULT_LINE_LENGTH, reg_mode)
1044             self.assertNotIn(path, normal_cache)
1045         self.assertEqual(actual, expected)
1046
1047     @event_loop(close=False)
1048     def test_multi_file_force_py36(self) -> None:
1049         reg_mode = black.FileMode.AUTO_DETECT
1050         py36_mode = black.FileMode.PYTHON36
1051         source, expected = read_data("force_py36")
1052         with cache_dir() as workspace:
1053             paths = [
1054                 (workspace / "file1.py").resolve(),
1055                 (workspace / "file2.py").resolve(),
1056             ]
1057             for path in paths:
1058                 with open(path, "w") as fh:
1059                     fh.write(source)
1060             result = CliRunner().invoke(
1061                 black.main, [str(p) for p in paths] + ["--py36"]
1062             )
1063             self.assertEqual(result.exit_code, 0)
1064             for path in paths:
1065                 with open(path, "r") as fh:
1066                     actual = fh.read()
1067                 self.assertEqual(actual, expected)
1068             # verify cache with --py36 is separate
1069             pyi_cache = black.read_cache(black.DEFAULT_LINE_LENGTH, py36_mode)
1070             normal_cache = black.read_cache(black.DEFAULT_LINE_LENGTH, reg_mode)
1071             for path in paths:
1072                 self.assertIn(path, pyi_cache)
1073                 self.assertNotIn(path, normal_cache)
1074
1075     def test_pipe_force_py36(self) -> None:
1076         source, expected = read_data("force_py36")
1077         result = CliRunner().invoke(black.main, ["-", "-q", "--py36"], input=source)
1078         self.assertEqual(result.exit_code, 0)
1079         actual = result.output
1080         self.assertFormatEqual(actual, expected)
1081
1082     def test_include_exclude(self) -> None:
1083         path = THIS_DIR / "include_exclude_tests"
1084         include = re.compile(r"\.pyi?$")
1085         exclude = re.compile(r"/exclude/|/\.definitely_exclude/")
1086         report = black.Report()
1087         sources: List[Path] = []
1088         expected = [
1089             Path(THIS_DIR / "include_exclude_tests/b/dont_exclude/a.py"),
1090             Path(THIS_DIR / "include_exclude_tests/b/dont_exclude/a.pyi"),
1091         ]
1092         this_abs = THIS_DIR.resolve()
1093         sources.extend(
1094             black.gen_python_files_in_dir(path, this_abs, include, exclude, report)
1095         )
1096         self.assertEqual(sorted(expected), sorted(sources))
1097
1098     def test_empty_include(self) -> None:
1099         path = THIS_DIR / "include_exclude_tests"
1100         report = black.Report()
1101         empty = re.compile(r"")
1102         sources: List[Path] = []
1103         expected = [
1104             Path(path / "b/exclude/a.pie"),
1105             Path(path / "b/exclude/a.py"),
1106             Path(path / "b/exclude/a.pyi"),
1107             Path(path / "b/dont_exclude/a.pie"),
1108             Path(path / "b/dont_exclude/a.py"),
1109             Path(path / "b/dont_exclude/a.pyi"),
1110             Path(path / "b/.definitely_exclude/a.pie"),
1111             Path(path / "b/.definitely_exclude/a.py"),
1112             Path(path / "b/.definitely_exclude/a.pyi"),
1113         ]
1114         this_abs = THIS_DIR.resolve()
1115         sources.extend(
1116             black.gen_python_files_in_dir(
1117                 path, this_abs, empty, re.compile(black.DEFAULT_EXCLUDES), report
1118             )
1119         )
1120         self.assertEqual(sorted(expected), sorted(sources))
1121
1122     def test_empty_exclude(self) -> None:
1123         path = THIS_DIR / "include_exclude_tests"
1124         report = black.Report()
1125         empty = re.compile(r"")
1126         sources: List[Path] = []
1127         expected = [
1128             Path(path / "b/dont_exclude/a.py"),
1129             Path(path / "b/dont_exclude/a.pyi"),
1130             Path(path / "b/exclude/a.py"),
1131             Path(path / "b/exclude/a.pyi"),
1132             Path(path / "b/.definitely_exclude/a.py"),
1133             Path(path / "b/.definitely_exclude/a.pyi"),
1134         ]
1135         this_abs = THIS_DIR.resolve()
1136         sources.extend(
1137             black.gen_python_files_in_dir(
1138                 path, this_abs, re.compile(black.DEFAULT_INCLUDES), empty, report
1139             )
1140         )
1141         self.assertEqual(sorted(expected), sorted(sources))
1142
1143     def test_invalid_include_exclude(self) -> None:
1144         for option in ["--include", "--exclude"]:
1145             result = CliRunner().invoke(black.main, ["-", option, "**()(!!*)"])
1146             self.assertEqual(result.exit_code, 2)
1147
1148     def test_preserves_line_endings(self) -> None:
1149         with TemporaryDirectory() as workspace:
1150             test_file = Path(workspace) / "test.py"
1151             for nl in ["\n", "\r\n"]:
1152                 contents = nl.join(["def f(  ):", "    pass"])
1153                 test_file.write_bytes(contents.encode())
1154                 ff(test_file, write_back=black.WriteBack.YES)
1155                 updated_contents: bytes = test_file.read_bytes()
1156                 self.assertIn(nl.encode(), updated_contents)  # type: ignore
1157                 if nl == "\n":
1158                     self.assertNotIn(b"\r\n", updated_contents)  # type: ignore
1159
1160     def test_assert_equivalent_different_asts(self) -> None:
1161         with self.assertRaises(AssertionError):
1162             black.assert_equivalent("{}", "None")
1163
1164
1165 if __name__ == "__main__":
1166     unittest.main()