aboutsummaryrefslogtreecommitdiff
path: root/tests/unittest_reporting.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/unittest_reporting.py')
-rw-r--r--tests/unittest_reporting.py271
1 files changed, 271 insertions, 0 deletions
diff --git a/tests/unittest_reporting.py b/tests/unittest_reporting.py
new file mode 100644
index 000000000..c0fdb61d4
--- /dev/null
+++ b/tests/unittest_reporting.py
@@ -0,0 +1,271 @@
+# Copyright (c) 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2014-2018, 2020 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Calin Don <calin.don@gmail.com>
+# Copyright (c) 2014 Google, Inc.
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2016-2017 Derek Gustafson <degustaf@gmail.com>
+# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
+# Copyright (c) 2019-2021 Pierre Sassoulas <pierre.sassoulas@gmail.com>
+# Copyright (c) 2019 Ashley Whetter <ashley@awhetter.co.uk>
+# Copyright (c) 2020 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2021 Daniƫl van Noord <13665637+DanielNoord@users.noreply.github.com>
+# Copyright (c) 2021 Marc Mueller <30130371+cdce8p@users.noreply.github.com>
+# Copyright (c) 2021 ruro <ruro.ruro@ya.ru>
+
+# 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
+# pylint: disable=redefined-outer-name
+
+import warnings
+from contextlib import redirect_stdout
+from io import StringIO
+from json import dumps
+from typing import TYPE_CHECKING
+
+import pytest
+
+from pylint import checkers
+from pylint.interfaces import IReporter
+from pylint.lint import PyLinter
+from pylint.reporters import BaseReporter
+from pylint.reporters.text import ParseableTextReporter, TextReporter
+from pylint.typing import FileItem
+
+if TYPE_CHECKING:
+ from pylint.reporters.ureports.nodes import Section
+
+
+@pytest.fixture(scope="module")
+def reporter():
+ return TextReporter
+
+
+@pytest.fixture(scope="module")
+def disable():
+ return ["I"]
+
+
+def test_template_option(linter):
+ output = StringIO()
+ linter.reporter.set_output(output)
+ linter.set_option("msg-template", "{msg_id}:{line:03d}")
+ linter.open()
+ linter.set_current_module("0123")
+ linter.add_message("C0301", line=1, args=(1, 2))
+ linter.add_message("line-too-long", line=2, args=(3, 4))
+ assert output.getvalue() == "************* Module 0123\nC0301:001\nC0301:002\n"
+
+
+def test_parseable_output_deprecated():
+ with warnings.catch_warnings(record=True) as cm:
+ warnings.simplefilter("always")
+ ParseableTextReporter()
+
+ assert len(cm) == 1
+ assert isinstance(cm[0].message, DeprecationWarning)
+
+
+def test_parseable_output_regression():
+ output = StringIO()
+ with warnings.catch_warnings(record=True):
+ linter = PyLinter(reporter=ParseableTextReporter())
+
+ checkers.initialize(linter)
+ linter.config.persistent = 0
+ linter.reporter.set_output(output)
+ linter.set_option("output-format", "parseable")
+ linter.open()
+ linter.set_current_module("0123")
+ linter.add_message("line-too-long", line=1, args=(1, 2))
+ assert (
+ output.getvalue() == "************* Module 0123\n"
+ "0123:1: [C0301(line-too-long), ] "
+ "Line too long (1/2)\n"
+ )
+
+
+class NopReporter(BaseReporter):
+ __implements__ = IReporter
+ name = "nop-reporter"
+ extension = ""
+
+ def __init__(self, output=None):
+ super().__init__(output)
+ print("A NopReporter was initialized.", file=self.out)
+
+ def writeln(self, string=""):
+ pass
+
+ def _display(self, layout: "Section") -> None:
+ pass
+
+
+def test_multi_format_output(tmp_path):
+ text = StringIO(newline=None)
+ json = tmp_path / "somefile.json"
+
+ source_file = tmp_path / "somemodule.py"
+ source_file.write_text('NOT_EMPTY = "This module is not empty"\n')
+ escaped_source_file = dumps(str(source_file))
+
+ nop_format = NopReporter.__module__ + "." + NopReporter.__name__
+ formats = ",".join(["json:" + str(json), "text", nop_format])
+
+ with redirect_stdout(text):
+ linter = PyLinter()
+ linter.set_option("persistent", False)
+ linter.set_option("output-format", formats)
+ linter.set_option("reports", True)
+ linter.set_option("score", True)
+ linter.load_default_plugins()
+
+ assert linter.reporter.linter is linter
+ with pytest.raises(NotImplementedError):
+ linter.reporter.set_output(text)
+
+ linter.open()
+ linter.check_single_file_item(FileItem("somemodule", source_file, "somemodule"))
+ linter.add_message("line-too-long", line=1, args=(1, 2))
+ linter.generate_reports()
+ linter.reporter.writeln("direct output")
+
+ # Ensure the output files are flushed and closed
+ linter.reporter.close_output_files()
+ del linter.reporter
+
+ with open(json, encoding="utf-8") as f:
+ assert (
+ f.read() == "[\n"
+ " {\n"
+ ' "type": "convention",\n'
+ ' "module": "somemodule",\n'
+ ' "obj": "",\n'
+ ' "line": 1,\n'
+ ' "column": 0,\n'
+ f' "path": {escaped_source_file},\n'
+ ' "symbol": "missing-module-docstring",\n'
+ ' "message": "Missing module docstring",\n'
+ ' "message-id": "C0114"\n'
+ " },\n"
+ " {\n"
+ ' "type": "convention",\n'
+ ' "module": "somemodule",\n'
+ ' "obj": "",\n'
+ ' "line": 1,\n'
+ ' "column": 0,\n'
+ f' "path": {escaped_source_file},\n'
+ ' "symbol": "line-too-long",\n'
+ ' "message": "Line too long (1/2)",\n'
+ ' "message-id": "C0301"\n'
+ " }\n"
+ "]\n"
+ "direct output\n"
+ )
+
+ assert (
+ text.getvalue() == "A NopReporter was initialized.\n"
+ "************* Module somemodule\n"
+ f"{source_file}:1:0: C0114: Missing module docstring (missing-module-docstring)\n"
+ f"{source_file}:1:0: C0301: Line too long (1/2) (line-too-long)\n"
+ "\n"
+ "\n"
+ "Report\n"
+ "======\n"
+ "1 statements analysed.\n"
+ "\n"
+ "Statistics by type\n"
+ "------------------\n"
+ "\n"
+ "+---------+-------+-----------+-----------+------------+---------+\n"
+ "|type |number |old number |difference |%documented |%badname |\n"
+ "+=========+=======+===========+===========+============+=========+\n"
+ "|module |1 |NC |NC |0.00 |0.00 |\n"
+ "+---------+-------+-----------+-----------+------------+---------+\n"
+ "|class |0 |NC |NC |0 |0 |\n"
+ "+---------+-------+-----------+-----------+------------+---------+\n"
+ "|method |0 |NC |NC |0 |0 |\n"
+ "+---------+-------+-----------+-----------+------------+---------+\n"
+ "|function |0 |NC |NC |0 |0 |\n"
+ "+---------+-------+-----------+-----------+------------+---------+\n"
+ "\n"
+ "\n"
+ "\n"
+ "Raw metrics\n"
+ "-----------\n"
+ "\n"
+ "+----------+-------+------+---------+-----------+\n"
+ "|type |number |% |previous |difference |\n"
+ "+==========+=======+======+=========+===========+\n"
+ "|code |2 |66.67 |NC |NC |\n"
+ "+----------+-------+------+---------+-----------+\n"
+ "|docstring |0 |0.00 |NC |NC |\n"
+ "+----------+-------+------+---------+-----------+\n"
+ "|comment |0 |0.00 |NC |NC |\n"
+ "+----------+-------+------+---------+-----------+\n"
+ "|empty |1 |33.33 |NC |NC |\n"
+ "+----------+-------+------+---------+-----------+\n"
+ "\n"
+ "\n"
+ "\n"
+ "Duplication\n"
+ "-----------\n"
+ "\n"
+ "+-------------------------+------+---------+-----------+\n"
+ "| |now |previous |difference |\n"
+ "+=========================+======+=========+===========+\n"
+ "|nb duplicated lines |0 |NC |NC |\n"
+ "+-------------------------+------+---------+-----------+\n"
+ "|percent duplicated lines |0.000 |NC |NC |\n"
+ "+-------------------------+------+---------+-----------+\n"
+ "\n"
+ "\n"
+ "\n"
+ "Messages by category\n"
+ "--------------------\n"
+ "\n"
+ "+-----------+-------+---------+-----------+\n"
+ "|type |number |previous |difference |\n"
+ "+===========+=======+=========+===========+\n"
+ "|convention |2 |NC |NC |\n"
+ "+-----------+-------+---------+-----------+\n"
+ "|refactor |0 |NC |NC |\n"
+ "+-----------+-------+---------+-----------+\n"
+ "|warning |0 |NC |NC |\n"
+ "+-----------+-------+---------+-----------+\n"
+ "|error |0 |NC |NC |\n"
+ "+-----------+-------+---------+-----------+\n"
+ "\n"
+ "\n"
+ "\n"
+ "Messages\n"
+ "--------\n"
+ "\n"
+ "+-------------------------+------------+\n"
+ "|message id |occurrences |\n"
+ "+=========================+============+\n"
+ "|missing-module-docstring |1 |\n"
+ "+-------------------------+------------+\n"
+ "|line-too-long |1 |\n"
+ "+-------------------------+------------+\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "-------------------------------------\n"
+ "Your code has been rated at -10.00/10\n"
+ "\n"
+ "direct output\n"
+ )
+
+
+def test_display_results_is_renamed():
+ class CustomReporter(TextReporter):
+ def _display(self, layout: "Section") -> None:
+ return None
+
+ reporter = CustomReporter()
+ with pytest.raises(AttributeError) as exc:
+ # pylint: disable=no-member
+ reporter.display_results()
+ assert "no attribute 'display_results'" in str(exc)