amiro-os / tools / cpplint / python / cpplint_unittest.py @ 53710ca3
History | View | Annotate | Download (216.815 KB)
| 1 | e545e620 | Thomas Schöpping | #!/usr/bin/python
|
|---|---|---|---|
| 2 | # -*- coding: utf-8; -*-
|
||
| 3 | #
|
||
| 4 | # Copyright (c) 2009 Google Inc. All rights reserved.
|
||
| 5 | #
|
||
| 6 | # Redistribution and use in source and binary forms, with or without
|
||
| 7 | # modification, are permitted provided that the following conditions are
|
||
| 8 | # met:
|
||
| 9 | #
|
||
| 10 | # * Redistributions of source code must retain the above copyright
|
||
| 11 | # notice, this list of conditions and the following disclaimer.
|
||
| 12 | # * Redistributions in binary form must reproduce the above
|
||
| 13 | # copyright notice, this list of conditions and the following disclaimer
|
||
| 14 | # in the documentation and/or other materials provided with the
|
||
| 15 | # distribution.
|
||
| 16 | # * Neither the name of Google Inc. nor the names of its
|
||
| 17 | # contributors may be used to endorse or promote products derived from
|
||
| 18 | # this software without specific prior written permission.
|
||
| 19 | #
|
||
| 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||
| 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||
| 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||
| 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||
| 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||
| 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||
| 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||
| 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||
| 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
| 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||
| 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
| 31 | |||
| 32 | """Unit test for cpplint.py."""
|
||
| 33 | |||
| 34 | # TODO(unknown): Add a good test that tests UpdateIncludeState.
|
||
| 35 | |||
| 36 | import codecs |
||
| 37 | import os |
||
| 38 | import random |
||
| 39 | import re |
||
| 40 | import sys |
||
| 41 | import unittest |
||
| 42 | |||
| 43 | import cpplint |
||
| 44 | |||
| 45 | |||
| 46 | # This class works as an error collector and replaces cpplint.Error
|
||
| 47 | # function for the unit tests. We also verify each category we see
|
||
| 48 | # is in cpplint._ERROR_CATEGORIES, to help keep that list up to date.
|
||
| 49 | class ErrorCollector(object): |
||
| 50 | # These are a global list, covering all categories seen ever.
|
||
| 51 | _ERROR_CATEGORIES = cpplint._ERROR_CATEGORIES |
||
| 52 | _SEEN_ERROR_CATEGORIES = {}
|
||
| 53 | |||
| 54 | def __init__(self, assert_fn): |
||
| 55 | """assert_fn: a function to call when we notice a problem."""
|
||
| 56 | self._assert_fn = assert_fn
|
||
| 57 | self._errors = []
|
||
| 58 | cpplint.ResetNolintSuppressions() |
||
| 59 | |||
| 60 | def __call__(self, unused_filename, linenum, |
||
| 61 | category, confidence, message): |
||
| 62 | self._assert_fn(category in self._ERROR_CATEGORIES, |
||
| 63 | 'Message "%s" has category "%s",'
|
||
| 64 | ' which is not in _ERROR_CATEGORIES' % (message, category))
|
||
| 65 | self._SEEN_ERROR_CATEGORIES[category] = 1 |
||
| 66 | if cpplint._ShouldPrintError(category, confidence, linenum):
|
||
| 67 | self._errors.append('%s [%s] [%d]' % (message, category, confidence)) |
||
| 68 | |||
| 69 | def Results(self): |
||
| 70 | if len(self._errors) < 2: |
||
| 71 | return ''.join(self._errors) # Most tests expect to have a string. |
||
| 72 | else:
|
||
| 73 | return self._errors # Let's give a list if there is more than one. |
||
| 74 | |||
| 75 | def ResultList(self): |
||
| 76 | return self._errors |
||
| 77 | |||
| 78 | def VerifyAllCategoriesAreSeen(self): |
||
| 79 | """Fails if there's a category in _ERROR_CATEGORIES~_SEEN_ERROR_CATEGORIES.
|
||
| 80 |
|
||
| 81 | This should only be called after all tests are run, so
|
||
| 82 | _SEEN_ERROR_CATEGORIES has had a chance to fully populate. Since
|
||
| 83 | this isn't called from within the normal unittest framework, we
|
||
| 84 | can't use the normal unittest assert macros. Instead we just exit
|
||
| 85 | when we see an error. Good thing this test is always run last!
|
||
| 86 | """
|
||
| 87 | for category in self._ERROR_CATEGORIES: |
||
| 88 | if category not in self._SEEN_ERROR_CATEGORIES: |
||
| 89 | sys.exit('FATAL ERROR: There are no tests for category "%s"' % category)
|
||
| 90 | |||
| 91 | def RemoveIfPresent(self, substr): |
||
| 92 | for (index, error) in enumerate(self._errors): |
||
| 93 | if error.find(substr) != -1: |
||
| 94 | self._errors = self._errors[0:index] + self._errors[(index + 1):] |
||
| 95 | break
|
||
| 96 | |||
| 97 | |||
| 98 | # This class is a lame mock of codecs. We do not verify filename, mode, or
|
||
| 99 | # encoding, but for the current use case it is not needed.
|
||
| 100 | class MockIo(object): |
||
| 101 | |||
| 102 | def __init__(self, mock_file): |
||
| 103 | self.mock_file = mock_file
|
||
| 104 | |||
| 105 | def open(self, # pylint: disable-msg=C6409 |
||
| 106 | unused_filename, unused_mode, unused_encoding, _): |
||
| 107 | return self.mock_file |
||
| 108 | |||
| 109 | |||
| 110 | class CpplintTestBase(unittest.TestCase): |
||
| 111 | """Provides some useful helper functions for cpplint tests."""
|
||
| 112 | |||
| 113 | def setUp(self): |
||
| 114 | # Allow subclasses to cheat os.path.abspath called in FileInfo class.
|
||
| 115 | self.os_path_abspath_orig = os.path.abspath
|
||
| 116 | |||
| 117 | def tearDown(self): |
||
| 118 | os.path.abspath = self.os_path_abspath_orig
|
||
| 119 | |||
| 120 | # Perform lint on single line of input and return the error message.
|
||
| 121 | def PerformSingleLineLint(self, code): |
||
| 122 | error_collector = ErrorCollector(self.assert_)
|
||
| 123 | lines = code.split('\n')
|
||
| 124 | cpplint.RemoveMultiLineComments('foo.h', lines, error_collector)
|
||
| 125 | clean_lines = cpplint.CleansedLines(lines) |
||
| 126 | include_state = cpplint._IncludeState() |
||
| 127 | function_state = cpplint._FunctionState() |
||
| 128 | nesting_state = cpplint.NestingState() |
||
| 129 | cpplint.ProcessLine('foo.cc', 'cc', clean_lines, 0, |
||
| 130 | include_state, function_state, |
||
| 131 | nesting_state, error_collector) |
||
| 132 | # Single-line lint tests are allowed to fail the 'unlintable function'
|
||
| 133 | # check.
|
||
| 134 | error_collector.RemoveIfPresent( |
||
| 135 | 'Lint failed to find start of function body.')
|
||
| 136 | return error_collector.Results()
|
||
| 137 | |||
| 138 | # Perform lint over multiple lines and return the error message.
|
||
| 139 | def PerformMultiLineLint(self, code): |
||
| 140 | error_collector = ErrorCollector(self.assert_)
|
||
| 141 | lines = code.split('\n')
|
||
| 142 | cpplint.RemoveMultiLineComments('foo.h', lines, error_collector)
|
||
| 143 | lines = cpplint.CleansedLines(lines) |
||
| 144 | nesting_state = cpplint.NestingState() |
||
| 145 | for i in xrange(lines.NumLines()): |
||
| 146 | nesting_state.Update('foo.h', lines, i, error_collector)
|
||
| 147 | cpplint.CheckStyle('foo.h', lines, i, 'h', nesting_state, |
||
| 148 | error_collector) |
||
| 149 | cpplint.CheckForNonStandardConstructs('foo.h', lines, i,
|
||
| 150 | nesting_state, error_collector) |
||
| 151 | nesting_state.CheckCompletedBlocks('foo.h', error_collector)
|
||
| 152 | return error_collector.Results()
|
||
| 153 | |||
| 154 | # Similar to PerformMultiLineLint, but calls CheckLanguage instead of
|
||
| 155 | # CheckForNonStandardConstructs
|
||
| 156 | def PerformLanguageRulesCheck(self, file_name, code): |
||
| 157 | error_collector = ErrorCollector(self.assert_)
|
||
| 158 | include_state = cpplint._IncludeState() |
||
| 159 | nesting_state = cpplint.NestingState() |
||
| 160 | lines = code.split('\n')
|
||
| 161 | cpplint.RemoveMultiLineComments(file_name, lines, error_collector) |
||
| 162 | lines = cpplint.CleansedLines(lines) |
||
| 163 | ext = file_name[file_name.rfind('.') + 1:] |
||
| 164 | for i in xrange(lines.NumLines()): |
||
| 165 | cpplint.CheckLanguage(file_name, lines, i, ext, include_state, |
||
| 166 | nesting_state, error_collector) |
||
| 167 | return error_collector.Results()
|
||
| 168 | |||
| 169 | def PerformFunctionLengthsCheck(self, code): |
||
| 170 | """Perform Lint function length check on block of code and return warnings.
|
||
| 171 |
|
||
| 172 | Builds up an array of lines corresponding to the code and strips comments
|
||
| 173 | using cpplint functions.
|
||
| 174 |
|
||
| 175 | Establishes an error collector and invokes the function length checking
|
||
| 176 | function following cpplint's pattern.
|
||
| 177 |
|
||
| 178 | Args:
|
||
| 179 | code: C++ source code expected to generate a warning message.
|
||
| 180 |
|
||
| 181 | Returns:
|
||
| 182 | The accumulated errors.
|
||
| 183 | """
|
||
| 184 | file_name = 'foo.cc'
|
||
| 185 | error_collector = ErrorCollector(self.assert_)
|
||
| 186 | function_state = cpplint._FunctionState() |
||
| 187 | lines = code.split('\n')
|
||
| 188 | cpplint.RemoveMultiLineComments(file_name, lines, error_collector) |
||
| 189 | lines = cpplint.CleansedLines(lines) |
||
| 190 | for i in xrange(lines.NumLines()): |
||
| 191 | cpplint.CheckForFunctionLengths(file_name, lines, i, |
||
| 192 | function_state, error_collector) |
||
| 193 | return error_collector.Results()
|
||
| 194 | |||
| 195 | def PerformIncludeWhatYouUse(self, code, filename='foo.h', io=codecs): |
||
| 196 | # First, build up the include state.
|
||
| 197 | error_collector = ErrorCollector(self.assert_)
|
||
| 198 | include_state = cpplint._IncludeState() |
||
| 199 | nesting_state = cpplint.NestingState() |
||
| 200 | lines = code.split('\n')
|
||
| 201 | cpplint.RemoveMultiLineComments(filename, lines, error_collector) |
||
| 202 | lines = cpplint.CleansedLines(lines) |
||
| 203 | for i in xrange(lines.NumLines()): |
||
| 204 | cpplint.CheckLanguage(filename, lines, i, '.h', include_state,
|
||
| 205 | nesting_state, error_collector) |
||
| 206 | # We could clear the error_collector here, but this should
|
||
| 207 | # also be fine, since our IncludeWhatYouUse unittests do not
|
||
| 208 | # have language problems.
|
||
| 209 | |||
| 210 | # Second, look for missing includes.
|
||
| 211 | cpplint.CheckForIncludeWhatYouUse(filename, lines, include_state, |
||
| 212 | error_collector, io) |
||
| 213 | return error_collector.Results()
|
||
| 214 | |||
| 215 | # Perform lint and compare the error message with "expected_message".
|
||
| 216 | def TestLint(self, code, expected_message): |
||
| 217 | self.assertEquals(expected_message, self.PerformSingleLineLint(code)) |
||
| 218 | |||
| 219 | def TestMultiLineLint(self, code, expected_message): |
||
| 220 | self.assertEquals(expected_message, self.PerformMultiLineLint(code)) |
||
| 221 | |||
| 222 | def TestMultiLineLintRE(self, code, expected_message_re): |
||
| 223 | message = |