@@ -155,6 +155,12 @@ def pytest_addoption(parser):
155155 results_path_help = "directory for test results, relative to location where py.test is run"
156156 group .addoption ('--mpl-results-path' , help = results_path_help , action = 'store' )
157157 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+
158164 parser .addini ('mpl-use-full-test-name' , help = "use fully qualified test name as the filename." ,
159165 type = 'bool' )
160166
@@ -175,6 +181,8 @@ def pytest_configure(config):
175181 results_dir = config .getoption ("--mpl-results-path" ) or config .getini ("mpl-results-path" )
176182 hash_library = config .getoption ("--mpl-hash-library" )
177183 generate_summary = config .getoption ("--mpl-generate-summary" )
184+ results_always = (config .getoption ("--mpl-results-always" ) or
185+ config .getini ("mpl-results-always" ))
178186
179187 if config .getoption ("--mpl-baseline-relative" ):
180188 baseline_relative_dir = config .getoption ("--mpl-baseline-path" )
@@ -205,7 +213,8 @@ def pytest_configure(config):
205213 results_dir = results_dir ,
206214 hash_library = hash_library ,
207215 generate_hash_library = generate_hash_lib ,
208- generate_summary = generate_summary ))
216+ generate_summary = generate_summary ,
217+ results_always = results_always ))
209218
210219 else :
211220
@@ -262,7 +271,8 @@ def __init__(self,
262271 results_dir = None ,
263272 hash_library = None ,
264273 generate_hash_library = None ,
265- generate_summary = None
274+ generate_summary = None ,
275+ results_always = False
266276 ):
267277 self .config = config
268278 self .baseline_dir = baseline_dir
@@ -274,6 +284,7 @@ def __init__(self,
274284 if generate_summary and generate_summary .lower () not in ("html" ,):
275285 raise ValueError (f"The mpl summary type '{ generate_summary } ' is not supported." )
276286 self .generate_summary = generate_summary
287+ self .results_always = results_always
277288
278289 # Generate the containing dir for all test results
279290 if not self .results_dir :
@@ -282,6 +293,7 @@ def __init__(self,
282293
283294 # We need global state to store all the hashes generated over the run
284295 self ._generated_hash_library = {}
296+ self ._test_results = {}
285297
286298 def get_compare (self , item ):
287299 """
@@ -389,7 +401,6 @@ def generate_baseline_image(self, item, fig):
389401 ** savefig_kwargs )
390402
391403 close_mpl_figure (fig )
392- pytest .skip ("Skipping test, since generating image" )
393404
394405 def generate_image_hash (self , item , fig ):
395406 """
@@ -455,6 +466,10 @@ def load_hash_library(self, library_path):
455466 return json .load (fp )
456467
457468 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+
458473 compare = self .get_compare (item )
459474 savefig_kwargs = compare .kwargs .get ('savefig_kwargs' , {})
460475
@@ -470,41 +485,59 @@ def compare_image_to_hash_library(self, item, fig, result_dir):
470485 test_hash = self .generate_image_hash (item , fig )
471486
472487 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 } ." )
475491
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 )
478495
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 } ." )
482503
483504 # If the compare has only been specified with hash and not baseline
484505 # 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 :
489507 return error_message
490508
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
497528
498529 if baseline_image is None :
499530 error_message += f"\n Unable to find baseline image for { item } ."
531+ if baseline_error :
532+ error_message += f"\n { baseline_error } "
500533 return error_message
501534
502535 # Override the tolerance (if not explicitly set) to 0 as the hashes are not forgiving
503536 tolerance = compare .kwargs .get ('tolerance' , None )
504537 if not tolerance :
505538 compare .kwargs ['tolerance' ] = 0
506539
507- comparison_error = (self . compare_image_to_baseline ( item , fig , result_dir ) or
540+ comparison_error = (baseline_comparison or
508541 "\n However, the comparison to the baseline image succeeded." )
509542
510543 return f"{ error_message } \n { comparison_error } "
@@ -548,14 +581,17 @@ def item_function_wrapper(*args, **kwargs):
548581 if remove_text :
549582 remove_ticks_and_titles (fig )
550583
584+ test_name = self .generate_test_name (item )
585+
551586 # What we do now depends on whether we are generating the
552587 # reference images or simply running the test.
553588 if self .generate_dir is not None :
554589 self .generate_baseline_image (item , fig )
590+ if self .generate_hash_library is None :
591+ pytest .skip ("Skipping test, since generating image." )
555592
556593 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 )
559595
560596 # Only test figures if not generating images
561597 if self .generate_dir is None :
@@ -571,8 +607,11 @@ def item_function_wrapper(*args, **kwargs):
571607
572608 close_mpl_figure (fig )
573609
610+ self ._test_results [str (pathify (test_name ))] = msg or True
611+
574612 if msg is None :
575- shutil .rmtree (result_dir )
613+ if not self .results_always :
614+ shutil .rmtree (result_dir )
576615 else :
577616 pytest .fail (msg , pytrace = False )
578617
@@ -592,8 +631,10 @@ def generate_summary_html(self, dir_list):
592631 f .write (HTML_INTRO )
593632
594633 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'
595636 f .write ('<tr>'
596- f'<td>{ directory . parts [ - 1 ] } \n '
637+ f'<td>{ test_name } ( { test_result } ) \n '
597638 f'<td><img src="{ directory / "baseline.png" } "></td>\n '
598639 f'<td><img src="{ directory / "result-failed-diff.png" } "></td>\n '
599640 f'<td><img src="{ directory / "result.png" } "></td>\n '
0 commit comments