Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(711)

Side by Side Diff: third_party/pylint/testutils.py

Issue 753543006: pylint: upgrade to 1.4.0 (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@master
Patch Set: Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « third_party/pylint/reporters/text.py ('k') | third_party/pylint/utils.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). 1 # Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
2 # http://www.logilab.fr/ -- mailto:contact@logilab.fr 2 # http://www.logilab.fr/ -- mailto:contact@logilab.fr
3 # 3 #
4 # This program is free software; you can redistribute it and/or modify it under 4 # This program is free software; you can redistribute it and/or modify it under
5 # the terms of the GNU General Public License as published by the Free Software 5 # the terms of the GNU General Public License as published by the Free Software
6 # Foundation; either version 2 of the License, or (at your option) any later 6 # Foundation; either version 2 of the License, or (at your option) any later
7 # version. 7 # version.
8 # 8 #
9 # This program is distributed in the hope that it will be useful, but WITHOUT 9 # This program is distributed in the hope that it will be useful, but WITHOUT
10 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 10 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 11 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 # 12 #
13 # You should have received a copy of the GNU General Public License along with 13 # You should have received a copy of the GNU General Public License along with
14 # this program; if not, write to the Free Software Foundation, Inc., 14 # this program; if not, write to the Free Software Foundation, Inc.,
15 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 15 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 """functional/non regression tests for pylint""" 16 """functional/non regression tests for pylint"""
17 from __future__ import with_statement 17 from __future__ import print_function
18 18
19 import collections 19 import collections
20 import contextlib 20 import contextlib
21 import functools 21 import functools
22 import os
22 import sys 23 import sys
23 import re 24 import re
25 import unittest
26 import tempfile
27 import tokenize
24 28
25 from glob import glob 29 from glob import glob
26 from os import linesep 30 from os import linesep, getcwd, sep
27 from os.path import abspath, basename, dirname, isdir, join, splitext 31 from os.path import abspath, basename, dirname, isdir, join, splitext
28 from cStringIO import StringIO
29 32
30 from logilab.common import testlib 33 from astroid import test_utils
31 34
32 from pylint import checkers 35 from pylint import checkers
33 from pylint.utils import PyLintASTWalker 36 from pylint.utils import PyLintASTWalker
34 from pylint.reporters import BaseReporter 37 from pylint.reporters import BaseReporter
35 from pylint.interfaces import IReporter 38 from pylint.interfaces import IReporter
36 from pylint.lint import PyLinter 39 from pylint.lint import PyLinter
37 40
41 import six
42 from six.moves import StringIO
43
38 44
39 # Utils 45 # Utils
40 46
41 SYS_VERS_STR = '%d%d%d' % sys.version_info[:3] 47 SYS_VERS_STR = '%d%d%d' % sys.version_info[:3]
42 TITLE_UNDERLINES = ['', '=', '-', '.'] 48 TITLE_UNDERLINES = ['', '=', '-', '.']
43 PREFIX = abspath(dirname(__file__)) 49 PREFIX = abspath(dirname(__file__))
44 PY3K = sys.version_info[0] == 3 50 PY3K = sys.version_info[0] == 3
45 51
46 def fix_path(): 52 def fix_path():
47 sys.path.insert(0, PREFIX) 53 sys.path.insert(0, PREFIX)
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
82 outfile = join(msg_dir, fbase + '.txt') 88 outfile = join(msg_dir, fbase + '.txt')
83 result.append((infile, outfile)) 89 result.append((infile, outfile))
84 return result 90 return result
85 91
86 92
87 class TestReporter(BaseReporter): 93 class TestReporter(BaseReporter):
88 """reporter storing plain text messages""" 94 """reporter storing plain text messages"""
89 95
90 __implements____ = IReporter 96 __implements____ = IReporter
91 97
92 def __init__(self): 98 def __init__(self): # pylint: disable=super-init-not-called
99
93 self.message_ids = {} 100 self.message_ids = {}
94 self.reset() 101 self.reset()
102 self.path_strip_prefix = getcwd() + sep
95 103
96 def reset(self): 104 def reset(self):
97 self.out = StringIO() 105 self.out = StringIO()
98 self.messages = [] 106 self.messages = []
99 107
100 def add_message(self, msg_id, location, msg): 108 def add_message(self, msg_id, location, msg):
101 """manage message of different type and in the context of path """ 109 """manage message of different type and in the context of path """
102 _, _, obj, line, _ = location 110 _, _, obj, line, _ = location
103 self.message_ids[msg_id] = 1 111 self.message_ids[msg_id] = 1
104 if obj: 112 if obj:
105 obj = ':%s' % obj 113 obj = ':%s' % obj
106 sigle = msg_id[0] 114 sigle = msg_id[0]
107 if PY3K and linesep != '\n': 115 if PY3K and linesep != '\n':
108 # 2to3 writes os.linesep instead of using 116 # 2to3 writes os.linesep instead of using
109 # the previosly used line separators 117 # the previosly used line separators
110 msg = msg.replace('\r\n', '\n') 118 msg = msg.replace('\r\n', '\n')
111 self.messages.append('%s:%3s%s: %s' % (sigle, line, obj, msg)) 119 self.messages.append('%s:%3s%s: %s' % (sigle, line, obj, msg))
112 120
113 def finalize(self): 121 def finalize(self):
114 self.messages.sort() 122 self.messages.sort()
115 for msg in self.messages: 123 for msg in self.messages:
116 print >> self.out, msg 124 print(msg, file=self.out)
117 result = self.out.getvalue() 125 result = self.out.getvalue()
118 self.reset() 126 self.reset()
119 return result 127 return result
120 128
121 def display_results(self, layout): 129 def display_results(self, layout):
122 """ignore layouts""" 130 """ignore layouts"""
123 131
124 132
125 if sys.version_info < (2, 6): 133 class Message(collections.namedtuple('Message',
126 class Message(tuple): 134 ['msg_id', 'line', 'node', 'args'])):
127 def __new__(cls, msg_id, line=None, node=None, args=None): 135 def __new__(cls, msg_id, line=None, node=None, args=None):
128 return tuple.__new__(cls, (msg_id, line, node, args)) 136 return tuple.__new__(cls, (msg_id, line, node, args))
129
130 @property
131 def msg_id(self):
132 return self[0]
133 @property
134 def line(self):
135 return self[1]
136 @property
137 def node(self):
138 return self[2]
139 @property
140 def args(self):
141 return self[3]
142
143
144 else:
145 class Message(collections.namedtuple('Message',
146 ['msg_id', 'line', 'node', 'args'])):
147 def __new__(cls, msg_id, line=None, node=None, args=None):
148 return tuple.__new__(cls, (msg_id, line, node, args))
149 137
150 138
151 class UnittestLinter(object): 139 class UnittestLinter(object):
152 """A fake linter class to capture checker messages.""" 140 """A fake linter class to capture checker messages."""
141 # pylint: disable=unused-argument, no-self-use
153 142
154 def __init__(self): 143 def __init__(self):
155 self._messages = [] 144 self._messages = []
156 self.stats = {} 145 self.stats = {}
157 146
158 def release_messages(self): 147 def release_messages(self):
159 try: 148 try:
160 return self._messages 149 return self._messages
161 finally: 150 finally:
162 self._messages = [] 151 self._messages = []
163 152
164 def add_message(self, msg_id, line=None, node=None, args=None): 153 def add_message(self, msg_id, line=None, node=None, args=None,
154 confidence=None):
165 self._messages.append(Message(msg_id, line, node, args)) 155 self._messages.append(Message(msg_id, line, node, args))
166 156
167 def is_message_enabled(self, *unused_args): 157 def is_message_enabled(self, *unused_args):
168 return True 158 return True
169 159
170 def add_stats(self, **kwargs): 160 def add_stats(self, **kwargs):
171 for name, value in kwargs.iteritems(): 161 for name, value in six.iteritems(kwargs):
172 self.stats[name] = value 162 self.stats[name] = value
173 return self.stats 163 return self.stats
174 164
175 @property 165 @property
176 def options_providers(self): 166 def options_providers(self):
177 return linter.options_providers 167 return linter.options_providers
178 168
179 def set_config(**kwargs): 169 def set_config(**kwargs):
180 """Decorator for setting config values on a checker.""" 170 """Decorator for setting config values on a checker."""
181 def _Wrapper(fun): 171 def _Wrapper(fun):
182 @functools.wraps(fun) 172 @functools.wraps(fun)
183 def _Forward(self): 173 def _Forward(self):
184 for key, value in kwargs.iteritems(): 174 for key, value in six.iteritems(kwargs):
185 setattr(self.checker.config, key, value) 175 setattr(self.checker.config, key, value)
186 if isinstance(self, CheckerTestCase): 176 if isinstance(self, CheckerTestCase):
187 # reopen checker in case, it may be interested in configuration change 177 # reopen checker in case, it may be interested in configuration change
188 self.checker.open() 178 self.checker.open()
189 fun(self) 179 fun(self)
190 180
191 return _Forward 181 return _Forward
192 return _Wrapper 182 return _Wrapper
193 183
194 184
195 class CheckerTestCase(testlib.TestCase): 185 class CheckerTestCase(unittest.TestCase):
196 """A base testcase class for unittesting individual checker classes.""" 186 """A base testcase class for unittesting individual checker classes."""
197 CHECKER_CLASS = None 187 CHECKER_CLASS = None
198 CONFIG = {} 188 CONFIG = {}
199 189
200 def setUp(self): 190 def setUp(self):
201 self.linter = UnittestLinter() 191 self.linter = UnittestLinter()
202 self.checker = self.CHECKER_CLASS(self.linter) # pylint: disable=not-cal lable 192 self.checker = self.CHECKER_CLASS(self.linter) # pylint: disable=not-cal lable
203 for key, value in self.CONFIG.iteritems(): 193 for key, value in six.iteritems(self.CONFIG):
204 setattr(self.checker.config, key, value) 194 setattr(self.checker.config, key, value)
205 self.checker.open() 195 self.checker.open()
206 196
207 @contextlib.contextmanager 197 @contextlib.contextmanager
208 def assertNoMessages(self): 198 def assertNoMessages(self):
209 """Assert that no messages are added by the given method.""" 199 """Assert that no messages are added by the given method."""
210 with self.assertAddsMessages(): 200 with self.assertAddsMessages():
211 yield 201 yield
212 202
213 @contextlib.contextmanager 203 @contextlib.contextmanager
(...skipping 29 matching lines...) Expand all
243 if linesep != '\n': 233 if linesep != '\n':
244 LINE_RGX = re.compile(linesep) 234 LINE_RGX = re.compile(linesep)
245 def ulines(string): 235 def ulines(string):
246 return LINE_RGX.sub('\n', string) 236 return LINE_RGX.sub('\n', string)
247 else: 237 else:
248 def ulines(string): 238 def ulines(string):
249 return string 239 return string
250 240
251 INFO_TEST_RGX = re.compile(r'^func_i\d\d\d\d$') 241 INFO_TEST_RGX = re.compile(r'^func_i\d\d\d\d$')
252 242
253 def exception_str(self, ex): 243 def exception_str(self, ex): # pylint: disable=unused-argument
254 """function used to replace default __str__ method of exception instances""" 244 """function used to replace default __str__ method of exception instances"""
255 return 'in %s\n:: %s' % (ex.file, ', '.join(ex.args)) 245 return 'in %s\n:: %s' % (ex.file, ', '.join(ex.args))
256 246
257 # Test classes 247 # Test classes
258 248
259 class LintTestUsingModule(testlib.TestCase): 249 class LintTestUsingModule(unittest.TestCase):
260 INPUT_DIR = None 250 INPUT_DIR = None
261 DEFAULT_PACKAGE = 'input' 251 DEFAULT_PACKAGE = 'input'
262 package = DEFAULT_PACKAGE 252 package = DEFAULT_PACKAGE
263 linter = linter 253 linter = linter
264 module = None 254 module = None
265 depends = None 255 depends = None
266 output = None 256 output = None
267 _TEST_TYPE = 'module' 257 _TEST_TYPE = 'module'
258 maxDiff = None
268 259
269 def shortDescription(self): 260 def shortDescription(self):
270 values = {'mode' : self._TEST_TYPE, 261 values = {'mode' : self._TEST_TYPE,
271 'input': self.module, 262 'input': self.module,
272 'pkg': self.package, 263 'pkg': self.package,
273 'cls': self.__class__.__name__} 264 'cls': self.__class__.__name__}
274 265
275 if self.package == self.DEFAULT_PACKAGE: 266 if self.package == self.DEFAULT_PACKAGE:
276 msg = '%(mode)s test of input file "%(input)s" (%(cls)s)' 267 msg = '%(mode)s test of input file "%(input)s" (%(cls)s)'
277 else: 268 else:
(...skipping 11 matching lines...) Expand all
289 self.assertMultiLineEqual(self._get_expected().strip()+'\n', 280 self.assertMultiLineEqual(self._get_expected().strip()+'\n',
290 got.strip()+'\n') 281 got.strip()+'\n')
291 282
292 def _test(self, tocheck): 283 def _test(self, tocheck):
293 if INFO_TEST_RGX.match(self.module): 284 if INFO_TEST_RGX.match(self.module):
294 self.linter.enable('I') 285 self.linter.enable('I')
295 else: 286 else:
296 self.linter.disable('I') 287 self.linter.disable('I')
297 try: 288 try:
298 self.linter.check(tocheck) 289 self.linter.check(tocheck)
299 except Exception, ex: 290 except Exception as ex:
300 # need finalization to restore a correct state 291 # need finalization to restore a correct state
301 self.linter.reporter.finalize() 292 self.linter.reporter.finalize()
302 ex.file = tocheck 293 ex.file = tocheck
303 print ex 294 print(ex)
304 ex.__str__ = exception_str 295 ex.__str__ = exception_str
305 raise 296 raise
306 self._check_result(self.linter.reporter.finalize()) 297 self._check_result(self.linter.reporter.finalize())
307 298
308 def _has_output(self): 299 def _has_output(self):
309 return not self.module.startswith('func_noerror_') 300 return not self.module.startswith('func_noerror_')
310 301
311 def _get_expected(self): 302 def _get_expected(self):
312 if self._has_output() and self.output: 303 if self._has_output() and self.output:
313 with open(self.output, 'U') as fobj: 304 with open(self.output, 'U') as fobj:
(...skipping 26 matching lines...) Expand all
340 except IOError: 331 except IOError:
341 expected = '' 332 expected = ''
342 if got != expected: 333 if got != expected:
343 with open(self.output, 'w') as fobj: 334 with open(self.output, 'w') as fobj:
344 fobj.write(got) 335 fobj.write(got)
345 336
346 # Callback 337 # Callback
347 338
348 def cb_test_gen(base_class): 339 def cb_test_gen(base_class):
349 def call(input_dir, msg_dir, module_file, messages_file, dependencies): 340 def call(input_dir, msg_dir, module_file, messages_file, dependencies):
341 # pylint: disable=no-init
350 class LintTC(base_class): 342 class LintTC(base_class):
351 module = module_file.replace('.py', '') 343 module = module_file.replace('.py', '')
352 output = messages_file 344 output = messages_file
353 depends = dependencies or None 345 depends = dependencies or None
354 tags = testlib.Tags(('generated', 'pylint_input_%s' % module))
355 INPUT_DIR = input_dir 346 INPUT_DIR = input_dir
356 MSG_DIR = msg_dir 347 MSG_DIR = msg_dir
357 return LintTC 348 return LintTC
358 return call 349 return call
359 350
360 # Main function 351 # Main function
361 352
362 def make_tests(input_dir, msg_dir, filter_rgx, callbacks): 353 def make_tests(input_dir, msg_dir, filter_rgx, callbacks):
363 """generate tests classes from test info 354 """generate tests classes from test info
364 355
365 return the list of generated test classes 356 return the list of generated test classes
366 """ 357 """
367 if filter_rgx: 358 if filter_rgx:
368 is_to_run = re.compile(filter_rgx).search 359 is_to_run = re.compile(filter_rgx).search
369 else: 360 else:
370 is_to_run = lambda x: 1 361 is_to_run = lambda x: 1
371 tests = [] 362 tests = []
372 for module_file, messages_file in ( 363 for module_file, messages_file in (
373 get_tests_info(input_dir, msg_dir, 'func_', '') 364 get_tests_info(input_dir, msg_dir, 'func_', '')
374 ): 365 ):
375 if not is_to_run(module_file): 366 if not is_to_run(module_file) or module_file.endswith('.pyc'):
376 continue 367 continue
377 base = module_file.replace('func_', '').replace('.py', '') 368 base = module_file.replace('func_', '').replace('.py', '')
378 369
379 dependencies = get_tests_info(input_dir, msg_dir, base, '.py') 370 dependencies = get_tests_info(input_dir, msg_dir, base, '.py')
380 371
381 for callback in callbacks: 372 for callback in callbacks:
382 test = callback(input_dir, msg_dir, module_file, messages_file, 373 test = callback(input_dir, msg_dir, module_file, messages_file,
383 dependencies) 374 dependencies)
384 if test: 375 if test:
385 tests.append(test) 376 tests.append(test)
386 return tests 377 return tests
378
379 def tokenize_str(code):
380 return list(tokenize.generate_tokens(StringIO(code).readline))
381
382 @contextlib.contextmanager
383 def create_tempfile(content=None):
384 """Create a new temporary file.
385
386 If *content* parameter is given, then it will be written
387 in the temporary file, before passing it back.
388 This is a context manager and should be used with a *with* statement.
389 """
390 # Can't use tempfile.NamedTemporaryFile here
391 # because on Windows the file must be closed before writing to it,
392 # see http://bugs.python.org/issue14243
393 fd, tmp = tempfile.mkstemp()
394 if content:
395 if sys.version_info >= (3, 0):
396 # erff
397 os.write(fd, bytes(content, 'ascii'))
398 else:
399 os.write(fd, content)
400 try:
401 yield tmp
402 finally:
403 os.close(fd)
404 os.remove(tmp)
405
406 @contextlib.contextmanager
407 def create_file_backed_module(code):
408 """Create an astroid module for the given code, backed by a real file."""
409 with create_tempfile() as temp:
410 module = test_utils.build_module(code)
411 module.file = temp
412 yield module
OLDNEW
« no previous file with comments | « third_party/pylint/reporters/text.py ('k') | third_party/pylint/utils.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698