URL: https://github.com/freeipa/freeipa/pull/618 Author: tiran Title: #618: [WIP] Tox testing support for client wheel packages Action: synchronized
To pull the PR as Git branch: git remote add ghfreeipa https://github.com/freeipa/freeipa git fetch ghfreeipa pull/618/head:pr618 git checkout pr618
From 69d19bb585b80aa45683676ef2cba34c511e302e Mon Sep 17 00:00:00 2001 From: Christian Heimes <chei...@redhat.com> Date: Thu, 17 Nov 2016 16:43:17 +0100 Subject: [PATCH 1/4] tox testing support for client wheel packages Signed-off-by: Christian Heimes <chei...@redhat.com> --- .gitignore | 2 ++ .tox-install.sh | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Makefile.am | 14 +++++++--- configure.ac | 1 + ipatests/conftest.py | 1 - tox.ini | 38 +++++++++++++++++++++++++++ 6 files changed, 123 insertions(+), 5 deletions(-) create mode 100755 .tox-install.sh create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore index 90d7d23..8f4c2aa 100644 --- a/.gitignore +++ b/.gitignore @@ -61,6 +61,8 @@ freeipa2-dev-doc # Root directory /freeipa.spec /dist/ +/.tox/ +/.cache/ /*/dist/ /RELEASE /rpmbuild/ diff --git a/.tox-install.sh b/.tox-install.sh new file mode 100755 index 0000000..ab4a4c5 --- /dev/null +++ b/.tox-install.sh @@ -0,0 +1,72 @@ +#!/bin/bash +set -x + +PYTHON="$1" +ENVSITEPACKAGESDIR="$2" +# 3...end are package requirements +shift 2 + +TOXINIDIR="$(cd "$(dirname "$0")" && pwd)" + +# sanity checks +if [ ! -x "${PYTHON}" ]; then + echo "${PYTHON}: no such executable" + exit 1 +fi + +if [ ! -d "${ENVSITEPACKAGESDIR}" ]; then + echo "${ENVSITEPACKAGESDIR}: no such directory" + exit 2 +fi + +if [ ! -f "${TOXINIDIR}/tox.ini" ]; then + echo "${TOXINIDIR}: no such directory" + exit 3 +fi + +# https://pip.pypa.io/en/stable/user_guide/#environment-variables +export PIP_CACHE_DIR="${TOXINIDIR}/.tox/cache" +mkdir -p "${PIP_CACHE_DIR}" + +DISTBUNDLE="${TOXINIDIR}/dist/bundle" +mkdir -p "${DISTBUNDLE}" + +# create configure +pushd "${TOXINIDIR}" +if [ ! -f "configure" ]; then + autoreconf -i -f +fi +# (re)create Makefile +./configure --disable-server +popd + +# copy pylint plugin +cp "${TOXINIDIR}/pylint_plugins.py" "${ENVSITEPACKAGESDIR}" + +# build packages and bundles +make -C "${TOXINIDIR}" \ + PYTHON="${PYTHON}" \ + IPA_EXTRA_SUBDIRS="ipatests" \ + wheel_bundle + +# chdir to prevent local .egg-info from messing up pip +pushd "${ENVSITEPACKAGESDIR}" + +# build additional wheels, e.g. pylint +$PYTHON -m pip wheel \ + --disable-pip-version-check \ + --constraint "${TOXINIDIR}/.wheelconstraints" \ + --find-links "${DISTBUNDLE}" \ + --wheel-dir "${DISTBUNDLE}" \ + $@ + +# Install packages with dist/bundle/ as extra source for wheels while ignoring +# upstream Python Package Index. +$PYTHON -m pip install \ + --no-index \ + --disable-pip-version-check \ + --constraint "${TOXINIDIR}/.wheelconstraints" \ + --find-links "${DISTBUNDLE}" \ + $@ + +popd diff --git a/Makefile.am b/Makefile.am index af22315..ba81c55 100644 --- a/Makefile.am +++ b/Makefile.am @@ -57,6 +57,7 @@ EXTRA_DIST = .mailmap \ clean-local: rm -rf "$(RPMBUILD)" rm -rf "$(top_builddir)/dist" + rm -rf "$(top_builddir)/.tox" rm -rf "$(top_srcdir)/__pycache__" rm -f "$(top_builddir)"/$(PACKAGE)-*.tar.gz @@ -183,6 +184,7 @@ pylint: $(top_builddir)/ipapython/version.py ipasetup.py -path './freeipa-*' -prune -o \ -path './dist' -prune -o \ -path './pypi' -prune -o \ + -path './.tox' -prune -o \ -name '.*' -o \ -name '*.in' -o \ -name '*~' -o \ @@ -223,7 +225,10 @@ jslint-html: jsl -nologo -nosummary -nofilelisting -conf jsl.conf endif # WITH_JSLINT -.PHONY: bdist_wheel wheel_bundle wheel_placeholder pypi_packages +# Python wheels +# IPA_EXTRA_SUBDIRS: extra subdirs to build wheels (e.g. ipatests) + +.PHONY: bdist_wheel wheel_bundle wheel_placeholder pypi_packages WHEELDISTDIR = $(top_builddir)/dist/wheels WHEELBUNDLEDIR = $(top_builddir)/dist/bundle @@ -234,19 +239,20 @@ $(WHEELBUNDLEDIR): mkdir -p $(WHEELBUNDLEDIR) bdist_wheel: $(WHEELDISTDIR) - for dir in $(IPACLIENT_SUBDIRS); do \ + rm -f $(foreach item,$(IPACLIENT_SUBDIRS) $(IPA_EXTRA_SUBDIRS),$(WHEELDISTDIR)/$(item)-*.whl) + for dir in $(IPACLIENT_SUBDIRS) $(IPA_EXTRA_SUBDIRS); do \ $(MAKE) $(AM_MAKEFLAGS) -C $${dir} $@ || exit 1; \ done wheel_bundle: $(WHEELBUNDLEDIR) bdist_wheel .wheelconstraints - rm -f $(foreach item,$(IPACLIENT_SUBDIRS),$(WHEELBUNDLEDIR)/$(item)-*.whl) + rm -f $(foreach item,$(IPACLIENT_SUBDIRS) $(IPA_EXTRA_SUBDIRS),$(WHEELBUNDLEDIR)/$(item)-*.whl) $(PYTHON) -m pip wheel \ --disable-pip-version-check \ --constraint .wheelconstraints \ --find-links $(WHEELDISTDIR) \ --find-links $(WHEELBUNDLEDIR) \ --wheel-dir $(WHEELBUNDLEDIR) \ - $(IPACLIENT_SUBDIRS) + $(IPACLIENT_SUBDIRS) $(IPA_EXTRA_SUBDIRS) wheel_placeholder: $(WHEELDISTDIR) for dir in $(IPA_PLACEHOLDERS); do \ diff --git a/configure.ac b/configure.ac index 7a67ec0..44d06f1 100644 --- a/configure.ac +++ b/configure.ac @@ -250,6 +250,7 @@ AC_CONFIG_COMMANDS([po/POTFILES.in], -path "./${PACKAGE}-*" -prune -o dnl dist directories -path '*/build' -prune -o dnl Python builds -path '*/dist' -prune -o dnl Python dists + -path './.tox' -prune -o dnl Python tox test -path './conf*' -prune -o dnl generated by configure -name '*.py' -print -o dnl -name '*.c' -print -o dnl diff --git a/ipatests/conftest.py b/ipatests/conftest.py index 61e889d..4ad3de3 100644 --- a/ipatests/conftest.py +++ b/ipatests/conftest.py @@ -100,7 +100,6 @@ def pytest_cmdline_main(config): ) for klass in cli_plugins: api.add_plugin(klass) - # XXX workaround until https://fedorahosted.org/freeipa/ticket/6408 has # been resolved. if ipaserver is not None: diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..18331af --- /dev/null +++ b/tox.ini @@ -0,0 +1,38 @@ +[tox] +minversion=2.3.1 +envlist=py27,py35,pylint2,pylint3 +skip_missing_interpreters=true +skipsdist=true + +[testenv] +# always re-create virtual env. A special install helper is used to configure, +# build and install packages. +recreate=True +install_command={toxinidir}/.tox-install.sh {envpython} {envsitepackagesdir} {packages} +changedir={envdir} +setenv= + HOME={envtmpdir} +deps= + ipaclient + ipatests +commands= + {envpython} -bb {envbindir}/ipa-run-tests --ipaclient-unittests + +[testenv:pylint2] +basepython=python2.7 +deps= + ipaclient + ipapython[certmonger] + pylint +commands= + {envpython} -m pylint \ + --rcfile={toxinidir}/pylintrc \ + --load-plugins pylint_plugins \ + {envsitepackagesdir}/ipaclient \ + {envsitepackagesdir}/ipalib \ + {envsitepackagesdir}/ipapython + +[testenv:pylint3] +basepython=python3 +deps={[testenv:pylint2]deps} +commands={[testenv:pylint2]commands} From 651f39ed67747e9dac87a445e15c9d39e2afd507 Mon Sep 17 00:00:00 2001 From: Christian Heimes <chei...@redhat.com> Date: Fri, 17 Mar 2017 18:20:38 +0100 Subject: [PATCH 2/4] pytest 3.x compatibility pytest 3.x does no longer support plain pytest.skip() on module level. Signed-off-by: Christian Heimes <chei...@redhat.com> --- ipatests/test_cmdline/__init__.py | 6 ++---- ipatests/test_install/__init__.py | 5 ++--- ipatests/test_integration/__init__.py | 5 ++--- ipatests/test_ipaserver/__init__.py | 6 ++---- ipatests/test_webui/__init__.py | 5 ++--- ipatests/test_xmlrpc/__init__.py | 5 ++--- ipatests/util.py | 14 ++++++++++++++ 7 files changed, 26 insertions(+), 20 deletions(-) diff --git a/ipatests/test_cmdline/__init__.py b/ipatests/test_cmdline/__init__.py index af8867e..52eb23e 100644 --- a/ipatests/test_cmdline/__init__.py +++ b/ipatests/test_cmdline/__init__.py @@ -1,9 +1,7 @@ # # Copyright (C) 2015 FreeIPA Contributors see COPYING for license # +import ipatests.util -import pytest - -if pytest.config.getoption('ipaclient_unittests', False): - pytest.skip("Skip in ipaclient unittest mode") +ipatests.util.check_ipaclient_unittests() diff --git a/ipatests/test_install/__init__.py b/ipatests/test_install/__init__.py index 54ef9eb..1d5fd0b 100644 --- a/ipatests/test_install/__init__.py +++ b/ipatests/test_install/__init__.py @@ -20,8 +20,7 @@ """ Package containing LDAP updates unit tests. """ -import pytest +import ipatests.util -if pytest.config.getoption('ipaclient_unittests', False): - pytest.skip("Skip in ipaclient unittest mode") +ipatests.util.check_ipaclient_unittests() diff --git a/ipatests/test_integration/__init__.py b/ipatests/test_integration/__init__.py index 8779f2f..2b4d535 100644 --- a/ipatests/test_integration/__init__.py +++ b/ipatests/test_integration/__init__.py @@ -16,8 +16,7 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import pytest +import ipatests.util -if pytest.config.getoption('ipaclient_unittests', False): - pytest.skip("Skip in ipaclient unittest mode") +ipatests.util.check_ipaclient_unittests() diff --git a/ipatests/test_ipaserver/__init__.py b/ipatests/test_ipaserver/__init__.py index 76942c7..22d36ea 100644 --- a/ipatests/test_ipaserver/__init__.py +++ b/ipatests/test_ipaserver/__init__.py @@ -20,9 +20,7 @@ """ Sub-package containing unit tests for `ipaserver` package. """ +import ipatests.util -import pytest - -if pytest.config.getoption('ipaclient_unittests', False): - pytest.skip("Skip in ipaclient unittest mode") +ipatests.util.check_ipaclient_unittests() diff --git a/ipatests/test_webui/__init__.py b/ipatests/test_webui/__init__.py index cb2f361..3f1b63a 100644 --- a/ipatests/test_webui/__init__.py +++ b/ipatests/test_webui/__init__.py @@ -20,8 +20,7 @@ """ Sub-package containing Web UI integration tests """ -import pytest +import ipatests.util -if pytest.config.getoption('ipaclient_unittests', False): - pytest.skip("Skip in ipaclient unittest mode") +ipatests.util.check_ipaclient_unittests() diff --git a/ipatests/test_xmlrpc/__init__.py b/ipatests/test_xmlrpc/__init__.py index 720c61b..0ee42fb 100644 --- a/ipatests/test_xmlrpc/__init__.py +++ b/ipatests/test_xmlrpc/__init__.py @@ -20,8 +20,7 @@ """ Sub-package containing unit tests for `xmlrpc` package. """ -import pytest +import ipatests.util -if pytest.config.getoption('ipaclient_unittests', False): - pytest.skip("Skip in ipaclient unittest mode") +ipatests.util.check_ipaclient_unittests() diff --git a/ipatests/util.py b/ipatests/util.py index 4379c30..92c47c2 100644 --- a/ipatests/util.py +++ b/ipatests/util.py @@ -61,6 +61,20 @@ unicode = str +PYTEST_VERSION = tuple(int(v) for v in pytest.__version__.split('.')) + + +def check_ipaclient_unittests(reason="Skip in ipaclient unittest mode"): + """Call this in a package to skip the package in ipaclient-unittest mode + """ + if pytest.config.getoption('ipaclient_unittests', False): + if PYTEST_VERSION[0] >= 3: + # pytest 3+ does no longer allow pytest.skip() on module leve + raise pytest.skip.Exception(reason, allow_module_level=True) + else: + raise pytest.skip(reason) + + class TempDir(object): def __init__(self): self.__path = tempfile.mkdtemp(prefix='ipa.tests.') From 77d86f71bb62ccc645e268316fce4782a954415e Mon Sep 17 00:00:00 2001 From: Christian Heimes <chei...@redhat.com> Date: Fri, 17 Mar 2017 18:27:47 +0100 Subject: [PATCH 3/4] Python 3 doc tests --- ipatests/util.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/ipatests/util.py b/ipatests/util.py index 92c47c2..dde0c95 100644 --- a/ipatests/util.py +++ b/ipatests/util.py @@ -192,9 +192,9 @@ class Fuzzy(object): Use of a regular expression by default implies the ``unicode`` type, so comparing with an ``str`` instance will evaluate to ``False``: - >>> phone.type - <type 'unicode'> - >>> '123-456-7890' == phone + >>> phone.type is six.text_type + True + >>> b'123-456-7890' == phone False The *type* kwarg allows you to specify a type constraint, so you can force @@ -234,15 +234,15 @@ class Fuzzy(object): >>> fuzzy = Fuzzy('.+', type=str, test=lambda other: True) >>> fuzzy.regex '.+' - >>> fuzzy.type - <type 'str'> + >>> fuzzy.type is str + True >>> fuzzy.test # doctest:+ELLIPSIS <function <lambda> at 0x...> To aid debugging, `Fuzzy.__repr__()` reveals these kwargs as well: >>> fuzzy # doctest:+ELLIPSIS - Fuzzy('.+', <type 'str'>, <function <lambda> at 0x...>) + Fuzzy('.+', <... 'str'>, <function <lambda> at 0x...>) """ def __init__(self, regex=None, type=None, test=None): @@ -342,20 +342,20 @@ def assert_deepequal(expected, got, doc='', stack=tuple()): If the tests fails, it will raise an ``AssertionError`` with detailed information, including the path to the offending value. For example: - >>> expected = [u'Hello', dict(world=u'how are you?')] - >>> got = [u'Hello', dict(world='how are you?')] + >>> expected = [u'Hello', dict(world=1)] + >>> got = [u'Hello', dict(world=1.0)] >>> expected == got True - >>> assert_deepequal(expected, got, doc='Testing my nested data') + >>> assert_deepequal(expected, got, doc='Testing my nested data') # doctest:+ELLIPSIS Traceback (most recent call last): ... AssertionError: assert_deepequal: type(expected) is not type(got). Testing my nested data - type(expected) = <type 'unicode'> - type(got) = <type 'str'> - expected = u'how are you?' - got = 'how are you?' - path = (0, 'world') + type(expected) = <... 'int'> + type(got) = <... 'float'> + expected = 1 + got = 1.0 + path = (..., 'world') Note that lists and tuples are considered equivalent, and the order of their elements does not matter. From a0e16d657f8860c1edd89046f412c4095221886c Mon Sep 17 00:00:00 2001 From: Christian Heimes <chei...@redhat.com> Date: Fri, 24 Mar 2017 11:15:47 +0100 Subject: [PATCH 4/4] Don't import integration plugin, skip session tests --- ipatests/conftest.py | 4 +++- ipatests/test_ipapython/test_session_storage.py | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ipatests/conftest.py b/ipatests/conftest.py index 4ad3de3..ab17575 100644 --- a/ipatests/conftest.py +++ b/ipatests/conftest.py @@ -52,7 +52,9 @@ 'ipaserver/build', 'ipatests/build', # install/share/wsgi.py - 'install/share' + 'install/share', + # integration plugin imports from ipaplatform + 'ipatests/pytest_plugins', ] diff --git a/ipatests/test_ipapython/test_session_storage.py b/ipatests/test_ipapython/test_session_storage.py index a89fdd9..1ae9f9c 100644 --- a/ipatests/test_ipapython/test_session_storage.py +++ b/ipatests/test_ipapython/test_session_storage.py @@ -5,10 +5,12 @@ """ Test the `session_storage.py` module. """ +import pytest from ipapython import session_storage +@pytest.mark.skip_ipaclient_unittest class test_session_storage(object): """ Test the session storage interface
-- Manage your subscription for the Freeipa-devel mailing list: https://www.redhat.com/mailman/listinfo/freeipa-devel Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code