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

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