1
1
from matplotlib .testing .noseclasses import KnownFailureTest , \
2
2
KnownFailureDidNotFailTest , ImageComparisonFailure
3
- import os , sys , shutil
3
+ import os , sys , shutil , new
4
4
import nose
5
5
import matplotlib
6
6
import matplotlib .tests
7
+ import matplotlib .units
8
+ from matplotlib import pyplot as plt
7
9
import numpy as np
8
10
from matplotlib .testing .compare import comparable_formats , compare_images
9
11
@@ -46,7 +48,81 @@ def failer(*args, **kwargs):
46
48
return nose .tools .make_decorator (f )(failer )
47
49
return known_fail_decorator
48
50
49
- def image_comparison (baseline_images = None ,extensions = None ,tol = 1e-3 ):
51
+ class CleanupTest :
52
+ @classmethod
53
+ def setup_class (cls ):
54
+ cls .original_units_registry = matplotlib .units .registry .copy ()
55
+
56
+ @classmethod
57
+ def teardown_class (cls ):
58
+ plt .close ('all' )
59
+
60
+ matplotlib .tests .setup ()
61
+
62
+ matplotlib .units .registry .clear ()
63
+ matplotlib .units .registry .update (cls .original_units_registry )
64
+
65
+ def test (self ):
66
+ self ._func ()
67
+
68
+ def cleanup (func ):
69
+ name = func .__name__
70
+ func = staticmethod (func )
71
+ func .__get__ (1 ).__name__ = '_private'
72
+ new_class = new .classobj (
73
+ name ,
74
+ (CleanupTest ,),
75
+ {'_func' : func })
76
+ return new_class
77
+
78
+ class ImageComparisonTest (CleanupTest ):
79
+ @classmethod
80
+ def setup_class (cls ):
81
+ CleanupTest .setup_class ()
82
+
83
+ cls ._func ()
84
+
85
+ def test (self ):
86
+ baseline_dir , result_dir = _image_directories (self ._func )
87
+
88
+ for fignum , baseline in zip (plt .get_fignums (), self ._baseline_images ):
89
+ figure = plt .figure (fignum )
90
+
91
+ for extension in self ._extensions :
92
+ will_fail = not extension in comparable_formats ()
93
+ if will_fail :
94
+ fail_msg = 'Cannot compare %s files on this system' % extension
95
+ else :
96
+ fail_msg = 'No failure expected'
97
+
98
+ orig_expected_fname = os .path .join (baseline_dir , baseline ) + '.' + extension
99
+ expected_fname = os .path .join (result_dir , 'expected-' + baseline ) + '.' + extension
100
+ actual_fname = os .path .join (result_dir , baseline ) + '.' + extension
101
+ if os .path .exists (orig_expected_fname ):
102
+ shutil .copyfile (orig_expected_fname , expected_fname )
103
+ else :
104
+ will_fail = True
105
+ fail_msg = 'Do not have baseline image %s' % expected_fname
106
+
107
+ @knownfailureif (
108
+ will_fail , fail_msg ,
109
+ known_exception_class = ImageComparisonFailure )
110
+ def do_test ():
111
+ figure .savefig (actual_fname )
112
+
113
+ if not os .path .exists (expected_fname ):
114
+ raise ImageComparisonFailure (
115
+ 'image does not exist: %s' % expected_fname )
116
+
117
+ err = compare_images (expected_fname , actual_fname , self ._tol , in_decorator = True )
118
+ if err :
119
+ raise ImageComparisonFailure (
120
+ 'images not close: %(actual)s vs. %(expected)s '
121
+ '(RMS %(rms).3f)' % err )
122
+
123
+ yield (do_test ,)
124
+
125
+ def image_comparison (baseline_images = None , extensions = None , tol = 1e-3 ):
50
126
"""
51
127
call signature::
52
128
@@ -76,56 +152,29 @@ def image_comparison(baseline_images=None,extensions=None,tol=1e-3):
76
152
# default extensions to test
77
153
extensions = ['png' , 'pdf' , 'svg' ]
78
154
79
- # The multiple layers of defs are required because of how
80
- # parameterized decorators work, and because we want to turn the
81
- # single test_foo function to a generator that generates a
82
- # separate test case for each file format.
83
155
def compare_images_decorator (func ):
84
- baseline_dir , result_dir = _image_directories (func )
85
-
86
- def compare_images_generator ():
87
- for extension in extensions :
88
- orig_expected_fnames = [os .path .join (baseline_dir ,fname ) + '.' + extension for fname in baseline_images ]
89
- expected_fnames = [os .path .join (result_dir ,'expected-' + fname ) + '.' + extension for fname in baseline_images ]
90
- actual_fnames = [os .path .join (result_dir , fname ) + '.' + extension for fname in baseline_images ]
91
- have_baseline_images = [os .path .exists (expected ) for expected in orig_expected_fnames ]
92
- have_baseline_image = np .all (have_baseline_images )
93
- is_comparable = extension in comparable_formats ()
94
- if not is_comparable :
95
- fail_msg = 'Cannot compare %s files on this system' % extension
96
- elif not have_baseline_image :
97
- fail_msg = 'Do not have baseline images %s' % expected_fnames
98
- else :
99
- fail_msg = 'No failure expected'
100
- will_fail = not (is_comparable and have_baseline_image )
101
- @knownfailureif (will_fail , fail_msg ,
102
- known_exception_class = ImageComparisonFailure )
103
- def decorated_compare_images ():
104
- # set the default format of savefig
105
- matplotlib .rc ('savefig' , extension = extension )
106
- # change to the result directory for the duration of the test
107
- old_dir = os .getcwd ()
108
- os .chdir (result_dir )
109
- try :
110
- result = func () # actually call the test function
111
- finally :
112
- os .chdir (old_dir )
113
- for original , expected in zip (orig_expected_fnames , expected_fnames ):
114
- if not os .path .exists (original ):
115
- raise ImageComparisonFailure (
116
- 'image does not exist: %s' % original )
117
- shutil .copyfile (original , expected )
118
- for actual ,expected in zip (actual_fnames ,expected_fnames ):
119
- # compare the images
120
- err = compare_images ( expected , actual , tol ,
121
- in_decorator = True )
122
- if err :
123
- raise ImageComparisonFailure (
124
- 'images not close: %(actual)s vs. %(expected)s '
125
- '(RMS %(rms).3f)' % err )
126
- return result
127
- yield (decorated_compare_images ,)
128
- return nose .tools .make_decorator (func )(compare_images_generator )
156
+ # We want to run the setup function (the actual test function
157
+ # that generates the figure objects) only once for each type
158
+ # of output file. The only way to achieve this with nose
159
+ # appears to be to create a test class with "setup_class" and
160
+ # "teardown_class" methods. Creating a class instance doesn't
161
+ # work, so we use new.classobj to actually create a class and
162
+ # fill it with the appropriate methods.
163
+ name = func .__name__
164
+ # For nose 1.0, we need to rename the test function to
165
+ # something without the word "test", or it will be run as
166
+ # well, outside of the context of our image comparison test
167
+ # generator.
168
+ func = staticmethod (func )
169
+ func .__get__ (1 ).__name__ = '_private'
170
+ new_class = new .classobj (
171
+ name ,
172
+ (ImageComparisonTest ,),
173
+ {'_func' : func ,
174
+ '_baseline_images' : baseline_images ,
175
+ '_extensions' : extensions ,
176
+ '_tol' : tol })
177
+ return new_class
129
178
return compare_images_decorator
130
179
131
180
def _image_directories (func ):
@@ -134,7 +183,7 @@ def _image_directories(func):
134
183
Create the result directory if it doesn't exist.
135
184
"""
136
185
module_name = func .__module__
137
- if module_name == '__main__' :
186
+ if module_name == '__main__' :
138
187
# FIXME: this won't work for nested packages in matplotlib.tests
139
188
import warnings
140
189
warnings .warn ('test module run as script. guessing baseline image locations' )
@@ -143,13 +192,13 @@ def _image_directories(func):
143
192
subdir = os .path .splitext (os .path .split (script_name )[1 ])[0 ]
144
193
else :
145
194
mods = module_name .split ('.' )
146
- assert mods .pop (0 )== 'matplotlib'
147
- assert mods .pop (0 )== 'tests'
195
+ assert mods .pop (0 ) == 'matplotlib'
196
+ assert mods .pop (0 ) == 'tests'
148
197
subdir = os .path .join (* mods )
149
198
basedir = os .path .dirname (matplotlib .tests .__file__ )
150
199
151
- baseline_dir = os .path .join (basedir ,'baseline_images' ,subdir )
152
- result_dir = os .path .abspath (os .path .join ('result_images' ,subdir ))
200
+ baseline_dir = os .path .join (basedir , 'baseline_images' , subdir )
201
+ result_dir = os .path .abspath (os .path .join ('result_images' , subdir ))
153
202
154
203
if not os .path .exists (result_dir ):
155
204
os .makedirs (result_dir )
0 commit comments