From f183922c613d0150d151aefe8670dfdf765a61fe Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 25 Jun 2014 15:01:47 -0700 Subject: [PATCH 1/8] Make nose run tests in parallel --- .travis.yml | 2 +- test-installed.py | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4de1fde99..c4964e166 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ language: python notifications: irc: "chat.freenode.net#pil" -env: MAX_CONCURRENCY=4 +env: MAX_CONCURRENCY=4 NOSE_PROCESSES=4 NOSE_PROCESS_TIMEOUT=30 python: - "pypy" diff --git a/test-installed.py b/test-installed.py index 7f58f4966..8e4ab84a2 100755 --- a/test-installed.py +++ b/test-installed.py @@ -20,5 +20,13 @@ if len(sys.argv) == 1: if ('--no-path-adjustment' not in sys.argv) and ('-P' not in sys.argv): sys.argv.insert(1, '--no-path-adjustment') +if 'NOSE_PROCESSES' not in os.environ: + for arg in sys.argv: + if '--processes' in arg: + break + else: # for + sys.argv.insert(1, '--processes=-1') # -1 == number of cores + sys.argv.insert(1, '--process-timeout=30') + if __name__ == '__main__': nose.main() From 3c40ab48a92aa7582f9bdb16313cf511956ebc4f Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 25 Jun 2014 17:08:24 -0700 Subject: [PATCH 2/8] Thread and race condition safe tempfiles for testing --- Tests/helper.py | 70 +++++++++++++----------------------------- Tests/test_font_pcf.py | 3 +- 2 files changed, 24 insertions(+), 49 deletions(-) diff --git a/Tests/helper.py b/Tests/helper.py index aacbfc009..c76d3fa16 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -3,37 +3,30 @@ Helper functions. """ from __future__ import print_function import sys +import tempfile +import os +import glob if sys.version_info[:2] <= (2, 6): import unittest2 as unittest else: import unittest - def tearDownModule(): - import glob - import os - import tempfile - temp_root = os.path.join(tempfile.gettempdir(), 'pillow-tests') - tempfiles = glob.glob(os.path.join(temp_root, "temp_*")) - if tempfiles: - print("===", "remaining temporary files") - for file in tempfiles: - print(file) - print("-"*68) - + #remove me later + pass class PillowTestCase(unittest.TestCase): - currentResult = None # holds last result object passed to run method - _tempfiles = [] + def __init__(self, *args, **kwargs): + unittest.TestCase.__init__(self, *args, **kwargs) + self.currentResult = None # holds last result object passed to run method def run(self, result=None): - self.addCleanup(self.delete_tempfiles) self.currentResult = result # remember result for use later unittest.TestCase.run(self, result) # call superclass run method - def delete_tempfiles(self): + def delete_tempfile(self, path): try: ok = self.currentResult.wasSuccessful() except AttributeError: # for nosetests @@ -42,19 +35,14 @@ class PillowTestCase(unittest.TestCase): if ok: # only clean out tempfiles if test passed - import os - import os.path - import tempfile - for file in self._tempfiles: - try: - os.remove(file) - except OSError: - pass # report? - temp_root = os.path.join(tempfile.gettempdir(), 'pillow-tests') try: - os.rmdir(temp_root) + print("Removing File: %s" % path) + os.remove(path) except OSError: - pass + pass # report? + else: + print("=== orphaned temp file") + print(path) def assert_almost_equal(self, a, b, msg=None, eps=1e-6): self.assertLess( @@ -138,27 +126,13 @@ class PillowTestCase(unittest.TestCase): self.assertTrue(found) return result - def tempfile(self, template, *extra): - import os - import os.path - import sys - import tempfile - files = [] - root = os.path.join(tempfile.gettempdir(), 'pillow-tests') - try: - os.mkdir(root) - except OSError: - pass - for temp in (template,) + extra: - assert temp[:5] in ("temp.", "temp_") - name = os.path.basename(sys.argv[0]) - name = temp[:4] + os.path.splitext(name)[0][4:] - name = name + "_%d" % len(self._tempfiles) + temp[4:] - name = os.path.join(root, name) - files.append(name) - self._tempfiles.extend(files) - return files[0] - + def tempfile(self, template): + assert template[:5] in ("temp.", "temp_") + (fd, path) = tempfile.mkstemp(template[4:], template[:4]) + os.close(fd) + + self.addCleanup(self.delete_tempfile, path) + return path # # require that deprecation warnings are triggered # import warnings diff --git a/Tests/test_font_pcf.py b/Tests/test_font_pcf.py index 24abcf73d..8c4c04cd4 100644 --- a/Tests/test_font_pcf.py +++ b/Tests/test_font_pcf.py @@ -22,7 +22,8 @@ class TestFontPcf(PillowTestCase): self.assertIsInstance(font, FontFile.FontFile) self.assertEqual(len([_f for _f in font.glyph if _f]), 192) - tempname = self.tempfile("temp.pil", "temp.pbm") + tempname = self.tempfile("temp.pil") + self.addCleanup(self.delete_tempfile, tempname[:-4]+'.pbm') font.save(tempname) return tempname From 2b4de326ae069b2221ba061b088c198c4ea00578 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 25 Jun 2014 22:47:21 -0700 Subject: [PATCH 3/8] Cleaned up prints --- Tests/helper.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Tests/helper.py b/Tests/helper.py index c76d3fa16..9068802f6 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -36,13 +36,11 @@ class PillowTestCase(unittest.TestCase): if ok: # only clean out tempfiles if test passed try: - print("Removing File: %s" % path) os.remove(path) except OSError: pass # report? else: - print("=== orphaned temp file") - print(path) + print("=== orphaned temp file: %s" %path) def assert_almost_equal(self, a, b, msg=None, eps=1e-6): self.assertLess( From 1f7c4fa98ca73924eb3d116eec7bc06486888dae Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 26 Jun 2014 23:09:51 -0700 Subject: [PATCH 4/8] put coverage in parallel mode --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c4964e166..de4777198 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,9 +36,10 @@ script: # Cover the others - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then time coverage run --append --include=PIL/* selftest.py; fi - - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then time coverage run --append --include=PIL/* -m nose Tests/test_*.py; fi + - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then time coverage run --parallel-mode --append --include=PIL/* -m nose Tests/test_*.py; fi after_success: + - coverage combine - coverage report - coveralls - pip install pep8 pyflakes From b69057001f4b47342dedd7685dc24eafec450f67 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 26 Jun 2014 23:22:20 -0700 Subject: [PATCH 5/8] Parallel mode, no append for coverage --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index de4777198..36ca20b06 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,10 +35,11 @@ script: - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then time nosetests Tests/test_*.py; fi # Cover the others - - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then time coverage run --append --include=PIL/* selftest.py; fi - - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then time coverage run --parallel-mode --append --include=PIL/* -m nose Tests/test_*.py; fi + - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then time coverage run --parallel-mode --include=PIL/* selftest.py; fi + - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then time coverage run --parallel-mode --include=PIL/* -m nose Tests/test_*.py; fi after_success: + - ls -l .coverage* - coverage combine - coverage report - coveralls From 63228dd974a3c0ef505420cd17282ab3240328aa Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 27 Jun 2014 11:11:11 -0700 Subject: [PATCH 6/8] use nose-cov plugin instead of coverage for multithread testing --- .travis.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 36ca20b06..3a8d8653f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,7 @@ python: install: - "sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-qt4 ghostscript libffi-dev cmake" - "pip install cffi" - - "pip install coveralls nose" + - "pip install coveralls nose nose-cov" - if [ "$TRAVIS_PYTHON_VERSION" == "2.6" ]; then pip install unittest2; fi # webp @@ -35,8 +35,9 @@ script: - if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then time nosetests Tests/test_*.py; fi # Cover the others - - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then time coverage run --parallel-mode --include=PIL/* selftest.py; fi - - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then time coverage run --parallel-mode --include=PIL/* -m nose Tests/test_*.py; fi + - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then coverage run --parallel-mode --include=PIL/* selftest.py; fi + # write html report, then ignore. Coverage needs to be combined first + - if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then nosetests --with-cov --cov='PIL/' --cov-report=html Tests/test_*.py; fi after_success: - ls -l .coverage* From 80e5e0a4140f2971ea7f8aab82cfcc9415e112ae Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 27 Jun 2014 12:07:34 -0700 Subject: [PATCH 7/8] Ignore debugging blocks --- .coveragerc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.coveragerc b/.coveragerc index bd93c4749..87e3e968f 100644 --- a/.coveragerc +++ b/.coveragerc @@ -9,3 +9,6 @@ exclude_lines = # Don't complain if non-runnable code isn't run: if 0: if __name__ == .__main__.: + # Don't complain about debug code + if Image.DEBUG: + if DEBUG: \ No newline at end of file From ced8a95dccf8a9b2d8e14bbd7254708555f697e8 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 27 Jun 2014 12:07:53 -0700 Subject: [PATCH 8/8] Help for testing --- Makefile | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/Makefile b/Makefile index a226ea602..eee9462fd 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,37 @@ + + pre: bin/python setup.py develop bin/python selftest.py check-manifest pyroma . viewdoc + +clean: + python setup.py clean + rm PIL/*.so || true + find . -name __pycache__ | xargs rm -r + +install: + python setup.py install + python selftest.py --installed + +test: install + python test-installed.py + +inplace: clean + python setup.py build_ext --inplace + +coverage: +# requires nose-cov + coverage erase + coverage run --parallel-mode --include=PIL/* selftest.py + nosetests --with-cov --cov='PIL/' --cov-report=html Tests/test_*.py +# doesn't combine properly before report, +# writing report instead of displaying invalid report + rm -r htmlcov || true + coverage combine + coverage report + +test-dep: + pip install coveralls nose nose-cov pep8 pyflakes