URL: https://github.com/freeipa/freeipa/pull/618
Author: tiran
 Title: #618: 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 c0f301f1b103268b0853ba569f9a4c9946cec7a8 Mon Sep 17 00:00:00 2001
From: Christian Heimes <chei...@redhat.com>
Date: Thu, 17 Nov 2016 16:43:17 +0100
Subject: [PATCH] tox testing support for client wheel packages

Add tox infrastructure to test client wheel packages workflow:

* build client packages
* install client packages
* ipa-run-tests --ipaclient-unittests under Python 2 and 3
* pylint of client packages under Python 2 and 3
* placeholder packages work as expected

Signed-off-by: Christian Heimes <chei...@redhat.com>
---
 .gitignore               |  2 ++
 .tox-install.sh          | 94 ++++++++++++++++++++++++++++++++++++++++++++++++
 Makefile.am              | 22 +++++++++---
 configure.ac             |  1 +
 ipatests/conftest.py     |  4 ++-
 pypi/test_placeholder.py | 47 ++++++++++++++++++++++++
 tox.ini                  | 56 +++++++++++++++++++++++++++++
 7 files changed, 220 insertions(+), 6 deletions(-)
 create mode 100755 .tox-install.sh
 create mode 100644 pypi/test_placeholder.py
 create mode 100644 tox.ini

diff --git a/.gitignore b/.gitignore
index 8941fd8..8b57dbc 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..8f9d221
--- /dev/null
+++ b/.tox-install.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+set -ex
+
+FLAVOR="$1"
+ENVPYTHON="$2"
+ENVSITEPACKAGESDIR="$3"
+# 3...end are package requirements
+shift 3
+
+TOXINIDIR="$(cd "$(dirname "$0")" && pwd)"
+
+# sanity checks
+if [ ! -x "${ENVPYTHON}" ]; then
+    echo "${ENVPYTHON}: 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}"
+
+DISTPYPI="${TOXINIDIR}/dist/pypi"
+mkdir -p "${DISTPYPI}"
+
+# create configure
+pushd "${TOXINIDIR}"
+if [ ! -f "configure" ]; then
+    autoreconf -i -f
+fi
+# (re)create Makefile
+./configure --disable-server
+popd
+
+case $FLAVOR in
+wheel_bundle)
+    # copy pylint plugin
+    cp "${TOXINIDIR}/pylint_plugins.py" "${ENVSITEPACKAGESDIR}"
+
+    # build packages and bundles
+    make -C "${TOXINIDIR}" \
+        wheel_bundle \
+        PYTHON="${ENVPYTHON}" \
+        IPA_EXTRA_WHEELS="$*"
+
+    # chdir to prevent local .egg-info from messing up pip
+    pushd "${ENVSITEPACKAGESDIR}"
+
+    # Install packages with dist/bundle/ as extra source for wheels while ignoring
+    # upstream Python Package Index.
+    $ENVPYTHON -m pip install \
+        --no-index \
+        --disable-pip-version-check \
+        --constraint "${TOXINIDIR}/.wheelconstraints" \
+        --find-links "${DISTBUNDLE}" \
+        $@
+
+    popd
+    ;;
+pypi_packages)
+    # build packages and bundles
+    make -C "${TOXINIDIR}" \
+        pypi_packages \
+        PYTHON="${ENVPYTHON}"
+
+    # chdir to prevent local .egg-info from messing up pip
+    pushd "${ENVSITEPACKAGESDIR}"
+
+    # Install packages from dist/pypi
+    $ENVPYTHON -m pip install \
+        --disable-pip-version-check \
+        --constraint "${TOXINIDIR}/.wheelconstraints" \
+        --find-links "${DISTPYPI}" \
+        $@
+
+    popd
+    ;;
+*)
+    echo "Unknown install flavor $FLAVOR"
+    exit 1
+    ;;
+esac
diff --git a/Makefile.am b/Makefile.am
index 1989b19..7608dfa 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
 
@@ -212,6 +213,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 \
@@ -254,6 +256,7 @@ endif  # WITH_JSLINT
 
 .PHONY: bdist_wheel wheel_bundle wheel_placeholder pypi_packages
 WHEELDISTDIR = $(top_builddir)/dist/wheels
+WHEELPYPIDIR = $(top_builddir)/dist/pypi
 WHEELBUNDLEDIR = $(top_builddir)/dist/bundle
 
 @MK_IFEQ@ ($(IPA_SERVER_WHEELS),1)
@@ -273,6 +276,9 @@ $(WHEELDISTDIR):
 $(WHEELBUNDLEDIR):
 	mkdir -p $(WHEELBUNDLEDIR)
 
