8
8
9
9
from argparse import ArgumentParser
10
10
import os
11
- import inspect
12
11
from pathlib import Path
13
12
import shutil
14
13
import subprocess
18
17
19
18
import matplotlib as mpl
20
19
import matplotlib .backends .backend_agg
21
- import matplotlib .testing .compare
20
+ import matplotlib .testing .decorators
22
21
import mplcairo .base
23
22
24
23
import pytest
25
24
26
25
26
+ _IGNORED_FAILURES = {}
27
+
28
+
27
29
def main (argv = None ):
28
30
parser = ArgumentParser (
29
31
description = """\
30
32
Run the Matplotlib test suite, using the mplcairo backend to patch out
31
33
Matplotlib's agg backend.
32
34
""" ,
33
35
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." )
36
38
args , rest = parser .parse_known_args (argv )
37
39
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
46
55
47
56
mplcairo .base .get_hinting_flag = mpl .backends .backend_agg .get_hinting_flag
48
57
mplcairo .base .FigureCanvasAgg = \
@@ -51,8 +60,20 @@ def compare_images(*args, **kwargs):
51
60
mplcairo .base .GraphicsContextRendererCairo
52
61
mpl .backends .backend_agg = \
53
62
sys .modules ["matplotlib.backends.backend_agg" ] = mplcairo .base
63
+
54
64
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
+
56
77
plt .switch_backend ("agg" )
57
78
58
79
return pytest .main ([
@@ -68,13 +89,14 @@ def pytest_collection_modifyitems(session, config, items):
68
89
if len (items ) == 0 :
69
90
pytest .exit ("No tests found; Matplotlib was likely installed without "
70
91
"test data." )
71
- excluded_modules = {
92
+ xfail_modules = {
72
93
"matplotlib.tests.test_compare_images" ,
73
94
}
74
- excluded_nodeids = {
95
+ xfail_nodeids = {
75
96
"matplotlib/tests/" + name for name in [
76
97
"test_agg.py::test_repeated_save_with_alpha" ,
77
98
"test_artist.py::test_cull_markers" ,
99
+ "test_axes.py::test_log_scales[png]" ,
78
100
"test_backend_pdf.py::test_composite_image" ,
79
101
"test_backend_pdf.py::test_multipage_keep_empty" ,
80
102
"test_backend_pdf.py::test_multipage_pagecount" ,
@@ -92,25 +114,34 @@ def pytest_collection_modifyitems(session, config, items):
92
114
"test_bbox_tight.py::test_bbox_inches_tight_suptile_legend[svg]" ,
93
115
"test_image.py::test_composite[True-1-ps- colorimage]" ,
94
116
"test_image.py::test_composite[False-2-ps- colorimage]" ,
117
+ "test_scale.py::test_logscale_mask[png]" ,
95
118
"test_simplification.py::test_throw_rendering_complexity_exceeded" ,
96
119
]
97
120
}
98
- selected = []
99
- deselected = []
121
+ xfails = []
100
122
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 ))
114
145
115
146
116
147
if __name__ == "__main__" :
0 commit comments