Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5c20a83

Browse files
committed
Use a specific version of Freetype for testing
This should theoretically allow us to turn down the tolerance on image comparison tests.
1 parent 8000e2f commit 5c20a83

File tree

9 files changed

+142
-41
lines changed

9 files changed

+142
-41
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ dist
3939
.eggs
4040
# tox testing tool
4141
.tox
42+
setup.cfg
4243

4344
# OS generated files #
4445
######################

.travis.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ install:
8484
# version since is it basically just a .ttf file
8585
# The current Travis Ubuntu image is to old to search .local/share/fonts so we store fonts in .fonts
8686

87-
# We install ipython to use the console highlighting. From IPython 3 this depends on jsonschema and misture.
87+
# We install ipython to use the console highlighting. From IPython 3 this depends on jsonschema and mistune.
8888
# Neihter is installed as a dependency of IPython since they are not used by the IPython console.
8989
- |
9090
if [[ $BUILD_DOCS == true ]]; then
@@ -99,6 +99,10 @@ install:
9999
cp Felipa-Regular.ttf ~/.fonts
100100
fc-cache -f -v
101101
fi;
102+
103+
# Use the special local version of freetype for testing
104+
- echo '[test]\ntesting_freetype = True' > setup.cfg
105+
102106
- python setup.py install
103107

104108
script:

doc/devel/testing.rst

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,22 @@ Optionally you can install:
3333

3434
- `pep8 <http://pep8.readthedocs.org/en/latest>`_ to test coding standards
3535

36+
Build matplotlib for image comparison tests
37+
-------------------------------------------
38+
39+
matplotlib's test suite makes heavy use of image comparison tests,
40+
meaning the result of a plot is compared against a known good result.
41+
Unfortunately, different versions of freetype produce differently
42+
formed characters, causing these image comparisons to fail. To make
43+
them reproducible, matplotlib can be built with a special local copy
44+
of freetype. This is recommended for all matplotlib developers.
45+
46+
Add the following content to a ``setup.cfg`` file at the root of the
47+
matplotlib source directory::
48+
49+
[test]
50+
testing_freetype = True
51+
3652
Running the tests
3753
-----------------
3854

@@ -185,17 +201,6 @@ decorator:
185201
If some variation is expected in the image between runs, this
186202
value may be adjusted.
187203

188-
Freetype version
189-
----------------
190-
191-
Due to subtle differences in the font rendering under different
192-
version of freetype some care must be taken when generating the
193-
baseline images. Currently (early 2015), almost all of the images
194-
were generated using ``freetype 2.5.3-21`` on Fedora 21 and only the
195-
fonts that ship with ``matplotlib`` (regenerated in PR #4031 / commit
196-
005cfde02751d274f2ab8016eddd61c3b3828446) and travis is using
197-
``freetype 2.4.8`` on ubuntu.
198-
199204
Known failing tests
200205
-------------------
201206

lib/matplotlib/__init__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1491,6 +1491,17 @@ def verify_test_dependencies():
14911491
if not os.path.isdir(os.path.join(os.path.dirname(__file__), 'tests')):
14921492
raise ImportError("matplotlib test data is not installed")
14931493

1494+
# The version of freetype to install locally for running the
1495+
# tests. This must match the value in `setupext.py`
1496+
LOCAL_FREETYPE_VERSION = '2.5.2'
1497+
1498+
from matplotlib import ft2font
1499+
if (ft2font.__freetype_version__ != LOCAL_FREETYPE_VERSION or
1500+
ft2font.__freetype_build_type__ != 'local'):
1501+
raise ImportError(
1502+
"matplotlib is not built with the correct freetype version to run "
1503+
"tests. Set local_freetype=True in setup.cfg and rebuild.")
1504+
14941505
try:
14951506
import nose
14961507
try:

setup.cfg.template

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@
88
# This can be a single directory or a comma-delimited list of directories.
99
#basedirlist = /usr
1010

11+
[test]
12+
# If you plan to develop matplotlib and run or add to the test suite,
13+
# set this to True. It will download and build a specific version of
14+
# freetype, and then use that to build the ft2font extension. This
15+
# ensures that test images are exactly reproducible.
16+
#local_freetype = False
17+
1118
[status]
1219
# To suppress display of the dependencies and their versions
1320
# at the top of the build log, uncomment the following line:

setup.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from distribute_setup import use_setuptools
1010
use_setuptools()
1111
from setuptools.command.test import test as TestCommand
12+
from setuptools.command.build_ext import build_ext as BuildExtCommand
1213

