From 118045abf007e26853c4f6af898a6c3e5f541e43 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Sat, 6 Feb 2016 20:59:00 +0000 Subject: [PATCH 01/11] [tox.ini] tell pytest to only collect tests from *_test.py modules; set pytest 'minversion' to 2.8 the glob pattern will restricts the search only for pytest's kind of tests; it has no effect on methods of unittest.TestCase derived class, since pytest still uses unittest's own collection framework to collect those. The 'minversion' is required to support some of the latest advanced features. The current py.test is 2.8.7. You can update it with `pip install --upgrade pytest`. --- tox.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tox.ini b/tox.ini index f977b8e2b..a4d0dd64e 100644 --- a/tox.ini +++ b/tox.ini @@ -9,8 +9,11 @@ commands = py.test [pytest] +minversion = 2.8 testpaths = Lib/fontTools +python_files = + *_test.py addopts = # run py.test in verbose mode -v From 41dff8456e2e1db195dcc4dc95ece97c56faf1a9 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Sat, 6 Feb 2016 21:53:01 +0000 Subject: [PATCH 02/11] [run-test.sh] use pytest to collect tests This way test discovery will work not only with the current unittest and doctest modules, but also with pytest's own kind of tests. When run with no argument, the shell script will call the py.test command, which will scan the whole Lib/fontTools directory for all relevant tests. If args are provided, tests are filtered if any of the substrings specified match. The filtering works not only on the modules' file names (as the previous grep approach), but also on the names of the test functions, classes and methods. Pretty cool, huh? We can still specify whether to run pytest with python -2 or -3 (provided pytest is installed on both Python versions). And we don't need to print the python version any more as pytest does it for us. Finally, there's no need to export PYTHONPATH -- pytest takes care of that too! --- run-tests.sh | 43 +++++++------------------------------------ 1 file changed, 7 insertions(+), 36 deletions(-) diff --git a/run-tests.sh b/run-tests.sh index e97e9c3f5..662d19aef 100755 --- a/run-tests.sh +++ b/run-tests.sh @@ -12,46 +12,17 @@ elif test "x$1" = x-2; then shift fi test "x$PYTHON" = x && PYTHON=python -echo "$(which $PYTHON) --version" -$PYTHON --version 2>&1 -echo - -# Setup environment -DIR=`dirname "$0"` -cd "$DIR/Lib" -if [ "x$PYTHONPATH" != "x" ]; then - PYTHONPATH=".:$PYTHONPATH" -else - PYTHONPATH="." -fi -export PYTHONPATH # Find tests -FILTER= +FILTERS= for arg in "$@"; do - test "x$FILTER" != x && FILTER="$FILTER|" - FILTER="$FILTER$arg" + test "x$FILTERS" != x && FILTERS="$FILTERS or " + FILTERS="$FILTERS$arg" done -test "x$FILTER" = "x" && FILTER=. -TESTS=`grep -r --include='*.py' -l -e doctest -e unittest * | grep -E "$FILTER"` - -ret=0 -FAILS= -for test in $TESTS; do - echo "Running tests in $test" - test=`echo "$test" | sed 's@[/\\]@.@g;s@[.]py$@@'` - if ! $PYTHON -m $test -v; then - ret=$((ret+1)) - FAILS="$FAILS -$test" - fi -done - echo - echo "SUMMARY:" -if test $ret = 0; then - echo "All tests passed." +# Run tests +if [ -z "$FILTERS" ]; then + $PYTHON -m pytest else - echo "$ret source file(s) had tests failing:$FAILS" >&2 + $PYTHON -m pytest -k "$FILTERS" fi -exit $ret From 258cb84c52c101da8a0d72e23854d0f0146affda Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Sat, 6 Feb 2016 22:15:02 +0000 Subject: [PATCH 03/11] [dev-requirements.txt] add pytest and tox to dev-requirements.txt In theory only pytest would be required for devs to run the tests locally, but we also use tox for setting up the test virtualenvs on the CI. --- dev-requirements.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 dev-requirements.txt diff --git a/dev-requirements.txt b/dev-requirements.txt new file mode 100644 index 000000000..18e00b343 --- /dev/null +++ b/dev-requirements.txt @@ -0,0 +1,2 @@ +pytest>=2.8 +tox>=2.3 From 627a54d76fb7d10951b0beab1704524b35c226e7 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Sun, 7 Feb 2016 00:32:38 +0000 Subject: [PATCH 04/11] [requirements.txt] install Brotli from v0.3.0 tagged release --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 2d7a35b5a..c0d1e9138 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -git+https://github.com/google/brotli@66db08156eba94f1fb0f77b6af519e4adedea8bf#egg=Brotli \ No newline at end of file +git+https://github.com/google/brotli@v0.3.0#egg=Brotli \ No newline at end of file From 93d93564395d07e37954e88c383757bae946b805 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Sat, 6 Feb 2016 22:04:51 +0000 Subject: [PATCH 05/11] [.appveyor.yml] use tox to run pytest in a virtual environment; remove extra scripts It seems like Appveyor no longer needs the batch script to configure the MSVC compiler variables. Also, since appveyor already comes with all the python versions we need, we don't need the install.ps1 script any more. Brotli is now installed by tox from requirements.txt inside the test virtualenv. --- .appveyor.yml | 44 +++++++++++++------------------------------- 1 file changed, 13 insertions(+), 31 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index a7e40f560..c7e158742 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,49 +1,37 @@ environment: - global: - # SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the - # /E:ON and /V:ON options are not enabled in the batch script intepreter - # See: http://stackoverflow.com/a/13751649/163740 - CMD_IN_ENV: "cmd /E:ON /V:ON /C run_with_env.cmd" - # links to python-appveyor-demo's scripts by Olivier Grisel's (@ogrisel): - RUN_WITH_ENV_CMD_URL: "https://raw.githubusercontent.com/ogrisel/python-appveyor-demo/master/appveyor/run_with_env.cmd" - INSTALL_PS1_URL: "https://raw.githubusercontent.com/ogrisel/python-appveyor-demo/master/appveyor/install.ps1" - matrix: - PYTHON: "C:\\Python27" PYTHON_VERSION: "2.7.x" PYTHON_ARCH: "32" + TOXENV: "py27" - PYTHON: "C:\\Python34" PYTHON_VERSION: "3.4.x" PYTHON_ARCH: "32" + TOXENV: "py34" - PYTHON: "C:\\Python35" - PYTHON_VERSION: "3.5.0" + PYTHON_VERSION: "3.5.x" PYTHON_ARCH: "32" + TOXENV: "py35" - PYTHON: "C:\\Python27-x64" PYTHON_VERSION: "2.7.x" PYTHON_ARCH: "64" + TOXENV: "py27" - PYTHON: "C:\\Python34-x64" PYTHON_VERSION: "3.4.x" PYTHON_ARCH: "64" + TOXENV: "py34" - PYTHON: "C:\\Python35-x64" - PYTHON_VERSION: "3.5.0" + PYTHON_VERSION: "3.5.x" PYTHON_ARCH: "64" + TOXENV: "py35" install: - # download 'run_with_env.cmd' script that configures environment variables for the - # MSVC compiler and Windows SDK versions. - - "appveyor DownloadFile %RUN_WITH_ENV_CMD_URL%" - - # download 'install.ps1' to install Python (official .msi of http://python.org) and - # pip when not already installed - - "appveyor DownloadFile %INSTALL_PS1_URL%" - - ps: if (-not(Test-Path($env:PYTHON))) { & install.ps1 } - - # Prepend newly installed Python to the PATH of this build + # Prepend Python to the PATH of this build - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" # check that we have the expected version and architecture for Python @@ -53,23 +41,17 @@ install: # upgrade pip to avoid out-of-date warnings - "pip install --disable-pip-version-check --user --upgrade pip" - # Install the build dependencies of the project. If some dependencies contain - # compiled extensions and are not provided as pre-built wheel packages, - # pip will build them from source using the MSVC compiler matching the - # target Python version and architecture - - "%CMD_IN_ENV% pip install -vr requirements.txt" + # install the dependencies to run the tests + - "pip install -r dev-requirements.txt" build: false test_script: - - "SET PATH=C:\\msys64\\usr\\bin;%PATH%" - # 'run-test.sh' interprets $PYTHON as the executable name - - "SET PYTHON=python" - - "bash run-tests.sh" + - "tox" after_test: # if tests are successful, create packages for the project - - "%CMD_IN_ENV% python setup.py sdist --formats=zip" + - python setup.py sdist --formats=zip" artifacts: # archive the generated packages in the ci.appveyor.com build report From 3495029726a8c950c70a874521d8824973a05fcc Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Sat, 6 Feb 2016 22:17:50 +0000 Subject: [PATCH 06/11] [.travis/install.sh] pip install from dev-requirements.txt --- .travis/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis/install.sh b/.travis/install.sh index ec511296c..0c2a1104e 100755 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -51,4 +51,4 @@ fi python -m virtualenv ~/.venv source ~/.venv/bin/activate -pip install tox +pip install -r dev-requirements.txt From 4874264dd9ba0d647b6577cc9a0300bff53d33e0 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Sat, 6 Feb 2016 21:56:38 +0000 Subject: [PATCH 07/11] [py23_test] pass the sys.path as PYTHONPATH to python subprocess Apparently I need to do that, or the child python process does not see fontTools in the path. Another workaround woud be to ensure that fontTools is installed, at least in editable or develop mode (via `pip install -e .`). But the way we temporarily extend the PYTHONPATH in run-test.sh is less intrusive. --- Lib/fontTools/misc/py23_test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/fontTools/misc/py23_test.py b/Lib/fontTools/misc/py23_test.py index 867945f01..05780d291 100644 --- a/Lib/fontTools/misc/py23_test.py +++ b/Lib/fontTools/misc/py23_test.py @@ -37,7 +37,8 @@ class OpenFuncWrapperTest(unittest.TestCase): with open(datafile, 'rb') as infile, \ tempfile.NamedTemporaryFile(delete=False) as outfile: check_call( - [sys.executable, script], stdin=infile, stdout=outfile) + [sys.executable, script], stdin=infile, stdout=outfile, + env={"PYTHONPATH": ":".join(sys.path)}) result = not filecmp.cmp(infile.name, outfile.name, shallow=False) finally: os.remove(script) From 4a783326b8f93fd1bbfb62467a10a68ebea48995 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Sat, 6 Feb 2016 22:28:39 +0000 Subject: [PATCH 08/11] [py23_test] need to use os.pathsep as Windows uses ";", not ":" --- Lib/fontTools/misc/py23_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/fontTools/misc/py23_test.py b/Lib/fontTools/misc/py23_test.py index 05780d291..aa0ce56d1 100644 --- a/Lib/fontTools/misc/py23_test.py +++ b/Lib/fontTools/misc/py23_test.py @@ -38,7 +38,7 @@ class OpenFuncWrapperTest(unittest.TestCase): tempfile.NamedTemporaryFile(delete=False) as outfile: check_call( [sys.executable, script], stdin=infile, stdout=outfile, - env={"PYTHONPATH": ":".join(sys.path)}) + env={"PYTHONPATH": os.pathsep.join(sys.path)}) result = not filecmp.cmp(infile.name, outfile.name, shallow=False) finally: os.remove(script) From 7c6744e6395007de0e3f3b07a3de6547a27c4f65 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Sat, 6 Feb 2016 23:13:22 +0000 Subject: [PATCH 09/11] [py23_test] pass os.environ copy instead of empty dict to avoid issue on Windows Otherwise I get this: Fatal Python error: Failed to initialize Windows random API (CryptoGen) https://ci.appveyor.com/project/anthrotype/fonttools/build/job/qhf8d89or4d5hiyd see: http://bugs.python.org/issue20614 --- Lib/fontTools/misc/py23_test.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Lib/fontTools/misc/py23_test.py b/Lib/fontTools/misc/py23_test.py index aa0ce56d1..b535e7373 100644 --- a/Lib/fontTools/misc/py23_test.py +++ b/Lib/fontTools/misc/py23_test.py @@ -36,9 +36,11 @@ class OpenFuncWrapperTest(unittest.TestCase): try: with open(datafile, 'rb') as infile, \ tempfile.NamedTemporaryFile(delete=False) as outfile: + env = dict(os.environ) + env["PYTHONPATH"] = os.pathsep.join(sys.path) check_call( [sys.executable, script], stdin=infile, stdout=outfile, - env={"PYTHONPATH": os.pathsep.join(sys.path)}) + env=env) result = not filecmp.cmp(infile.name, outfile.name, shallow=False) finally: os.remove(script) From e30ad7e8a7df9cbb3e0402237f266bfd39c985de Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Sun, 7 Feb 2016 00:04:01 +0000 Subject: [PATCH 10/11] [woff2_test] import sstruct from fontTool.misc oops. --- Lib/fontTools/ttLib/woff2_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/fontTools/ttLib/woff2_test.py b/Lib/fontTools/ttLib/woff2_test.py index ca4282bf6..8996ed8f0 100644 --- a/Lib/fontTools/ttLib/woff2_test.py +++ b/Lib/fontTools/ttLib/woff2_test.py @@ -7,7 +7,7 @@ from .woff2 import (WOFF2Reader, woff2DirectorySize, woff2DirectoryFormat, WOFF2FlavorData, woff2TransformedTableTags, WOFF2GlyfTable, WOFF2LocaTable, WOFF2Writer) import unittest -import sstruct +from fontTools.misc import sstruct import os import random import copy From 16f29ffd6d94e0dab324e49bf0d3a75c82e5d3a7 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Sun, 7 Feb 2016 00:08:53 +0000 Subject: [PATCH 11/11] [loggingTools] use ellipsis for doctests when printing time appveyor can be *very* slow https://ci.appveyor.com/project/anthrotype/fonttools/build/job/pdrl7o5ggan7qlej --- Lib/fontTools/misc/loggingTools.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Lib/fontTools/misc/loggingTools.py b/Lib/fontTools/misc/loggingTools.py index f2a302ea7..7295c5520 100644 --- a/Lib/fontTools/misc/loggingTools.py +++ b/Lib/fontTools/misc/loggingTools.py @@ -229,19 +229,19 @@ class Timer(object): >>> timer = Timer() >>> time.sleep(0.01) >>> print("First lap:", timer.split()) - First lap: 0.0... + First lap: ... >>> time.sleep(0.02) >>> print("Second lap:", timer.split()) - Second lap: 0.0... + Second lap: ... >>> print("Overall time:", timer.time()) - Overall time: 0.0... + Overall time: ... Can be used as a context manager inside with-statements. >>> with Timer() as t: ... time.sleep(0.01) >>> print("%0.3f seconds" % t.elapsed) - 0.0... seconds + 0... seconds If initialised with a logger, it can log the elapsed time automatically upon exiting the with-statement. @@ -259,7 +259,7 @@ class Timer(object): >>> timer = Timer(log) >>> with timer(): ... time.sleep(0.01) - elapsed time: 0.01...s + elapsed time: ...s >>> with timer('redo it', level=logging.INFO): ... time.sleep(0.02) Took ... to redo it @@ -274,7 +274,7 @@ class Timer(object): ... def test2(): ... time.sleep(0.02) >>> test1() - Took 0.01... to run 'test1' + Took ... to run 'test1' >>> test2() Took ... to run test 2 """