X-Git-Url: https://git.madduck.net/etc/vim.git/blobdiff_plain/6b5eb7d4651c7333cc3f5df4bf7aa7a1f1ffb45b..fc804d5f59be498c6bf7c3fb8180819e4c4ff714:/tests/test_black.py

diff --git a/tests/test_black.py b/tests/test_black.py
index e09feff..acbaade 100644
--- a/tests/test_black.py
+++ b/tests/test_black.py
@@ -3,24 +3,14 @@ import asyncio
 import logging
 from concurrent.futures import ThreadPoolExecutor
 from contextlib import contextmanager
-from functools import partial, wraps
+from functools import partial
 from io import BytesIO, TextIOWrapper
 import os
 from pathlib import Path
-import re
+import regex as re
 import sys
 from tempfile import TemporaryDirectory
-from typing import (
-    Any,
-    BinaryIO,
-    Callable,
-    Coroutine,
-    Generator,
-    List,
-    Tuple,
-    Iterator,
-    TypeVar,
-)
+from typing import Any, BinaryIO, Generator, List, Tuple, Iterator, TypeVar
 import unittest
 from unittest.mock import patch, MagicMock
 
@@ -32,16 +22,20 @@ from black import Feature, TargetVersion
 
 try:
     import blackd
-    from aiohttp.test_utils import TestClient, TestServer
+    from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop
+    from aiohttp import web
 except ImportError:
     has_blackd_deps = False
 else:
     has_blackd_deps = True
 
+from pathspec import PathSpec
+
 ff = partial(black.format_file_in_place, mode=black.FileMode(), fast=True)
 fs = partial(black.format_str, mode=black.FileMode())
 THIS_FILE = Path(__file__)
 THIS_DIR = THIS_FILE.parent
