@@ -155,6 +155,12 @@ def pytest_addoption(parser):
155
155
results_path_help = "directory for test results, relative to location where py.test is run"
156
156
group .addoption ('--mpl-results-path' , help = results_path_help , action = 'store' )
157
157
parser .addini ('mpl-results-path' , help = results_path_help )
158
+
159
+ results_always_help = "Always generate result images, not just for failed tests."
160
+ group .addoption ('--mpl-results-always' , action = 'store_true' ,
161
+ help = results_always_help )
162
+ parser .addini ('mpl-results-always' , help = results_always_help )
163
+
158
164
parser .addini ('mpl-use-full-test-name' , help = "use fully qualified test name as the filename." ,
159
165
type = 'bool' )
160
166
@@ -175,6 +181,8 @@ def pytest_configure(config):
175
181
results_dir = config .getoption ("--mpl-results-path" ) or config .getini ("mpl-results-path" )
176
182
hash_library = config .getoption ("--mpl-hash-library" )
177
183
generate_summary = config .getoption ("--mpl-generate-summary" )
184
+ results_always = (config .getoption ("--mpl-results-always" ) or
185
+ config .getini ("mpl-results-always" ))
178
186
179
187
if config .getoption ("--mpl-baseline-relative" ):
180
188
baseline_relative_dir = config .getoption ("--mpl-baseline-path" )
@@ -205,7 +213,8 @@ def pytest_configure(config):
205
213
results_dir = results_dir ,
206
214
hash_library = hash_library ,
207
215
generate_hash_library = generate_hash_lib ,
208
- generate_summary = generate_summary ))
216
+ generate_summary = generate_summary ,
217
+ results_always = results_always ))
209
218
210
219
else :
211
220
@@ -262,7 +271,8 @@ def __init__(self,
262
271
results_dir = None ,
263
272
hash_library = None ,
264
273
generate_hash_library = None ,
265
- generate_summary = None
274
+ generate_summary = None ,
275
+ results_always = False
266
276
):
267
277
self .config = config
268
278
self .baseline_dir = baseline_dir
@@ -274,6 +284,7 @@ def __init__(self,
274
284
if generate_summary and generate_summary .lower () not in ("html" ,):
275
285
raise ValueError (f"The mpl summary type '{ generate_summary } ' is not supported." )
276
286
self .generate_summary = generate_summary
287
+ self .results_always = results_always
277
288
278
289
# Generate the containing dir for all test results
279
290
if not self .results_dir :
@@ -282,6 +293,7 @@ def __init__(self,
282
293
283
294
# We need global state to store all the hashes generated over the run
284
295
self ._generated_hash_library = {}
296
+ self ._test_results = {}
285
297
286
298
def get_compare (self , item ):
287
299
"""
@@ -389,7 +401,6 @@ def generate_baseline_image(self, item, fig):
389
401
** savefig_kwargs )
390
402
391
403
close_mpl_figure (fig )
392
- pytest .skip ("Skipping test, since generating image" )
393
404
394
405
def generate_image_hash (self , item , fig ):
395
406
"""
@@ -455,6 +466,10 @@ def load_hash_library(self, library_path):
455
466
return json .load (fp )
456
467
457
468
def compare_image_to_hash_library (self , item , fig , result_dir ):
469
+ new_test = False
470
+ hash_comparison_pass = False
471
+ baseline_image_path = None
472
+
458
473
compare = self .get_compare (item )
459
474
savefig_kwargs = compare .kwargs .get ('savefig_kwargs' , {})
460
475
@@ -470,41 +485,59 @@ def compare_image_to_hash_library(self, item, fig, result_dir):
470
485
test_hash = self .generate_image_hash (item , fig )
471
486
472
487
if hash_name not in hash_library :
473
- return (f"Hash for test '{ hash_name } ' not found in { hash_library_filename } . "
474
- f"Generated hash is { test_hash } ." )
488
+ new_test = True
489
+ error_message = (f"Hash for test '{ hash_name } ' not found in { hash_library_filename } . "
490
+ f"Generated hash is { test_hash } ." )
475
491
476
- if test_hash == hash_library [hash_name ]:
477
- return
492
+ # Save the figure for later summary (will be removed later if not needed)
493
+ test_image = (result_dir / "result.png" ).absolute ()
494
+ fig .savefig (str (test_image ), ** savefig_kwargs )
478
495
479
- error_message = (f"Hash { test_hash } doesn't match hash "
480
- f"{ hash_library [hash_name ]} in library "
481
- f"{ hash_library_filename } for test { hash_name } ." )
496
+ if not new_test :
497
+ if test_hash == hash_library [hash_name ]:
498
+ hash_comparison_pass = True
499
+ else :
500
+ error_message = (f"Hash { test_hash } doesn't match hash "
501
+ f"{ hash_library [hash_name ]} in library "
502
+ f"{ hash_library_filename } for test { hash_name } ." )
482
503
483
504
# If the compare has only been specified with hash and not baseline
484
505
# dir, don't attempt to find a baseline image at the default path.
485
- if not self .baseline_directory_specified (item ):
486
- # Save the figure for later summary
487
- test_image = (result_dir / "result.png" ).absolute ()
488
- fig .savefig (str (test_image ), ** savefig_kwargs )
506
+ if not hash_comparison_pass and not self .baseline_directory_specified (item ) or new_test :
489
507
return error_message
490
508
491
- try :
492
- baseline_image_path = self .obtain_baseline_image (item , result_dir )
493
- baseline_image = baseline_image_path
494
- baseline_image = None if not baseline_image .exists () else baseline_image
495
- except Exception :
496
- baseline_image = None
509
+ # If this is not a new test try and get the baseline image.
510
+ if not new_test :
511
+ baseline_error = None
512
+ # Ignore Errors here as it's possible the reference image dosen't exist yet.
513
+ try :
514
+ baseline_image_path = self .obtain_baseline_image (item , result_dir )
515
+ baseline_image = baseline_image_path
516
+ if baseline_image and not baseline_image .exists ():
517
+ baseline_image = None
518
+ # Get the baseline and generate a diff image, always so that
519
+ # --mpl-results-always can be respected.
520
+ baseline_comparison = self .compare_image_to_baseline (item , fig , result_dir )
521
+ except Exception as e :
522
+ baseline_image = None
523
+ baseline_error = e
524
+
525
+ # If the hash comparison passes then return
526
+ if hash_comparison_pass :
527
+ return
497
528
498
529
if baseline_image is None :
499
530
error_message += f"\n Unable to find baseline image for { item } ."
531
+ if baseline_error :
532
+ error_message += f"\n { baseline_error } "
500
533
return error_message
501
534
502
535
# Override the tolerance (if not explicitly set) to 0 as the hashes are not forgiving
503
536
tolerance = compare .kwargs .get ('tolerance' , None )
504
537
if not tolerance :
505
538
compare .kwargs ['tolerance' ] = 0
506
539
507
- comparison_error = (self . compare_image_to_baseline ( item , fig , result_dir ) or
540
+ comparison_error = (baseline_comparison or
508
541
"\n However, the comparison to the baseline image succeeded." )
509
542
510
543
return f"{ error_message } \n { comparison_error } "
@@ -548,14 +581,17 @@ def item_function_wrapper(*args, **kwargs):
548
581
if remove_text :
549
582
remove_ticks_and_titles (fig )
550
583
584
+ test_name = self .generate_test_name (item )
585
+
551
586
# What we do now depends on whether we are generating the
552
587
# reference images or simply running the test.
553
588
if self .generate_dir is not None :
554
589
self .generate_baseline_image (item , fig )
590
+ if self .generate_hash_library is None :
591
+ pytest .skip ("Skipping test, since generating image." )
555
592
556
593
if self .generate_hash_library is not None :
557
- hash_name = self .generate_test_name (item )
558
- self ._generated_hash_library [hash_name ] = self .generate_image_hash (item , fig )
594
+ self ._generated_hash_library [test_name ] = self .generate_image_hash (item , fig )
559
595
560
596
# Only test figures if not generating images
561
597
if self .generate_dir is None :
@@ -571,8 +607,11 @@ def item_function_wrapper(*args, **kwargs):
571
607
572
608
close_mpl_figure (fig )
573
609
610
+ self ._test_results [str (pathify (test_name ))] = msg or True
611
+
574
612
if msg is None :
575
- shutil .rmtree (result_dir )
613
+ if not self .results_always :
614
+ shutil .rmtree (result_dir )
576
615
else :
577
616
pytest .fail (msg , pytrace = False )
578
617
@@ -592,8 +631,10 @@ def generate_summary_html(self, dir_list):
592
631
f .write (HTML_INTRO )
593
632
594
633
for directory in dir_list :
634
+ test_name = directory .parts [- 1 ]
635
+ test_result = 'passed' if self ._test_results [test_name ] is True else 'failed'
595
636
f .write ('<tr>'
596
- f'<td>{ directory . parts [ - 1 ] } \n '
637
+ f'<td>{ test_name } ( { test_result } ) \n '
597
638
f'<td><img src="{ directory / "baseline.png" } "></td>\n '
598
639
f'<td><img src="{ directory / "result-failed-diff.png" } "></td>\n '
599
640
f'<td><img src="{ directory / "result.png" } "></td>\n '
0 commit comments