Index: third_party/pylint/testutils.py |
diff --git a/third_party/pylint/testutils.py b/third_party/pylint/testutils.py |
deleted file mode 100644 |
index 1dd083fe78eec50527c9a2cf31102ebfa36cfbdc..0000000000000000000000000000000000000000 |
--- a/third_party/pylint/testutils.py |
+++ /dev/null |
@@ -1,386 +0,0 @@ |
-# 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 |