diff options
author | Daniƫl van Noord <13665637+DanielNoord@users.noreply.github.com> | 2021-12-13 23:17:48 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-12-13 23:17:48 +0100 |
commit | 71bbbdb29407a2b4525f0bf2da8b0025da5fc4d8 (patch) | |
tree | b76887ba7216a30ee99f812bc33142a510a2e7eb | |
parent | 78eed6af303e0e9854e43528d17d2ada96f11c09 (diff) | |
download | pylint-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-- | ChangeLog | 3 | ||||
-rw-r--r-- | doc/whatsnew/2.13.rst | 3 | ||||
-rw-r--r-- | pylint/testutils/checker_test_case.py | 35 | ||||
-rw-r--r-- | pylint/testutils/output_line.py | 2 | ||||
-rw-r--r-- | pylint/testutils/unittest_linter.py | 30 | ||||
-rw-r--r-- | tests/checkers/unittest_base.py | 20 | ||||
-rw-r--r-- | tests/checkers/unittest_deprecated.py | 100 | ||||
-rw-r--r-- | tests/checkers/unittest_imports.py | 13 | ||||
-rw-r--r-- | tests/checkers/unittest_typecheck.py | 20 | ||||
-rw-r--r-- | tests/checkers/unittest_variables.py | 28 |
10 files changed, 239 insertions, 15 deletions
@@ -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) |