summaryrefslogtreecommitdiff
path: root/pylint/test/test_func.py
blob: 1566df9928c8aada6804d93e1d11331bf2d50b29 (plain)
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# Copyright (c) 2006-2010, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
# Copyright (c) 2012 FELD Boris <lothiraldan@gmail.com>
# Copyright (c) 2013-2014 Google, Inc.
# Copyright (c) 2014-2017 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2014 Michal Nowikowski <godfryd@gmail.com>
# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
# Copyright (c) 2016 Derek Gustafson <degustaf@gmail.com>
# Copyright (c) 2017 Michka Popoff <michkapopoff@gmail.com>

# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/PyCQA/pylint/blob/master/COPYING

"""functional/non regression tests for pylint"""

import re
import sys
from os.path import abspath, dirname, join

import pytest

from pylint.testutils import _get_tests_info, linter

PY3K = sys.version_info >= (3, 0)
SYS_VERS_STR = "%d%d%d" % sys.version_info[:3]

# Configure paths
INPUT_DIR = join(dirname(abspath(__file__)), "input")
MSG_DIR = join(dirname(abspath(__file__)), "messages")

FILTER_RGX = None
UPDATE = False
INFO_TEST_RGX = re.compile(r"^func_i\d\d\d\d$")

# Classes

quote = "'" if sys.version_info >= (3, 3) else ""


def exception_str(self, ex):  # pylint: disable=unused-argument
    """function used to replace default __str__ method of exception instances"""
    return "in %s\n:: %s" % (ex.file, ", ".join(ex.args))


class LintTestUsingModule(object):
    INPUT_DIR = None
    DEFAULT_PACKAGE = "input"
    package = DEFAULT_PACKAGE
    linter = linter
    module = None
    depends = None
    output = None
    _TEST_TYPE = "module"

    # def runTest(self):
    #     # This is a hack to make ./test/test_func.py work under pytest.
    #     pass

    def _test_functionality(self):
        tocheck = [self.package + "." + self.module]
        # pylint: disable=not-an-iterable; can't handle boolean checks for now
        if self.depends:
            tocheck += [
                self.package + ".%s" % name.replace(".py", "")
                for name, _ in self.depends
            ]
        self._test(tocheck)

    def _check_result(self, got):
        assert self._get_expected().strip() + "\n" == got.strip() + "\n"

    def _test(self, tocheck):
        if INFO_TEST_RGX.match(self.module):
            self.linter.enable("I")
        else:
            self.linter.disable("I")
        try:
            self.linter.check(tocheck)
        except Exception as ex:
            # need finalization to restore a correct state
            self.linter.reporter.finalize()
            ex.file = tocheck
            print(ex)
            ex.__str__ = exception_str
            raise
        self._check_result(self.linter.reporter.finalize())

    def _has_output(self):
        return not self.module.startswith("func_noerror_")

    def _get_expected(self):
        if self._has_output() and self.output:
            with open(self.output, "r") as fobj:
                return fobj.read().strip() + "\n"
        else:
            return ""


class LintTestUpdate(LintTestUsingModule):

    _TEST_TYPE = "update"

    def _check_result(self, got):
        if self._has_output():
            try:
                expected = self._get_expected()
            except IOError:
                expected = ""
            if got != expected:
                with open(self.output, "w") as fobj:
                    fobj.write(got)


def gen_tests(filter_rgx):
    if filter_rgx:
        is_to_run = re.compile(filter_rgx).search
    else:
        is_to_run = lambda x: 1
    tests = []
    for module_file, messages_file in _get_tests_info(INPUT_DIR, MSG_DIR, "func_", ""):
        if not is_to_run(module_file) or module_file.endswith((".pyc", "$py.class")):
            continue
        base = module_file.replace(".py", "").split("_")[1]
        dependencies = _get_tests_info(INPUT_DIR, MSG_DIR, base, ".py")
        tests.append((module_file, messages_file, dependencies))

    if UPDATE:
        return tests

    assert len(tests) < 196, "Please do not add new test cases here."
    return tests


@pytest.mark.parametrize(
    "module_file,messages_file,dependencies",
    gen_tests(FILTER_RGX),
    ids=[o[0] for o in gen_tests(FILTER_RGX)],
)
def test_functionality(module_file, messages_file, dependencies):

    LT = LintTestUpdate() if UPDATE else LintTestUsingModule()

    LT.module = module_file.replace(".py", "")
    LT.output = messages_file
    LT.depends = dependencies or None
    LT.INPUT_DIR = INPUT_DIR
    LT._test_functionality()


if __name__ == "__main__":
    if "-u" in sys.argv:
        UPDATE = True
        sys.argv.remove("-u")

    if len(sys.argv) > 1:
        FILTER_RGX = sys.argv[1]
        del sys.argv[1]
    pytest.main(sys.argv)