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

Reply via email to