88
99from argparse import ArgumentParser
1010import os
11- import inspect
1211from pathlib import Path
1312import shutil
1413import subprocess
1817
1918import matplotlib as mpl
2019import matplotlib .backends .backend_agg
21- import matplotlib .testing .compare
20+ import matplotlib .testing .decorators
2221import mplcairo .base
2322
2423import pytest
2524
2625
26+ _IGNORED_FAILURES = {}
27+
28+
2729def main (argv = None ):
2830 parser = ArgumentParser (
2931 description = """\
3032 Run the Matplotlib test suite, using the mplcairo backend to patch out
3133Matplotlib's agg backend.
3234""" ,
3335 epilog = "Other arguments are forwarded to pytest." )
34- parser .add_argument ("--infinite- tolerance" , action = "store_true" ,
35- help = "Set image comparison tolerance to infinity ." )
36+ parser .add_argument ("--tolerance" , type = float ,
37+ help = "Set image comparison tolerance." )
3638 args , rest = parser .parse_known_args (argv )
3739
38- if args .infinite_tolerance :
39- sig = inspect .signature (mpl .testing .compare .compare_images )
40- def compare_images (* args , ** kwargs ):
41- ba = sig .bind (* args , ** kwargs )
42- ba .arguments ["tol" ] = float ("inf" )
43- return compare_images .__wrapped__ (* ba .args , ** ba .kwargs )
44- compare_images .__wrapped__ = mpl .testing .compare .compare_images
45- mpl .testing .compare .compare_images = compare_images
40+ if args .tolerance is not None :
41+ def _raise_on_image_difference (expected , actual , tol ):
42+ cmp = mpl .testing .compare .compare_images (
43+ expected , actual , tol , in_decorator = True )
44+ if cmp :
45+ if cmp ["rms" ] < args .tolerance :
46+ expected = Path (expected )
47+ expected = expected .relative_to (expected .parent .parent )
48+ _IGNORED_FAILURES [expected ] = cmp ["rms" ]
49+ else :
50+ __orig_raise_on_image_tolerance (expected , actual , tol )
51+ __orig_raise_on_image_tolerance = \
52+ mpl .testing .decorators ._raise_on_image_difference
53+ mpl .testing .decorators ._raise_on_image_difference = \
54+ _raise_on_image_difference
4655
4756 mplcairo .base .get_hinting_flag = mpl .backends .backend_agg .get_hinting_flag
4857 mplcairo .base .FigureCanvasAgg = \
@@ -51,8 +60,20 @@ def compare_images(*args, **kwargs):
5160 mplcairo .base .GraphicsContextRendererCairo
5261 mpl .backends .backend_agg = \
5362 sys .modules ["matplotlib.backends.backend_agg" ] = mplcairo .base
63+
5464 mpl .use ("agg" , warn = False , force = True )
55- import matplotlib .pyplot as plt
65+ from matplotlib import pyplot as plt
66+
67+ __orig_switch_backend = plt .switch_backend
68+ def switch_backend (backend ):
69+ __orig_switch_backend ({
70+ "gtk3agg" : "module://mplcairo.gtk" ,
71+ "qt5agg" : "module://mplcairo.qt" ,
72+ "tkagg" : "module://mplcairo.tk" ,
73+ "wxagg" : "module://mplcairo.wx" ,
74+ }.get (backend .lower (), backend ))
75+ plt .switch_backend = switch_backend
76+
5677 plt .switch_backend ("agg" )
5778
5879 return pytest .main ([
@@ -68,13 +89,14 @@ def pytest_collection_modifyitems(session, config, items):
6889 if len (items ) == 0 :
6990 pytest .exit ("No tests found; Matplotlib was likely installed without "
7091 "test data." )
71- excluded_modules = {
92+ xfail_modules = {
7293 "matplotlib.tests.test_compare_images" ,
7394 }
74- excluded_nodeids = {
95+ xfail_nodeids = {
7596 "matplotlib/tests/" + name for name in [
7697 "test_agg.py::test_repeated_save_with_alpha" ,
7798 "test_artist.py::test_cull_markers" ,
99+ "test_axes.py::test_log_scales[png]" ,
78100 "test_backend_pdf.py::test_composite_image" ,
79101 "test_backend_pdf.py::test_multipage_keep_empty" ,
80102 "test_backend_pdf.py::test_multipage_pagecount" ,
@@ -92,25 +114,34 @@ def pytest_collection_modifyitems(session, config, items):
92114 "test_bbox_tight.py::test_bbox_inches_tight_suptile_legend[svg]" ,
93115 "test_image.py::test_composite[True-1-ps- colorimage]" ,
94116 "test_image.py::test_composite[False-2-ps- colorimage]" ,
117+ "test_scale.py::test_logscale_mask[png]" ,
95118 "test_simplification.py::test_throw_rendering_complexity_exceeded" ,
96119 ]
97120 }
98- selected = []
99- deselected = []
121+ xfails = []
100122 for item in items :
101- if (item .module .__name__ in excluded_modules
102- or item .nodeid in excluded_nodeids ):
103- deselected .append (item )
104- else :
105- selected .append (item )
106- items [:] = selected
107- config .hook .pytest_deselected (items = deselected )
108- invalid_exclusions = (
109- (excluded_modules - {item .module .__name__ for item in deselected })
110- | (excluded_nodeids - {item .nodeid for item in deselected }))
111- if invalid_exclusions :
112- warnings .warn ("Unused exclusions:\n {}"
113- .format ("\n " .join (sorted (invalid_exclusions ))))
123+ if (item .module .__name__ in xfail_modules
124+ or item .nodeid in xfail_nodeids ):
125+ xfails .append (item )
126+ item .add_marker (
127+ pytest .mark .xfail (reason = "Test known to fail with mplcairo" ))
128+ invalid_xfails = (
129+ (xfail_modules - {item .module .__name__ for item in xfails })
130+ | (xfail_nodeids - {item .nodeid for item in xfails }))
131+ if invalid_xfails :
132+ warnings .warn ("Unused xfails:\n {}"
133+ .format ("\n " .join (sorted (invalid_xfails ))))
134+
135+
136+ def pytest_terminal_summary (terminalreporter , exitstatus ):
137+ write = terminalreporter .write
138+ if _IGNORED_FAILURES :
139+ write ("\n "
140+ "Ignored the following image comparison failures:\n "
141+ "RMS\t expected\n " )
142+ for rms , expected in sorted (
143+ ((v , k ) for k , v in _IGNORED_FAILURES .items ()), reverse = True ):
144+ write ("{:#.2f}\t {}\n " .format (rms , expected ))
114145
115146
116147if __name__ == "__main__" :
0 commit comments