+DETERMINISTIC_HEADER = "[Deterministic header]"
 EMPTY_LINE = "# EMPTY LINE WITH WHITESPACE" + " (this comment will be removed)"
 PY36_ARGS = [
     f"--target-version={version.name.lower()}" for version in black.PY36_VERSIONS
@@ -90,27 +84,16 @@ def cache_dir(exists: bool = True) -> Iterator[Path]:
 @contextmanager
 def event_loop(close: bool) -> Iterator[None]:
     policy = asyncio.get_event_loop_policy()
-    old_loop = policy.get_event_loop()
     loop = policy.new_event_loop()
     asyncio.set_event_loop(loop)
     try:
         yield
 
     finally:
-        policy.set_event_loop(old_loop)
         if close:
             loop.close()
 
 
-def async_test(f: Callable[..., Coroutine[Any, None, R]]) -> Callable[..., None]:
-    @event_loop(close=True)
-    @wraps(f)
-    def wrapper(*args: Any, **kwargs: Any) -> None:
-        asyncio.get_event_loop().run_until_complete(f(*args, **kwargs))
-
-    return wrapper
-
-
 @contextmanager
 def skip_if_exception(e: str) -> Iterator[None]:
     try:
@@ -118,6 +101,8 @@ def skip_if_exception(e: str) -> Iterator[None]:
     except Exception as exc:
         if exc.__class__.__name__ == e:
             unittest.skip(f"Encountered expected exception {exc}, skipping")
+        else:
+            raise
 
 
 class BlackRunner(CliRunner):
@@ -176,6 +161,16 @@ class BlackTestCase(unittest.TestCase):
         result = runner.invoke(black.main, args)
         self.assertEqual(result.exit_code, exit_code, msg=runner.stderr_bytes.decode())
 
+    @patch("black.dump_to_file", dump_to_stderr)
+    def checkSourceFile(self, name: str) -> None:
+        path = THIS_DIR.parent / name
+        source, expected = read_data(str(path), data=False)
+        actual = fs(source)
+        self.assertFormatEqual(expected, actual)
+        black.assert_equivalent(source, actual)
+        black.assert_stable(source, actual, black.FileMode())
+        self.assertFalse(ff(path))
+
     @patch("black.dump_to_file", dump_to_stderr)
     def test_empty(self) -> None:
         source = expected = ""
@@ -195,23 +190,44 @@ class BlackTestCase(unittest.TestCase):
             os.unlink(tmp_file)
         self.assertFormatEqual(expected, actual)
 
-    @patch("black.dump_to_file", dump_to_stderr)
     def test_self(self) -> None:
-        source, expected = read_data("test_black", data=False)
-        actual = fs(source)
-        self.assertFormatEqual(expected, actual)
-        black.assert_equivalent(source, actual)
-        black.assert_stable(source, actual, black.FileMode())
-        self.assertFalse(ff(THIS_FILE))
+        self.checkSourceFile("tests/test_black.py")
 
-    @patch("black.dump_to_file", dump_to_stderr)
     def test_black(self) -> None:
-        source, expected = read_data("../black", data=False)
-        actual = fs(source)
-        self.assertFormatEqual(expected, actual)
-        black.assert_equivalent(source, actual)
-        black.assert_stable(source, actual, black.FileMode())
-        self.assertFalse(ff(THIS_DIR / ".." / "black.py"))
+        self.checkSourceFile("black.py")
+
+    def test_pygram(self) -> None:
+        self.checkSourceFile("blib2to3/pygram.py")
+
+    def test_pytree(self) -> None:
+        self.checkSourceFile("blib2to3/pytree.py")
+
+    def test_conv(self) -> None:
+        self.checkSourceFile("blib2to3/pgen2/conv.py")
+
+    def test_driver(self) -> None:
+        self.checkSourceFile("blib2to3/pgen2/driver.py")
+
+    def test_grammar(self) -> None:
+        self.checkSourceFile("blib2to3/pgen2/grammar.py")
+
+    def test_literals(self) -> None:
+        self.checkSourceFile("blib2to3/pgen2/literals.py")
+
+    def test_parse(self) -> None:
+        self.checkSourceFile("blib2to3/pgen2/parse.py")
+
+    def test_pgen(self) -> None:
+        self.checkSourceFile("blib2to3/pgen2/pgen.py")
+
+    def test_tokenize(self) -> None:
+        self.checkSourceFile("blib2to3/pgen2/tokenize.py")
+
+    def test_token(self) -> None:
+        self.checkSourceFile("blib2to3/pgen2/token.py")
+
+    def test_setup(self) -> None:
+        self.checkSourceFile("setup.py")
 
     def test_piping(self) -> None:
         source, expected = read_data("../black", data=False)
@@ -244,19 +260,10 @@ class BlackTestCase(unittest.TestCase):
             black.main, args, input=BytesIO(source.encode("utf8"))
         )
         self.assertEqual(result.exit_code, 0)
-        actual = diff_header.sub("[Deterministic header]", result.output)
+        actual = diff_header.sub(DETERMINISTIC_HEADER, result.output)
         actual = actual.rstrip() + "\n"  # the diff output has a trailing space
         self.assertEqual(expected, actual)
 
-    @patch("black.dump_to_file", dump_to_stderr)
-    def test_setup(self) -> None:
-        source, expected = read_data("../setup", data=False)
-        actual = fs(source)
-        self.assertFormatEqual(expected, actual)
-        black.assert_equivalent(source, actual)
-        black.assert_stable(source, actual, black.FileMode())
-        self.assertFalse(ff(THIS_DIR / ".." / "setup.py"))
-
     @patch("black.dump_to_file", dump_to_stderr)
     def test_function(self) -> None:
         source, expected = read_data("function")
@@ -334,7 +341,7 @@ class BlackTestCase(unittest.TestCase):
         finally:
             os.unlink(tmp_file)
         actual = result.output
-        actual = diff_header.sub("[Deterministic header]", actual)
+        actual = diff_header.sub(DETERMINISTIC_HEADER, actual)
         actual = actual.rstrip() + "\n"  # the diff output has a trailing space
         if expected != actual:
             dump = black.dump_to_file(actual)
@@ -591,6 +598,16 @@ class BlackTestCase(unittest.TestCase):
         # but not on 3.6, because we use async as a reserved keyword
         self.invokeBlack([str(source_path), "--target-version", "py36"], exit_code=123)
 
