X-Git-Url: https://git.madduck.net/etc/vim.git/blobdiff_plain/33baccd88ef45ebbb35e4c2ad7b87c7b91432f55..975ca9cefc9ee0b718a7849c0842e1fccb035baa:/tests/test_black.py?ds=sidebyside diff --git a/tests/test_black.py b/tests/test_black.py index 1271290..85ab2b6 100644 --- a/tests/test_black.py +++ b/tests/test_black.py @@ -11,7 +11,7 @@ import sys from tempfile import TemporaryDirectory from typing import Any, BinaryIO, Generator, List, Tuple, Iterator import unittest -from unittest.mock import patch +from unittest.mock import patch, MagicMock from click import unstyle from click.testing import CliRunner @@ -31,13 +31,14 @@ def dump_to_stderr(*output: str) -> str: return "\n" + "\n".join(output) + "\n" -def read_data(name: str) -> Tuple[str, str]: +def read_data(name: str, data: bool = True) -> Tuple[str, str]: """read_data('test_name') -> 'input', 'output'""" if not name.endswith((".py", ".pyi", ".out", ".diff")): name += ".py" _input: List[str] = [] _output: List[str] = [] - with open(THIS_DIR / name, "r", encoding="utf8") as test: + base_dir = THIS_DIR / "data" if data else THIS_DIR + with open(base_dir / name, "r", encoding="utf8") as test: lines = test.readlines() result = _input for line in lines: @@ -83,8 +84,11 @@ class BlackRunner(CliRunner): This is a hack that can be removed once we depend on Click 7.x""" - def __init__(self, stderrbuf: BinaryIO) -> None: - self.stderrbuf = stderrbuf + def __init__(self) -> None: + self.stderrbuf = BytesIO() + self.stdoutbuf = BytesIO() + self.stdout_bytes = b"" + self.stderr_bytes = b"" super().__init__() @contextmanager @@ -95,6 +99,8 @@ class BlackRunner(CliRunner): sys.stderr = TextIOWrapper(self.stderrbuf, encoding=self.charset) yield output finally: + self.stdout_bytes = sys.stdout.buffer.getvalue() # type: ignore + self.stderr_bytes = sys.stderr.buffer.getvalue() # type: ignore sys.stderr = hold_stderr @@ -141,7 +147,7 @@ class BlackTestCase(unittest.TestCase): @patch("black.dump_to_file", dump_to_stderr) def test_self(self) -> None: - source, expected = read_data("test_black") + source, expected = read_data("test_black", data=False) actual = fs(source) self.assertFormatEqual(expected, actual) black.assert_equivalent(source, actual) @@ -150,7 +156,7 @@ class BlackTestCase(unittest.TestCase): @patch("black.dump_to_file", dump_to_stderr) def test_black(self) -> None: - source, expected = read_data("../black") + source, expected = read_data("../black", data=False) actual = fs(source) self.assertFormatEqual(expected, actual) black.assert_equivalent(source, actual) @@ -158,10 +164,11 @@ class BlackTestCase(unittest.TestCase): self.assertFalse(ff(THIS_DIR / ".." / "black.py")) def test_piping(self) -> None: - source, expected = read_data("../black") - stderrbuf = BytesIO() - result = BlackRunner(stderrbuf).invoke( - black.main, ["-", "--fast", f"--line-length={ll}"], input=source + source, expected = read_data("../black", data=False) + result = BlackRunner().invoke( + black.main, + ["-", "--fast", f"--line-length={ll}"], + input=BytesIO(source.encode("utf8")), ) self.assertEqual(result.exit_code, 0) self.assertFormatEqual(expected, result.output) @@ -175,9 +182,10 @@ class BlackTestCase(unittest.TestCase): ) source, _ = read_data("expression.py") expected, _ = read_data("expression.diff") - stderrbuf = BytesIO() - result = BlackRunner(stderrbuf).invoke( - black.main, ["-", "--fast", f"--line-length={ll}", "--diff"], input=source + config = THIS_DIR / "data" / "empty_pyproject.toml" + args = ["-", "--fast", f"--line-length={ll}", "--diff", f"--config={config}"] + result = BlackRunner().invoke( + black.main, args, input=BytesIO(source.encode("utf8")) ) self.assertEqual(result.exit_code, 0) actual = diff_header.sub("[Deterministic header]", result.output) @@ -186,7 +194,7 @@ class BlackTestCase(unittest.TestCase): @patch("black.dump_to_file", dump_to_stderr) def test_setup(self) -> None: - source, expected = read_data("../setup") + source, expected = read_data("../setup", data=False) actual = fs(source) self.assertFormatEqual(expected, actual) black.assert_equivalent(source, actual) @@ -239,11 +247,8 @@ class BlackTestCase(unittest.TestCase): rf"{re.escape(str(tmp_file))}\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" ) - stderrbuf = BytesIO() try: - result = BlackRunner(stderrbuf).invoke( - black.main, ["--diff", str(tmp_file)] - ) + result = BlackRunner().invoke(black.main, ["--diff", str(tmp_file)]) self.assertEqual(result.exit_code, 0) finally: os.unlink(tmp_file) @@ -368,6 +373,21 @@ class BlackTestCase(unittest.TestCase): black.assert_equivalent(source, actual) black.assert_stable(source, actual, line_length=ll) + @patch("black.dump_to_file", dump_to_stderr) + def test_numeric_literals(self) -> None: + source, expected = read_data("numeric_literals") + actual = fs(source, mode=black.FileMode.PYTHON36) + self.assertFormatEqual(expected, actual) + black.assert_equivalent(source, actual) + black.assert_stable(source, actual, line_length=ll) + + @patch("black.dump_to_file", dump_to_stderr) + def test_numeric_literals_py2(self) -> None: + source, expected = read_data("numeric_literals_py2") + actual = fs(source) + self.assertFormatEqual(expected, actual) + black.assert_stable(source, actual, line_length=ll) + @patch("black.dump_to_file", dump_to_stderr) def test_python2(self) -> None: source, expected = read_data("python2") @@ -391,6 +411,16 @@ class BlackTestCase(unittest.TestCase): self.assertFormatEqual(expected, actual) black.assert_stable(source, actual, line_length=ll, mode=mode) + @patch("black.dump_to_file", dump_to_stderr) + def test_python37(self) -> None: + source, expected = read_data("python37") + actual = fs(source) + self.assertFormatEqual(expected, actual) + major, minor = sys.version_info[:2] + if major > 3 or (major == 3 and minor >= 7): + black.assert_equivalent(source, actual) + black.assert_stable(source, actual, line_length=ll) + @patch("black.dump_to_file", dump_to_stderr) def test_fmtonoff(self) -> None: source, expected = read_data("fmtonoff") @@ -399,6 +429,14 @@ class BlackTestCase(unittest.TestCase): black.assert_equivalent(source, actual) black.assert_stable(source, actual, line_length=ll) + @patch("black.dump_to_file", dump_to_stderr) + def test_fmtonoff2(self) -> None: + source, expected = read_data("fmtonoff2") + actual = fs(source) + self.assertFormatEqual(expected, actual) + black.assert_equivalent(source, actual) + black.assert_stable(source, actual, line_length=ll) + @patch("black.dump_to_file", dump_to_stderr) def test_remove_empty_parentheses_after_class(self) -> None: source, expected = read_data("class_blank_parentheses") @@ -415,6 +453,14 @@ class BlackTestCase(unittest.TestCase): black.assert_equivalent(source, actual) black.assert_stable(source, actual, line_length=ll) + @patch("black.dump_to_file", dump_to_stderr) + def test_bracket_match(self) -> None: + source, expected = read_data("bracketmatch") + actual = fs(source) + self.assertFormatEqual(expected, actual) + black.assert_equivalent(source, actual) + black.assert_stable(source, actual, line_length=ll) + def test_report_verbose(self) -> None: report = black.Report(verbose=True) out_lines = [] @@ -694,6 +740,10 @@ class BlackTestCase(unittest.TestCase): self.assertTrue(black.is_python36(node)) node = black.lib2to3_parse("def f(*, arg): f'string'\n") self.assertTrue(black.is_python36(node)) + node = black.lib2to3_parse("123_456\n") + self.assertTrue(black.is_python36(node)) + node = black.lib2to3_parse("123456\n") + self.assertFalse(black.is_python36(node)) source, expected = read_data("function") node = black.lib2to3_parse(source) self.assertTrue(black.is_python36(node)) @@ -726,6 +776,14 @@ class BlackTestCase(unittest.TestCase): self.assertEqual(set(), black.get_future_imports(node)) node = black.lib2to3_parse("from some.module import black\n") self.assertEqual(set(), black.get_future_imports(node)) + node = black.lib2to3_parse( + "from __future__ import unicode_literals as _unicode_literals" + ) + self.assertEqual({"unicode_literals"}, black.get_future_imports(node)) + node = black.lib2to3_parse( + "from __future__ import unicode_literals as _lol, print" + ) + self.assertEqual({"unicode_literals", "print"}, black.get_future_imports(node)) def test_debug_visitor(self) -> None: source, _ = read_data("debug_visitor.py") @@ -860,7 +918,9 @@ class BlackTestCase(unittest.TestCase): def test_no_cache_when_stdin(self) -> None: mode = black.FileMode.AUTO_DETECT with cache_dir(): - result = CliRunner().invoke(black.main, ["-"], input="print('hello')") + result = CliRunner().invoke( + black.main, ["-"], input=BytesIO(b"print('hello')") + ) self.assertEqual(result.exit_code, 0) cache_file = black.get_cache_file(black.DEFAULT_LINE_LENGTH, mode) self.assertFalse(cache_file.exists()) @@ -931,15 +991,13 @@ class BlackTestCase(unittest.TestCase): def test_check_diff_use_together(self) -> None: with cache_dir(): # Files which will be reformatted. - src1 = (THIS_DIR / "string_quotes.py").resolve() + src1 = (THIS_DIR / "data" / "string_quotes.py").resolve() result = CliRunner().invoke(black.main, [str(src1), "--diff", "--check"]) - self.assertEqual(result.exit_code, 1) - + self.assertEqual(result.exit_code, 1, result.output) # Files which will not be reformatted. - src2 = (THIS_DIR / "composition.py").resolve() + src2 = (THIS_DIR / "data" / "composition.py").resolve() result = CliRunner().invoke(black.main, [str(src2), "--diff", "--check"]) - self.assertEqual(result.exit_code, 0) - + self.assertEqual(result.exit_code, 0, result.output) # Multi file command. result = CliRunner().invoke( black.main, [str(src1), str(src2), "--diff", "--check"] @@ -1020,7 +1078,9 @@ class BlackTestCase(unittest.TestCase): def test_pipe_force_pyi(self) -> None: source, expected = read_data("force_pyi") - result = CliRunner().invoke(black.main, ["-", "-q", "--pyi"], input=source) + result = CliRunner().invoke( + black.main, ["-", "-q", "--pyi"], input=BytesIO(source.encode("utf8")) + ) self.assertEqual(result.exit_code, 0) actual = result.output self.assertFormatEqual(actual, expected) @@ -1074,20 +1134,22 @@ class BlackTestCase(unittest.TestCase): def test_pipe_force_py36(self) -> None: source, expected = read_data("force_py36") - result = CliRunner().invoke(black.main, ["-", "-q", "--py36"], input=source) + result = CliRunner().invoke( + black.main, ["-", "-q", "--py36"], input=BytesIO(source.encode("utf8")) + ) self.assertEqual(result.exit_code, 0) actual = result.output self.assertFormatEqual(actual, expected) def test_include_exclude(self) -> None: - path = THIS_DIR / "include_exclude_tests" + path = THIS_DIR / "data" / "include_exclude_tests" include = re.compile(r"\.pyi?$") exclude = re.compile(r"/exclude/|/\.definitely_exclude/") report = black.Report() sources: List[Path] = [] expected = [ - Path(THIS_DIR / "include_exclude_tests/b/dont_exclude/a.py"), - Path(THIS_DIR / "include_exclude_tests/b/dont_exclude/a.pyi"), + Path(path / "b/dont_exclude/a.py"), + Path(path / "b/dont_exclude/a.pyi"), ] this_abs = THIS_DIR.resolve() sources.extend( @@ -1096,7 +1158,7 @@ class BlackTestCase(unittest.TestCase): self.assertEqual(sorted(expected), sorted(sources)) def test_empty_include(self) -> None: - path = THIS_DIR / "include_exclude_tests" + path = THIS_DIR / "data" / "include_exclude_tests" report = black.Report() empty = re.compile(r"") sources: List[Path] = [] @@ -1120,7 +1182,7 @@ class BlackTestCase(unittest.TestCase): self.assertEqual(sorted(expected), sorted(sources)) def test_empty_exclude(self) -> None: - path = THIS_DIR / "include_exclude_tests" + path = THIS_DIR / "data" / "include_exclude_tests" report = black.Report() empty = re.compile(r"") sources: List[Path] = [] @@ -1153,14 +1215,80 @@ class BlackTestCase(unittest.TestCase): test_file.write_bytes(contents.encode()) ff(test_file, write_back=black.WriteBack.YES) updated_contents: bytes = test_file.read_bytes() - self.assertIn(nl.encode(), updated_contents) # type: ignore + self.assertIn(nl.encode(), updated_contents) if nl == "\n": - self.assertNotIn(b"\r\n", updated_contents) # type: ignore + self.assertNotIn(b"\r\n", updated_contents) + + def test_preserves_line_endings_via_stdin(self) -> None: + for nl in ["\n", "\r\n"]: + contents = nl.join(["def f( ):", " pass"]) + runner = BlackRunner() + result = runner.invoke( + black.main, ["-", "--fast"], input=BytesIO(contents.encode("utf8")) + ) + self.assertEqual(result.exit_code, 0) + output = runner.stdout_bytes + self.assertIn(nl.encode("utf8"), output) + if nl == "\n": + self.assertNotIn(b"\r\n", output) def test_assert_equivalent_different_asts(self) -> None: with self.assertRaises(AssertionError): black.assert_equivalent("{}", "None") + def test_symlink_out_of_root_directory(self) -> None: + path = MagicMock() + root = THIS_DIR + child = MagicMock() + include = re.compile(black.DEFAULT_INCLUDES) + exclude = re.compile(black.DEFAULT_EXCLUDES) + report = black.Report() + # `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.is_symlink.return_value = True + try: + list(black.gen_python_files_in_dir(path, root, include, exclude, report)) + except ValueError as ve: + self.fail("`get_python_files_in_dir()` failed: {ve}") + path.iterdir.assert_called_once() + child.resolve.assert_called_once() + child.is_symlink.assert_called_once() + # `child` should behave like a strange file which resolved path is clearly + # 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)) + path.iterdir.assert_called() + self.assertEqual(path.iterdir.call_count, 2) + child.resolve.assert_called() + self.assertEqual(child.resolve.call_count, 2) + child.is_symlink.assert_called() + self.assertEqual(child.is_symlink.call_count, 2) + + def test_shhh_click(self) -> None: + try: + from click import _unicodefun # type: ignore + except ModuleNotFoundError: + self.skipTest("Incompatible Click version") + if not hasattr(_unicodefun, "_verify_python3_env"): + self.skipTest("Incompatible Click version") + # First, let's see if Click is crashing with a preferred ASCII charset. + with patch("locale.getpreferredencoding") as gpe: + gpe.return_value = "ASCII" + with self.assertRaises(RuntimeError): + _unicodefun._verify_python3_env() + # Now, let's silence Click... + black.patch_click() + # ...and confirm it's silent. + with patch("locale.getpreferredencoding") as gpe: + gpe.return_value = "ASCII" + try: + _unicodefun._verify_python3_env() + except RuntimeError as re: + self.fail(f"`patch_click()` failed, exception still raised: {re}") + if __name__ == "__main__": - unittest.main() + unittest.main(module="test_black")