third_party.pylibs.pylint.src/pylint/testutils/checker_test_case.py
Pierre Sassoulas 9e0baf370a Simplify hard to maintain copyright notice
git is the source of truth for the copyright, copyrite (the tool)
was taking exponentially longer with each release, and it's
polluting the code with sometime as much as 50 lines of names.
2022-03-24 13:06:15 +01:00

96 lines
4.0 KiB
Python

# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/PyCQA/pylint/graphs/contributors
import contextlib
import warnings
from typing import Dict, Generator, Optional, Type
from pylint.constants import PY38_PLUS
from pylint.testutils.global_test_linter import linter
from pylint.testutils.output_line import MessageTest
from pylint.testutils.unittest_linter import UnittestLinter
from pylint.utils import ASTWalker
class CheckerTestCase:
"""A base testcase class for unit testing individual checker classes."""
CHECKER_CLASS: Optional[Type] = None
CONFIG: Dict = {}
def setup_method(self):
self.linter = UnittestLinter()
self.checker = self.CHECKER_CLASS(self.linter) # pylint: disable=not-callable
for key, value in self.CONFIG.items():
setattr(self.checker.config, key, value)
self.checker.open()
@contextlib.contextmanager
def assertNoMessages(self):
"""Assert that no messages are added by the given method."""
with self.assertAddsMessages():
yield
@contextlib.contextmanager
def assertAddsMessages(
self, *messages: MessageTest, ignore_position: bool = False
) -> Generator[None, None, None]:
"""Assert that exactly the given method adds the given messages.
The list of messages must exactly match *all* the messages added by the
method. Additionally, we check to see whether the args in each message can
actually be substituted into the message string.
Using the keyword argument `ignore_position`, all checks for position
arguments (line, col_offset, ...) will be skipped. This can be used to
just test messages for the correct node.
"""
yield
got = self.linter.release_messages()
no_msg = "No message."
expected = "\n".join(repr(m) for m in messages) or no_msg
got_str = "\n".join(repr(m) for m in got) or no_msg
msg = (
"Expected messages did not match actual.\n"
f"\nExpected:\n{expected}\n\nGot:\n{got_str}\n"
)
assert len(messages) == len(got), msg
for expected_msg, gotten_msg in zip(messages, got):
assert expected_msg.msg_id == gotten_msg.msg_id, msg
assert expected_msg.node == gotten_msg.node, msg
assert expected_msg.args == gotten_msg.args, msg
assert expected_msg.confidence == gotten_msg.confidence, msg
if ignore_position:
# Do not check for line, col_offset etc...
continue
assert expected_msg.line == gotten_msg.line, msg
assert expected_msg.col_offset == gotten_msg.col_offset, msg
if PY38_PLUS:
# pylint: disable=fixme
# TODO: Require end_line and end_col_offset and remove the warning
if not expected_msg.end_line == gotten_msg.end_line:
warnings.warn( # pragma: no cover
f"The end_line attribute of {gotten_msg} does not match "
f"the expected value in {expected_msg}. In pylint 3.0 correct end_line "
"attributes will be required for MessageTest.",
DeprecationWarning,
)
if not expected_msg.end_col_offset == gotten_msg.end_col_offset:
warnings.warn( # pragma: no cover
f"The end_col_offset attribute of {gotten_msg} does not match "
f"the expected value in {expected_msg}. In pylint 3.0 correct end_col_offset "
"attributes will be required for MessageTest.",
DeprecationWarning,
)
def walk(self, node):
"""Recursive walk on the given node."""
walker = ASTWalker(linter)
walker.add_checker(self.checker)
walker.walk(node)