@@ -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,34 @@ 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
+ plt .close ('all' )
295
+ self .setup_class ()
296
+ try :
297
+ matplotlib .style .use (self .style )
298
+ matplotlib .testing .set_font_settings_for_testing ()
299
+ func ()
300
+ assert len (plt .get_fignums ()) == len (self .baseline_images ), (
301
+ "Test generated {} images but there are {} baseline images"
302
+ .format (len (plt .get_fignums ()), len (self .baseline_images )))
303
+ except :
304
+ # Restore original settings before raising errors.
305
+ self .teardown_class ()
306
+ raise
307
+
308
+ def teardown (self ):
309
+ self .teardown_class ()
310
+
307
311
def nose_runner (self ):
308
312
func = self .compare
309
313
func = _checked_on_freetype_version (self .freetype_version )(func )
@@ -313,52 +317,59 @@ def nose_runner(self):
313
317
for extension in self .extensions :
314
318
yield funcs [extension ], idx , baseline , extension
315
319
316
- def pytest_runner (self ):
317
- from pytest import mark
320
+ def __call__ (self , func ):
321
+ self .delayed_init (func )
322
+ import nose .tools
318
323
319
- extensions = map (_mark_xfail_if_format_is_uncomparable ,
320
- self .extensions )
324
+ @nose .tools .with_setup (self .setup , self .teardown )
325
+ def runner_wrapper ():
326
+ try :
327
+ for case in self .nose_runner ():
328
+ yield case
329
+ except GeneratorExit :
330
+ # nose bug...
331
+ self .teardown ()
321
332
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 )
333
+ return _copy_metadata (func , runner_wrapper )
337
334
338
335
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
336
+ def _pytest_image_comparison (baseline_images , extensions , tol ,
337
+ freetype_version , remove_text , savefig_kwargs ,
338
+ style ):
339
+ import pytest
342
340
343
- return wrapper
341
+ extensions = map ( _mark_xfail_if_format_is_uncomparable , extensions )
344
342
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
343
+ def decorator (func ):
344
+ @pytest .mark .usefixtures ('mpl_image_comparison_parameters' )
345
+ @pytest .mark .parametrize ('extension' , extensions )
346
+ @pytest .mark .style (style )
347
+ @_checked_on_freetype_version (freetype_version )
348
+ @functools .wraps (func )
349
+ def wrapper (* args , ** kwargs ):
350
+ __tracebackhide__ = True
351
+ img = _ImageComparisonBase (tol = tol , remove_text = remove_text ,
352
+ savefig_kwargs = savefig_kwargs )
353
+ img .delayed_init (func )
354
+ matplotlib .testing .set_font_settings_for_testing ()
355
+ func (* args , ** kwargs )
351
356
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 ()
357
+ # This is hacked on via the mpl_image_comparison_parameters fixture
358
+ # so that we don't need to modify the function's real signature for
359
+ # any parametrization. Modifying the signature is very very tricky
360
+ # and likely to confuse pytest.
361
+ extension , = func .parameters
362
+
363
+ assert len (plt .get_fignums ()) == len (baseline_images ), (
364
+ "Test generated {} images but there are {} baseline images"
365
+ .format (len (plt .get_fignums ()), len (baseline_images )))
366
+ for idx , baseline in enumerate (baseline_images ):
367
+ img .compare (idx , baseline , extension )
360
368
361
- return _copy_metadata (func , runner_wrapper )
369
+ wrapper .__wrapped__ = func # For Python 2.7.
370
+ return _copy_metadata (func , wrapper )
371
+
372
+ return decorator
362
373
363
374
364
375
def image_comparison (baseline_images = None , extensions = None , tol = 0 ,
@@ -414,10 +425,16 @@ def image_comparison(baseline_images=None, extensions=None, tol=0,
414
425
#default no kwargs to savefig
415
426
savefig_kwarg = dict ()
416
427
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 )
428
+ if is_called_from_pytest ():
429
+ return _pytest_image_comparison (
430
+ baseline_images = baseline_images , extensions = extensions , tol = tol ,
431
+ freetype_version = freetype_version , remove_text = remove_text ,
432
+ savefig_kwargs = savefig_kwarg , style = style )
433
+ else :
434
+ return ImageComparisonDecorator (
435
+ baseline_images = baseline_images , extensions = extensions , tol = tol ,
436
+ freetype_version = freetype_version , remove_text = remove_text ,
437
+ savefig_kwargs = savefig_kwarg , style = style )
421
438
422
439
423
440
def _image_directories (func ):
0 commit comments