flake8/tests/unit/test_utils.py
2022-10-26 20:39:12 -07:00

213 lines
6.2 KiB
Python

"""Tests for flake8's utils module."""
from __future__ import annotations
import io
import logging
import os
import sys
from unittest import mock
import pytest
from flake8 import exceptions
from flake8 import utils
RELATIVE_PATHS = ["flake8", "pep8", "pyflakes", "mccabe"]
@pytest.mark.parametrize(
"value,expected",
[
("E123,\n\tW234,\n E206", ["E123", "W234", "E206"]),
("E123,W234,E206", ["E123", "W234", "E206"]),
("E123 W234 E206", ["E123", "W234", "E206"]),
("E123\nW234 E206", ["E123", "W234", "E206"]),
("E123\nW234\nE206", ["E123", "W234", "E206"]),
("E123,W234,E206,", ["E123", "W234", "E206"]),
("E123,W234,E206, ,\n", ["E123", "W234", "E206"]),
("E123,W234,,E206,,", ["E123", "W234", "E206"]),
("E123, W234,, E206,,", ["E123", "W234", "E206"]),
("E123,,W234,,E206,,", ["E123", "W234", "E206"]),
("", []),
],
)
def test_parse_comma_separated_list(value, expected):
"""Verify that similar inputs produce identical outputs."""
assert utils.parse_comma_separated_list(value) == expected
@pytest.mark.parametrize(
("value", "expected"),
(
# empty option configures nothing
("", []),
(" ", []),
("\n\n\n", []),
# basic case
(
"f.py:E123",
[("f.py", ["E123"])],
),
# multiple filenames, multiple codes
(
"f.py,g.py:E,F",
[("f.py", ["E", "F"]), ("g.py", ["E", "F"])],
),
# demonstrate that whitespace is not important around tokens
(
" f.py , g.py : E , F ",
[("f.py", ["E", "F"]), ("g.py", ["E", "F"])],
),
# whitespace can separate groups of configuration
(
"f.py:E g.py:F",
[("f.py", ["E"]), ("g.py", ["F"])],
),
# newlines can separate groups of configuration
(
"f.py: E\ng.py: F\n",
[("f.py", ["E"]), ("g.py", ["F"])],
),
# whitespace can be used in place of commas
(
"f.py g.py: E F",
[("f.py", ["E", "F"]), ("g.py", ["E", "F"])],
),
# go ahead, indent your codes
(
"f.py:\n E,F\ng.py:\n G,H",
[("f.py", ["E", "F"]), ("g.py", ["G", "H"])],
),
# capitalized filenames are ok too
(
"F.py,G.py: F,G",
[("F.py", ["F", "G"]), ("G.py", ["F", "G"])],
),
# it's easier to allow zero filenames or zero codes than forbid it
(":E", []),
("f.py:", []),
(":E f.py:F", [("f.py", ["F"])]),
("f.py: g.py:F", [("g.py", ["F"])]),
("f.py:E:", []),
("f.py:E.py:", []),
("f.py:Eg.py:F", [("Eg.py", ["F"])]),
# sequences are also valid (?)
(
["f.py:E,F", "g.py:G,H"],
[("f.py", ["E", "F"]), ("g.py", ["G", "H"])],
),
# six-digits codes are allowed
(
"f.py: ABC123",
[("f.py", ["ABC123"])],
),
),
)
def test_parse_files_to_codes_mapping(value, expected):
"""Test parsing of valid files-to-codes mappings."""
assert utils.parse_files_to_codes_mapping(value) == expected
@pytest.mark.parametrize(
"value",
(
# code while looking for filenames
"E123",
"f.py,E123",
"f.py E123",
# eof while looking for filenames
"f.py",
"f.py:E,g.py"
# colon while looking for codes
"f.py::",
# no separator between
"f.py:E1F1",
),
)
def test_invalid_file_list(value):
"""Test parsing of invalid files-to-codes mappings."""
with pytest.raises(exceptions.ExecutionError):
utils.parse_files_to_codes_mapping(value)
@pytest.mark.parametrize(
"value,expected",
[
("flake8", "flake8"),
(".", os.path.abspath(".")),
("../flake8", os.path.abspath("../flake8")),
("flake8/", os.path.abspath("flake8")),
],
)
def test_normalize_path(value, expected):
"""Verify that we normalize paths provided to the tool."""
assert utils.normalize_path(value) == expected
@pytest.mark.parametrize(
"value,expected",
[
(
["flake8", "pep8", "pyflakes", "mccabe"],
["flake8", "pep8", "pyflakes", "mccabe"],
),
(
["../flake8", "../pep8", "../pyflakes", "../mccabe"],
[os.path.abspath(f"../{p}") for p in RELATIVE_PATHS],
),
],
)
def test_normalize_paths(value, expected):
"""Verify we normalizes a sequence of paths provided to the tool."""
assert utils.normalize_paths(value) == expected
def test_matches_filename_for_excluding_dotfiles():
"""Verify that `.` and `..` are not matched by `.*`."""
logger = logging.Logger(__name__)
assert not utils.matches_filename(".", (".*",), "", logger)
assert not utils.matches_filename("..", (".*",), "", logger)
@pytest.mark.parametrize(
"filename,patterns,expected",
[
("foo.py", [], True),
("foo.py", ["*.pyc"], False),
("foo.pyc", ["*.pyc"], True),
("foo.pyc", ["*.swp", "*.pyc", "*.py"], True),
],
)
def test_fnmatch(filename, patterns, expected):
"""Verify that our fnmatch wrapper works as expected."""
assert utils.fnmatch(filename, patterns) is expected
def test_stdin_get_value_crlf():
"""Ensure that stdin is normalized from crlf to lf."""
stdin = io.TextIOWrapper(io.BytesIO(b"1\r\n2\r\n"), "UTF-8")
with mock.patch.object(sys, "stdin", stdin):
assert utils.stdin_get_value.__wrapped__() == "1\n2\n"
def test_stdin_unknown_coding_token():
"""Ensure we produce source even for unknown encodings."""
stdin = io.TextIOWrapper(io.BytesIO(b"# coding: unknown\n"), "UTF-8")
with mock.patch.object(sys, "stdin", stdin):
assert utils.stdin_get_value.__wrapped__() == "# coding: unknown\n"
@pytest.mark.parametrize(
("s", "expected"),
(
("", ""),
("my-plugin", "my-plugin"),
("MyPlugin", "myplugin"),
("my_plugin", "my-plugin"),
("my.plugin", "my-plugin"),
("my--plugin", "my-plugin"),
("my__plugin", "my-plugin"),
),
)
def test_normalize_pypi_name(s, expected):
assert utils.normalize_pypi_name(s) == expected