diff --git a/HACKING.rst b/HACKING.rst index 1340e1289df..bc17b7f3a13 100644 --- a/HACKING.rst +++ b/HACKING.rst @@ -25,6 +25,7 @@ Neutron Specific Commandments assertEqual(observed_http_code, expected_http_code). - [N333] Validate that LOG.warning is used instead of LOG.warn. The latter is deprecated. +- [N334] Use unittest2 uniformly across Neutron. - [N340] Check usage of .i18n (and neutron.i18n) - [N341] Check usage of _ from python builtins @@ -38,7 +39,9 @@ without the patch and passes with the patch. All unittest classes must ultimately inherit from testtools.TestCase. In the Neutron test suite, this should be done by inheriting from -neutron.tests.base.BaseTestCase. +neutron.tests.base.BaseTestCase. If the third party unittest library has to +be used directly then it is recommended to use unittest2 as it contains bug +fixes to unittest for all versions of Python prior to version 3.5. All setUp and tearDown methods must upcall using the super() method. tearDown methods should be avoided and addCleanup calls should be preferred. diff --git a/neutron/hacking/checks.py b/neutron/hacking/checks.py index 00d49e835d8..ed40edf9b27 100644 --- a/neutron/hacking/checks.py +++ b/neutron/hacking/checks.py @@ -18,6 +18,8 @@ import re import pep8 import six +from hacking import core + def flake8ext(f): """Decorator to indicate flake8 extension. @@ -69,6 +71,8 @@ log_translation_hint = re.compile( log_warn = re.compile( r"(.)*LOG\.(warn)\(\s*('|\"|_)") contextlib_nested = re.compile(r"^with (contextlib\.)?nested\(") +unittest_imports_dot = re.compile(r"\bimport[\s]+unittest\b") +unittest_imports_from = re.compile(r"\bfrom[\s]+unittest\b") @flake8ext @@ -321,6 +325,16 @@ def check_builtins_gettext(logical_line, tokens, filename, lines, noqa): yield (0, msg) +@core.flake8ext +@core.off_by_default +def check_unittest_imports(logical_line): + if (re.match(unittest_imports_from, logical_line) or + re.match(unittest_imports_dot, logical_line)): + msg = "N334: '%s' must be used instead of '%s'." % ( + logical_line.replace('unittest', 'unittest2'), logical_line) + yield (0, msg) + + def factory(register): register(validate_log_translations) register(use_jsonutils) @@ -339,3 +353,4 @@ def factory(register): register(check_log_warn_deprecated) register(check_oslo_i18n_wrapper) register(check_builtins_gettext) + register(check_unittest_imports) diff --git a/neutron/tests/common/base.py b/neutron/tests/common/base.py index eff4a0bf44c..38a4d1a9cf4 100644 --- a/neutron/tests/common/base.py +++ b/neutron/tests/common/base.py @@ -12,10 +12,10 @@ # import functools -import unittest.case from oslo_db.sqlalchemy import test_base import testtools.testcase +import unittest2.case from neutron.common import constants as n_const from neutron.tests import base @@ -61,7 +61,8 @@ def no_skip_on_missing_deps(wrapped): def wrapper(*args, **kwargs): try: return wrapped(*args, **kwargs) - except (testtools.TestCase.skipException, unittest.case.SkipTest) as e: + except (testtools.TestCase.skipException, + unittest2.case.SkipTest) as e: if base.bool_from_env('OS_FAIL_ON_MISSING_DEPS'): tools.fail( '%s cannot be skipped because OS_FAIL_ON_MISSING_DEPS ' diff --git a/neutron/tests/functional/pecan_wsgi/__init__.py b/neutron/tests/functional/pecan_wsgi/__init__.py index 426ba244f4b..c128503fafd 100644 --- a/neutron/tests/functional/pecan_wsgi/__init__.py +++ b/neutron/tests/functional/pecan_wsgi/__init__.py @@ -15,8 +15,8 @@ import os -import unittest +import unittest2 from pecan import set_config from pecan.testing import load_test_app @@ -24,7 +24,7 @@ from pecan.testing import load_test_app __all__ = ['FunctionalTest'] -class FunctionalTest(unittest.TestCase): +class FunctionalTest(unittest2.TestCase): """ Used for functional tests where you need to test your literal application and its integration with the framework. diff --git a/neutron/tests/tools.py b/neutron/tests/tools.py index 445e5077092..1b8ef1288c2 100644 --- a/neutron/tests/tools.py +++ b/neutron/tests/tools.py @@ -26,6 +26,7 @@ import fixtures import mock import netaddr import six +import unittest2 import neutron from neutron.api.v2 import attributes @@ -117,8 +118,6 @@ class SafeCleanupFixture(fixtures.Fixture): self.addCleanup(cleanUp) -import unittest - from neutron.common import utils @@ -175,7 +174,7 @@ def fail(msg=None): This method is equivalent to TestCase.fail without requiring a testcase instance (usefully for reducing coupling). """ - raise unittest.TestCase.failureException(msg) + raise unittest2.TestCase.failureException(msg) class UnorderedList(list): diff --git a/neutron/tests/unit/hacking/test_checks.py b/neutron/tests/unit/hacking/test_checks.py index 8b5966309a6..8bf80c99a0a 100644 --- a/neutron/tests/unit/hacking/test_checks.py +++ b/neutron/tests/unit/hacking/test_checks.py @@ -282,6 +282,19 @@ class HackingTestCase(base.BaseTestCase): 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') + + # 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. diff --git a/test-requirements.txt b/test-requirements.txt index afe596184eb..5dfbe207d00 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,7 +1,7 @@ # The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -hacking<0.11,>=0.10.0 +hacking>=0.11.0,<0.12 # Apache-2.0 cliff!=1.16.0,!=1.17.0,>=1.15.0 # Apache-2.0 coverage>=3.6 # Apache-2.0 diff --git a/tox.ini b/tox.ini index 0b3d0838cc9..bd14ff59b93 100644 --- a/tox.ini +++ b/tox.ini @@ -126,6 +126,10 @@ commands = sphinx-build -W -b html doc/source doc/build/html ignore = E125,E126,E128,E129,E265,H404,H405 show-source = true exclude = ./.*,build,dist,neutron/openstack/common/* +# N334 unittest2 must be used instead of unittest +# Off by default to avoid breaking external projects. +# Compliant projects can enable it like this: +select = N334 [hacking] import_exceptions = neutron._i18n