Skip to content

Commit 54550d7

Browse files
committed
check_parallel| Adds new test suite for multiple workers/check_parallel
These regression tests put check_parallel() and related functions under tests, ensuring that the functionality remains consistent ahead of any bug fixes and performance work we may do.
1 parent b67830f commit 54550d7

File tree

1 file changed

+262
-0
lines changed

1 file changed

+262
-0
lines changed

tests/test_check_parallel.py

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
""" Puts the check_parallel system under test """
2+
# Copyright (c) 2020 Frank Harrison <[email protected]>
3+
4+
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
5+
# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
6+
7+
# pylint: disable=protected-access,missing-function-docstring,no-self-use
8+
9+
import collections
10+
import os
11+
12+
import pytest
13+
14+
import pylint.interfaces
15+
import pylint.lint.parallel
16+
from pylint.checkers.base_checker import BaseChecker
17+
from pylint.lint import PyLinter
18+
from pylint.lint.parallel import _worker_check_single_file as worker_check_single_file
19+
from pylint.lint.parallel import _worker_initialize as worker_initialize
20+
from pylint.lint.parallel import check_parallel
21+
from pylint.testutils import TestReporter as Reporter
22+
23+
24+
def _gen_file_data(idx=0):
25+
""" Generates a file to use as a stream """
26+
filepath = os.path.abspath(
27+
os.path.join(os.path.dirname(__file__), "input", "similar1")
28+
)
29+
file_data = (
30+
"--test-file_data-name-%d--" % idx,
31+
filepath,
32+
"--test-file_data-modname--",
33+
)
34+
return file_data
35+
36+
37+
def _gen_file_datas(count=1):
38+
file_infos = [_gen_file_data(idx) for idx in range(count)]
39+
return file_infos
40+
41+
42+
class SequentialTestChecker(BaseChecker):
43+
""" A checker that does not need to consolidate data across run invocations """
44+
45+
__implements__ = (pylint.interfaces.IRawChecker,)
46+
47+
name = "sequential-checker"
48+
test_data = "sequential"
49+
msgs = {"R9999": ("Test", "sequential-test-check", "Some helpful text.",)}
50+
51+
def __init__(self, linter, *args, **kwargs):
52+
super().__init__(linter, *args, **kwargs)
53+
self.data = []
54+
self.linter = linter
55+
56+
def process_module(self, _astroid):
57+
""" Called once per stream/file/astroid object """
58+
# record the number of invocations with the data object
59+
record = self.test_data + str(len(self.data))
60+
self.data.append(record)
61+
62+
63+
class TestCheckParallelFramework:
64+
""" Tests the check_parallel() function's framework """
65+
66+
def setUp(self):
67+
self._prev_global_linter = pylint.lint.parallel._worker_linter
68+
69+
def tearDown(self):
70+
pylint.lint.parallel._worker_linter = self._prev_global_linter
71+
72+
def test_worker_initialize(self):
73+
linter = PyLinter(reporter=Reporter())
74+
worker_initialize(linter=linter)
75+
assert pylint.lint.parallel._worker_linter == linter
76+
77+
def test_worker_check_single_file_uninitialised(self):
78+
pylint.lint.parallel._worker_linter = None
79+
with pytest.raises(AttributeError):
80+
worker_check_single_file(_gen_file_data())
81+
82+
def test_worker_check_single_file_no_checkers(self):
83+
linter = PyLinter(reporter=Reporter())
84+
worker_initialize(linter=linter)
85+
(name, msgs, stats, msg_status) = worker_check_single_file(_gen_file_data())
86+
assert name == "--test-file_data-name-0--"
87+
assert [] == msgs
88+
no_errors_status = 0
89+
assert no_errors_status == msg_status
90+
assert {
91+
"by_module": {
92+
"--test-file_data-name-0--": {
93+
"convention": 0,
94+
"error": 0,
95+
"fatal": 0,
96+
"info": 0,
97+
"refactor": 0,
98+
"statement": 18,
99+
"warning": 0,
100+
}
101+
},
102+
"by_msg": {},
103+
"convention": 0,
104+
"error": 0,
105+
"fatal": 0,
106+
"info": 0,
107+
"refactor": 0,
108+
"statement": 18,
109+
"warning": 0,
110+
} == stats
111+
112+
def test_worker_check_sequential_checker(self):
113+
""" Same as test_worker_check_single_file_no_checkers with SequentialTestChecker
114+
"""
115+
linter = PyLinter(reporter=Reporter())
116+
worker_initialize(linter=linter)
117+
118+
# Add the only checker we care about in this test
119+
linter.register_checker(SequentialTestChecker(linter))
120+
121+
(name, msgs, stats, msg_status) = worker_check_single_file(_gen_file_data())
122+
123+
# Ensure we return the same data as the single_file_no_checkers test
124+
assert name == "--test-file_data-name-0--"
125+
assert [] == msgs
126+
no_errors_status = 0
127+
assert no_errors_status == msg_status
128+
assert {
129+
"by_module": {
130+
"--test-file_data-name-0--": {
131+
"convention": 0,
132+
"error": 0,
133+
"fatal": 0,
134+
"info": 0,
135+
"refactor": 0,
136+
"statement": 18,
137+
"warning": 0,
138+
}
139+
},
140+
"by_msg": {},
141+
"convention": 0,
142+
"error": 0,
143+
"fatal": 0,
144+
"info": 0,
145+
"refactor": 0,
146+
"statement": 18,
147+
"warning": 0,
148+
} == stats
149+
150+
151+
class TestCheckParallel:
152+
""" Tests the check_parallel() function """
153+
154+
def test_sequential_checkers_work(self):
155+
"""Tests original basic types of checker works as expected in -jN
156+
157+
This means that a sequential checker should return the same data for a given
158+
file-stream irrespective of whether its run in -j1 or -jN
159+
"""
160+
linter = PyLinter(reporter=Reporter())
161+
162+
# Add a sequential checker to ensure it records data against some streams
163+
linter.register_checker(SequentialTestChecker(linter))
164+
linter.enable("R9999")
165+
166+
# Create a dummy file, the actual contents of which will be ignored by the
167+
# register test checkers, but it will trigger at least a single-job to be run.
168+
single_file_container = _gen_file_datas(count=1)
169+
170+
# Invoke the lint process in a multiprocess way, although we only specify one
171+
# job.
172+
check_parallel(linter, jobs=1, files=single_file_container, arguments=None)
173+
assert len(linter.get_checkers()) == 2, (
174+
"We should only have the 'master' and 'sequential-checker' "
175+
"checkers registered"
176+
)
177+
assert {
178+
"by_module": {
179+
"--test-file_data-name-0--": {
180+
"convention": 0,
181+
"error": 0,
182+
"fatal": 0,
183+
"info": 0,
184+
"refactor": 0,
185+
"statement": 18,
186+
"warning": 0,
187+
}
188+
},
189+
"by_msg": {},
190+
"convention": 0,
191+
"error": 0,
192+
"fatal": 0,
193+
"info": 0,
194+
"refactor": 0,
195+
"statement": 18,
196+
"warning": 0,
197+
} == linter.stats
198+
199+
# now run the regular mode of checking files and check that, in this proc, we
200+
# collect the right data
201+
filepath = single_file_container[0][1] # get the filepath element
202+
linter.check(filepath)
203+
assert {
204+
"by_module": {
205+
"input.similar1": { # module is the only change from previous
206+
"convention": 0,
207+
"error": 0,
208+
"fatal": 0,
209+
"info": 0,
210+
"refactor": 0,
211+
"statement": 18,
212+
"warning": 0,
213+
}
214+
},
215+
"by_msg": {},
216+
"convention": 0,
217+
"error": 0,
218+
"fatal": 0,
219+
"info": 0,
220+
"refactor": 0,
221+
"statement": 18,
222+
"warning": 0,
223+
} == linter.stats
224+
225+
def test_invoke_single_job(self):
226+
"""Tests basic checkers functionality using just a single workderdo
227+
228+
This is *not* the same -j1 and does not happen under normal operation """
229+
linter = PyLinter(reporter=Reporter())
230+
231+
linter.register_checker(SequentialTestChecker(linter))
232+
233+
# Create a dummy file, the actual contents of which will be ignored by the
234+
# register test checkers, but it will trigger at least a single-job to be run.
235+
single_file_container = _gen_file_datas(count=1)
236+
237+
# Invoke the lint process in a multiprocess way, although we only specify one
238+
# job.
239+
check_parallel(linter, jobs=1, files=single_file_container, arguments=None)
240+
241+
assert {
242+
"by_module": {
243+
"--test-file_data-name-0--": {
244+
"convention": 0,
245+
"error": 0,
246+
"fatal": 0,
247+
"info": 0,
248+
"refactor": 0,
249+
"statement": 18,
250+
"warning": 0,
251+
}
252+
},
253+
"by_msg": collections.Counter(),
254+
"convention": 0,
255+
"error": 0,
256+
"fatal": 0,
257+
"info": 0,
258+
"refactor": 0,
259+
"statement": 18,
260+
"warning": 0,
261+
} == linter.stats
262+
assert linter.msg_status == 0, "We expect a single-file check to exit cleanly"

0 commit comments

Comments
 (0)