From 43f8c3b1f5e9bfc7166364262b3ab54536ea04fa Mon Sep 17 00:00:00 2001 From: Ivan Kolodyazhny Date: Fri, 1 Feb 2019 15:26:37 +0200 Subject: [PATCH] Switch integration tests to run with python3 tox env for the integration tests is now renamed to 'integration' as it is no longer run under python 2.7. __hash__() method is now defined in integration_tests.helpers.BaseTestCase to avoid unhashable error during loading django test runner. This is originally caused by the fact that the base testcase class for integration tests is implemented on top of testtools and testtools does not define __hash__() method. In Python 3, __hash__() method must be defined if a class (re)defines __eq__() method to make the class hashable [1]. Ideally integration base TestCase class can be implemented on top of Django test case, but the current implementation depends on features from testtools a lot, so it seems better to add __hash__() method as a workaround. Unbound methods don't exist in Python 3[2], so six.create_unbound_method doesn' work as expected. To dynamic method generation we have to use new descriptors interface [3] or just generate new functools.partialmethod [4] function introduced in Python 3.4 to generate class methods with pre-defined 'path' argument and pass 'self' as a first function argument for class instance. [1] https://docs.python.org/3.5/reference/datamodel.html#object.__hash__ [2] https://six.readthedocs.io/#six.create_unbound_method [3] https://docs.python.org/3/howto/descriptor.html [4] https://docs.python.org/3/library/functools.html#functools.partialmethod Change-Id: I5c3078bdc66e33fe676d431bb28d92b35faaf479 --- .zuul.yaml | 2 +- .../test/integration_tests/helpers.py | 3 +++ .../test/integration_tests/pages/navigation.py | 13 ++++++++++++- tox.ini | 6 +++--- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/.zuul.yaml b/.zuul.yaml index bab2773ee7..4f3d725ba9 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -39,7 +39,7 @@ vars: devstack_services: horizon: true - tox_envlist: py27integration + tox_envlist: integration - job: name: horizon-dsvm-tempest-plugin diff --git a/openstack_dashboard/test/integration_tests/helpers.py b/openstack_dashboard/test/integration_tests/helpers.py index 605257c434..7a3d01a1a1 100644 --- a/openstack_dashboard/test/integration_tests/helpers.py +++ b/openstack_dashboard/test/integration_tests/helpers.py @@ -200,6 +200,9 @@ class BaseTestCase(testtools.TestCase): super(BaseTestCase, self).addOnException(wrapped_handler) + def __hash__(self): + return hash((type(self), self._testMethodName)) + def _configure_log(self): """Configure log to capture test logs include selenium logs. diff --git a/openstack_dashboard/test/integration_tests/pages/navigation.py b/openstack_dashboard/test/integration_tests/pages/navigation.py index 34af077282..04fb760d73 100644 --- a/openstack_dashboard/test/integration_tests/pages/navigation.py +++ b/openstack_dashboard/test/integration_tests/pages/navigation.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +import functools import importlib import json @@ -316,7 +317,17 @@ class Navigation(object): def _create_go_to_method(cls, path, class_name=None): go_to_method = Navigation.GoToMethodFactory(path, class_name) inst_method = six.create_unbound_method(go_to_method, Navigation) - setattr(Navigation, inst_method.name, inst_method) + + # TODO(e0ne): remove python2 support once all integration jobs + # will be switched to python3. + if six.PY3: + def _go_to_page(self, path): + return Navigation._go_to_page(self, path) + + wrapped_go_to = functools.partialmethod(_go_to_page, path) + setattr(Navigation, inst_method.name, wrapped_go_to) + else: + setattr(Navigation, inst_method.name, inst_method) @classmethod def unify_page_path(cls, path, preserve_spaces=True): diff --git a/tox.ini b/tox.ini index 0443d10b75..5bd47b7026 100644 --- a/tox.ini +++ b/tox.ini @@ -107,15 +107,15 @@ setenv = SKIP_UNITTESTS=1 commands = {[unit_tests]commands} -[testenv:py27integration] -envdir = {toxworkdir}/py27 +[testenv:integration] +basepython = python3 +envdir = {toxworkdir}/venv # Run integration tests only passenv = AVCONV_INSTALLED setenv = PYTHONHASHSEED=0 INTEGRATION_TESTS=1 SELENIUM_HEADLESS=1 -basepython = python2.7 commands = {envpython} {toxinidir}/manage.py test openstack_dashboard --settings=openstack_dashboard.test.settings --verbosity 2 --tag integration {posargs} [testenv:npm]