Files
neutron/neutron/tests/unit/hacking/test_checks.py
Ihar Hrachyshka 9c1e48e79d Switch to neutron-lib hacking factory
This allows us to kill a bunch of in-tree checks. There are still some
checks that are either not yet in the library, or don't belong there in
the first place, so we still stick to our own factory, just reusing
whatever is there in neutron-lib.

This change skips some new checks. We gotta figure out what to do with
those, that will belong in a separate follow-up.

Change-Id: Ifeb40ec0e0c4ca623b33a6b9f500dec15cec4de0
2017-04-13 07:41:25 -07:00

312 lines
12 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 re
from flake8 import engine
from hacking.tests import test_doctest as hacking_doctest
import pep8
import testscenarios
import testtools
from testtools import content
from testtools import matchers
from neutron.hacking import checks
from neutron.tests import base
CREATE_DUMMY_MATCH_OBJECT = re.compile('a')
class HackingTestCase(base.BaseTestCase):
def assertLinePasses(self, func, line):
with testtools.ExpectedException(StopIteration):
next(func(line))
def assertLineFails(self, func, line):
self.assertIsInstance(next(func(line)), tuple)
def test_assert_called_once_with(self):
fail_code1 = """
mock = Mock()
mock.method(1, 2, 3, test='wow')
mock.method.assert_called_once()
"""
fail_code2 = """
mock = Mock()
mock.method(1, 2, 3, test='wow')
mock.method.assertCalledOnceWith()
"""
fail_code3 = """
mock = Mock()
mock.method(1, 2, 3, test='wow')
mock.method.called_once_with()
"""
fail_code4 = """
mock = Mock()
mock.method(1, 2, 3, test='wow')
mock.method.assert_has_called()
"""
pass_code = """
mock = Mock()
mock.method(1, 2, 3, test='wow')
mock.method.assert_called_once_with()
"""
pass_code2 = """
mock = Mock()
mock.method(1, 2, 3, test='wow')
mock.method.assert_has_calls()
"""
self.assertEqual(
1, len(list(checks.check_assert_called_once_with(fail_code1,
"neutron/tests/test_assert.py"))))
self.assertEqual(
1, len(list(checks.check_assert_called_once_with(fail_code2,
"neutron/tests/test_assert.py"))))
self.assertEqual(
1, len(list(checks.check_assert_called_once_with(fail_code3,
"neutron/tests/test_assert.py"))))
self.assertEqual(
0, len(list(checks.check_assert_called_once_with(pass_code,
"neutron/tests/test_assert.py"))))
self.assertEqual(
1, len(list(checks.check_assert_called_once_with(fail_code4,
"neutron/tests/test_assert.py"))))
self.assertEqual(
0, len(list(checks.check_assert_called_once_with(pass_code2,
"neutron/tests/test_assert.py"))))
def test_asserttruefalse(self):
true_fail_code1 = """
test_bool = True
self.assertEqual(True, test_bool)
"""
true_fail_code2 = """
test_bool = True
self.assertEqual(test_bool, True)
"""
true_pass_code = """
test_bool = True
self.assertTrue(test_bool)
"""
false_fail_code1 = """
test_bool = False
self.assertEqual(False, test_bool)
"""
false_fail_code2 = """
test_bool = False
self.assertEqual(test_bool, False)
"""
false_pass_code = """
test_bool = False
self.assertFalse(test_bool)
"""
self.assertEqual(
1, len(list(
checks.check_asserttruefalse(true_fail_code1,
"neutron/tests/test_assert.py"))))
self.assertEqual(
1, len(list(
checks.check_asserttruefalse(true_fail_code2,
"neutron/tests/test_assert.py"))))
self.assertEqual(
0, len(list(
checks.check_asserttruefalse(true_pass_code,
"neutron/tests/test_assert.py"))))
self.assertEqual(
1, len(list(
checks.check_asserttruefalse(false_fail_code1,
"neutron/tests/test_assert.py"))))
self.assertEqual(
1, len(list(
checks.check_asserttruefalse(false_fail_code2,
"neutron/tests/test_assert.py"))))
self.assertFalse(
list(
checks.check_asserttruefalse(false_pass_code,
"neutron/tests/test_assert.py")))
def test_assertempty(self):
fail_code = """
test_empty = %s
self.assertEqual(test_empty, %s)
"""
pass_code1 = """
test_empty = %s
self.assertEqual(%s, test_empty)
"""
pass_code2 = """
self.assertEqual(123, foo(abc, %s))
"""
empty_cases = ['{}', '[]', '""', "''", '()', 'set()']
for ec in empty_cases:
self.assertEqual(
1, len(list(checks.check_assertempty(fail_code % (ec, ec),
"neutron/tests/test_assert.py"))))
self.assertEqual(
0, len(list(checks.check_asserttruefalse(pass_code1 % (ec, ec),
"neutron/tests/test_assert.py"))))
self.assertEqual(
0, len(list(checks.check_asserttruefalse(pass_code2 % ec,
"neutron/tests/test_assert.py"))))
def test_assertisinstance(self):
fail_code = """
self.assertTrue(isinstance(observed, ANY_TYPE))
"""
pass_code1 = """
self.assertEqual(ANY_TYPE, type(observed))
"""
pass_code2 = """
self.assertIsInstance(observed, ANY_TYPE)
"""
self.assertEqual(
1, len(list(checks.check_assertisinstance(fail_code,
"neutron/tests/test_assert.py"))))
self.assertEqual(
0, len(list(checks.check_assertisinstance(pass_code1,
"neutron/tests/test_assert.py"))))
self.assertEqual(
0, len(list(checks.check_assertisinstance(pass_code2,
"neutron/tests/test_assert.py"))))
def test_assertequal_for_httpcode(self):
fail_code = """
self.assertEqual(res.status_int, webob.exc.HTTPNoContent.code)
"""
pass_code = """
self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int)
"""
self.assertEqual(
1, len(list(checks.check_assertequal_for_httpcode(fail_code,
"neutron/tests/test_assert.py"))))
self.assertEqual(
0, len(list(checks.check_assertequal_for_httpcode(pass_code,
"neutron/tests/test_assert.py"))))
def test_unittest_imports(self):
f = checks.check_unittest_imports
self.assertLinePasses(f, 'from unittest2')
self.assertLinePasses(f, 'import unittest2')
self.assertLinePasses(f, 'from unitest2 import case')
self.assertLinePasses(f, 'unittest2.TestSuite')
self.assertLineFails(f, 'from unittest import case')
self.assertLineFails(f, 'from unittest.TestSuite')
self.assertLineFails(f, 'import unittest')
def test_check_no_imports_from_tests(self):
fail_codes = ('from neutron import tests',
'from neutron.tests import base',
'import neutron.tests.base')
for fail_code in fail_codes:
self.assertEqual(
1, len(list(
checks.check_no_imports_from_tests(
fail_code, "neutron/common/rpc.py", None))))
self.assertEqual(
0, len(list(
checks.check_no_imports_from_tests(
fail_code, "neutron/tests/test_fake.py", None))))
def test_check_python3_filter(self):
f = checks.check_python3_no_filter
self.assertLineFails(f, "filter(lambda obj: test(obj), data)")
self.assertLinePasses(f, "[obj for obj in data if test(obj)]")
self.assertLinePasses(f, "filter(function, range(0,10))")
self.assertLinePasses(f, "lambda x, y: x+y")
# The following is borrowed from hacking/tests/test_doctest.py.
# Tests defined in docstring is easier to understand
# in some cases, for example, hacking rules which take tokens as argument.
# TODO(amotoki): Migrate existing unit tests above to docstring tests.
# NOTE(amotoki): Is it better to enhance HackingDocTestCase in hacking repo to
# pass filename to pep8.Checker so that we can reuse it in this test.
# I am not sure whether unit test class is public.
SELFTEST_REGEX = re.compile(r'\b(Okay|N\d{3})(\((\S+)\))?:\s(.*)')
# Each scenario is (name, dict(filename=..., lines=.., options=..., code=...))
file_cases = []
class HackingDocTestCase(hacking_doctest.HackingTestCase):
scenarios = file_cases
def test_pep8(self):
# NOTE(jecarey): Add tests marked as off_by_default to enable testing
turn_on = set(['H106'])
if self.options.select:
turn_on.update(self.options.select)
self.options.select = tuple(turn_on)
self.options.ignore = ('N530',)
report = pep8.BaseReport(self.options)
checker = pep8.Checker(filename=self.filename, lines=self.lines,
options=self.options, report=report)
checker.check_all()
self.addDetail('doctest', content.text_content(self.raw))
if self.code == 'Okay':
self.assertThat(
len(report.counters),
matchers.Not(matchers.GreaterThan(
len(self.options.benchmark_keys))),
"incorrectly found %s" % ', '.join(
[key for key in report.counters
if key not in self.options.benchmark_keys]))
else:
self.addDetail('reason',
content.text_content("Failed to trigger rule %s" %
self.code))
self.assertIn(self.code, report.counters)
def _get_lines(check):
for line in check.__doc__.splitlines():
line = line.lstrip()
match = SELFTEST_REGEX.match(line)
if match is None:
continue
yield (line, match.groups())
def load_tests(loader, tests, pattern):
flake8_style = engine.get_style_guide(parse_argv=False,
# Ignore H104 otherwise it's
# raised on doctests.
ignore=('F', 'H104'))
options = flake8_style.options
for name, check in checks.__dict__.items():
if not hasattr(check, 'name'):
continue
if check.name != checks.__name__:
continue
if not check.__doc__:
continue
for (lineno, (raw, line)) in enumerate(_get_lines(check)):
code, __, filename, source = line
lines = [part.replace(r'\t', '\t') + '\n'
for part in source.split(r'\n')]
file_cases.append(("%s-line-%s" % (name, lineno),
dict(lines=lines, raw=raw, options=options,
code=code, filename=filename)))
return testscenarios.load_tests_apply_scenarios(loader, tests, pattern)