X-Git-Url: https://git.madduck.net/etc/vim.git/blobdiff_plain/7567cdf3b4f32d4fb12bd5ca0da838f7ff252cfc..16275d24e3c2ad88d953d67e7d8795fd7f73f7cb:/tests/test_black.py diff --git a/tests/test_black.py b/tests/test_black.py index f0a14aa..398a528 100644 --- a/tests/test_black.py +++ b/tests/test_black.py @@ -6,6 +6,7 @@ from concurrent.futures import ThreadPoolExecutor from contextlib import contextmanager from dataclasses import replace import inspect +import io from io import BytesIO import os from pathlib import Path @@ -25,6 +26,7 @@ from typing import ( import pytest import unittest from unittest.mock import patch, MagicMock +from parameterized import parameterized import click from click import unstyle @@ -43,6 +45,7 @@ from pathspec import PathSpec # Import other test classes from tests.util import ( THIS_DIR, + change_directory, read_data, DETERMINISTIC_HEADER, BlackBaseTestCase, @@ -119,6 +122,8 @@ class BlackTestCase(BlackBaseTestCase): if ignore_config: args = ["--verbose", "--config", str(THIS_DIR / "empty.toml"), *args] result = runner.invoke(black.main, args) + assert result.stdout_bytes is not None + assert result.stderr_bytes is not None self.assertEqual( result.exit_code, exit_code, @@ -295,6 +300,14 @@ class BlackTestCase(BlackBaseTestCase): versions = black.detect_target_versions(root) self.assertIn(black.TargetVersion.PY38, versions) + @parameterized.expand([(3, 9), (3, 10)]) + def test_pep_572_newer_syntax(self, major: int, minor: int) -> None: + source, expected = read_data(f"pep_572_py{major}{minor}") + actual = fs(source, mode=DEFAULT_MODE) + self.assertFormatEqual(expected, actual) + if sys.version_info >= (major, minor): + black.assert_equivalent(source, actual) + def test_expression_ff(self) -> None: source, expected = read_data("expression") tmp_file = Path(black.dump_to_file(source)) @@ -448,37 +461,6 @@ class BlackTestCase(BlackBaseTestCase): ) self.assertEqual(expected, actual, msg) - @pytest.mark.no_python2 - def test_python2_should_fail_without_optional_install(self) -> None: - if sys.version_info < (3, 8): - self.skipTest( - "Python 3.6 and 3.7 will install typed-ast to work and as such will be" - " able to parse Python 2 syntax without explicitly specifying the" - " python2 extra" - ) - - source = "x = 1234l" - tmp_file = Path(black.dump_to_file(source)) - try: - runner = BlackRunner() - result = runner.invoke(black.main, [str(tmp_file)]) - self.assertEqual(result.exit_code, 123) - finally: - os.unlink(tmp_file) - actual = ( - result.stderr_bytes.decode() - .replace("\n", "") - .replace("\\n", "") - .replace("\\r", "") - .replace("\r", "") - ) - msg = ( - "The requested source code has invalid Python 3 syntax." - "If you are trying to format Python 2 files please reinstall Black" - " with the 'python2' extra: `python3 -m pip install black[python2]`." - ) - self.assertIn(msg, actual) - @pytest.mark.python2 @patch("black.dump_to_file", dump_to_stderr) def test_python2_print_function(self) -> None: @@ -1406,6 +1388,8 @@ class BlackTestCase(BlackBaseTestCase): None, report, gitignore, + verbose=False, + quiet=False, ) ) self.assertEqual(sorted(expected), sorted(sources)) @@ -1655,6 +1639,30 @@ class BlackTestCase(BlackBaseTestCase): # __BLACK_STDIN_FILENAME__ should have been stripped report.done.assert_called_with(expected, black.Changed.YES) + def test_reformat_one_with_stdin_filename_ipynb(self) -> None: + with patch( + "black.format_stdin_to_stdout", + return_value=lambda *args, **kwargs: black.Changed.YES, + ) as fsts: + report = MagicMock() + p = "foo.ipynb" + path = Path(f"__BLACK_STDIN_FILENAME__{p}") + expected = Path(p) + black.reformat_one( + path, + fast=True, + write_back=black.WriteBack.YES, + mode=DEFAULT_MODE, + report=report, + ) + fsts.assert_called_once_with( + fast=True, + write_back=black.WriteBack.YES, + mode=replace(DEFAULT_MODE, is_ipynb=True), + ) + # __BLACK_STDIN_FILENAME__ should have been stripped + report.done.assert_called_with(expected, black.Changed.YES) + def test_reformat_one_with_stdin_and_existing_path(self) -> None: with patch( "black.format_stdin_to_stdout", @@ -1679,6 +1687,20 @@ class BlackTestCase(BlackBaseTestCase): # __BLACK_STDIN_FILENAME__ should have been stripped report.done.assert_called_with(expected, black.Changed.YES) + def test_reformat_one_with_stdin_empty(self) -> None: + output = io.StringIO() + with patch("io.TextIOWrapper", lambda *args, **kwargs: output): + try: + black.format_stdin_to_stdout( + fast=True, + content="", + write_back=black.WriteBack.YES, + mode=DEFAULT_MODE, + ) + except io.UnsupportedOperation: + pass # StringIO does not support detach + assert output.getvalue() == "" + def test_gitignore_exclude(self) -> None: path = THIS_DIR / "data" / "include_exclude_tests" include = re.compile(r"\.pyi?$") @@ -1703,6 +1725,8 @@ class BlackTestCase(BlackBaseTestCase): None, report, gitignore, + verbose=False, + quiet=False, ) ) self.assertEqual(sorted(expected), sorted(sources)) @@ -1730,10 +1754,36 @@ class BlackTestCase(BlackBaseTestCase): None, report, root_gitignore, + verbose=False, + quiet=False, ) ) self.assertEqual(sorted(expected), sorted(sources)) + def test_invalid_gitignore(self) -> None: + path = THIS_DIR / "data" / "invalid_gitignore_tests" + empty_config = path / "pyproject.toml" + result = BlackRunner().invoke( + black.main, ["--verbose", "--config", str(empty_config), str(path)] + ) + assert result.exit_code == 1 + assert result.stderr_bytes is not None + + gitignore = path / ".gitignore" + assert f"Could not parse {gitignore}" in result.stderr_bytes.decode() + + def test_invalid_nested_gitignore(self) -> None: + path = THIS_DIR / "data" / "invalid_nested_gitignore_tests" + empty_config = path / "pyproject.toml" + result = BlackRunner().invoke( + black.main, ["--verbose", "--config", str(empty_config), str(path)] + ) + assert result.exit_code == 1 + assert result.stderr_bytes is not None + + gitignore = path / "a" / ".gitignore" + assert f"Could not parse {gitignore}" in result.stderr_bytes.decode() + def test_empty_include(self) -> None: path = THIS_DIR / "data" / "include_exclude_tests" report = black.Report() @@ -1764,6 +1814,8 @@ class BlackTestCase(BlackBaseTestCase): None, report, gitignore, + verbose=False, + quiet=False, ) ) self.assertEqual(sorted(expected), sorted(sources)) @@ -1788,6 +1840,8 @@ class BlackTestCase(BlackBaseTestCase): None, report, gitignore, + verbose=False, + quiet=False, ) ) self.assertEqual(sorted(expected), sorted(sources)) @@ -1796,6 +1850,16 @@ class BlackTestCase(BlackBaseTestCase): for option in ["--include", "--exclude", "--extend-exclude", "--force-exclude"]: self.invokeBlack(["-", option, "**()(!!*)"], exit_code=2) + def test_required_version_matches_version(self) -> None: + self.invokeBlack( + ["--required-version", black.__version__], exit_code=0, ignore_config=True + ) + + def test_required_version_does_not_match_version(self) -> None: + self.invokeBlack( + ["--required-version", "20.99b"], exit_code=1, ignore_config=True + ) + def test_preserves_line_endings(self) -> None: with TemporaryDirectory() as workspace: test_file = Path(workspace) / "test.py" @@ -1850,6 +1914,8 @@ class BlackTestCase(BlackBaseTestCase): None, report, gitignore, + verbose=False, + quiet=False, ) ) except ValueError as ve: @@ -1871,6 +1937,8 @@ class BlackTestCase(BlackBaseTestCase): None, report, gitignore, + verbose=False, + quiet=False, ) ) path.iterdir.assert_called() @@ -1882,7 +1950,7 @@ class BlackTestCase(BlackBaseTestCase): def test_shhh_click(self) -> None: try: - from click import _unicodefun # type: ignore + from click import _unicodefun except ModuleNotFoundError: self.skipTest("Incompatible Click version") if not hasattr(_unicodefun, "_verify_python3_env"): @@ -1891,14 +1959,14 @@ class BlackTestCase(BlackBaseTestCase): with patch("locale.getpreferredencoding") as gpe: gpe.return_value = "ASCII" with self.assertRaises(RuntimeError): - _unicodefun._verify_python3_env() + _unicodefun._verify_python3_env() # type: ignore # 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() + _unicodefun._verify_python3_env() # type: ignore except RuntimeError as re: self.fail(f"`patch_click()` failed, exception still raised: {re}") @@ -2013,17 +2081,12 @@ class BlackTestCase(BlackBaseTestCase): return # https://bugs.python.org/issue33660 - - old_cwd = Path.cwd() - try: - root = Path("/") - os.chdir(str(root)) + root = Path("/") + with change_directory(root): path = Path("workspace") / "project" report = black.Report(verbose=True) normalized_path = black.normalize_path_maybe_ignore(path, root, report) self.assertEqual(normalized_path, "workspace/project") - finally: - os.chdir(str(old_cwd)) def test_newline_comment_interaction(self) -> None: source = "class A:\\\r\n# type: ignore\n pass\n" @@ -2174,10 +2237,6 @@ class BlackTestCase(BlackBaseTestCase): Test that the code option finds the pyproject.toml in the current directory. """ with patch.object(black, "parse_pyproject_toml", return_value={}) as parse: - # Make sure we are in the project root with the pyproject file - if not Path("tests").exists(): - os.chdir("..") - args = ["--code", "print"] CliRunner().invoke(black.main, args) @@ -2196,22 +2255,19 @@ class BlackTestCase(BlackBaseTestCase): Test that the code option finds the pyproject.toml in the parent directory. """ with patch.object(black, "parse_pyproject_toml", return_value={}) as parse: - # Make sure we are in the tests directory - if Path("tests").exists(): - os.chdir("tests") - - args = ["--code", "print"] - CliRunner().invoke(black.main, args) - - pyproject_path = Path(Path().cwd().parent, "pyproject.toml").resolve() - assert ( - len(parse.mock_calls) >= 1 - ), "Expected config parse to be called with the current directory." - - _, call_args, _ = parse.mock_calls[0] - assert ( - call_args[0].lower() == str(pyproject_path).lower() - ), "Incorrect config loaded." + with change_directory(Path("tests")): + args = ["--code", "print"] + CliRunner().invoke(black.main, args) + + pyproject_path = Path(Path().cwd().parent, "pyproject.toml").resolve() + assert ( + len(parse.mock_calls) >= 1 + ), "Expected config parse to be called with the current directory." + + _, call_args, _ = parse.mock_calls[0] + assert ( + call_args[0].lower() == str(pyproject_path).lower() + ), "Incorrect config loaded." with open(black.__file__, "r", encoding="utf-8") as _bf: