Skip to content

Commit b50bf7b

Browse files
committed
Change matplotlib's nose dependency to a test dependency
- add setup.py test command for test execution - add nose to test requirements
1 parent 4884115 commit b50bf7b

File tree

8 files changed

+172
-28
lines changed

8 files changed

+172
-28
lines changed

.travis.yml

+6-5
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ env:
2525
- secure: E7OCdqhZ+PlwJcn+Hd6ns9TDJgEUXiUNEI0wu7xjxB2vBRRIKtZMbuaZjd+iKDqCKuVOJKu0ClBUYxmgmpLicTwi34CfTUYt6D4uhrU+8hBBOn1iiK51cl/aBvlUUrqaRLVhukNEBGZcyqAjXSA/Qsnp2iELEmAfOUa92ZYo1sk=
2626
- secure: "dfjNqGKzQG5bu3FnDNwLG8H/C4QoieFo4PfFmZPdM2RY7WIzukwKFNT6kiDfOrpwt+2bR7FhzjOGlDECGtlGOtYPN8XuXGjhcP4a4IfakdbDfF+D3NPIpf5VlE6776k0VpvcZBTMYJKNFIMc7QPkOwjvNJ2aXyfe3hBuGlKJzQU="
2727
- BUILD_DOCS=false
28-
- TEST_ARGS=--no-pep8
2928
- NUMPY=numpy
3029
- NPROC=2
30+
- TEST_ARGS=--omit-pep8
3131

3232
language: python
3333

@@ -39,7 +39,7 @@ matrix:
3939
- python: 3.3
4040
- python: 3.4
4141
- python: 2.7
42-
env: TEST_ARGS=--pep8
42+
env: TEST_ARGS=--pep8-only
4343
- python: 2.7
4444
env: BUILD_DOCS=true
4545
- python: "nightly"
@@ -96,9 +96,10 @@ script:
9696
- |
9797
if [[ $BUILD_DOCS == false ]]; then
9898
export MPL_REPO_DIR=$PWD # needed for pep8-conformance test of the examples
99-
mkdir ../tmp_test_dir
100-
cd ../tmp_test_dir
101-
gdb -return-child-result -batch -ex r -ex bt --args python ../matplotlib/tests.py -s --processes=$NPROC --process-timeout=300 $TEST_ARGS
99+
# mkdir ../tmp_test_dir
100+
# cd ../tmp_test_dir
101+
# gdb -return-child-result -batch -ex r -ex bt --args python ../matplotlib/tests.py -s --processes=$NPROC --process-timeout=300 $TEST_ARGS
102+
gdb -return-child-result -batch -ex r -ex bt --args python setup.py test --nocapture --nose-verbose --processes=8 --process-timeout=300 $TEST_ARGS
102103
else
103104
cd doc
104105
python make.py html --small --warningsaserrors

Makefile

+2-2
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,10 @@ jdh_doc_snapshot:
4545

4646

4747
test:
48-
${PYTHON} tests.py
48+
${PYTHON} setup.py test
4949

5050

5151
test-coverage:
52-
${PYTHON} tests.py --with-coverage --cover-package=matplotlib
52+
${PYTHON} setup.py test --with-coverage --cover-package=matplotlib
5353

5454

README.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Testing
2020

2121
After installation, you can launch the test suite::
2222

23-
python tests.py
23+
python setup.py tests
2424

2525
Consider reading http://matplotlib.org/devel/coding_guide.html#testing for
2626
more information.

doc/devel/release_guide.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ A guide for developers who are doing a matplotlib release.
1313
Testing
1414
=======
1515

16-
* Run all of the regression tests by running the `tests.py` script at
17-
the root of the source tree.
16+
* Run all of the regression tests by running ``python setup.py tests`` script
17+
at the root of the source tree.
1818

1919
* Run :file:`unit/memleak_hawaii3.py` and make sure there are no
2020
memory leaks

doc/devel/testing.rst

+33-17
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,24 @@ Running the tests
3737
-----------------
3838

3939
Running the tests is simple. Make sure you have nose installed and run
40-
the script :file:`tests.py` in the root directory of the distribution.
41-
The script can take any of the usual `nosetest arguments`_, such as
40+
the setup script's ``test`` command::
4241

43-
=================== ===========
44-
``-v`` increase verbosity
45-
``-d`` detailed error messages
46-
``--with-coverage`` enable collecting coverage information
47-
=================== ===========
42+
python setup.py test
43+
44+
in the root directory of the distribution. The script takes a set of
45+
commands, such as:
46+
47+
======================== ===========
48+
``--pep8-only`` pep8 checks
49+
``--omit-pep8`` Do not perform pep8 checks
50+
``--nocapture`` do not capture stdout (nosetests)
51+
``--nose-verbose`` be verbose (nosetests)
52+
``--processes`` number of processes (nosetests)
53+
``--process-timeout`` process timeout (nosetests)
54+
``--with-coverage`` with coverage
55+
``--detailed-error-msg`` detailed error message (nosetest)
56+
``--tests`` comma separated selection of tests (nosetest)
57+
======================== ===========
4858

4959
Additionally it is possible to run only coding standard test or disable them:
5060

@@ -57,28 +67,34 @@ To run a single test from the command line, you can provide a
5767
dot-separated path to the module followed by the function separated by
5868
a colon, e.g., (this is assuming the test is installed)::
5969

60-
python tests.py matplotlib.tests.test_simplification:test_clipping
61-
62-
If you want to run the full test suite, but want to save wall time try running the
63-
tests in parallel::
70+
python setup.py test --tests=matplotlib.tests.test_simplification:test_clipping
6471

65-
python ../matplotlib/tests.py -sv --processes=5 --process-timeout=300
72+
If you want to run the full test suite, but want to save wall time try
73+
running the tests in parallel::
6674

67-
as we do on Travis.ci.
75+
python setup.py test --nocapture --nose-verbose --processes=5 --process-timeout=300
6876

6977

7078
An alternative implementation that does not look at command line
71-
arguments works from within Python::
79+
arguments works from within Python is to run the tests from the
80+
matplotlibs library function :func:`matplotlib.test`::
7281

7382
import matplotlib
7483
matplotlib.test()
7584

85+
.. hint::
86+
87+
You might need to install nose for this::
88+
89+
pip install nose
90+
91+
7692
.. _`nosetest arguments`: http://nose.readthedocs.org/en/latest/usage.html
7793

7894

79-
Running tests by any means other than `matplotlib.test()`
80-
does not load the nose "knownfailureif" (Known failing tests) plugin,
81-
causing known-failing tests to fail for real.
95+
Running tests by any means other than `matplotlib.test()` does not
96+
load the nose "knownfailureif" (Known failing tests) plugin, causing
97+
known-failing tests to fail for real.
8298

8399
Writing a simple test
84100
---------------------

setup.py

+120
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
# This needs to be the very first thing to use distribute
99
from distribute_setup import use_setuptools
1010
use_setuptools()
11+
from setuptools.command.test import test as TestCommand
1112

1213
import sys
1314

@@ -121,6 +122,121 @@
121122
'Topic :: Scientific/Engineering :: Visualization',
122123
]
123124

125+
126+
class NoseTestCommand(TestCommand):
127+
"""Invoke unit tests using nose after an in-place build."""
128+
129+
description = "Invoke unit tests using nose after an in-place build."
130+
user_options = [
131+
("pep8-only", None, "pep8 checks"),
132+
("omit-pep8", None, "Do not perform pep8 checks"),
133+
("nocapture", None, "do not capture stdout (nosetests)"),
134+
("nose-verbose", None, "be verbose (nosetests)"),
135+
("processes=", None, "number of processes (nosetests)"),
136+
("process-timeout=", None, "process timeout (nosetests)"),
137+
("with-coverage", None, "with coverage"),
138+
("detailed-error-msg", None, "detailed error message (nosetest)"),
139+
("tests=", None, "comma separated selection of tests (nosetest)"),
140+
]
141+
142+
def initialize_options(self):
143+
self.pep8_only = None
144+
self.omit_pep8 = None
145+
146+
# parameters passed to nose tests
147+
self.processes = None
148+
self.process_timeout = None
149+
self.nose_verbose = None
150+
self.nocapture = None
151+
self.with_coverage = None
152+
self.detailed_error_msg = None
153+
self.tests = None
154+
155+
def finalize_options(self):
156+
self.test_args = []
157+
if self.pep8_only:
158+
self.pep8_only = True
159+
if self.omit_pep8:
160+
self.omit_pep8 = True
161+
162+
if self.pep8_only and self.omit_pep8:
163+
from distutils.errors import DistutilsOptionError
164+
raise DistutilsOptionError(
165+
"You are using several options for the test command in an "
166+
"incompatible manner. Please use either one of --pep8-only,"
167+
"--omit-pep8"
168+
)
169+
170+
if self.processes:
171+
self.test_args.append("--processes={prc}".format(
172+
prc=self.processes))
173+
174+
if self.process_timeout:
175+
self.test_args.append("--process-timeout={tout}".format(
176+
tout=self.process_timeout))
177+
178+
if self.nose_verbose:
179+
self.test_args.append("--verbose")
180+
181+
if self.nocapture:
182+
self.test_args.append("--nocapture")
183+
184+
if self.with_coverage:
185+
self.test_args.append("--with-coverage")
186+
187+
if self.detailed_error_msg:
188+
self.test_args.append("-d")
189+
190+
if self.tests:
191+
self.test_args.append("--tests={names}".format(names=self.tests))
192+
193+
194+
def run(self):
195+
if self.distribution.install_requires:
196+
self.distribution.fetch_build_eggs(
197+
self.distribution.install_requires)
198+
if self.distribution.tests_require:
199+
self.distribution.fetch_build_eggs(self.distribution.tests_require)
200+
201+
self.announce('running unittests with nose')
202+
self.with_project_on_sys_path(self.run_tests)
203+
204+
205+
def run_tests(self):
206+
try:
207+
import matplotlib
208+
matplotlib.use('agg')
209+
import nose
210+
from matplotlib.testing.noseclasses import KnownFailure
211+
from matplotlib import default_test_modules as testmodules
212+
from matplotlib import font_manager
213+
import time
214+
# Make sure the font caches are created before starting any possibly
215+
# parallel tests
216+
if font_manager._fmcache is not None:
217+
while not os.path.exists(font_manager._fmcache):
218+
time.sleep(0.5)
219+
plugins = [KnownFailure]
220+
221+
# Nose doesn't automatically instantiate all of the plugins in the
222+
# child processes, so we have to provide the multiprocess plugin
223+
# with a list.
224+
from nose.plugins import multiprocess
225+
multiprocess._instantiate_plugins = plugins
226+
227+
if self.omit_pep8:
228+
testmodules.remove('matplotlib.tests.test_coding_standards')
229+
elif self.pep8_only:
230+
testmodules = ['matplotlib.tests.test_coding_standards']
231+
232+
nose.main(addplugins=[x() for x in plugins],
233+
defaultTest=testmodules,
234+
argv=['nosetests'] + self.test_args,
235+
exit=False)
236+
except ImportError:
237+
sys.exit(-1)
238+
239+
124240
# One doesn't normally see `if __name__ == '__main__'` blocks in a setup.py,
125241
# however, this is needed on Windows to avoid creating infinite subprocesses
126242
# when using multiprocessing.
@@ -135,6 +251,7 @@
135251
package_dir = {'': 'lib'}
136252
install_requires = []
137253
setup_requires = []
254+
tests_require = []
138255
default_backend = None
139256

140257
# Go through all of the packages and figure out which ones we are
@@ -195,6 +312,7 @@
195312
package_data[key] = list(set(val + package_data[key]))
196313
install_requires.extend(package.get_install_requires())
197314
setup_requires.extend(package.get_setup_requires())
315+
tests_require.extend(package.get_tests_require())
198316

199317
# Write the default matplotlibrc file
200318
if default_backend is None:
@@ -254,11 +372,13 @@
254372
# List third-party Python packages that we require
255373
install_requires=install_requires,
256374
setup_requires=setup_requires,
375+
tests_require=tests_require,
257376

258377
# matplotlib has C/C++ extensions, so it's not zip safe.
259378
# Telling setuptools this prevents it from doing an automatic
260379
# check for zip safety.
261380
zip_safe=False,
381+
cmdclass={'test': NoseTestCommand},
262382

263383
**extra_args
264384
)

setupext.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,12 @@ def get_setup_requires(self):
415415
"""
416416
return []
417417

418+
def get_tests_require(self):
419+
"""
420+
Get a list of Python packages that we require for executing tests.
421+
"""
422+
return []
423+
418424
def _check_for_pkg_config(self, package, include_file, min_version=None,
419425
version=None):
420426
"""
@@ -694,7 +700,7 @@ def get_package_data(self):
694700
'sphinxext/tests/tinypages/_static/*',
695701
]}
696702

697-
def get_install_requires(self):
703+
def get_tests_require(self):
698704
requires = ['nose>=%s' % self.nose_min_version]
699705
if not sys.version_info >= (3, 3):
700706
requires += ['mock']

tox.ini

+1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ commands =
1313
{envpython} {toxinidir}/tests.py --processes=-1 --process-timeout=300
1414
deps =
1515
nose
16+
mock
1617
numpy

0 commit comments

Comments
 (0)