+    @patch("black.dump_to_file", dump_to_stderr)
+    def test_python38(self) -> None:
+        source, expected = read_data("python38")
+        actual = fs(source)
+        self.assertFormatEqual(expected, actual)
+        major, minor = sys.version_info[:2]
+        if major > 3 or (major == 3 and minor >= 8):
+            black.assert_equivalent(source, actual)
+        black.assert_stable(source, actual, black.FileMode())
+
     @patch("black.dump_to_file", dump_to_stderr)
     def test_fmtonoff(self) -> None:
         source, expected = read_data("fmtonoff")
@@ -607,6 +624,14 @@ class BlackTestCase(unittest.TestCase):
         black.assert_equivalent(source, actual)
         black.assert_stable(source, actual, black.FileMode())
 
+    @patch("black.dump_to_file", dump_to_stderr)
+    def test_fmtonoff3(self) -> None:
+        source, expected = read_data("fmtonoff3")
+        actual = fs(source)
+        self.assertFormatEqual(expected, actual)
+        black.assert_equivalent(source, actual)
+        black.assert_stable(source, actual, black.FileMode())
+
     @patch("black.dump_to_file", dump_to_stderr)
     def test_remove_empty_parentheses_after_class(self) -> None:
         source, expected = read_data("class_blank_parentheses")
@@ -639,6 +664,14 @@ class BlackTestCase(unittest.TestCase):
         black.assert_equivalent(source, actual)
         black.assert_stable(source, actual, black.FileMode())
 
+    @patch("black.dump_to_file", dump_to_stderr)
+    def test_beginning_backslash(self) -> None:
+        source, expected = read_data("beginning_backslash")
+        actual = fs(source)
+        self.assertFormatEqual(expected, actual)
+        black.assert_equivalent(source, actual)
+        black.assert_stable(source, actual, black.FileMode())
+
     def test_tab_comment_indentation(self) -> None:
         contents_tab = "if 1:\n\tif 2:\n\t\tpass\n\t# comment\n\tpass\n"
         contents_spc = "if 1:\n    if 2:\n        pass\n    # comment\n    pass\n"
@@ -755,6 +788,13 @@ class BlackTestCase(unittest.TestCase):
                 "2 files would be reformatted, 3 files would be left unchanged, "
                 "2 files would fail to reformat.",
             )
+            report.check = False
+            report.diff = True
+            self.assertEqual(
+                unstyle(str(report)),
+                "2 files would be reformatted, 3 files would be left unchanged, "
+                "2 files would fail to reformat.",
+            )
 
     def test_report_quiet(self) -> None:
         report = black.Report(quiet=True)
@@ -842,6 +882,13 @@ class BlackTestCase(unittest.TestCase):
                 "2 files would be reformatted, 3 files would be left unchanged, "
                 "2 files would fail to reformat.",
             )
+            report.check = False
+            report.diff = True
+            self.assertEqual(
+                unstyle(str(report)),
+                "2 files would be reformatted, 3 files would be left unchanged, "
+                "2 files would fail to reformat.",
+            )
 
     def test_report_normal(self) -> None:
         report = black.Report()
@@ -932,6 +979,13 @@ class BlackTestCase(unittest.TestCase):
                 "2 files would be reformatted, 3 files would be left unchanged, "
                 "2 files would fail to reformat.",
             )
+            report.check = False
+            report.diff = True
+            self.assertEqual(
+                unstyle(str(report)),
+                "2 files would be reformatted, 3 files would be left unchanged, "
+                "2 files would fail to reformat.",
+            )
 
     def test_lib2to3_parse(self) -> None:
         with self.assertRaises(black.InvalidInput):
@@ -1051,10 +1105,10 @@ class BlackTestCase(unittest.TestCase):
         just_nl = "\n"
         with self.assertRaises(black.NothingChanged):
             black.format_file_contents(just_nl, mode=mode, fast=False)
-        same = "l = [1, 2, 3]\n"
+        same = "j = [1, 2, 3]\n"
         with self.assertRaises(black.NothingChanged):
             black.format_file_contents(same, mode=mode, fast=False)
-        different = "l = [1,2,3]"
+        different = "j = [1,2,3]"
         expected = same
         actual = black.format_file_contents(different, mode=mode, fast=False)
         self.assertEqual(expected, actual)
@@ -1082,7 +1136,7 @@ class BlackTestCase(unittest.TestCase):
 
         with patch("black.out", out), patch("black.err", err):
             with self.assertRaises(AssertionError):
