From ee141f20a5e924497664a22e55c7244274a6ffa0 Mon Sep 17 00:00:00 2001 From: Holger Peters Date: Thu, 16 Oct 2014 16:50:11 +0200 Subject: [PATCH 01/12] Nose and Mock as tests_require deps, test command - require nose and mock for tests, not for normal installation - implement a test command in setup.py for invoking tests --- setup.py | 47 +++++++++++++++++++++++++++++++++++++++++++++++ setupext.py | 8 +++++++- tox.ini | 1 + 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index aeeb5816f38b..2aa1f3b043dd 100644 --- a/setup.py +++ b/setup.py @@ -122,6 +122,49 @@ 'Topic :: Scientific/Engineering :: Visualization', ] +from setuptools.command.test import test as TestCommand +class NoseTestCommand(TestCommand): + def finalize_options(self): + TestCommand.finalize_options(self) + self.test_args = [] + self.test_suite = True + + def run_tests(self): + try: + import matplotlib + matplotlib.use('agg') + import nose + from matplotlib.testing.noseclasses import KnownFailure + from matplotlib import default_test_modules + from matplotlib import font_manager + import time + # Make sure the font caches are created before starting any possibly + # parallel tests + if font_manager._fmcache is not None: + while not os.path.exists(font_manager._fmcache): + time.sleep(0.5) + plugins = [KnownFailure] + + # Nose doesn't automatically instantiate all of the plugins in the + # child processes, so we have to provide the multiprocess plugin + # with a list. + from nose.plugins import multiprocess + multiprocess._instantiate_plugins = plugins + + if '--no-pep8' in sys.argv: + default_test_modules.remove('matplotlib.tests.test_coding_standards') + sys.argv.remove('--no-pep8') + elif '--pep8' in sys.argv: + default_test_modules = ['matplotlib.tests.test_coding_standards'] + sys.argv.remove('--pep8') + nose.main(addplugins=[x() for x in plugins], + defaultTest=default_test_modules, + argv=['nosetests'], + exit=False) + except ImportError: + sys.exit(-1) + + # One doesn't normally see `if __name__ == '__main__'` blocks in a setup.py, # however, this is needed on Windows to avoid creating infinite subprocesses # when using multiprocessing. @@ -136,6 +179,7 @@ package_dir = {'': 'lib'} install_requires = [] setup_requires = [] + tests_require = [] default_backend = None @@ -199,6 +243,7 @@ package_data[key] = list(set(val + package_data[key])) install_requires.extend(package.get_install_requires()) setup_requires.extend(package.get_setup_requires()) + tests_require.extend(package.get_tests_require()) # Write the default matplotlibrc file if default_backend is None: @@ -259,11 +304,13 @@ # List third-party Python packages that we require install_requires=install_requires, setup_requires=setup_requires, + tests_require=tests_require, # matplotlib has C/C++ extensions, so it's not zip safe. # Telling setuptools this prevents it from doing an automatic # check for zip safety. zip_safe=False, + cmdclass={'test': NoseTestCommand}, **extra_args ) diff --git a/setupext.py b/setupext.py index 0d0624b31333..3ac39352bd0b 100755 --- a/setupext.py +++ b/setupext.py @@ -414,6 +414,12 @@ def get_setup_requires(self): """ return [] + def get_tests_require(self): + """ + Get a list of Python packages that we require for executing tests. + """ + return [] + def _check_for_pkg_config(self, package, include_file, min_version=None, version=None): """ @@ -688,7 +694,7 @@ def get_package_data(self): 'tests/test_rcparams.rc' ]} - def get_install_requires(self): + def get_tests_require(self): requires = ['nose>=%s' % self.nose_min_version] if not sys.version_info >= (3, 3): requires += ['mock'] diff --git a/tox.ini b/tox.ini index b34e6ea7af13..296cefb56281 100644 --- a/tox.ini +++ b/tox.ini @@ -13,4 +13,5 @@ commands = {envpython} {toxinidir}/tests.py --processes=-1 --process-timeout=300 deps = nose + mock numpy From 896fa87252ff744e401355705e3d65b9e5ea4fe1 Mon Sep 17 00:00:00 2001 From: Holger Peters Date: Fri, 17 Oct 2014 17:11:10 +0200 Subject: [PATCH 02/12] Feature test command in readme --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 6e01ea7275cc..af3dbdd95b17 100644 --- a/README.rst +++ b/README.rst @@ -20,7 +20,7 @@ Testing After installation, you can launch the test suite:: - python tests.py + python setup.py tests Consider reading http://matplotlib.org/devel/coding_guide.html#testing for more information. From 3fc933147ab85852afc5e4dcc83cf545acea5225 Mon Sep 17 00:00:00 2001 From: Holger Peters Date: Tue, 21 Oct 2014 10:39:23 +0200 Subject: [PATCH 03/12] Rework test command for matplotlib - specifiy parameters properly using user_options - add parameters used by travis to be passed on to nose --- setup.py | 76 +++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 12 deletions(-) diff --git a/setup.py b/setup.py index 2aa1f3b043dd..9690a35057a6 100644 --- a/setup.py +++ b/setup.py @@ -8,6 +8,7 @@ # This needs to be the very first thing to use distribute from distribute_setup import use_setuptools use_setuptools() +from setuptools.command.test import test as TestCommand import sys @@ -122,12 +123,64 @@ 'Topic :: Scientific/Engineering :: Visualization', ] -from setuptools.command.test import test as TestCommand + class NoseTestCommand(TestCommand): + """Invoke unit tests using nose after an in-place build.""" + + description = "Invoke unit tests using nose after an in-place build." + user_options = [ + ("pep8-only", None, "pep8 checks"), + ("omit-pep8", None, "Do not perform pep8 checks"), + ("no-capture", None, "do not capture stdout (nosetests)"), + ("nose-verbose", None, "be verbose (nosetests)"), + ("processes=", None, "number of processes (nosetests)"), + ("process-timeout=", None, "process timeout (nosetests)"), + ] + + def initialize_options(self): + self.pep8_only = None + self.omit_pep8 = None + + # parameters passed to nose tests + self.processes = None + self.process_timeout = None + self.nose_verbose = None + def finalize_options(self): - TestCommand.finalize_options(self) self.test_args = [] - self.test_suite = True + if self.pep8_only: + self.pep8_only = True + if self.omit_pep8: + self.omit_pep8 = True + + if self.pep8_only and self.omit_pep8: + from distutils.errors import DistutilsOptionError + raise DistutilsOptionError( + "You are using several options for the test command in an " + "incompatible manner. Please use either one of --pep8-only," + "--omit-pep8" + ) + + if self.processes: + self.test_args.append("--processes={}".format(self.processes)) + + if self.process_timeout: + self.test_args.append("--process-timeout={}".format( + self.process_timeout)) + + if self.nose_verbose: + self.test_args.append("--verbose") + + def run(self): + if self.distribution.install_requires: + self.distribution.fetch_build_eggs( + self.distribution.install_requires) + if self.distribution.tests_require: + self.distribution.fetch_build_eggs(self.distribution.tests_require) + + self.announce('running unittests with nose') + self.with_project_on_sys_path(self.run_tests) + def run_tests(self): try: @@ -135,7 +188,7 @@ def run_tests(self): matplotlib.use('agg') import nose from matplotlib.testing.noseclasses import KnownFailure - from matplotlib import default_test_modules + from matplotlib import default_test_modules as testmodules from matplotlib import font_manager import time # Make sure the font caches are created before starting any possibly @@ -151,15 +204,14 @@ def run_tests(self): from nose.plugins import multiprocess multiprocess._instantiate_plugins = plugins - if '--no-pep8' in sys.argv: - default_test_modules.remove('matplotlib.tests.test_coding_standards') - sys.argv.remove('--no-pep8') - elif '--pep8' in sys.argv: - default_test_modules = ['matplotlib.tests.test_coding_standards'] - sys.argv.remove('--pep8') + if self.omit_pep8: + testmodules.remove('matplotlib.tests.test_coding_standards') + elif self.pep8_only: + testmodules = ['matplotlib.tests.test_coding_standards'] + nose.main(addplugins=[x() for x in plugins], - defaultTest=default_test_modules, - argv=['nosetests'], + defaultTest=testmodules, + argv=['nosetests'] + self.test_args, exit=False) except ImportError: sys.exit(-1) From a992a31257a876944c1aa58893de36b1dfc81638 Mon Sep 17 00:00:00 2001 From: Holger Peters Date: Tue, 21 Oct 2014 11:16:13 +0200 Subject: [PATCH 04/12] Travis should use setup.py --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index fefdf90e7aeb..5ca49e5722f1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ env: - secure: RgJI7BBL8aX5FTOQe7xiXqWHMxWokd6GNUWp1NUV2mRLXPb9dI0RXqZt3UJwKTAzf1z/OtlHDmEkBoTVK81E9iUxK5npwyyjhJ8yTJmwfQtQF2n51Q1Ww9p+XSLORrOzZc7kAo6Kw6FIXN1pfctgYq2bQkrwJPRx/oPR8f6hcbY= - secure: E7OCdqhZ+PlwJcn+Hd6ns9TDJgEUXiUNEI0wu7xjxB2vBRRIKtZMbuaZjd+iKDqCKuVOJKu0ClBUYxmgmpLicTwi34CfTUYt6D4uhrU+8hBBOn1iiK51cl/aBvlUUrqaRLVhukNEBGZcyqAjXSA/Qsnp2iELEmAfOUa92ZYo1sk= - BUILD_DOCS=false - - TEST_ARGS=--no-pep8 + - TEST_ARGS=--omit-pep8 language: python @@ -16,7 +16,7 @@ matrix: - python: 3.3 - python: 3.4 - python: 2.7 - env: TEST_ARGS=--pep8 + env: TEST_ARGS=--pep8-only - python: 2.7 env: BUILD_DOCS=true @@ -38,7 +38,7 @@ script: - python -c "from matplotlib import font_manager" - if [[ $BUILD_DOCS == false ]]; then mkdir ../tmp_test_dir; fi - if [[ $BUILD_DOCS == false ]]; then cd ../tmp_test_dir; fi - - if [[ $BUILD_DOCS == false ]]; then python ../matplotlib/tests.py -sv --processes=8 --process-timeout=300 $TEST_ARGS; fi + - if [[ $BUILD_DOCS == false ]]; then python ../matplotlib/setup.py --nocapture --verbose --processes=8 --process-timeout=300 $TEST_ARGS; fi - if [[ $BUILD_DOCS == true ]]; then cd doc; python make.py html --small; fi # We don't build the LaTeX docs here, so linkchecker will complain - if [[ $BUILD_DOCS == true ]]; then touch build/html/Matplotlib.pdf; fi From 97d57530f028c23792baffcdc764de8ec2662718 Mon Sep 17 00:00:00 2001 From: Holger Peters Date: Tue, 21 Oct 2014 15:08:39 +0200 Subject: [PATCH 05/12] do not switch directories for tests --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5ca49e5722f1..3d477183b286 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,8 +36,6 @@ script: # Generate the font caches in a single process before starting the # multiple processes - python -c "from matplotlib import font_manager" - - if [[ $BUILD_DOCS == false ]]; then mkdir ../tmp_test_dir; fi - - if [[ $BUILD_DOCS == false ]]; then cd ../tmp_test_dir; fi - if [[ $BUILD_DOCS == false ]]; then python ../matplotlib/setup.py --nocapture --verbose --processes=8 --process-timeout=300 $TEST_ARGS; fi - if [[ $BUILD_DOCS == true ]]; then cd doc; python make.py html --small; fi # We don't build the LaTeX docs here, so linkchecker will complain From e0bfc75d95bf208a08033ca4940593d4b4342913 Mon Sep 17 00:00:00 2001 From: Holger Peters Date: Tue, 21 Oct 2014 15:13:37 +0200 Subject: [PATCH 06/12] fix path to setup.py --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3d477183b286..de56bbe4e432 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,7 +36,7 @@ script: # Generate the font caches in a single process before starting the # multiple processes - python -c "from matplotlib import font_manager" - - if [[ $BUILD_DOCS == false ]]; then python ../matplotlib/setup.py --nocapture --verbose --processes=8 --process-timeout=300 $TEST_ARGS; fi + - if [[ $BUILD_DOCS == false ]]; then python setup.py --nocapture --verbose --processes=8 --process-timeout=300 $TEST_ARGS; fi - if [[ $BUILD_DOCS == true ]]; then cd doc; python make.py html --small; fi # We don't build the LaTeX docs here, so linkchecker will complain - if [[ $BUILD_DOCS == true ]]; then touch build/html/Matplotlib.pdf; fi From ace56df47401c18677cb4338c836c823eff6ce57 Mon Sep 17 00:00:00 2001 From: Holger Peters Date: Tue, 21 Oct 2014 15:22:18 +0200 Subject: [PATCH 07/12] fixup travis test invocation --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index de56bbe4e432..17cd199fc587 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,7 +36,7 @@ script: # Generate the font caches in a single process before starting the # multiple processes - python -c "from matplotlib import font_manager" - - if [[ $BUILD_DOCS == false ]]; then python setup.py --nocapture --verbose --processes=8 --process-timeout=300 $TEST_ARGS; fi + - if [[ $BUILD_DOCS == false ]]; then python setup.py test --nocapture --verbose --processes=8 --process-timeout=300 $TEST_ARGS; fi - if [[ $BUILD_DOCS == true ]]; then cd doc; python make.py html --small; fi # We don't build the LaTeX docs here, so linkchecker will complain - if [[ $BUILD_DOCS == true ]]; then touch build/html/Matplotlib.pdf; fi From 4c65d50df73fca4e15fa49e53d1d9805ea081a12 Mon Sep 17 00:00:00 2001 From: Holger Peters Date: Tue, 21 Oct 2014 15:33:40 +0200 Subject: [PATCH 08/12] fix python setup.py test arguments --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 17cd199fc587..375f911c710b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,7 +36,7 @@ script: # Generate the font caches in a single process before starting the # multiple processes - python -c "from matplotlib import font_manager" - - if [[ $BUILD_DOCS == false ]]; then python setup.py test --nocapture --verbose --processes=8 --process-timeout=300 $TEST_ARGS; fi + - if [[ $BUILD_DOCS == false ]]; then python setup.py test --no-capture --nose-verbose --processes=8 --process-timeout=300 $TEST_ARGS; fi - if [[ $BUILD_DOCS == true ]]; then cd doc; python make.py html --small; fi # We don't build the LaTeX docs here, so linkchecker will complain - if [[ $BUILD_DOCS == true ]]; then touch build/html/Matplotlib.pdf; fi From cfbae336230d033502da87c758657a20602787a4 Mon Sep 17 00:00:00 2001 From: Holger Peters Date: Tue, 21 Oct 2014 15:52:14 +0200 Subject: [PATCH 09/12] fixup test command argument "--nocapture" --- .travis.yml | 2 +- setup.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 375f911c710b..e885924c7489 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,7 +36,7 @@ script: # Generate the font caches in a single process before starting the # multiple processes - python -c "from matplotlib import font_manager" - - if [[ $BUILD_DOCS == false ]]; then python setup.py test --no-capture --nose-verbose --processes=8 --process-timeout=300 $TEST_ARGS; fi + - if [[ $BUILD_DOCS == false ]]; then python setup.py test --nocapture --nose-verbose --processes=8 --process-timeout=300 $TEST_ARGS; fi - if [[ $BUILD_DOCS == true ]]; then cd doc; python make.py html --small; fi # We don't build the LaTeX docs here, so linkchecker will complain - if [[ $BUILD_DOCS == true ]]; then touch build/html/Matplotlib.pdf; fi diff --git a/setup.py b/setup.py index 9690a35057a6..fa2286b4bdd1 100644 --- a/setup.py +++ b/setup.py @@ -131,7 +131,7 @@ class NoseTestCommand(TestCommand): user_options = [ ("pep8-only", None, "pep8 checks"), ("omit-pep8", None, "Do not perform pep8 checks"), - ("no-capture", None, "do not capture stdout (nosetests)"), + ("nocapture", None, "do not capture stdout (nosetests)"), ("nose-verbose", None, "be verbose (nosetests)"), ("processes=", None, "number of processes (nosetests)"), ("process-timeout=", None, "process timeout (nosetests)"), @@ -145,6 +145,7 @@ def initialize_options(self): self.processes = None self.process_timeout = None self.nose_verbose = None + self.nocapture = None def finalize_options(self): self.test_args = [] @@ -171,6 +172,9 @@ def finalize_options(self): if self.nose_verbose: self.test_args.append("--verbose") + if self.nocapture: + self.test_args.append("--nocapture") + def run(self): if self.distribution.install_requires: self.distribution.fetch_build_eggs( From e3d786c0dddfae012e14ef800bd02b9799d9fe71 Mon Sep 17 00:00:00 2001 From: Holger Peters Date: Tue, 21 Oct 2014 16:19:45 +0200 Subject: [PATCH 10/12] fix format for python 2.6 --- setup.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index fa2286b4bdd1..fe48ed777819 100644 --- a/setup.py +++ b/setup.py @@ -163,10 +163,11 @@ def finalize_options(self): ) if self.processes: - self.test_args.append("--processes={}".format(self.processes)) + self.test_args.append("--processes={prc}".format( + prc=self.processes)) if self.process_timeout: - self.test_args.append("--process-timeout={}".format( + self.test_args.append("--process-timeout={tout}".format( self.process_timeout)) if self.nose_verbose: From 98d462f1336c31b6b4c7c01fb2878b9006296ff6 Mon Sep 17 00:00:00 2001 From: Holger Peters Date: Tue, 21 Oct 2014 16:34:25 +0200 Subject: [PATCH 11/12] fixup of python2.6 compatible formatting --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index fe48ed777819..4eec84ae1919 100644 --- a/setup.py +++ b/setup.py @@ -168,7 +168,7 @@ def finalize_options(self): if self.process_timeout: self.test_args.append("--process-timeout={tout}".format( - self.process_timeout)) + tout=self.process_timeout)) if self.nose_verbose: self.test_args.append("--verbose") From 6fb68ad1de2a75c2bb292239c9a3c58af51e18fc Mon Sep 17 00:00:00 2001 From: Holger Peters Date: Tue, 21 Oct 2014 18:11:24 +0200 Subject: [PATCH 12/12] Removed tests.py in favor of python setup.py test --- tests.py | 50 -------------------------------------------------- 1 file changed, 50 deletions(-) delete mode 100755 tests.py diff --git a/tests.py b/tests.py deleted file mode 100755 index d27055c75257..000000000000 --- a/tests.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python -# -# This allows running the matplotlib tests from the command line: e.g. -# -# $ python tests.py -v -d -# -# The arguments are identical to the arguments accepted by nosetests. -# -# See https://nose.readthedocs.org/ for a detailed description of -# these options. - -import os -import sys -import time - -import matplotlib -matplotlib.use('agg') - -import nose -from matplotlib.testing.noseclasses import KnownFailure -from matplotlib import default_test_modules - -from matplotlib import font_manager -# Make sure the font caches are created before starting any possibly -# parallel tests -if font_manager._fmcache is not None: - while not os.path.exists(font_manager._fmcache): - time.sleep(0.5) - -plugins = [KnownFailure] - -# Nose doesn't automatically instantiate all of the plugins in the -# child processes, so we have to provide the multiprocess plugin with -# a list. -from nose.plugins import multiprocess -multiprocess._instantiate_plugins = plugins - -def run(): - nose.main(addplugins=[x() for x in plugins], - defaultTest=default_test_modules) - -if __name__ == '__main__': - if '--no-pep8' in sys.argv: - default_test_modules.remove('matplotlib.tests.test_coding_standards') - sys.argv.remove('--no-pep8') - elif '--pep8' in sys.argv: - default_test_modules = ['matplotlib.tests.test_coding_standards'] - sys.argv.remove('--pep8') - - run()