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

Unified Diff: third_party/pylint/testutils.py

Issue 739393004: Revert "Revert "pylint: upgrade to 1.3.1"" (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools/
Patch Set: Created 6 years, 1 month 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 side-by-side diff with in-line comments
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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/pylint/testutils.py
===================================================================
--- third_party/pylint/testutils.py (revision 0)
+++ third_party/pylint/testutils.py (working copy)
@@ -0,0 +1,386 @@
+# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
+# http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+"""functional/non regression tests for pylint"""
+from __future__ import with_statement
+
+import collections
+import contextlib
+import functools
+import sys
+import re
+
+from glob import glob
+from os import linesep
+from os.path import abspath, basename, dirname, isdir, join, splitext
+from cStringIO import StringIO
+
+from logilab.common import testlib
+
+from pylint import checkers
+from pylint.utils import PyLintASTWalker
+from pylint.reporters import BaseReporter
+from pylint.interfaces import IReporter
+from pylint.lint import PyLinter
+
+
+# Utils
+
+SYS_VERS_STR = '%d%d%d' % sys.version_info[:3]
+TITLE_UNDERLINES = ['', '=', '-', '.']
+PREFIX = abspath(dirname(__file__))
+PY3K = sys.version_info[0] == 3
+
+def fix_path():
+ sys.path.insert(0, PREFIX)
+
+def get_tests_info(input_dir, msg_dir, prefix, suffix):
+ """get python input examples and output messages
+
+ We use following conventions for input files and messages:
+ for different inputs:
+ test for python >= x.y -> input = <name>_pyxy.py
+ test for python < x.y -> input = <name>_py_xy.py
+ for one input and different messages:
+ message for python >= x.y -> message = <name>_pyxy.txt
+ lower versions -> message with highest num
+ """
+ result = []
+ for fname in glob(join(input_dir, prefix + '*' + suffix)):
+ infile = basename(fname)
+ fbase = splitext(infile)[0]
+ # filter input files :
+ pyrestr = fbase.rsplit('_py', 1)[-1] # like _26 or 26
+ if pyrestr.isdigit(): # '24', '25'...
+ if SYS_VERS_STR < pyrestr:
+ continue
+ if pyrestr.startswith('_') and pyrestr[1:].isdigit():
+ # skip test for higher python versions
+ if SYS_VERS_STR >= pyrestr[1:]:
+ continue
+ messages = glob(join(msg_dir, fbase + '*.txt'))
+ # the last one will be without ext, i.e. for all or upper versions:
+ if messages:
+ for outfile in sorted(messages, reverse=True):
+ py_rest = outfile.rsplit('_py', 1)[-1][:-4]
+ if py_rest.isdigit() and SYS_VERS_STR >= py_rest:
+ break
+ else:
+ # This will provide an error message indicating the missing filename.
+ outfile = join(msg_dir, fbase + '.txt')
+ result.append((infile, outfile))
+ return result
+
+
+class TestReporter(BaseReporter):
+ """reporter storing plain text messages"""
+
+ __implements____ = IReporter
+
+ def __init__(self):
+ self.message_ids = {}
+ self.reset()
+
+ def reset(self):
+ self.out = StringIO()
+ self.messages = []
+
+ def add_message(self, msg_id, location, msg):
+ """manage message of different type and in the context of path """
+ _, _, obj, line, _ = location
+ self.message_ids[msg_id] = 1
+ if obj:
+ obj = ':%s' % obj
+ sigle = msg_id[0]
+ if PY3K and linesep != '\n':
+ # 2to3 writes os.linesep instead of using
+ # the previosly used line separators
+ msg = msg.replace('\r\n', '\n')
+ self.messages.append('%s:%3s%s: %s' % (sigle, line, obj, msg))
+
+ def finalize(self):
+ self.messages.sort()
+ for msg in self.messages:
+ print >> self.out, msg
+ result = self.out.getvalue()
+ self.reset()
+ return result
+
+ def display_results(self, layout):
+ """ignore layouts"""
+
+
+if sys.version_info < (2, 6):
+ class Message(tuple):
+ def __new__(cls, msg_id, line=None, node=None, args=None):
+ return tuple.__new__(cls, (msg_id, line, node, args))
+
+ @property
+ def msg_id(self):
+ return self[0]
+ @property
+ def line(self):
+ return self[1]
+ @property
+ def node(self):
+ return self[2]
+ @property
+ def args(self):
+ return self[3]
+
+
+else:
+ class Message(collections.namedtuple('Message',
+ ['msg_id', 'line', 'node', 'args'])):
+ def __new__(cls, msg_id, line=None, node=None, args=None):
+ return tuple.__new__(cls, (msg_id, line, node, args))
+
+
+class UnittestLinter(object):
+ """A fake linter class to capture checker messages."""
+
+ def __init__(self):
+ self._messages = []
+ self.stats = {}
+
+ def release_messages(self):
+ try:
+ return self._messages
+ finally:
+ self._messages = []
+
+ def add_message(self, msg_id, line=None, node=None, args=None):
+ self._messages.append(Message(msg_id, line, node, args))
+
+ def is_message_enabled(self, *unused_args):
+ return True
+
+ def add_stats(self, **kwargs):
+ for name, value in kwargs.iteritems():
+ self.stats[name] = value
+ return self.stats
+
+ @property
+ def options_providers(self):
+ return linter.options_providers
+
+def set_config(**kwargs):
+ """Decorator for setting config values on a checker."""
+ def _Wrapper(fun):
+ @functools.wraps(fun)
+ def _Forward(self):
+ for key, value in kwargs.iteritems():
+ setattr(self.checker.config, key, value)
+ if isinstance(self, CheckerTestCase):
+ # reopen checker in case, it may be interested in configuration change
+ self.checker.open()
+ fun(self)
+
+ return _Forward
+ return _Wrapper
+
+
+class CheckerTestCase(testlib.TestCase):
+ """A base testcase class for unittesting individual checker classes."""
+ CHECKER_CLASS = None
+ CONFIG = {}
+
+ def setUp(self):
+ self.linter = UnittestLinter()
+ self.checker = self.CHECKER_CLASS(self.linter) # pylint: disable=not-callable
+ for key, value in self.CONFIG.iteritems():
+ setattr(self.checker.config, key, value)
+ self.checker.open()
+
+ @contextlib.contextmanager
+ def assertNoMessages(self):
+ """Assert that no messages are added by the given method."""
+ with self.assertAddsMessages():
+ yield
+
+ @contextlib.contextmanager
+ def assertAddsMessages(self, *messages):
+ """Assert that exactly the given method adds the given messages.
+
+ The list of messages must exactly match *all* the messages added by the
+ method. Additionally, we check to see whether the args in each message can
+ actually be substituted into the message string.
+ """
+ yield
+ got = self.linter.release_messages()
+ msg = ('Expected messages did not match actual.\n'
+ 'Expected:\n%s\nGot:\n%s' % ('\n'.join(repr(m) for m in messages),
+ '\n'.join(repr(m) for m in got)))
+ self.assertEqual(list(messages), got, msg)
+
+ def walk(self, node):
+ """recursive walk on the given node"""
+ walker = PyLintASTWalker(linter)
+ walker.add_checker(self.checker)
+ walker.walk(node)
+
+
+# Init
+test_reporter = TestReporter()
+linter = PyLinter()
+linter.set_reporter(test_reporter)
+linter.config.persistent = 0
+checkers.initialize(linter)
+linter.global_set_option('required-attributes', ('__revision__',))
+
+if linesep != '\n':
+ LINE_RGX = re.compile(linesep)
+ def ulines(string):
+ return LINE_RGX.sub('\n', string)
+else:
+ def ulines(string):
+ return string
+
+INFO_TEST_RGX = re.compile(r'^func_i\d\d\d\d$')
+
+def exception_str(self, ex):
+ """function used to replace default __str__ method of exception instances"""
+ return 'in %s\n:: %s' % (ex.file, ', '.join(ex.args))
+
+# Test classes
+
+class LintTestUsingModule(testlib.TestCase):
+ INPUT_DIR = None
+ DEFAULT_PACKAGE = 'input'
+ package = DEFAULT_PACKAGE
+ linter = linter
+ module = None
+ depends = None
+ output = None
+ _TEST_TYPE = 'module'
+
+ def shortDescription(self):
+ values = {'mode' : self._TEST_TYPE,
+ 'input': self.module,
+ 'pkg': self.package,
+ 'cls': self.__class__.__name__}
+
+ if self.package == self.DEFAULT_PACKAGE:
+ msg = '%(mode)s test of input file "%(input)s" (%(cls)s)'
+ else:
+ msg = '%(mode)s test of input file "%(input)s" in "%(pkg)s" (%(cls)s)'
+ return msg % values
+
+ def test_functionality(self):
+ tocheck = [self.package+'.'+self.module]
+ if self.depends:
+ tocheck += [self.package+'.%s' % name.replace('.py', '')
+ for name, _ in self.depends]
+ self._test(tocheck)
+
+ def _check_result(self, got):
+ self.assertMultiLineEqual(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, 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, 'U') as fobj:
+ return fobj.read().strip() + '\n'
+ else:
+ return ''
+
+class LintTestUsingFile(LintTestUsingModule):
+
+ _TEST_TYPE = 'file'
+
+ def test_functionality(self):
+ importable = join(self.INPUT_DIR, self.module)
+ # python also prefers packages over simple modules.
+ if not isdir(importable):
+ importable += '.py'
+ tocheck = [importable]
+ if self.depends:
+ tocheck += [join(self.INPUT_DIR, name) for name, _file in self.depends]
+ self._test(tocheck)
+
+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)
+
+# Callback
+
+def cb_test_gen(base_class):
+ def call(input_dir, msg_dir, module_file, messages_file, dependencies):
+ class LintTC(base_class):
+ module = module_file.replace('.py', '')
+ output = messages_file
+ depends = dependencies or None
+ tags = testlib.Tags(('generated', 'pylint_input_%s' % module))
+ INPUT_DIR = input_dir
+ MSG_DIR = msg_dir
+ return LintTC
+ return call
+
+# Main function
+
+def make_tests(input_dir, msg_dir, filter_rgx, callbacks):
+ """generate tests classes from test info
+
+ return the list of generated test classes
+ """
+ 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):
+ continue
+ base = module_file.replace('func_', '').replace('.py', '')
+
+ dependencies = get_tests_info(input_dir, msg_dir, base, '.py')
+
+ for callback in callbacks:
+ test = callback(input_dir, msg_dir, module_file, messages_file,
+ dependencies)
+ if test:
+ tests.append(test)
+ return tests
« 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