1314
import sys
1415

@@ -233,8 +234,19 @@ def run_tests(self):
233234
argv=['nosetests'] + self.test_args,
234235
exit=True)
235236

237+
238+
class BuildExtraLibraries(BuildExtCommand):
239+
def run(self):
240+
for package in good_packages:
241+
package.do_custom_build()
242+
243+
return super(BuildExtraLibraries, self).run()
244+
245+
236246
cmdclass = versioneer.get_cmdclass()
237247
cmdclass['test'] = NoseTestCommand
248+
cmdclass['build_ext'] = BuildExtraLibraries
249+
238250

239251
# One doesn't normally see `if __name__ == '__main__'` blocks in a setup.py,
240252
# however, this is needed on Windows to avoid creating infinite subprocesses
@@ -297,8 +309,6 @@ def run_tests(self):
297309
# Now collect all of the information we need to build all of the
298310
# packages.
299311
for package in good_packages:
300-
if isinstance(package, str):
301-
continue
302312
packages.extend(package.get_packages())
303313
namespace_packages.extend(package.get_namespace_packages())
304314
py_modules.extend(package.get_py_modules())

setupext.py

Lines changed: 80 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@
1919
PY3 = (sys.version_info[0] >= 3)
2020

2121

22+
# This is the version of freetype to use when building a local version
23+
# of freetype. It must match the value in
24+
# lib/matplotlib.__init__.py:validate_test_dependencies
25+
LOCAL_FREETYPE_VERSION = '2.5.2'
26+
27+
2228
try:
2329
from subprocess import check_output
2430
except ImportError:
@@ -72,22 +78,19 @@ def check_output(*popenargs, **kwargs):
7278
config = configparser.SafeConfigParser()
7379
config.read(setup_cfg)
7480

75-
try:
81+
if config.has_option('status', 'suppress'):
7682
options['display_status'] = not config.getboolean("status", "suppress")
77-
except:
78-
pass
7983

80-
try:
84+
if config.has_option('rc_options', 'backend'):
8185
options['backend'] = config.get("rc_options", "backend")
82-
except:
83-
pass
8486

85-
try:
87+
if config.has_option('directories', 'basedirlist'):
8688
options['basedirlist'] = [
8789
x.strip() for x in
8890
config.get("directories", "basedirlist").split(',')]
89-
except:
90-
pass
91+
92+
if config.has_option('test', 'local_freetype'):
93+
options['local_freetype'] = config.get("test", "local_freetype")
9194
else:
9295
config = None
9396