-                self.assertFormatEqual("l = [1, 2, 3]", "l = [1, 2, 3,]")
+                self.assertFormatEqual("j = [1, 2, 3]", "j = [1, 2, 3,]")
 
         out_str = "".join(out_lines)
         self.assertTrue("Expected tree:" in out_str)
@@ -1255,6 +1309,13 @@ class BlackTestCase(unittest.TestCase):
             two = black.read_cache(short_mode)
             self.assertNotIn(path, two)
 
+    def test_tricky_unicode_symbols(self) -> None:
+        source, expected = read_data("tricky_unicode_symbols")
+        actual = fs(source)
+        self.assertFormatEqual(expected, actual)
+        black.assert_equivalent(source, actual)
+        black.assert_stable(source, actual, black.FileMode())
+
     def test_single_file_force_pyi(self) -> None:
         reg_mode = black.FileMode()
         pyi_mode = black.FileMode(is_pyi=True)
@@ -1350,6 +1411,13 @@ class BlackTestCase(unittest.TestCase):
                 self.assertIn(path, pyi_cache)
                 self.assertNotIn(path, normal_cache)
 
+    def test_collections(self) -> None:
+        source, expected = read_data("collections")
+        actual = fs(source)
+        self.assertFormatEqual(expected, actual)
+        black.assert_equivalent(source, actual)
+        black.assert_stable(source, actual, black.FileMode())
+
     def test_pipe_force_py36(self) -> None:
         source, expected = read_data("force_py36")
         result = CliRunner().invoke(
@@ -1366,6 +1434,7 @@ class BlackTestCase(unittest.TestCase):
         include = re.compile(r"\.pyi?$")
         exclude = re.compile(r"/exclude/|/\.definitely_exclude/")
         report = black.Report()
+        gitignore = PathSpec.from_lines("gitwildmatch", [])
         sources: List[Path] = []
         expected = [
             Path(path / "b/dont_exclude/a.py"),
@@ -1373,13 +1442,37 @@ class BlackTestCase(unittest.TestCase):
         ]
         this_abs = THIS_DIR.resolve()
         sources.extend(
-            black.gen_python_files_in_dir(path, this_abs, include, exclude, report)
+            black.gen_python_files_in_dir(
+                path, this_abs, include, exclude, report, gitignore
+            )
+        )
+        self.assertEqual(sorted(expected), sorted(sources))
+
+    def test_gitignore_exclude(self) -> None:
+        path = THIS_DIR / "data" / "include_exclude_tests"
+        include = re.compile(r"\.pyi?$")
+        exclude = re.compile(r"")
+        report = black.Report()
+        gitignore = PathSpec.from_lines(
+            "gitwildmatch", ["exclude/", ".definitely_exclude"]
+        )
+        sources: List[Path] = []
+        expected = [
+            Path(path / "b/dont_exclude/a.py"),
+            Path(path / "b/dont_exclude/a.pyi"),
+        ]
+        this_abs = THIS_DIR.resolve()
+        sources.extend(
+            black.gen_python_files_in_dir(
+                path, this_abs, include, exclude, report, gitignore
+            )
         )
         self.assertEqual(sorted(expected), sorted(sources))
 
     def test_empty_include(self) -> None:
         path = THIS_DIR / "data" / "include_exclude_tests"
         report = black.Report()
+        gitignore = PathSpec.from_lines("gitwildmatch", [])
         empty = re.compile(r"")
         sources: List[Path] = []
         expected = [
@@ -1396,7 +1489,12 @@ class BlackTestCase(unittest.TestCase):
         this_abs = THIS_DIR.resolve()
         sources.extend(
             black.gen_python_files_in_dir(
-                path, this_abs, empty, re.compile(black.DEFAULT_EXCLUDES), report
+                path,
+                this_abs,
+                empty,
+                re.compile(black.DEFAULT_EXCLUDES),
+                report,
+                gitignore,
             )
         )
         self.assertEqual(sorted(expected), sorted(sources))
@@ -1404,6 +1502,7 @@ class BlackTestCase(unittest.TestCase):
     def test_empty_exclude(self) -> None:
         path = THIS_DIR / "data" / "include_exclude_tests"
         report = black.Report()
+        gitignore = PathSpec.from_lines("gitwildmatch", [])
         empty = re.compile(r"")
         sources: List[Path] = []
         expected = [
@@ -1417,7 +1516,12 @@ class BlackTestCase(unittest.TestCase):
         this_abs = THIS_DIR.resolve()
         sources.extend(
             black.gen_python_files_in_dir(
-                path, this_abs, re.compile(black.DEFAULT_INCLUDES), empty, report
+                path,
+                this_abs,
+                re.compile(black.DEFAULT_INCLUDES),
+                empty,
+                report,
+                gitignore,
             )
         )
         self.assertEqual(sorted(expected), sorted(sources))
@@ -1462,13 +1566,19 @@ class BlackTestCase(unittest.TestCase):
         include = re.compile(black.DEFAULT_INCLUDES)
         exclude = re.compile(black.DEFAULT_EXCLUDES)
         report = black.Report()
+        gitignore = PathSpec.from_lines("gitwildmatch", [])
         # `child` should behave like a symlink which resolved path is clearly
         # outside of the `root` directory.
         path.iterdir.return_value = [child]
         child.resolve.return_value = Path("/a/b/c")
+        child.as_posix.return_value = "/a/b/c"
         child.is_symlink.return_value = True
         try:
-            list(black.gen_python_files_in_dir(path, root, include, exclude, report))
+            list(
+                black.gen_python_files_in_dir(
+                    path, root, include, exclude, report, gitignore
+                )
+            )
         except ValueError as ve:
             self.fail(f"`get_python_files_in_dir()` failed: {ve}")
         path.iterdir.assert_called_once()
@@ -1478,7 +1588,11 @@ class BlackTestCase(unittest.TestCase):
         # outside of the `root` directory.
         child.is_symlink.return_value = False
         with self.assertRaises(ValueError):
-            list(black.gen_python_files_in_dir(path, root, include, exclude, report))
+            list(
+                black.gen_python_files_in_dir(
+                    path, root, include, exclude, report, gitignore
+                )
+            )
         path.iterdir.assert_called()
         self.assertEqual(path.iterdir.call_count, 2)
         child.resolve.assert_called()
@@ -1523,108 +1637,124 @@ class BlackTestCase(unittest.TestCase):
         ):
             ff(THIS_FILE)
 
+    @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
+    def test_blackd_main(self) -> None:
+        with patch("blackd.web.run_app"):
+            result = CliRunner().invoke(blackd.main, [])
+            if result.exception is not None:
+                raise result.exception
+            self.assertEqual(result.exit_code, 0)
+
+
+class BlackDTestCase(AioHTTPTestCase):
+    async def get_application(self) -> web.Application:
+        return blackd.make_app()
+
     # TODO: remove these decorators once the below is released
-    # https://github.com/aio-libs/aiohttp/commit/bfb99eb92bbc4a7462bc51dda3e480feed43882d
+    # https://github.com/aio-libs/aiohttp/pull/3727
     @skip_if_exception("ClientOSError")
     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
-    @async_test
+    @unittest_run_loop
     async def test_blackd_request_needs_formatting(self) -> None:
-        app = blackd.make_app()
-        async with TestClient(TestServer(app)) as client:
-            response = await client.post("/", data=b"print('hello world')")
-            self.assertEqual(response.status, 200)
-            self.assertEqual(response.charset, "utf8")
-            self.assertEqual(await response.read(), b'print("hello world")\n')
+        response = await self.client.post("/", data=b"print('hello world')")
+        self.assertEqual(response.status, 200)
+        self.assertEqual(response.charset, "utf8")
+        self.assertEqual(await response.read(), b'print("hello world")\n')
 
     @skip_if_exception("ClientOSError")
     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
-    @async_test
+    @unittest_run_loop
     async def test_blackd_request_no_change(self) -> None:
-        app = blackd.make_app()
-        async with TestClient(TestServer(app)) as client:
-            response = await client.post("/", data=b'print("hello world")\n')
-            self.assertEqual(response.status, 204)
-            self.assertEqual(await response.read(), b"")
+        response = await self.client.post("/", data=b'print("hello world")\n')
+        self.assertEqual(response.status, 204)
+        self.assertEqual(await response.read(), b"")
 
     @skip_if_exception("ClientOSError")
     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
-    @async_test
+    @unittest_run_loop
     async def test_blackd_request_syntax_error(self) -> None:
-        app = blackd.make_app()
-        async with TestClient(TestServer(app)) as client:
-            response = await client.post("/", data=b"what even ( is")
-            self.assertEqual(response.status, 400)
-            content = await response.text()
-            self.assertTrue(
-                content.startswith("Cannot parse"),
-                msg=f"Expected error to start with 'Cannot parse', got {repr(content)}",
-            )
+        response = await self.client.post("/", data=b"what even ( is")
+        self.assertEqual(response.status, 400)
+        content = await response.text()
+        self.assertTrue(
+            content.startswith("Cannot parse"),
+            msg=f"Expected error to start with 'Cannot parse', got {repr(content)}",
+        )
 
     @skip_if_exception("ClientOSError")
     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
-    @async_test
+    @unittest_run_loop
     async def test_blackd_unsupported_version(self) -> None:
-        app = blackd.make_app()
-        async with TestClient(TestServer(app)) as client:
-            response = await client.post(
-                "/", data=b"what", headers={blackd.VERSION_HEADER: "2"}
-            )
-            self.assertEqual(response.status, 501)
+        response = await self.client.post(
+            "/", data=b"what", headers={blackd.PROTOCOL_VERSION_HEADER: "2"}
+        )
+        self.assertEqual(response.status, 501)
 
     @skip_if_exception("ClientOSError")
     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
-    @async_test
+    @unittest_run_loop
     async def test_blackd_supported_version(self) -> None:
-        app = blackd.make_app()
-        async with TestClient(TestServer(app)) as client:
-            response = await client.post(
-                "/", data=b"what", headers={blackd.VERSION_HEADER: "1"}
-            )
-            self.assertEqual(response.status, 200)
+        response = await self.client.post(
+            "/", data=b"what", headers={blackd.PROTOCOL_VERSION_HEADER: "1"}
+        )
+        self.assertEqual(response.status, 200)
 
     @skip_if_exception("ClientOSError")
     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
-    @async_test
+    @unittest_run_loop
     async def test_blackd_invalid_python_variant(self) -> None:
-        app = blackd.make_app()
-        async with TestClient(TestServer(app)) as client:
-
-            async def check(header_value: str, expected_status: int = 400) -> None:
-                response = await client.post(
-                    "/",
-                    data=b"what",
-                    headers={blackd.PYTHON_VARIANT_HEADER: header_value},
-                )
-                self.assertEqual(response.status, expected_status)
-
-            await check("lol")
-            await check("ruby3.5")
-            await check("pyi3.6")
-            await check("py1.5")
-            await check("2.8")
-            await check("py2.8")
-            await check("3.0")
-            await check("pypy3.0")
-            await check("jython3.4")
+        async def check(header_value: str, expected_status: int = 400) -> None:
+            response = await self.client.post(
+                "/", data=b"what", headers={blackd.PYTHON_VARIANT_HEADER: header_value}
+            )
+            self.assertEqual(response.status, expected_status)
+
+        await check("lol")
+        await check("ruby3.5")
+        await check("pyi3.6")
+        await check("py1.5")
+        await check("2.8")
+        await check("py2.8")
+        await check("3.0")
+        await check("pypy3.0")
+        await check("jython3.4")
 
     @skip_if_exception("ClientOSError")
     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
-    @async_test
+    @unittest_run_loop
     async def test_blackd_pyi(self) -> None:
-        app = blackd.make_app()
-        async with TestClient(TestServer(app)) as client:
-            source, expected = read_data("stub.pyi")
-            response = await client.post(
-                "/", data=source, headers={blackd.PYTHON_VARIANT_HEADER: "pyi"}
-            )
-            self.assertEqual(response.status, 200)
-            self.assertEqual(await response.text(), expected)
+        source, expected = read_data("stub.pyi")
+        response = await self.client.post(
+            "/", data=source, headers={blackd.PYTHON_VARIANT_HEADER: "pyi"}
+        )
+        self.assertEqual(response.status, 200)
+        self.assertEqual(await response.text(), expected)
 
     @skip_if_exception("ClientOSError")
     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
-    @async_test
+    @unittest_run_loop
+    async def test_blackd_diff(self) -> None:
+        diff_header = re.compile(
+            rf"(In|Out)\t\d\d\d\d-\d\d-\d\d "
+            rf"\d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d"
+        )
+
+        source, _ = read_data("blackd_diff.py")
+        expected, _ = read_data("blackd_diff.diff")
+
+        response = await self.client.post(
+            "/", data=source, headers={blackd.DIFF_HEADER: "true"}
+        )
+        self.assertEqual(response.status, 200)
+
+        actual = await response.text()
+        actual = diff_header.sub(DETERMINISTIC_HEADER, actual)
+        self.assertEqual(actual, expected)
+
+    @skip_if_exception("ClientOSError")
+    @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
+    @unittest_run_loop
     async def test_blackd_python_variant(self) -> None:
-        app = blackd.make_app()
         code = (
             "def f(\n"
             "    and_has_a_bunch_of,\n"
@@ -1634,56 +1764,55 @@ class BlackTestCase(unittest.TestCase):
             "):\n"
             "    pass\n"
         )
-        async with TestClient(TestServer(app)) as client:
-
-            async def check(header_value: str, expected_status: int) -> None:
-                response = await client.post(
-                    "/", data=code, headers={blackd.PYTHON_VARIANT_HEADER: header_value}
-                )
-                self.assertEqual(response.status, expected_status)
 
-            await check("3.6", 200)
-            await check("py3.6", 200)
-            await check("3.6,3.7", 200)
-            await check("3.6,py3.7", 200)
+        async def check(header_value: str, expected_status: int) -> None:
+            response = await self.client.post(
+                "/", data=code, headers={blackd.PYTHON_VARIANT_HEADER: header_value}
+            )
+            self.assertEqual(
+                response.status, expected_status, msg=await response.text()
+            )
 
-            await check("2", 204)
-            await check("2.7", 204)
-            await check("py2.7", 204)
-            await check("3.4", 204)
-            await check("py3.4", 204)
+        await check("3.6", 200)
+        await check("py3.6", 200)
+        await check("3.6,3.7", 200)
+        await check("3.6,py3.7", 200)
+        await check("py36,py37", 200)
+        await check("36", 200)
+        await check("3.6.4", 200)
+
+        await check("2", 204)
+        await check("2.7", 204)
+        await check("py2.7", 204)
+        await check("3.4", 204)
+        await check("py3.4", 204)
+        await check("py34,py36", 204)
+        await check("34", 204)
 
     @skip_if_exception("ClientOSError")
     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
-    @async_test
+    @unittest_run_loop
     async def test_blackd_line_length(self) -> None:
-        app = blackd.make_app()
-        async with TestClient(TestServer(app)) as client:
-            response = await client.post(
-                "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "7"}
-            )
-            self.assertEqual(response.status, 200)
+        response = await self.client.post(
+            "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "7"}
+        )
+        self.assertEqual(response.status, 200)
 
     @skip_if_exception("ClientOSError")
     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
-    @async_test
+    @unittest_run_loop
     async def test_blackd_invalid_line_length(self) -> None:
-        app = blackd.make_app()
-        async with TestClient(TestServer(app)) as client:
-            response = await client.post(
-                "/",
-                data=b'print("hello")\n',
-                headers={blackd.LINE_LENGTH_HEADER: "NaN"},
-            )
-            self.assertEqual(response.status, 400)
+        response = await self.client.post(
+            "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "NaN"}
+        )
+        self.assertEqual(response.status, 400)
 
+    @skip_if_exception("ClientOSError")
     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
-    def test_blackd_main(self) -> None:
-        with patch("blackd.web.run_app"):
-            result = CliRunner().invoke(blackd.main, [])
-            if result.exception is not None:
-                raise result.exception
-            self.assertEqual(result.exit_code, 0)
+    @unittest_run_loop
+    async def test_blackd_response_black_version_header(self) -> None:
+        response = await self.client.post("/")
+        self.assertIsNotNone(response.headers.get(blackd.BLACK_VERSION_HEADER))
 
 
 if __name__ == "__main__":