@@ -233,42 +233,18 @@ def _mark_xfail_if_format_is_uncomparable(extension):
233
233
return extension
234
234
235
235
236
- class ImageComparisonDecorator (CleanupTest ):
237
- def __init__ (self , baseline_images , extensions , tol ,
238
- freetype_version , remove_text , savefig_kwargs , style ):
236
+ class _ImageComparisonBase (object ):
237
+ def __init__ (self , tol , remove_text , savefig_kwargs ):
239
238
self .func = self .baseline_dir = self .result_dir = None
240
- self .baseline_images = baseline_images
241
- self .extensions = extensions
242
239
self .tol = tol
243
- self .freetype_version = freetype_version
244
240
self .remove_text = remove_text
245
241
self .savefig_kwargs = savefig_kwargs
246
- self .style = style
247
242
248
243
def delayed_init (self , func ):
249
244
assert self .func is None , "it looks like same decorator used twice"
250
245
self .func = func
251
246
self .baseline_dir , self .result_dir = _image_directories (func )
252
247
253
- def setup (self ):
254
- func = self .func
255
- plt .close ('all' )
256
- self .setup_class ()
257
- try :
258
- matplotlib .style .use (self .style )
259
- matplotlib .testing .set_font_settings_for_testing ()
260
- func ()
261
- assert len (plt .get_fignums ()) == len (self .baseline_images ), (
262
- "Test generated {} images but there are {} baseline images"
263
- .format (len (plt .get_fignums ()), len (self .baseline_images )))
264
- except :
265
- # Restore original settings before raising errors during the update.
266
- self .teardown_class ()
267
- raise
268
-
269
- def teardown (self ):
270
- self .teardown_class ()
271
-
272
248
def copy_baseline (self , baseline , extension ):
273
249
baseline_path = os .path .join (self .baseline_dir , baseline )
274
250
orig_expected_fname = baseline_path + '.' + extension
@@ -304,6 +280,35 @@ def compare(self, idx, baseline, extension):
304
280
expected_fname = self .copy_baseline (baseline , extension )
305
281
_raise_on_image_difference (expected_fname , actual_fname , self .tol )
306
282
283
+
284
+ class ImageComparisonDecorator (CleanupTest , _ImageComparisonBase ):
285
+ def __init__ (self , baseline_images , extensions , tol ,
286
+ freetype_version , remove_text , savefig_kwargs , style ):
287
+ _ImageComparisonBase .__init__ (self , tol , remove_text , savefig_kwargs )
288
+ self .baseline_images = baseline_images
289
+ self .extensions = extensions
290
+ self .freetype_version = freetype_version
291
+ self .style = style
292
+
293
+ def setup (self ):
294
+ func = self .func
295
+ plt .close ('all' )
296
+ self .setup_class ()
297
+ try :
298
+ matplotlib .style .use (self .style )
299
+ matplotlib .testing .set_font_settings_for_testing ()
300
+ func ()
301
+ assert len (plt .get_fignums ()) == len (self .baseline_images ), (
302
+ "Test generated {} images but there are {} baseline images"
303
+ .format (len (plt .get_fignums ()), len (self .baseline_images )))
304
+ except :
305
+ # Restore original settings before raising errors.
306
+ self .teardown_class ()
307
+ raise
308
+
309
+ def teardown (self ):
310
+ self .teardown_class ()
311
+
307
312
def nose_runner (self ):
308
313
func = self .compare
309
314
func = _checked_on_freetype_version (self .freetype_version )(func )
@@ -313,52 +318,59 @@ def nose_runner(self):
313
318
for extension in self .extensions :
314
319
yield funcs [extension ], idx , baseline , extension
315
320
316
- def pytest_runner (self ):
317
- from pytest import mark
321
+ def __call__ (self , func ):
322
+ self .delayed_init (func )
323
+ import nose .tools
318
324
319
- extensions = map (_mark_xfail_if_format_is_uncomparable ,
320
- self .extensions )
325
+ @nose .tools .with_setup (self .setup , self .teardown )
326
+ def runner_wrapper ():
327
+ try :
328
+ for case in self .nose_runner ():
329
+ yield case
330
+ except GeneratorExit :
331
+ # nose bug...
332
+ self .teardown ()
321
333
322
- if len (set (self .baseline_images )) == len (self .baseline_images ):
323
- @mark .parametrize ("extension" , extensions )
324
- @mark .parametrize ("idx,baseline" , enumerate (self .baseline_images ))
325
- @_checked_on_freetype_version (self .freetype_version )
326
- def wrapper (idx , baseline , extension ):
327
- __tracebackhide__ = True
328
- self .compare (idx , baseline , extension )
329
- else :
330
- # Some baseline images are repeated, so run this in serial.
331
- @mark .parametrize ("extension" , extensions )
332
- @_checked_on_freetype_version (self .freetype_version )
333
- def wrapper (extension ):
334
- __tracebackhide__ = True
335
- for idx , baseline in enumerate (self .baseline_images ):
336
- self .compare (idx , baseline , extension )
334
+ return _copy_metadata (func , runner_wrapper )
337
335
338
336
339
- # sadly we cannot use fixture here because of visibility problems
340
- # and for for obvious reason avoid `_nose.tools.with_setup`
341
- wrapper .setup , wrapper .teardown = self .setup , self .teardown
337
+ def _pytest_image_comparison (baseline_images , extensions , tol ,
338
+ freetype_version , remove_text , savefig_kwargs ,
339
+ style ):
340
+ import pytest
342
341
343
- return wrapper
342
+ extensions = map ( _mark_xfail_if_format_is_uncomparable , extensions )
344
343
345
- def __call__ (self , func ):
346
- self .delayed_init (func )
347
- if is_called_from_pytest ():
348
- return _copy_metadata (func , self .pytest_runner ())
349
- else :
350
- import nose .tools
344
+ def decorator (func ):
345
+ @pytest .mark .usefixtures ('mpl_image_comparison_parameters' )
346
+ @pytest .mark .parametrize ('extension' , extensions )
347
+ @pytest .mark .style (style )
348
+ @_checked_on_freetype_version (freetype_version )
349
+ @functools .wraps (func )
350
+ def wrapper (* args , ** kwargs ):
351
+ __tracebackhide__ = True
352
+ img = _ImageComparisonBase (tol = tol , remove_text = remove_text ,
353
+ savefig_kwargs = savefig_kwargs )
354
+ img .delayed_init (func )
355
+ matplotlib .testing .set_font_settings_for_testing ()
356
+ func (* args , ** kwargs )
351
357
352
- @nose .tools .with_setup (self .setup , self .teardown )
353
- def runner_wrapper ():
354
- try :
355
- for case in self .nose_runner ():
356
- yield case
357
- except GeneratorExit :
358
- # nose bug...
359
- self .teardown ()
358
+ # This is hacked on via the mpl_image_comparison_parameters fixture
359
+ # so that we don't need to modify the function's real signature for
360
+ # any parametrization. Modifying the signature is very very tricky
361
+ # and likely to confuse pytest.
362
+ extension , = func .parameters
363
+
364
+ assert len (plt .get_fignums ()) == len (baseline_images ), (
365
+ "Test generated {} images but there are {} baseline images"
366
+ .format (len (plt .get_fignums ()), len (baseline_images )))
367
+ for idx , baseline in enumerate (baseline_images ):
368
+ img .compare (idx , baseline , extension )
369
+
370
+ wrapper .__wrapped__ = func # For Python 2.7.
371
+ return _copy_metadata (func , wrapper )
360
372
361
- return _copy_metadata ( func , runner_wrapper )
373
+ return decorator
362
374
363
375
364
376
def image_comparison (baseline_images = None , extensions = None , tol = 0 ,
@@ -414,10 +426,16 @@ def image_comparison(baseline_images=None, extensions=None, tol=0,
414
426
#default no kwargs to savefig
415
427
savefig_kwarg = dict ()
416
428
417
- return ImageComparisonDecorator (
418
- baseline_images = baseline_images , extensions = extensions , tol = tol ,
419
- freetype_version = freetype_version , remove_text = remove_text ,
420
- savefig_kwargs = savefig_kwarg , style = style )
429
+ if is_called_from_pytest ():
430
+ return _pytest_image_comparison (
431
+ baseline_images = baseline_images , extensions = extensions , tol = tol ,
432
+ freetype_version = freetype_version , remove_text = remove_text ,
433
+ savefig_kwargs = savefig_kwarg , style = style )
434
+ else :
435
+ return ImageComparisonDecorator (
436
+ baseline_images = baseline_images , extensions = extensions , tol = tol ,
437
+ freetype_version = freetype_version , remove_text = remove_text ,
438
+ savefig_kwargs = savefig_kwarg , style = style )
421
439
422
440
423
441
def _image_directories (func ):
0 commit comments