+$(WHEELPYPIDIR):
+	mkdir -p $(WHEELPYPIDIR)
+
 bdist_wheel: $(WHEELDISTDIR)
 	rm -f $(foreach item,$(IPA_WHEEL_PACKAGES) ipatests,$(WHEELDISTDIR)/$(item)-*.whl)
 	export IPA_OMIT_INSTALL=$(IPA_OMIT_INSTALL); \
@@ -291,14 +297,20 @@ wheel_bundle: $(WHEELBUNDLEDIR) bdist_wheel .wheelconstraints
 	    --wheel-dir $(WHEELBUNDLEDIR) \
 	    $(IPA_WHEEL_PACKAGES) $(IPA_EXTRA_WHEELS)
 
-wheel_placeholder: $(WHEELDISTDIR)
+pypi_packages: $(WHEELPYPIDIR) .wheelconstraints
+	rm -f $(WHEELPYPIDIR)/*
+	for dir in $(IPACLIENT_SUBDIRS); do \
+	    $(MAKE) $(AM_MAKEFLAGS) \
+	        IPA_OMIT_INSTALL=1 WHEELDISTDIR="$(abspath $(WHEELPYPIDIR))" \
+	        -C $${dir} bdist_wheel || exit 1; \
+	done
 	for dir in $(IPA_PLACEHOLDERS); do \
-	    $(MAKE) $(AM_MAKEFLAGS) -C $(top_srcdir)/pypi/$${dir} bdist_wheel || exit 1; \
+	    $(MAKE) $(AM_MAKEFLAGS) \
+	        IPA_OMIT_INSTALL=1 WHEELDISTDIR="$(abspath $(WHEELPYPIDIR))" \
+	        -C $(top_srcdir)/pypi/$${dir} bdist_wheel || exit 1; \
 	done
-
-pypi_packages: bdist_wheel wheel_placeholder
 	@echo -e "\n\nTo upload packages to PyPI, run:\n"
-	@echo -e "    twine upload $(WHEELDISTDIR)/*-$(VERSION)-py2.py3-none-any.whl\n"
+	@echo -e "    twine upload $(WHEELPYPIDIR)/*-$(VERSION)-py2.py3-none-any.whl\n"
 
 .PHONY:
 strip-po:
diff --git a/configure.ac b/configure.ac
index 8f8751a..68601de 100644
--- a/configure.ac
+++ b/configure.ac
@@ -276,6 +276,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..a57bf7d 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/pypi/test_placeholder.py b/pypi/test_placeholder.py
new file mode 100644
index 0000000..d17b23a
--- /dev/null
+++ b/pypi/test_placeholder.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+# Copyright (C) 2017  FreeIPA Contributors see COPYING for license
+
+import importlib
+
+import pkg_resources
+
+import pytest
+
+@pytest.mark.parametrize("modname", [
+    # placeholder packages raise ImportError
+    'ipaplatform',
+    'ipaserver',
+    'ipatests',
+    # PyPI packages do not have install subpackage
+    'ipaclient.install',
+    'ipalib.install',
+    'ipapython.install',
+])
+def test_fail_import(modname):
+    try:
+        importlib.import_module(modname)
+    except ImportError:
+        pass
+    else:
+        pytest.fail("'import {}' does not fail".format(modname))
+
+
+@pytest.mark.parametrize("modname", [
+    'ipaclient',
+    'ipalib',
+    'ipapython',
+])
+def test_import(modname):
+    importlib.import_module(modname)
+
+
+@pytest.mark.parametrize("pkgname", [
+    'ipaclient',
+    'ipalib',
+    'ipaplatform',
+    'ipapython',
+    'ipaserver',
+    'ipatests',
+])
+def test_package_installed(pkgname):
+    pkg_resources.require(pkgname)
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..8823930
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,56 @@
+[tox]
+minversion=2.3.1
+envlist=py27,py35,py36,pylint2,pylint3,pypi
+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 wheel_bundle {envpython} {envsitepackagesdir} {packages}
+changedir={envdir}
+setenv=
+    HOME={envtmpdir}
+deps=
+    ipaclient
+    ipatests
+commands=
+    {envbindir}/ipa --help
+    {envpython} -m ipaclient --help
+    {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}
+
+[testenv:pypi]
+recreate=True
+install_command={toxinidir}/.tox-install.sh pypi_packages {envpython} {envsitepackagesdir} {packages}
+changedir={envdir}
+setenv=
+    HOME={envtmpdir}
+deps=
+    pytest
+    ipaclient
+    # placeholder
+    ipaplatform
+    ipaserver
+    ipatests
+commands=
+    {envpython} -m pytest {toxinidir}/pypi/test_placeholder.py
-- 
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