@@ -473,6 +476,14 @@ def _check_for_pkg_config(self, package, include_file, min_version=None,
473476

474477
return 'version %s' % version
475478

479+
def do_custom_build(self):
480+
"""
481+
If a package needs to do extra custom things, such as building a
482+
third-party library, before building an extension, it should
483+
override this method.
484+
"""
485+
pass
486+
476487

477488
class OptionalPackage(SetupPackage):
478489
optional = True
@@ -486,10 +497,9 @@ def get_config(cls):
486497
if the package is at default state ("auto"), forced by the user (True)
487498
or opted-out (False).
488499
"""
489-
try:
490-
return config.getboolean(cls.config_category, cls.name)
491-
except:
492-
return "auto"
500+
if config is not None and config.has_option(cls.config_category, cls.name):
501+
return config.get(cls.config_category, cls.name)
502+
return "auto"
493503

494504
def check(self):
495505
"""
@@ -897,6 +907,9 @@ class FreeType(SetupPackage):
897907
name = "freetype"
898908

899909
def check(self):
910+
if options.get('local_freetype'):
911+
return "Using local version for testing"
912+
900913
if sys.platform == 'win32':
901914
check_include_file(get_include_dirs(), 'ft2build.h', 'freetype')
902915
return 'Using unknown version found on system.'
@@ -942,15 +955,60 @@ def version_from_header(self):
942955
return '.'.join([major, minor, patch])
943956

944957
def add_flags(self, ext):
945-
pkg_config.setup_extension(
946-
ext, 'freetype2',
947-
default_include_dirs=[
948-
'include/freetype2', 'freetype2',
949-
'lib/freetype2/include',
950-
'lib/freetype2/include/freetype2'],
951-
default_library_dirs=[
952-
'freetype2/lib'],
953-
default_libraries=['freetype', 'z'])
958+
if options.get('local_freetype'):
959+
src_path = os.path.join(
960+
'build', 'freetype-{0}'.format(LOCAL_FREETYPE_VERSION))
961+
# Statically link to the locally-built freetype.
962+
# This is certainly broken on Windows.
963+
ext.include_dirs.insert(0, os.path.join(src_path, 'include'))
964+
ext.extra_objects.insert(
965+
0, os.path.join(src_path, 'objs', '.libs', 'libfreetype.a'))
966+
ext.define_macros.append(('FREETYPE_BUILD_TYPE', 'local'))
967+
else:
968+
pkg_config.setup_extension(
969+
ext, 'freetype2',
970+
default_include_dirs=[
971+
'include/freetype2', 'freetype2',
972+
'lib/freetype2/include',
973+
'lib/freetype2/include/freetype2'],
974+
default_library_dirs=[
975+
'freetype2/lib'],
976+
default_libraries=['freetype', 'z'])
977+
ext.define_macros.append(('FREETYPE_BUILD_TYPE', 'system'))
978+
979+
def do_custom_build(self):
980+
# We're using a system freetype
981+
if not options.get('local_freetype'):
982+
return
983+
984+
src_path = os.path.join(
985+
'build', 'freetype-{0}'.format(LOCAL_FREETYPE_VERSION))
986+
987+
# We've already built freetype
988+
if os.path.isfile(os.path.join(src_path, 'objs', '.libs', 'libfreetype.a')):
989+
return
990+
991+
tarball = 'freetype-{0}.tar.gz'.format(LOCAL_FREETYPE_VERSION)
992+
tarball_path = os.path.join('build', tarball)
993+
if not os.path.isfile(tarball_path):
994+
print("Downloading {0}".format(tarball))
995+
if sys.version_info[0] == 2:
996+
from urllib import urlretrieve
997+
else:
998+
from urllib.request import urlretrieve
999+
1000+
urlretrieve(
1001+
'http://download.savannah.gnu.org/releases/freetype/{0}'.format(tarball),
1002+
tarball_path)
1003+
1004+
print("Building {0}".format(tarball))
1005+
subprocess.check_call(
1006+
['tar zxf {0}'.format(tarball)], shell=True, cwd='build')
1007+
subprocess.check_call(
1008+
['./configure --without-zlib --without-bzip2 --without-png'],
1009+
shell=True, cwd=src_path)
1010+
subprocess.check_call(
1011+
['make'], shell=True, cwd=src_path)
9541012

9551013

9561014
class FT2Font(SetupPackage):

src/ft2font_wrapper.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
// From Python
88
#include <structmember.h>
99

10+
#define STRINGIFY(s) XSTRINGIFY(s)
11+
#define XSTRINGIFY(s) #s
12+
1013
static PyObject *convert_xys_to_array(std::vector<double> &xys)
1114
{
1215
npy_intp dims[] = {(npy_intp)xys.size() / 2, 2 };
@@ -1733,7 +1736,7 @@ PyMODINIT_FUNC initft2font(void)
17331736
int error = FT_Init_FreeType(&_ft2Library);
17341737

17351738
if (error) {
1736-
PyErr_SetString(PyExc_RuntimeError, "Could not find initialize the freetype2 library");
1739+
PyErr_SetString(PyExc_RuntimeError, "Could not initialize the freetype2 library");
17371740
INITERROR;
17381741
}
17391742

@@ -1748,6 +1751,10 @@ PyMODINIT_FUNC initft2font(void)
17481751
}
17491752
}
17501753

1754+
if (PyModule_AddStringConstant(m, "__freetype_build_type__", STRINGIFY(FREETYPE_BUILD_TYPE))) {
1755+
INITERROR;
1756+
}
1757+
17511758
import_array();
17521759

17531760
#if PY3K

tests.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,15 @@
3838

3939

4040
def run(extra_args):
41+
matplotlib.verify_test_dependencies()
42+
4143
try:
4244
import faulthandler
4345
except ImportError:
4446
pass
4547
else:
4648
faulthandler.enable()
4749

48-
if not os.path.isdir(
49-
os.path.join(os.path.dirname(matplotlib.__file__), 'tests')):
50-
raise ImportError("matplotlib test data is not installed")
51-
5250
nose.main(addplugins=[x() for x in plugins],
5351
defaultTest=default_test_modules,
5452
argv=sys.argv + extra_args)

0 commit comments

Comments
 (0)