keystone/keystone/tests/unit/test_hacking_checks.py
Steve Martinelli b63cc5f71d Use public interfaces of pep8 for hacking
Rather than using mock and pep8 internals, we can use
public interfaces of pep8 to register our custom
hacking checks. Further, if pep8 isn't present for testing purposes,
we can simply use pycodestyle. Many distributions are shipping the
latter these days instead of pep8.

Closes-Bug: 1652458

Co-Authored-By: Ian Cordasco <graffatcolmingov@gmail.com>
Change-Id: Ica719703c54a295d10ea800467b25a5d0f65348a
Signed-off-by: Adam Williamson <awilliam@redhat.com>
2017-01-09 16:30:01 +00:00

149 lines
5.1 KiB
Python

# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import textwrap
try:
import pep8
except ImportError:
# NTOE(stevemar): pycodestyle is not yet in global-requirements, but
# packagers have begun pycodestyle (the renamed version of pep8)
# See: https://github.com/PyCQA/pycodestyle/issues/466
import pycodestyle as pep8
from keystone.tests.hacking import checks
from keystone.tests import unit
from keystone.tests.unit.ksfixtures import hacking as hacking_fixtures
class BaseStyleCheck(unit.BaseTestCase):
def setUp(self):
super(BaseStyleCheck, self).setUp()
self.code_ex = self.useFixture(self.get_fixture())
self.addCleanup(delattr, self, 'code_ex')
def get_checker(self):
"""Return the checker to be used for tests in this class."""
raise NotImplementedError('subclasses must provide '
'a real implementation')
def get_fixture(self):
return hacking_fixtures.HackingCode()
def run_check(self, code):
pep8.register_check(self.get_checker())
lines = textwrap.dedent(code).strip().splitlines(True)
# Load all keystone hacking checks, they are of the form Kddd,
# where ddd can from range from 000-999
guide = pep8.StyleGuide(select='K')
checker = pep8.Checker(lines=lines, options=guide.options)
checker.check_all()
checker.report._deferred_print.sort()
return checker.report._deferred_print
def assert_has_errors(self, code, expected_errors=None):
actual_errors = [e[:3] for e in self.run_check(code)]
self.assertItemsEqual(expected_errors or [], actual_errors)
class TestCheckForMutableDefaultArgs(BaseStyleCheck):
def get_checker(self):
return checks.CheckForMutableDefaultArgs
def test(self):
code = self.code_ex.mutable_default_args['code']
errors = self.code_ex.mutable_default_args['expected_errors']
self.assert_has_errors(code, expected_errors=errors)
class TestBlockCommentsBeginWithASpace(BaseStyleCheck):
def get_checker(self):
return checks.block_comments_begin_with_a_space
def test(self):
code = self.code_ex.comments_begin_with_space['code']
errors = self.code_ex.comments_begin_with_space['expected_errors']
self.assert_has_errors(code, expected_errors=errors)
class TestAssertingNoneEquality(BaseStyleCheck):
def get_checker(self):
return checks.CheckForAssertingNoneEquality
def test(self):
code = self.code_ex.asserting_none_equality['code']
errors = self.code_ex.asserting_none_equality['expected_errors']
self.assert_has_errors(code, expected_errors=errors)
class BaseLoggingCheck(BaseStyleCheck):
def get_checker(self):
return checks.CheckForLoggingIssues
def get_fixture(self):
return hacking_fixtures.HackingLogging()
def assert_has_errors(self, code, expected_errors=None):
# pull out the parts of the error that we'll match against
actual_errors = (e[:3] for e in self.run_check(code))
# adjust line numbers to make the fixture data more readable.
import_lines = len(self.code_ex.shared_imports.split('\n')) - 1
actual_errors = [(e[0] - import_lines, e[1], e[2])
for e in actual_errors]
self.assertEqual(expected_errors or [], actual_errors)
class TestCheckForDebugLoggingIssues(BaseLoggingCheck):
def test_for_translations(self):
fixture = self.code_ex.assert_no_translations_for_debug_logging
code = self.code_ex.shared_imports + fixture['code']
errors = fixture['expected_errors']
self.assert_has_errors(code, expected_errors=errors)
class TestLoggingWithWarn(BaseLoggingCheck):
def test(self):
data = self.code_ex.assert_not_using_deprecated_warn
code = self.code_ex.shared_imports + data['code']
errors = data['expected_errors']
self.assert_has_errors(code, expected_errors=errors)
class TestCheckForNonDebugLoggingIssues(BaseLoggingCheck):
def test_for_translations(self):
for example in self.code_ex.examples:
code = self.code_ex.shared_imports + example['code']
errors = example['expected_errors']
self.assert_has_errors(code, expected_errors=errors)
class TestDictConstructorWithSequenceCopy(BaseStyleCheck):
def get_checker(self):
return checks.dict_constructor_with_sequence_copy
def test(self):
code = self.code_ex.dict_constructor['code']
errors = self.code_ex.dict_constructor['expected_errors']
self.assert_has_errors(code, expected_errors=errors)