1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
# 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
"""
This launches the configuration functional tests. This permits to test configuration
files by providing a file with the appropriate extension in the ``tests/config/functional``
directory.
Let's say you have a regression_list_crash.toml file to test. Then if there is an error in the conf,
add ``regression_list_crash.out`` alongside your file with the expected output of pylint in it. Use
``{relpath}`` and ``{abspath}`` for the path of the file. The exit code will have to be 2 (error)
if this file exists.
You must also define a ``regression_list_crash.result.json`` if you want to check the parsed configuration.
This file will be loaded as a dict and will override the default value of the default pylint
configuration. If you need to append or remove a value use the special key ``"functional_append"``
and ``"functional_remove":``. Check the existing code for examples.
"""
# pylint: disable=redefined-outer-name
import logging
from pathlib import Path
import pytest
from pytest import CaptureFixture, LogCaptureFixture
from pylint.testutils.configuration_test import (
PylintConfiguration,
get_expected_configuration,
get_expected_output,
run_using_a_configuration_file,
)
HERE = Path(__file__).parent
FUNCTIONAL_DIR = HERE / "functional"
# We use string then recast to path so we can use -k in pytest.
# Otherwise we get 'configuration_path0' as a test name. The path is relative to the functional
# directory because otherwise the string would be very lengthy.
ACCEPTED_CONFIGURATION_EXTENSIONS = ("toml", "ini", "cfg")
CONFIGURATION_PATHS = [
str(path.relative_to(FUNCTIONAL_DIR))
for ext in ACCEPTED_CONFIGURATION_EXTENSIONS
for path in FUNCTIONAL_DIR.rglob(f"*.{ext}")
]
@pytest.fixture()
def default_configuration(
tmp_path: Path, file_to_lint_path: str
) -> PylintConfiguration:
empty_pylintrc = tmp_path / "pylintrc"
empty_pylintrc.write_text("")
mock_exit, _, runner = run_using_a_configuration_file(
str(empty_pylintrc), file_to_lint_path
)
mock_exit.assert_called_once_with(0)
return runner.linter.config.__dict__
@pytest.mark.parametrize("configuration_path", CONFIGURATION_PATHS)
def test_functional_config_loading(
configuration_path: str,
default_configuration: PylintConfiguration,
file_to_lint_path: str,
capsys: CaptureFixture,
caplog: LogCaptureFixture,
):
"""Functional tests for configurations."""
# logging is helpful to see what's expected and why. The output of the
# program is checked during the test so printing messes with the result.
caplog.set_level(logging.INFO)
configuration_path = str(FUNCTIONAL_DIR / configuration_path)
msg = f"Wrong result with configuration {configuration_path}"
expected_code, expected_output = get_expected_output(configuration_path)
expected_loaded_configuration = get_expected_configuration(
configuration_path, default_configuration
)
mock_exit, _, runner = run_using_a_configuration_file(
configuration_path, file_to_lint_path
)
mock_exit.assert_called_once_with(expected_code)
out, err = capsys.readouterr()
# rstrip() applied so we can have a final newline in the expected test file
assert expected_output.rstrip() == out.rstrip(), msg
assert sorted(expected_loaded_configuration.keys()) == sorted(
runner.linter.config.__dict__.keys()
), msg
for key, expected_value in expected_loaded_configuration.items():
key_msg = f"{msg} for key '{key}':"
if isinstance(expected_value, list):
assert sorted(expected_value) == sorted(
runner.linter.config.__dict__[key]
), key_msg
else:
assert expected_value == runner.linter.config.__dict__[key], key_msg
assert not err, msg
|