summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniƫl van Noord <13665637+DanielNoord@users.noreply.github.com>2021-12-13 23:17:48 +0100
committerGitHub <noreply@github.com>2021-12-13 23:17:48 +0100
commit71bbbdb29407a2b4525f0bf2da8b0025da5fc4d8 (patch)
treeb76887ba7216a30ee99f812bc33142a510a2e7eb
parent78eed6af303e0e9854e43528d17d2ada96f11c09 (diff)
downloadpylint-git-71bbbdb29407a2b4525f0bf2da8b0025da5fc4d8.tar.gz
Check `node` location attributes in unittests and update tests (#5383)
* Allow checking of `end_col_offset` and `end_lineno` in unittests * Allow checking of `col_offset` in unittests * Allow checking of `lineno` in unittests * Update tests for ``TestVariablesChecker`` * Fix ``TestMultiNamingStyle`` * Update tests for ``TestImportsChecker`` * Add location attributes to messages of ``TestTypeChecker`` * Add location params to ``TestMessage``'s of ``TestDeprecatedChecker`` * Add location params to ``TestMessage``'s of ``TestTypeCheckerOnDecorators`` * Add changelog and ``DeprecationWarning``
-rw-r--r--ChangeLog3
-rw-r--r--doc/whatsnew/2.13.rst3
-rw-r--r--pylint/testutils/checker_test_case.py35
-rw-r--r--pylint/testutils/output_line.py2
-rw-r--r--pylint/testutils/unittest_linter.py30
-rw-r--r--tests/checkers/unittest_base.py20
-rw-r--r--tests/checkers/unittest_deprecated.py100
-rw-r--r--tests/checkers/unittest_imports.py13
-rw-r--r--tests/checkers/unittest_typecheck.py20
-rw-r--r--tests/checkers/unittest_variables.py28
10 files changed, 239 insertions, 15 deletions
diff --git a/ChangeLog b/ChangeLog
index 8e99896db..44e8f5459 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -67,6 +67,9 @@ Release date: TBA
Closes #5371
+* The ``testutils`` for unittests now accept ``end_lineno`` and ``end_column``. Tests
+ without these will trigger a ``DeprecationWarning``.
+
..
Insert your changelog randomly, it will reduce merge conflicts
(Ie. not necessarily at the end)
diff --git a/doc/whatsnew/2.13.rst b/doc/whatsnew/2.13.rst
index 3f4584fde..a0f9a4a70 100644
--- a/doc/whatsnew/2.13.rst
+++ b/doc/whatsnew/2.13.rst
@@ -70,3 +70,6 @@ Other Changes
* The ``PyLinter`` class will now be initialiazed with a ``TextReporter``
as its reporter if none is provided.
+
+* The ``testutils`` for unittests now accept ``end_lineno`` and ``end_column``. Tests
+ without these will trigger a ``DeprecationWarning``.
diff --git a/pylint/testutils/checker_test_case.py b/pylint/testutils/checker_test_case.py
index 5c8815dab..af72136a9 100644
--- a/pylint/testutils/checker_test_case.py
+++ b/pylint/testutils/checker_test_case.py
@@ -2,9 +2,12 @@
# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
import contextlib
-from typing import Dict, Optional, Type
+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
@@ -29,7 +32,7 @@ class CheckerTestCase:
yield
@contextlib.contextmanager
- def assertAddsMessages(self, *messages):
+ def assertAddsMessages(self, *messages: MessageTest) -> 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
@@ -45,7 +48,33 @@ class CheckerTestCase:
"Expected messages did not match actual.\n"
f"\nExpected:\n{expected}\n\nGot:\n{got_str}\n"
)
- assert got == list(messages), msg
+
+ 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.line == gotten_msg.line, 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
+ 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(
+ 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(
+ 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"""
diff --git a/pylint/testutils/output_line.py b/pylint/testutils/output_line.py
index 420b58c88..c3836a763 100644
--- a/pylint/testutils/output_line.py
+++ b/pylint/testutils/output_line.py
@@ -21,6 +21,8 @@ class MessageTest(NamedTuple):
args: Optional[Any] = None
confidence: Optional[Confidence] = UNDEFINED
col_offset: Optional[int] = None
+ end_line: Optional[int] = None
+ end_col_offset: Optional[int] = None
"""Used to test messages produced by pylint. Class name cannot start with Test as pytest doesn't allow constructors in test classes."""
diff --git a/pylint/testutils/unittest_linter.py b/pylint/testutils/unittest_linter.py
index d37f17099..0c45264f7 100644
--- a/pylint/testutils/unittest_linter.py
+++ b/pylint/testutils/unittest_linter.py
@@ -30,6 +30,8 @@ class UnittestLinter:
self,
msg_id: str,
line: Optional[int] = None,
+ # pylint: disable=fixme
+ # TODO: Make node non optional
node: Optional[nodes.NodeNG] = None,
args: Any = None,
confidence: Optional[Confidence] = None,
@@ -41,16 +43,28 @@ class UnittestLinter:
# If confidence is None we set it to UNDEFINED as well in PyLinter
if confidence is None:
confidence = UNDEFINED
+ if not line and node:
+ line = node.fromlineno
# pylint: disable=fixme
- # TODO: Test col_offset
- # pylint: disable=fixme
- # TODO: Initialize col_offset on every node (can be None) -> astroid
- # if col_offset is None and hasattr(node, "col_offset"):
- # col_offset = node.col_offset
- # pylint: disable=fixme
- # TODO: Test end_lineno and end_col_offset :)
+ # TODO: Initialize col_offset, end_lineno and end_col_offset on every node -> astroid
+ # See https://github.com/PyCQA/astroid/issues/1273
+ if col_offset is None and node and hasattr(node, "col_offset"):
+ col_offset = node.col_offset
+ if not end_lineno and node and hasattr(node, "end_lineno"):
+ end_lineno = node.end_lineno
+ if not end_col_offset and node and hasattr(node, "end_col_offset"):
+ end_col_offset = node.end_col_offset
self._messages.append(
- MessageTest(msg_id, line, node, args, confidence, col_offset)
+ MessageTest(
+ msg_id,
+ line,
+ node,
+ args,
+ confidence,
+ col_offset,
+ end_lineno,
+ end_col_offset,
+ )
)
@staticmethod
diff --git a/tests/checkers/unittest_base.py b/tests/checkers/unittest_base.py
index bbe2be2fa..3dddd859b 100644
--- a/tests/checkers/unittest_base.py
+++ b/tests/checkers/unittest_base.py
@@ -64,6 +64,10 @@ class TestMultiNamingStyle(CheckerTestCase):
"the `UP` group in the '(?:(?P<UP>[A-Z]+)|(?P<down>[a-z]+))$' pattern",
),
confidence=HIGH,
+ line=2,
+ col_offset=0,
+ end_line=3,
+ end_col_offset=8,
)
with self.assertAddsMessages(message):
cls = None
@@ -94,6 +98,10 @@ class TestMultiNamingStyle(CheckerTestCase):
"'(?:(?P<UP>[A-Z]+)|(?P<down>[a-z]+))$' pattern",
),
confidence=HIGH,
+ line=2,
+ col_offset=0,
+ end_line=3,
+ end_col_offset=8,
),
MessageTest(
"invalid-name",
@@ -104,6 +112,10 @@ class TestMultiNamingStyle(CheckerTestCase):
"the `down` group in the '(?:(?P<UP>[A-Z]+)|(?P<down>[a-z]+))$' pattern",
),
confidence=HIGH,
+ line=6,
+ col_offset=0,
+ end_line=7,
+ end_col_offset=8,
),
]
with self.assertAddsMessages(*messages):
@@ -139,6 +151,10 @@ class TestMultiNamingStyle(CheckerTestCase):
"the `down` group in the '(?:(?P<UP>[A-Z]+)|(?P<down>[a-z]+))$' pattern",
),
confidence=HIGH,
+ line=6,
+ col_offset=0,
+ end_line=7,
+ end_col_offset=8,
)
with self.assertAddsMessages(message):
func = None
@@ -172,6 +188,10 @@ class TestMultiNamingStyle(CheckerTestCase):
"the `down` group in the '(?:(?P<ignore>FOO)|(?P<UP>[A-Z]+)|(?P<down>[a-z]+))$' pattern",
),
confidence=HIGH,
+ line=8,
+ col_offset=0,
+ end_line=9,
+ end_col_offset=8,
)
with self.assertAddsMessages(message):
func = None
diff --git a/tests/checkers/unittest_deprecated.py b/tests/checkers/unittest_deprecated.py
index fbf6c1ce8..75608acf4 100644
--- a/tests/checkers/unittest_deprecated.py
+++ b/tests/checkers/unittest_deprecated.py
@@ -71,6 +71,10 @@ class TestDeprecatedChecker(CheckerTestCase):
args=("deprecated_func",),
node=node,
confidence=UNDEFINED,
+ line=5,
+ col_offset=0,
+ end_line=5,
+ end_col_offset=17,
)
):
self.checker.visit_call(node)
@@ -93,6 +97,10 @@ class TestDeprecatedChecker(CheckerTestCase):
args=("deprecated_method",),
node=node,
confidence=UNDEFINED,
+ line=7,
+ col_offset=0,
+ end_line=7,
+ end_col_offset=21,
)
):
self.checker.visit_call(node)
@@ -118,6 +126,10 @@ class TestDeprecatedChecker(CheckerTestCase):
args=("deprecated_method",),
node=node,
confidence=UNDEFINED,
+ line=9,
+ col_offset=0,
+ end_line=9,
+ end_col_offset=21,
)
):
self.checker.visit_call(node)
@@ -157,6 +169,10 @@ class TestDeprecatedChecker(CheckerTestCase):
args=("deprecated_arg1", "myfunction1"),
node=node,
confidence=UNDEFINED,
+ line=5,
+ col_offset=0,
+ end_line=5,
+ end_col_offset=31,
)
):
self.checker.visit_call(node)
@@ -177,6 +193,10 @@ class TestDeprecatedChecker(CheckerTestCase):
args=("deprecated_arg1", "myfunction1"),
node=node,
confidence=UNDEFINED,
+ line=5,
+ col_offset=0,
+ end_line=5,
+ end_col_offset=47,
)
):
self.checker.visit_call(node)
@@ -210,6 +230,10 @@ class TestDeprecatedChecker(CheckerTestCase):
args=("deprecated_arg1", "myfunction3"),
node=node,
confidence=UNDEFINED,
+ line=5,
+ col_offset=0,
+ end_line=5,
+ end_col_offset=47,
)
):
self.checker.visit_call(node)
@@ -231,6 +255,10 @@ class TestDeprecatedChecker(CheckerTestCase):
args=("deprecated_arg1", "mymethod1"),
node=node,
confidence=UNDEFINED,
+ line=6,
+ col_offset=0,
+ end_line=6,
+ end_col_offset=39,
)
):
self.checker.visit_call(node)
@@ -252,6 +280,10 @@ class TestDeprecatedChecker(CheckerTestCase):
args=("deprecated_arg1", "mymethod1"),
node=node,
confidence=UNDEFINED,
+ line=6,
+ col_offset=0,
+ end_line=6,
+ end_col_offset=55,
)
):
self.checker.visit_call(node)
@@ -287,6 +319,10 @@ class TestDeprecatedChecker(CheckerTestCase):
args=("deprecated_arg1", "mymethod3"),
node=node,
confidence=UNDEFINED,
+ line=6,
+ col_offset=0,
+ end_line=6,
+ end_col_offset=55,
)
):
self.checker.visit_call(node)
@@ -308,12 +344,20 @@ class TestDeprecatedChecker(CheckerTestCase):
args=("deprecated_arg1", "myfunction2"),
node=node,
confidence=UNDEFINED,
+ line=5,
+ col_offset=0,
+ end_line=5,
+ end_col_offset=61,
),
MessageTest(
msg_id="deprecated-argument",
args=("deprecated_arg2", "myfunction2"),
node=node,
confidence=UNDEFINED,
+ line=5,
+ col_offset=0,
+ end_line=5,
+ end_col_offset=61,
),
):
self.checker.visit_call(node)
@@ -334,12 +378,20 @@ class TestDeprecatedChecker(CheckerTestCase):
args=("deprecated_arg1", "myfunction2"),
node=node,
confidence=UNDEFINED,
+ line=5,
+ col_offset=0,
+ end_line=5,
+ end_col_offset=77,
),
MessageTest(
msg_id="deprecated-argument",
args=("deprecated_arg2", "myfunction2"),
node=node,
confidence=UNDEFINED,
+ line=5,
+ col_offset=0,
+ end_line=5,
+ end_col_offset=77,
),
):
self.checker.visit_call(node)
@@ -362,12 +414,20 @@ class TestDeprecatedChecker(CheckerTestCase):
args=("deprecated_arg1", "mymethod2"),
node=node,
confidence=UNDEFINED,
+ line=6,
+ col_offset=0,
+ end_line=6,
+ end_col_offset=69,
),
MessageTest(
msg_id="deprecated-argument",
args=("deprecated_arg2", "mymethod2"),
node=node,
confidence=UNDEFINED,
+ line=6,
+ col_offset=0,
+ end_line=6,
+ end_col_offset=69,
),
):
self.checker.visit_call(node)
@@ -389,12 +449,20 @@ class TestDeprecatedChecker(CheckerTestCase):
args=("deprecated_arg1", "mymethod2"),
node=node,
confidence=UNDEFINED,
+ line=6,
+ col_offset=0,
+ end_line=6,
+ end_col_offset=85,
),
MessageTest(
msg_id="deprecated-argument",
args=("deprecated_arg2", "mymethod2"),
node=node,
confidence=UNDEFINED,
+ line=6,
+ col_offset=0,
+ end_line=6,
+ end_col_offset=85,
),
):
self.checker.visit_call(node)
@@ -416,6 +484,10 @@ class TestDeprecatedChecker(CheckerTestCase):
args=("deprecated_arg", "MyClass"),
node=node,
confidence=UNDEFINED,
+ line=6,
+ col_offset=0,
+ end_line=6,
+ end_col_offset=10,
)
):
self.checker.visit_call(node)
@@ -433,6 +505,10 @@ class TestDeprecatedChecker(CheckerTestCase):
args="deprecated_module",
node=node,
confidence=UNDEFINED,
+ line=2,
+ col_offset=0,
+ end_line=2,
+ end_col_offset=24,
)
):
self.checker.visit_import(node)
@@ -450,6 +526,10 @@ class TestDeprecatedChecker(CheckerTestCase):
args="deprecated_module",
node=node,
confidence=UNDEFINED,
+ line=2,
+ col_offset=0,
+ end_line=2,
+ end_col_offset=40,
)
):
self.checker.visit_importfrom(node)
@@ -467,6 +547,10 @@ class TestDeprecatedChecker(CheckerTestCase):
args=("DeprecatedClass", "deprecated"),
node=node,
confidence=UNDEFINED,
+ line=2,
+ col_offset=0,
+ end_line=2,
+ end_col_offset=38,
)
):
self.checker.visit_importfrom(node)
@@ -484,6 +568,10 @@ class TestDeprecatedChecker(CheckerTestCase):
args=("DeprecatedClass", "deprecated"),
node=node,
confidence=UNDEFINED,
+ line=2,
+ col_offset=0,
+ end_line=2,
+ end_col_offset=33,
)
):
self.checker.visit_import(node)
@@ -502,6 +590,10 @@ class TestDeprecatedChecker(CheckerTestCase):
args=("DeprecatedClass", "deprecated"),
node=node,
confidence=UNDEFINED,
+ line=3,
+ col_offset=0,
+ end_line=3,
+ end_col_offset=28,
)
):
self.checker.visit_call(node)
@@ -526,6 +618,10 @@ class TestDeprecatedChecker(CheckerTestCase):
args=".deprecated_decorator",
node=node,
confidence=UNDEFINED,
+ line=7,
+ col_offset=0,
+ end_line=7,
+ end_col_offset=21,
)
):
self.checker.visit_decorators(node)
@@ -552,6 +648,10 @@ class TestDeprecatedChecker(CheckerTestCase):
args=".deprecated_decorator",
node=node,
confidence=UNDEFINED,
+ line=9,
+ col_offset=0,
+ end_line=9,
+ end_col_offset=27,
)
):
self.checker.visit_decorators(node)
diff --git a/tests/checkers/unittest_imports.py b/tests/checkers/unittest_imports.py
index 9dbcf2fe1..507b0d055 100644
--- a/tests/checkers/unittest_imports.py
+++ b/tests/checkers/unittest_imports.py
@@ -39,7 +39,14 @@ class TestImportsChecker(CheckerTestCase):
module = astroid.MANAGER.ast_from_module_name("beyond_top", REGR_DATA)
import_from = module.body[0]
- msg = MessageTest(msg_id="relative-beyond-top-level", node=import_from)
+ msg = MessageTest(
+ msg_id="relative-beyond-top-level",
+ node=import_from,
+ line=1,
+ col_offset=0,
+ end_line=1,
+ end_col_offset=25,
+ )
with self.assertAddsMessages(msg):
self.checker.visit_importfrom(import_from)
with self.assertNoMessages():
@@ -95,6 +102,10 @@ class TestImportsChecker(CheckerTestCase):
node=import_from,
args="empty",
confidence=UNDEFINED,
+ line=1,
+ col_offset=0,
+ end_line=1,
+ end_col_offset=19,
)
with self.assertAddsMessages(msg):
self.checker.visit_importfrom(import_from)
diff --git a/tests/checkers/unittest_typecheck.py b/tests/checkers/unittest_typecheck.py
index fbb4d53de..8411203ae 100644
--- a/tests/checkers/unittest_typecheck.py
+++ b/tests/checkers/unittest_typecheck.py
@@ -63,6 +63,10 @@ class TestTypeChecker(CheckerTestCase):
node=node,
args=("Module", "coverage.tracer", "CTracer", ""),
confidence=INFERENCE,
+ line=3,
+ col_offset=0,
+ end_line=3,
+ end_col_offset=14,
)
with self.assertAddsMessages(message):
self.checker.visit_attribute(node)
@@ -81,6 +85,10 @@ class TestTypeChecker(CheckerTestCase):
node=node,
args=("Module", "coverage.tracer", "CTracer", ""),
confidence=INFERENCE,
+ line=3,
+ col_offset=0,
+ end_line=3,
+ end_col_offset=14,
)
with self.assertAddsMessages(message):
self.checker.visit_attribute(node)
@@ -128,6 +136,10 @@ class TestTypeCheckerOnDecorators(CheckerTestCase):
node=subscript.value,
args="collections",
confidence=UNDEFINED,
+ line=3,
+ col_offset=7,
+ end_line=3,
+ end_col_offset=18,
)
):
self.checker.visit_subscript(subscript)
@@ -179,6 +191,10 @@ class TestTypeCheckerOnDecorators(CheckerTestCase):
node=subscript.value,
args="decorated",
confidence=UNDEFINED,
+ line=18,
+ col_offset=7,
+ end_line=18,
+ end_col_offset=16,
)
):
self.checker.visit_subscript(subscript)
@@ -219,6 +235,10 @@ class TestTypeCheckerOnDecorators(CheckerTestCase):
node=subscript.value,
args="decorated",
confidence=UNDEFINED,
+ line=17,
+ col_offset=7,
+ end_line=17,
+ end_col_offset=16,
)
):
self.checker.visit_subscript(subscript)
diff --git a/tests/checkers/unittest_variables.py b/tests/checkers/unittest_variables.py
index 43ecedb90..a59278bb9 100644
--- a/tests/checkers/unittest_variables.py
+++ b/tests/checkers/unittest_variables.py
@@ -90,7 +90,14 @@ class TestVariablesCheckerWithTearDown(CheckerTestCase):
)
with self.assertAddsMessages(
MessageTest(
- "unused-argument", node=node["abc"], args="abc", confidence=HIGH
+ "unused-argument",
+ node=node["abc"],
+ args="abc",
+ confidence=HIGH,
+ line=2,
+ col_offset=16,
+ end_line=2,
+ end_col_offset=19,
)
):
self.checker.visit_functiondef(node)
@@ -104,7 +111,14 @@ class TestVariablesCheckerWithTearDown(CheckerTestCase):
)
with self.assertAddsMessages(
MessageTest(
- "unused-argument", node=node["abc"], args="abc", confidence=HIGH
+ "unused-argument",
+ node=node["abc"],
+ args="abc",
+ confidence=HIGH,
+ line=2,
+ col_offset=12,
+ end_line=2,
+ end_col_offset=15,
)
):
self.checker.visit_functiondef(node)
@@ -118,7 +132,15 @@ class TestVariablesCheckerWithTearDown(CheckerTestCase):
"""
)
with self.assertAddsMessages(
- MessageTest("redefined-builtin", node=node.body[0], args="open")
+ MessageTest(
+ "redefined-builtin",
+ node=node.body[0],
+ args="open",
+ line=2,
+ col_offset=0,
+ end_line=2,
+ end_col_offset=32,
+ )
):
self.checker.visit_module(node)