diff options
Diffstat (limited to 'tests/unittest_reporting.py')
-rw-r--r-- | tests/unittest_reporting.py | 271 |
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) |