From 35404a0d18eab80843638b5442f87f71e7e976cf Mon Sep 17 00:00:00 2001 From: Conor MacBride Date: Thu, 3 Feb 2022 22:11:35 +0000 Subject: [PATCH 01/22] Helpers to diff JSON summary --- tests/subtests/helpers.py | 96 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 tests/subtests/helpers.py diff --git a/tests/subtests/helpers.py b/tests/subtests/helpers.py new file mode 100644 index 00000000..be62d17a --- /dev/null +++ b/tests/subtests/helpers.py @@ -0,0 +1,96 @@ +import re +from pathlib import Path + +__all__ = ['diff_summary', 'assert_existence'] + + +class MatchError(Exception): + pass + + +def diff_summary(baseline, result): + """Diff a pytest-mpl summary dictionary.""" + # Get test names + baseline_tests = set(baseline.keys()) + result_tests = set(result.keys()) + + # Test names must be identical + diff_set(baseline_tests, result_tests, error='Test names are not identical.') + + for test in baseline_tests: + + # Get baseline and result summary for the specific test + baseline_summary = baseline[test] + result_summary = result[test] + + # Get keys of recorded items + baseline_keys = set(baseline_summary.keys()) + result_keys = set(result_summary.keys()) + + # Summaries must have the same keys + diff_set(baseline_keys, result_keys, error=f'Summary for {test} is not identical.') + + for key in baseline_keys: + error = f'Summary item {key} for {test} does not match.\n' + diff_dict_item(baseline_summary[key], result_summary[key], error=error) + + +def diff_set(baseline, result, error=''): + """Raise and show the difference between Python sets.""" + if baseline != result: + missing_from_result = baseline - result + missing_from_baseline = result - baseline + if len(missing_from_result) > 0: + error += f'\nKeys {sorted(missing_from_result)} missing from the result.' + if len(missing_from_baseline) > 0: + error += f'\nKeys {sorted(missing_from_baseline)} missing from the baseline.' + raise MatchError(error) + + +def diff_dict_item(baseline, result, error=''): + """Diff a specific item in a pytest-mpl summary dictionary.""" + # Comparison makes the following (good) assumptions + expected_types = (str, int, float, bool, type(None)) + assert isinstance(baseline, expected_types) + assert isinstance(result, expected_types) + + # Prepare error message + error += f'Baseline:\n"{baseline}"\n\n' + error += f'Result:\n"{result}"\n' + + # Matching items must have the same type + if type(baseline) is not type(result): + raise MatchError(error + '\nTypes are not equal.\n') + + # Handle regex in baseline string (so things like paths can be ignored) + if isinstance(baseline, str) and baseline.startswith('REGEX:'): + if re.fullmatch(baseline[6:], result) is not None: + return + + # Handle bool and NoneType + if isinstance(baseline, (bool, type(None))) and baseline is result: + return + + # Handle str, int and float + if baseline == result: + return + + raise MatchError(error) + + +def assert_existence(summary, items=('baseline_image', 'diff_image', 'result_image'), path=''): + """Assert that images included in a pytest-mpl summary exist. + + Parameters + ---------- + summary : dict + The pytest-mpl summary dictionary to check. + items : tuple or list, optional + The image keys to check if reported. + path : str or path_like, optional, default='' + Path to results directory. Defaults to current directory. + """ + for test in summary.values(): + for item in items: + if test[item] is not None: + assert (Path(path) / test[item]).exists() From 5f66e2e747cfaf661c1f4d1dd03fd26ea39d86de Mon Sep 17 00:00:00 2001 From: Conor MacBride Date: Thu, 3 Feb 2022 22:17:15 +0000 Subject: [PATCH 02/22] Runner for subtests --- tests/subtests/test_subtest.py | 71 ++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 tests/subtests/test_subtest.py diff --git a/tests/subtests/test_subtest.py b/tests/subtests/test_subtest.py new file mode 100644 index 00000000..57ca4ed6 --- /dev/null +++ b/tests/subtests/test_subtest.py @@ -0,0 +1,71 @@ +import sys +import json +import shutil +import subprocess +from pathlib import Path + +from helpers import assert_existence, diff_summary + +TEST_FILE = Path(__file__).parent / 'subtest.py' + +# Set to True to replace existing baseline summaries with current result +UPDATE_BASELINE_SUMMARIES = True + + +def run_subtest(baseline_summary, tmp_path, args, summaries=None, xfail=True): + """ Run pytest (within pytest) and check JSON summary report. + + Parameters + ---------- + baseline_summary : str + String of the filename without extension for the baseline summary. + tmp_path : pathlib.Path + Path of a temporary directory to store results. + args : list + Extra arguments to pass to pytest. + summaries : tuple or list or set, optional, default=[] + Summaries to generate in addition to `json`. + xfail : bool, optional, default=True + Whether the overall pytest run should fail. + """ + # Parse arguments + if summaries is None: + summaries = [] + assert isinstance(summaries, (tuple, list, set)) + summaries = ','.join({'json'} | set(summaries)) + + # Create the results path + results_path = tmp_path / 'results' + results_path.mkdir() + + # Configure the arguments to run the test + pytest_args = [sys.executable, '-m', 'pytest', str(TEST_FILE)] + mpl_args = ['--mpl', rf'--mpl-results-path={results_path.as_posix()}', + f'--mpl-generate-summary={summaries}'] + + # Run the test and record exit status + status = subprocess.call(pytest_args + mpl_args + args) + + # Ensure exit status is as expected + if xfail: + assert status != 0 + else: + assert status == 0 + + # Load summaries + baseline_path = Path(__file__).parent / 'summaries' + baseline_file = baseline_path / (baseline_summary + '.json') + with open(baseline_file, 'r') as f: + baseline_summary = json.load(f) + results_file = results_path / 'results.json' + with open(results_file, 'r') as f: + result_summary = json.load(f) + + if UPDATE_BASELINE_SUMMARIES: + shutil.copy(results_file, baseline_file) + + # Compare summaries + diff_summary(baseline_summary, result_summary) + + # Ensure reported images exist + assert_existence(result_summary, path=results_path) From 9f7deb0d3b0d194d3d16f07308aded9bb0d1c8eb Mon Sep 17 00:00:00 2001 From: Conor MacBride Date: Fri, 4 Feb 2022 19:05:50 +0000 Subject: [PATCH 03/22] Support for version specific patches --- tests/subtests/__init__.py | 0 tests/subtests/helpers.py | 54 ++++++++++++++++++++++++++++++++-- tests/subtests/test_subtest.py | 50 ++++++++++++++++++++++++------- 3 files changed, 92 insertions(+), 12 deletions(-) create mode 100644 tests/subtests/__init__.py diff --git a/tests/subtests/__init__.py b/tests/subtests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/subtests/helpers.py b/tests/subtests/helpers.py index be62d17a..934df7c1 100644 --- a/tests/subtests/helpers.py +++ b/tests/subtests/helpers.py @@ -1,15 +1,21 @@ import re +import json from pathlib import Path -__all__ = ['diff_summary', 'assert_existence'] +__all__ = ['diff_summary', 'assert_existence', 'replace_baseline_hash', 'patch_summary'] class MatchError(Exception): pass -def diff_summary(baseline, result): +def diff_summary(baseline, result, hash_library=None): """Diff a pytest-mpl summary dictionary.""" + if hash_library and hash_library.exists(): + # Load "correct" baseline hashes + with open(hash_library, 'r') as f: + hash_library = json.load(f) + # Get test names baseline_tests = set(baseline.keys()) result_tests = set(result.keys()) @@ -23,6 +29,10 @@ def diff_summary(baseline, result): baseline_summary = baseline[test] result_summary = result[test] + # Swap the baseline hashes in the summary for the baseline hashes in the hash library + if hash_library: + baseline_summary = replace_baseline_hash(baseline_summary, hash_library[test]) + # Get keys of recorded items baseline_keys = set(baseline_summary.keys()) result_keys = set(result_summary.keys()) @@ -78,6 +88,46 @@ def diff_dict_item(baseline, result, error=''): raise MatchError(error) +def patch_summary(summary, patch_file): + """Replace in `summary` any items defined in `patch_file`.""" + # By only applying patches, changes between MPL versions are more obvious. + with open(patch_file, 'r') as f: + patch = json.load(f) + for test, test_summary in patch.items(): + for k, v in test_summary.items(): + summary[test][k] = v + return summary + + +def replace_baseline_hash(summary, new_baseline): + """Replace a baseline hash in a pytest-mpl summary with a different baseline. + + Result hashes which match the existing baseline are also updated. + + Parameters + ---------- + summary : dict + A single test from a pytest-mpl summary. + new_baseline : str + The new baseline. + """ + assert isinstance(new_baseline, str) + old_baseline = summary['baseline_hash'] + if not isinstance(old_baseline, str) or old_baseline == new_baseline: + return summary # Either already correct or missing + + # If the old result hash matches the old baseline hash, also update the result hash + old_result = summary['result_hash'] + if isinstance(old_result, str) and old_result == old_baseline: + summary['result_hash'] = new_baseline + + # Update the baseline hash + summary['baseline_hash'] = new_baseline + summary['status_msg'] = summary['status_msg'].replace(old_baseline, new_baseline) + + return summary + + def assert_existence(summary, items=('baseline_image', 'diff_image', 'result_image'), path=''): """Assert that images included in a pytest-mpl summary exist. diff --git a/tests/subtests/test_subtest.py b/tests/subtests/test_subtest.py index 57ca4ed6..033e59dc 100644 --- a/tests/subtests/test_subtest.py +++ b/tests/subtests/test_subtest.py @@ -4,20 +4,36 @@ import subprocess from pathlib import Path -from helpers import assert_existence, diff_summary +import matplotlib +import matplotlib.ft2font +from packaging.version import Version + +from .helpers import assert_existence, diff_summary, patch_summary + +# Handle Matplotlib and FreeType versions +MPL_VERSION = Version(matplotlib.__version__) +FTV = matplotlib.ft2font.__freetype_version__.replace('.', '') +VERSION_ID = f"mpl{MPL_VERSION.major}{MPL_VERSION.minor}_ft{FTV}" +HASH_LIBRARY = Path(__file__).parent / 'hashes' / (VERSION_ID + ".json") +HASH_LIBRARY_FLAG = rf'--mpl-hash-library={HASH_LIBRARY}' TEST_FILE = Path(__file__).parent / 'subtest.py' -# Set to True to replace existing baseline summaries with current result -UPDATE_BASELINE_SUMMARIES = True +# Global settings to update baselines when running pytest +# Note: when updating baseline make sure you don't commit "fixes" +# for tests that are expected to fail +# (See also `run_subtest` argument `update_baseline` and `update_summary`.) +UPDATE_BASELINE = False # baseline images and hashes +UPDATE_SUMMARY = False # baseline summaries -def run_subtest(baseline_summary, tmp_path, args, summaries=None, xfail=True): +def run_subtest(baseline_summary_name, tmp_path, args, summaries=None, xfail=True, + update_baseline=UPDATE_BASELINE, update_summary=UPDATE_SUMMARY): """ Run pytest (within pytest) and check JSON summary report. Parameters ---------- - baseline_summary : str + baseline_summary_name : str String of the filename without extension for the baseline summary. tmp_path : pathlib.Path Path of a temporary directory to store results. @@ -42,10 +58,19 @@ def run_subtest(baseline_summary, tmp_path, args, summaries=None, xfail=True): pytest_args = [sys.executable, '-m', 'pytest', str(TEST_FILE)] mpl_args = ['--mpl', rf'--mpl-results-path={results_path.as_posix()}', f'--mpl-generate-summary={summaries}'] + if update_baseline: + mpl_args += ['--mpl-generate-path=baseline'] + if HASH_LIBRARY.exists(): + mpl_args += [rf'--mpl-generate-hash-library={HASH_LIBRARY}'] # Run the test and record exit status status = subprocess.call(pytest_args + mpl_args + args) + # If updating baseline, don't check summaries + if update_baseline: + assert status == 0 + return + # Ensure exit status is as expected if xfail: assert status != 0 @@ -54,18 +79,23 @@ def run_subtest(baseline_summary, tmp_path, args, summaries=None, xfail=True): # Load summaries baseline_path = Path(__file__).parent / 'summaries' - baseline_file = baseline_path / (baseline_summary + '.json') + baseline_file = baseline_path / (baseline_summary_name + '.json') + results_file = results_path / 'results.json' + if update_summary: + shutil.copy(results_file, baseline_file) with open(baseline_file, 'r') as f: baseline_summary = json.load(f) - results_file = results_path / 'results.json' with open(results_file, 'r') as f: result_summary = json.load(f) - if UPDATE_BASELINE_SUMMARIES: - shutil.copy(results_file, baseline_file) + # Apply version specific patches + patch = baseline_path / (baseline_summary_name + f'_{VERSION_ID}.patch.json') + if patch.exists(): + baseline_summary = patch_summary(baseline_summary, patch) + # Note: version specific hashes should be handled by diff_summary instead # Compare summaries - diff_summary(baseline_summary, result_summary) + diff_summary(baseline_summary, result_summary, hash_library=HASH_LIBRARY) # Ensure reported images exist assert_existence(result_summary, path=results_path) From 2854e03057e4bd3ff184cb76e8a3dd62c713c2de Mon Sep 17 00:00:00 2001 From: Conor MacBride Date: Fri, 4 Feb 2022 22:27:41 +0000 Subject: [PATCH 04/22] Only show match errors at end of test --- tests/subtests/helpers.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/subtests/helpers.py b/tests/subtests/helpers.py index 934df7c1..b1635e3d 100644 --- a/tests/subtests/helpers.py +++ b/tests/subtests/helpers.py @@ -23,6 +23,8 @@ def diff_summary(baseline, result, hash_library=None): # Test names must be identical diff_set(baseline_tests, result_tests, error='Test names are not identical.') + item_match_errors = [] # Raise a MatchError for all mismatched values at the end + for test in baseline_tests: # Get baseline and result summary for the specific test @@ -42,7 +44,13 @@ def diff_summary(baseline, result, hash_library=None): for key in baseline_keys: error = f'Summary item {key} for {test} does not match.\n' - diff_dict_item(baseline_summary[key], result_summary[key], error=error) + try: + diff_dict_item(baseline_summary[key], result_summary[key], error=error) + except MatchError as e: + item_match_errors.append(str(e)) + + if len(item_match_errors) > 0: + raise MatchError('\n\n----------\n\n'.join(item_match_errors)) def diff_set(baseline, result, error=''): From 7e95ae844f39de06fa47771bc701539844bedb62 Mon Sep 17 00:00:00 2001 From: Conor MacBride Date: Fri, 4 Feb 2022 22:34:46 +0000 Subject: [PATCH 05/22] Hash libraries for baseline and result --- tests/subtests/helpers.py | 72 ++++++++++++++++++++++------------ tests/subtests/test_subtest.py | 4 +- 2 files changed, 49 insertions(+), 27 deletions(-) diff --git a/tests/subtests/helpers.py b/tests/subtests/helpers.py index b1635e3d..6fabe30e 100644 --- a/tests/subtests/helpers.py +++ b/tests/subtests/helpers.py @@ -2,19 +2,39 @@ import json from pathlib import Path -__all__ = ['diff_summary', 'assert_existence', 'replace_baseline_hash', 'patch_summary'] +__all__ = ['diff_summary', 'assert_existence', 'patch_summary'] class MatchError(Exception): pass -def diff_summary(baseline, result, hash_library=None): - """Diff a pytest-mpl summary dictionary.""" - if hash_library and hash_library.exists(): +def diff_summary(baseline, result, baseline_hash_library=None, result_hash_library=None): + """Diff a pytest-mpl summary dictionary. + + Parameters + ---------- + baseline : dict + Baseline pytest-mpl summary. + result : dict + Generated result pytest-mpl summary. + baseline_hash_library : Path, optional, default=None + Path to the baseline hash library. + Baseline hashes in the baseline summary are updated to these values + to handle different Matplotlib versions. + result_hash_library : Path, optional, default=None + Path to the "baseline" image hash library. + Result hashes in the baseline summary are updated to these values + to handle different Matplotlib versions. + """ + if baseline_hash_library and baseline_hash_library.exists(): # Load "correct" baseline hashes - with open(hash_library, 'r') as f: - hash_library = json.load(f) + with open(baseline_hash_library, 'r') as f: + baseline_hash_library = json.load(f) + if result_hash_library and result_hash_library.exists(): + # Load "correct" result hashes + with open(result_hash_library, 'r') as f: + result_hash_library = json.load(f) # Get test names baseline_tests = set(baseline.keys()) @@ -31,9 +51,14 @@ def diff_summary(baseline, result, hash_library=None): baseline_summary = baseline[test] result_summary = result[test] - # Swap the baseline hashes in the summary for the baseline hashes in the hash library - if hash_library: - baseline_summary = replace_baseline_hash(baseline_summary, hash_library[test]) + # Swap the baseline and result hashes in the summary + # for the corresponding hashes in each hash library + if baseline_hash_library: + baseline_summary = replace_hash(baseline_summary, 'baseline_hash', + baseline_hash_library[test]) + if result_hash_library: + baseline_summary = replace_hash(baseline_summary, 'result_hash', + result_hash_library[test]) # Get keys of recorded items baseline_keys = set(baseline_summary.keys()) @@ -107,31 +132,26 @@ def patch_summary(summary, patch_file): return summary -def replace_baseline_hash(summary, new_baseline): - """Replace a baseline hash in a pytest-mpl summary with a different baseline. - - Result hashes which match the existing baseline are also updated. +def replace_hash(summary, hash_key, new_hash): + """Replace a hash in a pytest-mpl summary with a different hash. Parameters ---------- summary : dict A single test from a pytest-mpl summary. - new_baseline : str - The new baseline. + hash_key : str + Key of the hash. Either `baseline_hash` or `result_hash`. + new_hash : str + The new hash. """ - assert isinstance(new_baseline, str) - old_baseline = summary['baseline_hash'] - if not isinstance(old_baseline, str) or old_baseline == new_baseline: + assert isinstance(new_hash, str) + old_hash = summary[hash_key] + if not isinstance(old_hash, str) or old_hash == new_hash: return summary # Either already correct or missing - # If the old result hash matches the old baseline hash, also update the result hash - old_result = summary['result_hash'] - if isinstance(old_result, str) and old_result == old_baseline: - summary['result_hash'] = new_baseline - - # Update the baseline hash - summary['baseline_hash'] = new_baseline - summary['status_msg'] = summary['status_msg'].replace(old_baseline, new_baseline) + # Update the hash + summary[hash_key] = new_hash + summary['status_msg'] = summary['status_msg'].replace(old_hash, new_hash) return summary diff --git a/tests/subtests/test_subtest.py b/tests/subtests/test_subtest.py index 033e59dc..137d9c8d 100644 --- a/tests/subtests/test_subtest.py +++ b/tests/subtests/test_subtest.py @@ -15,6 +15,7 @@ FTV = matplotlib.ft2font.__freetype_version__.replace('.', '') VERSION_ID = f"mpl{MPL_VERSION.major}{MPL_VERSION.minor}_ft{FTV}" HASH_LIBRARY = Path(__file__).parent / 'hashes' / (VERSION_ID + ".json") +RESULT_LIBRARY = Path(__file__).parent / 'result_hashes' / (VERSION_ID + ".json") HASH_LIBRARY_FLAG = rf'--mpl-hash-library={HASH_LIBRARY}' TEST_FILE = Path(__file__).parent / 'subtest.py' @@ -95,7 +96,8 @@ def run_subtest(baseline_summary_name, tmp_path, args, summaries=None, xfail=Tru # Note: version specific hashes should be handled by diff_summary instead # Compare summaries - diff_summary(baseline_summary, result_summary, hash_library=HASH_LIBRARY) + diff_summary(baseline_summary, result_summary, + baseline_hash_library=HASH_LIBRARY, result_hash_library=RESULT_LIBRARY) # Ensure reported images exist assert_existence(result_summary, path=results_path) From 2796fad4debdc3836db3c4f02b8ac1b4061f1ca9 Mon Sep 17 00:00:00 2001 From: Conor MacBride Date: Fri, 4 Feb 2022 22:47:57 +0000 Subject: [PATCH 06/22] Add subtests --- tests/subtests/subtest.py | 115 +++++++++++++++++++++++++++++++++ tests/subtests/test_subtest.py | 47 ++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 tests/subtests/subtest.py diff --git a/tests/subtests/subtest.py b/tests/subtests/subtest.py new file mode 100644 index 00000000..e97066e4 --- /dev/null +++ b/tests/subtests/subtest.py @@ -0,0 +1,115 @@ +import matplotlib.pyplot as plt +import pytest + + +def plot(line, **kwargs): + fig = plt.figure() + ax = fig.add_subplot(1, 1, 1) + ax.plot(line, **kwargs) + return fig + + +# ### Test all permutations of: +# baseline hash: match, diff, or missing +# baseline image: match, diff, or missing + +# hash match + +@pytest.mark.mpl_image_compare() +def test_hmatch_imatch(): + return plot([1, 2, 3, 4]) + + +@pytest.mark.mpl_image_compare() +def test_hmatch_idiff(): + return plot([1, 3, 2, 4]) + + +@pytest.mark.mpl_image_compare() +def test_hmatch_imissing(): + return plot([4, 3, 2, 1]) + + +# hash diff + +@pytest.mark.mpl_image_compare() +def test_hdiff_imatch(): + return plot([1, 4, 2, 3]) + + +@pytest.mark.mpl_image_compare() +def test_hdiff_idiff(): + return plot([1, 2, 4, 3]) + + +@pytest.mark.mpl_image_compare() +def test_hdiff_imissing(): + return plot([3, 2, 4, 1]) + + +# hash missing + +@pytest.mark.mpl_image_compare() +def test_hmissing_imatch(): + return plot([1, 3, 4, 2]) + + +@pytest.mark.mpl_image_compare() +def test_hmissing_idiff(): + return plot([1, 4, 3, 2]) + + +@pytest.mark.mpl_image_compare() +def test_hmissing_imissing(): + return plot([2, 4, 3, 1]) + + +# ### Specialized tests + +# Tolerance: high to force image match +@pytest.mark.mpl_image_compare(tolerance=200) +def test_hdiff_imatch_tolerance(): + return plot([1, 2, 3, 4], linestyle='--') + + +# Tolerance: non-default to verify option recorded in JSON +@pytest.mark.mpl_image_compare(tolerance=3) +def test_hdiff_idiff_tolerance(): + return plot([1, 2, 3, 4], linestyle='--') + + +# Savefig kwargs +@pytest.mark.mpl_image_compare(savefig_kwargs={'facecolor': 'r'}) +def test_hdiff_imatch_savefig(): + return plot([1, 2, 3, 4]) + + +# Different baseline directory +# TODO: Test with a remote `baseline_dir` +@pytest.mark.mpl_image_compare(baseline_dir='baseline/other') +def test_hdiff_imatch_baselinedir(): + return plot([4, 2, 1, 4]) + + +# Different filename +@pytest.mark.mpl_image_compare(filename='test_hdiff_imatch_filename_other.png') +def test_hdiff_imatch_filename(): + return plot([4, 2, 1, 4]) + + +# Different hash library +@pytest.mark.mpl_image_compare(hash_library='hashes/other/other.json') +def test_hdiff_imatch_filename(): + return plot([4, 2, 1, 4]) + + +# Different style +@pytest.mark.mpl_image_compare(style='fivethirtyeight') +def test_hdiff_imatch_style(): + return plot([4, 2, 1, 4]) + + +# Remove text +@pytest.mark.mpl_image_compare(remove_text=True) +def test_hdiff_imatch_removetext(): + return plot([4, 2, 1, 4]) diff --git a/tests/subtests/test_subtest.py b/tests/subtests/test_subtest.py index 137d9c8d..4bf096dd 100644 --- a/tests/subtests/test_subtest.py +++ b/tests/subtests/test_subtest.py @@ -18,6 +18,8 @@ RESULT_LIBRARY = Path(__file__).parent / 'result_hashes' / (VERSION_ID + ".json") HASH_LIBRARY_FLAG = rf'--mpl-hash-library={HASH_LIBRARY}' +BASELINE_IMAGES_FLAG = '--mpl-baseline-path=baseline' + TEST_FILE = Path(__file__).parent / 'subtest.py' # Global settings to update baselines when running pytest @@ -101,3 +103,48 @@ def run_subtest(baseline_summary_name, tmp_path, args, summaries=None, xfail=Tru # Ensure reported images exist assert_existence(result_summary, path=results_path) + + +def test_default(tmp_path): + run_subtest('test_default', tmp_path, []) + + +def test_hash(tmp_path): + run_subtest('test_hash', tmp_path, [HASH_LIBRARY_FLAG]) + + +def test_hybrid(tmp_path): + run_subtest('test_hybrid', tmp_path, [HASH_LIBRARY_FLAG, BASELINE_IMAGES_FLAG]) + + +def test_results_always(tmp_path): + run_subtest('test_results_always', tmp_path, + [HASH_LIBRARY_FLAG, BASELINE_IMAGES_FLAG, '--mpl-results-always']) + + +def test_html(tmp_path): + run_subtest('test_results_always', tmp_path, + [HASH_LIBRARY_FLAG, BASELINE_IMAGES_FLAG], summaries=['html']) + assert (tmp_path / 'results' / 'fig_comparison.html').exists() + assert (tmp_path / 'results' / 'extra.js').exists() + assert (tmp_path / 'results' / 'styles.css').exists() + + +def test_html_hashes_only(tmp_path): + run_subtest('test_html_hashes_only', tmp_path, [HASH_LIBRARY_FLAG], summaries=['html']) + assert (tmp_path / 'results' / 'fig_comparison.html').exists() + assert (tmp_path / 'results' / 'extra.js').exists() + assert (tmp_path / 'results' / 'styles.css').exists() + + +def test_html_images_only(tmp_path): + run_subtest('test_html_images_only', tmp_path, [], summaries=['html']) + assert (tmp_path / 'results' / 'fig_comparison.html').exists() + assert (tmp_path / 'results' / 'extra.js').exists() + assert (tmp_path / 'results' / 'styles.css').exists() + + +def test_basic_html(tmp_path): + run_subtest('test_results_always', tmp_path, + [HASH_LIBRARY_FLAG, BASELINE_IMAGES_FLAG], summaries=['basic-html']) + assert (tmp_path / 'results' / 'fig_comparison_basic.html').exists() From 49fcd8d646be3fa3c9894b3ef86903b61ed64b4d Mon Sep 17 00:00:00 2001 From: Conor MacBride Date: Fri, 4 Feb 2022 23:47:14 +0000 Subject: [PATCH 07/22] Generate baseline with full path for tox --- tests/subtests/subtest.py | 35 +++++++++++++++++----------------- tests/subtests/test_subtest.py | 6 +++++- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/tests/subtests/subtest.py b/tests/subtests/subtest.py index e97066e4..ba927e57 100644 --- a/tests/subtests/subtest.py +++ b/tests/subtests/subtest.py @@ -84,23 +84,24 @@ def test_hdiff_imatch_savefig(): return plot([1, 2, 3, 4]) -# Different baseline directory -# TODO: Test with a remote `baseline_dir` -@pytest.mark.mpl_image_compare(baseline_dir='baseline/other') -def test_hdiff_imatch_baselinedir(): - return plot([4, 2, 1, 4]) - - -# Different filename -@pytest.mark.mpl_image_compare(filename='test_hdiff_imatch_filename_other.png') -def test_hdiff_imatch_filename(): - return plot([4, 2, 1, 4]) - - -# Different hash library -@pytest.mark.mpl_image_compare(hash_library='hashes/other/other.json') -def test_hdiff_imatch_filename(): - return plot([4, 2, 1, 4]) +# TODO: Implement these path altering tests later +# # Different baseline directory +# # TODO: Test with a remote `baseline_dir` +# @pytest.mark.mpl_image_compare(baseline_dir='baseline/other') +# def test_hdiff_imatch_baselinedir(): +# return plot([4, 2, 1, 4]) +# +# +# # Different filename +# @pytest.mark.mpl_image_compare(filename='test_hdiff_imatch_filename_other.png') +# def test_hdiff_imatch_filename(): +# return plot([4, 2, 1, 4]) +# +# +# # Different hash library +# @pytest.mark.mpl_image_compare(hash_library='hashes/other/other.json') +# def test_hdiff_imatch_hashlibrary(): +# return plot([4, 2, 1, 4]) # Different style diff --git a/tests/subtests/test_subtest.py b/tests/subtests/test_subtest.py index 4bf096dd..cf578923 100644 --- a/tests/subtests/test_subtest.py +++ b/tests/subtests/test_subtest.py @@ -17,6 +17,7 @@ HASH_LIBRARY = Path(__file__).parent / 'hashes' / (VERSION_ID + ".json") RESULT_LIBRARY = Path(__file__).parent / 'result_hashes' / (VERSION_ID + ".json") HASH_LIBRARY_FLAG = rf'--mpl-hash-library={HASH_LIBRARY}' +FULL_BASELINE_PATH = Path(__file__).parent / 'baseline' BASELINE_IMAGES_FLAG = '--mpl-baseline-path=baseline' @@ -62,7 +63,7 @@ def run_subtest(baseline_summary_name, tmp_path, args, summaries=None, xfail=Tru mpl_args = ['--mpl', rf'--mpl-results-path={results_path.as_posix()}', f'--mpl-generate-summary={summaries}'] if update_baseline: - mpl_args += ['--mpl-generate-path=baseline'] + mpl_args += [rf'--mpl-generate-path={FULL_BASELINE_PATH}'] if HASH_LIBRARY.exists(): mpl_args += [rf'--mpl-generate-hash-library={HASH_LIBRARY}'] @@ -72,6 +73,9 @@ def run_subtest(baseline_summary_name, tmp_path, args, summaries=None, xfail=Tru # If updating baseline, don't check summaries if update_baseline: assert status == 0 + if HASH_LIBRARY.exists(): + # Keep in sync. Use `git add -p` to commit specific lines. + shutil.copy(HASH_LIBRARY, RESULT_LIBRARY) return # Ensure exit status is as expected From cd4575386b93f16157328d4b56175ba1050295ce Mon Sep 17 00:00:00 2001 From: Conor MacBride Date: Sat, 5 Feb 2022 00:00:41 +0000 Subject: [PATCH 08/22] Skip tests without required libraries --- tests/subtests/test_subtest.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/subtests/test_subtest.py b/tests/subtests/test_subtest.py index cf578923..3e0276c4 100644 --- a/tests/subtests/test_subtest.py +++ b/tests/subtests/test_subtest.py @@ -7,6 +7,7 @@ import matplotlib import matplotlib.ft2font from packaging.version import Version +import pytest from .helpers import assert_existence, diff_summary, patch_summary @@ -113,19 +114,23 @@ def test_default(tmp_path): run_subtest('test_default', tmp_path, []) +@pytest.mark.skipif(not HASH_LIBRARY.exists(), reason="No hash library for this mpl version") def test_hash(tmp_path): run_subtest('test_hash', tmp_path, [HASH_LIBRARY_FLAG]) +@pytest.mark.skipif(not HASH_LIBRARY.exists(), reason="No hash library for this mpl version") def test_hybrid(tmp_path): run_subtest('test_hybrid', tmp_path, [HASH_LIBRARY_FLAG, BASELINE_IMAGES_FLAG]) +@pytest.mark.skipif(not HASH_LIBRARY.exists(), reason="No hash library for this mpl version") def test_results_always(tmp_path): run_subtest('test_results_always', tmp_path, [HASH_LIBRARY_FLAG, BASELINE_IMAGES_FLAG, '--mpl-results-always']) +@pytest.mark.skipif(not HASH_LIBRARY.exists(), reason="No hash library for this mpl version") def test_html(tmp_path): run_subtest('test_results_always', tmp_path, [HASH_LIBRARY_FLAG, BASELINE_IMAGES_FLAG], summaries=['html']) @@ -134,6 +139,7 @@ def test_html(tmp_path): assert (tmp_path / 'results' / 'styles.css').exists() +@pytest.mark.skipif(not HASH_LIBRARY.exists(), reason="No hash library for this mpl version") def test_html_hashes_only(tmp_path): run_subtest('test_html_hashes_only', tmp_path, [HASH_LIBRARY_FLAG], summaries=['html']) assert (tmp_path / 'results' / 'fig_comparison.html').exists() @@ -148,6 +154,7 @@ def test_html_images_only(tmp_path): assert (tmp_path / 'results' / 'styles.css').exists() +@pytest.mark.skipif(not HASH_LIBRARY.exists(), reason="No hash library for this mpl version") def test_basic_html(tmp_path): run_subtest('test_results_always', tmp_path, [HASH_LIBRARY_FLAG, BASELINE_IMAGES_FLAG], summaries=['basic-html']) From f82256d81d085deedb5e0de3dc19b20b814834cf Mon Sep 17 00:00:00 2001 From: Conor MacBride Date: Sat, 5 Feb 2022 17:18:04 +0000 Subject: [PATCH 09/22] Fix bug in html without hash comparison --- pytest_mpl/summary/templates/filter.html | 2 ++ pytest_mpl/summary/templates/result_images.html | 2 ++ 2 files changed, 4 insertions(+) diff --git a/pytest_mpl/summary/templates/filter.html b/pytest_mpl/summary/templates/filter.html index 2cf18da6..036ee80b 100644 --- a/pytest_mpl/summary/templates/filter.html +++ b/pytest_mpl/summary/templates/filter.html @@ -37,11 +37,13 @@
Show tests which have...
{{ filter_option('image-diff', 'differing images') }} {{ filter_option('image-missing', 'no baseline image') }} + {% if results.warn_missing['baseline_hash'] -%}
{{ filter_option('hash-match', 'matching hashes') }} {{ filter_option('hash-diff', 'differing hashes') }} {{ filter_option('hash-missing', 'no baseline hash') }}
+ {%- endif %}
diff --git a/pytest_mpl/summary/templates/result_images.html b/pytest_mpl/summary/templates/result_images.html index 2affdd65..5560b3ae 100644 --- a/pytest_mpl/summary/templates/result_images.html +++ b/pytest_mpl/summary/templates/result_images.html @@ -44,6 +44,7 @@
{{ r.name }}
{{ pre_data('tolerance', r.tolerance, 'Tolerance') }}
+ {% if results.warn_missing['baseline_hash'] -%}
{{ r.hash_status | hash_status_msg }}
@@ -51,6 +52,7 @@
{{ r.name }}
{{ pre_data('result_hash', r.result_hash, 'Result') }}
+ {%- endif %} From ea144f79a912cca932e621c1faf5d13c62c8a261 Mon Sep 17 00:00:00 2001 From: Conor MacBride Date: Sat, 5 Feb 2022 17:44:09 +0000 Subject: [PATCH 10/22] Add subtest baselines --- tests/subtests/baseline/test_hdiff_idiff.png | Bin 0 -> 24460 bytes .../baseline/test_hdiff_idiff_tolerance.png | Bin 0 -> 32950 bytes tests/subtests/baseline/test_hdiff_imatch.png | Bin 0 -> 24420 bytes .../baseline/test_hdiff_imatch_removetext.png | Bin 0 -> 17802 bytes .../baseline/test_hdiff_imatch_savefig.png | Bin 0 -> 17381 bytes .../baseline/test_hdiff_imatch_style.png | Bin 0 -> 26038 bytes .../baseline/test_hdiff_imatch_tolerance.png | Bin 0 -> 32950 bytes tests/subtests/baseline/test_hmatch_idiff.png | Bin 0 -> 20709 bytes tests/subtests/baseline/test_hmatch_imatch.png | Bin 0 -> 19994 bytes tests/subtests/baseline/test_hmissing_idiff.png | Bin 0 -> 23505 bytes .../subtests/baseline/test_hmissing_imatch.png | Bin 0 -> 23405 bytes tests/subtests/hashes/mpl35_ft261.json | 13 +++++++++++++ tests/subtests/result_hashes/mpl35_ft261.json | 16 ++++++++++++++++ 13 files changed, 29 insertions(+) create mode 100644 tests/subtests/baseline/test_hdiff_idiff.png create mode 100644 tests/subtests/baseline/test_hdiff_idiff_tolerance.png create mode 100644 tests/subtests/baseline/test_hdiff_imatch.png create mode 100644 tests/subtests/baseline/test_hdiff_imatch_removetext.png create mode 100644 tests/subtests/baseline/test_hdiff_imatch_savefig.png create mode 100644 tests/subtests/baseline/test_hdiff_imatch_style.png create mode 100644 tests/subtests/baseline/test_hdiff_imatch_tolerance.png create mode 100644 tests/subtests/baseline/test_hmatch_idiff.png create mode 100644 tests/subtests/baseline/test_hmatch_imatch.png create mode 100644 tests/subtests/baseline/test_hmissing_idiff.png create mode 100644 tests/subtests/baseline/test_hmissing_imatch.png create mode 100644 tests/subtests/hashes/mpl35_ft261.json create mode 100644 tests/subtests/result_hashes/mpl35_ft261.json diff --git a/tests/subtests/baseline/test_hdiff_idiff.png b/tests/subtests/baseline/test_hdiff_idiff.png new file mode 100644 index 0000000000000000000000000000000000000000..b377b07108cf3eaa0f5fc773f74df61fd97fd21a GIT binary patch literal 24460 zcmeFZcRZGT|37|MkrI`1MMfbjR93bI8QFVlpwozuJx&_R$ljsMY-MGWP$6|%AuF3~ zviZJ_Q(f0}-=FXA^ZW1j*YAGZ_jTQl^BC{>8qeqR^?s_Nbd7?XmK;G43OU)UY6wEa zgdl`EWc%PstfP-D{7cwDTE{`%#>~O#hMg&*c*DWg%ErM8bCb!@)XpAbbC-`tfam;K zraKM}w)P^tyw-o;z++=)&in1gVkNv}zpbpUJ%Ui)K>tVZN-`aTAV%wQS1)NeKcDII zaBp4NX0HA149;|gQ`^~f zl0?Xd8d!bGxX9vgBR8}(_QvMUj`Qvq3t#z~XZ74%{jg{GjF!ZvXUVv`M~QfKV(aFL z<68rsC};~(bKtTSDf)k?#Mqh8e^)#vB!qv}SpNTi{(m(Z>&wl2tsjPLUn#|lte0)K zO73Q_tvN?g=f=+LGp^l`|RT|`9B z*lYWKLW!iyVhSz)ofpf?4uOB2zkK7yjaYt6C`@(L)}n%;+f0#>{2_SDTI9`QqO}Lt z7+kI@#tCX_X+=IeDQs8Y+ZuiTO1a|_A;_lk%E5ZFW z#~kE+!@>s$#A=hL5IiY!!wksUu69OGISjO$h;lV8!8GJ>^b$ zA`TNTUcF-Py|;CQgoOqjw`|>l!%4>la(W_|Xa7ZKn|W{vs={N~Eim zj-HIaHe`hh}^O~;klzRh=Y*6bI_WqHsw;I%z%9Cgy`$5S>3m>tCgFthE>{QdpgZUu>_aqAZwL_b~`*xk`)^jHpIa2W|{^6Y=!lxxwEnYxdH_U)2_ z58YK6nZ>tj=NK3V>TeC}JI;oiwMNPW1WRnNtHlWksW`vYC@-G5y(XR@S{2wddLRgf z0*1Q}7F5*9R^j1=-qn@KwriGMxg9no>Mj#6efiyfS>6BnO*Ket@yWY)@A&WixT^U| zCiULQI=T03Tg+`# zH6+n}QLgHl9B!q{IM1{x(nXL8LDrYslyM(Ia99FdywlFy+KWrLHf06-arqz#3tmR- zVC@MirbO3YKQW?&i#5Z;<<3?=?MqX98A9bF!_Qn_8(2sXcgu8l$uL(gVBg+yaXWF3 zBSDl*gBqS%yENGA?Cwqz6zHK4CI9gwZh2BI=EAOM!qPVuRyoNnleaUdq}=VZt5&RgjGHM@2xD^-qQeeeB_KeUuoQG=JUOu<0&~3-^qe_{TplYC zPId%jM%2&=!uCu}99Nz!Zxy?tlVN{J<>uYVjp;lC4dlfu)K3t?eB;gV|JjnHgv)0`= z&*(hzg&&V`onbvAuMjEgd#IF+aqs!T@v2=T*B|>iIOHrko5dLmyu1n?-|jrS>Mn+S zK;KalRBE7h*+-+QLKi#BW7;IY+Q-%sT`OyZ%u3^bZb{_L(Y^IqBb;-zKPWz&B{fyf zrfRZ3=m67ORVgqG>KF{}$ziob3AN2%v%72er(~V_F`ZEewp<1Ovz(mT!Vld0rwN-U zulULvyPvY|87XZgV0w%H`3jd-v_f*4QbcZLr+s4jt!o*20!uDP4Lv%#Yca-N5swd^ z-@NZIH{0EMjMhSCv1;M5!ywjNk_I~{g|@F%Y~z)~Mq!M~sIBwPlK4EfwB2&-daD-? z;vEEYh-K6*kck!5jy-oN)1X^4vEcfN3>#y0sSA9SOTq-iJmh2u;`w74Q!suFW(G^p zzy1B`Q(Wx>+#A#W?FxjD5@rdJTo?(%?QG+IcB?&!^784*G?m#_O0W`?B*wl&r+JW6 z&?fSXVrav&2#faou-eJ+Vyh6ryM;^Z1jPH$FG$S()J2zxkvL|})=l{4tyDUj+qW(w z(pS(fG*-_0EYFmfEx5eC`OEI%!{`>uDBdW~oo=H;$PT&?v6Q`4h8vp$sUgfQC#ZTi zR>I`NO;~ub?~>7vC%-9ZDa@L-PO#Ib@9)-?}>k5|ghRQOhAZn;$=R6IPqoMi{>-IkO7^^+|LD z7>DiTC?(hp?E{y$mWta(K0_DpMs((t#1mtyU!twJb(}X+E9b(2N`fx;YXJ_G_2)z#>cQ!yHTt3(l=Rof}(}3v8<-IzbO&b7~_`_!Q`ab4^6A= z_N=HG{&k_-PnM_iaF%)9N`!xI2og7w|Ii#WcI2#=YGqk_#!=|o2qW6GCW-BK>Y(w& zs7$S*__V?2N|;_j%#0p7JVfa595=l!OSDh6WZ*7O?cz1-Uip&m?_hhZP0!|7R_UwoxMz-@-2Lebp)@t4_#Gaa=y~D5XVQbg zvlVTQw5=abe2ja3cBbE2lmanEt#r)+1D?s3zRiWN_8+xMU&wZwi^wgNiIwPcJc3j` zLDw(MSwoA@4&&xR_q&r`7gND& z&N5d%CtvP}&Ve@m>)?n|Fl{O6iIJI_1@R3D)mzejeiyuG5GHhpY|H<+0sDPS(Gl!7 zc&@}r8jE;-ccewCu;>WHi7y2#_c@s{27FQJ#r&ib)A{rv`1o0T15rCaD6_jBwEW#3 z_exbQUZl8f?eV9G76x{h!@Up0WuLX5)NF~xO?-tPkEgdiKc-jo2?n?r|NSu79o+gU z!uBciw!Ij4g|;-c>jzI z8~iCs=)jkd@-^p-%OnNIiKXVzy{B?@XvJZgVQ5l1uz5L2JG~rW-xX7 z=-^Rb(u?K4`LI1bsJ*IkhQG?@3)AFJUG*M>m@$9Q=ZpWhLE>h((JtAQUn zbF}-I)>NeI7FvV`Ub(H#mJL0oqdt82C4^E%R^4hJKYrA%nDZeyFk*Yr&O_985KDt2 zLMlwbVzdag@V0ds!8+cx6-gC$OL6Ld4YN#9Z$&yUz4>dV?degr_7WRo%}fosbmhd( zIGd^r9>cPU(I{iB%a`9BlWcfd zY|-iGP;VZ9Na!-b$NI#bWwz}}Wv{Xcotrn4Xa%ia!AHijumC(ir(+!IMPs!Xw|3bE)a22&t(Z@-qrqsNakF5>8inzAJLhOIfs^jw4`I`@oJl9R- z)MRC!%$ANbK&r*vx_R~L)mO&Vo;2jL^74LXl%BsDYl-6$5YU8CT##`;UCLIzV%Sl}7r%#`7-38`L70T}J z6+ecDdAHVQ%jdKp{c3+>svy49%oi(cpO$`$2obxzWCrp7Wa)SctpMzNuhAw2aX0z4 zG`YIRa`kID#AM_Zj$ytqkGeBGMe2zX9?QRaZKg*)hE07hEimBG_LflcKhE8rB6rjS z+Q1-Bj?5Ppn5XV+r@F4B4Aohu?>{Lghoh8~U5|F4WRjZKyF&23z8>@C#igE7`}DhI zlWBx~B``A^b6&e~dA*0|1h0pLgwW7WcNe5X2(0^}bw6U8NMjUSwXN0j=8^VPU&f6q zlfQaXVb9&|vq~r6DZc&j2)FK=k)&IblarA+;zL!Bj!VhNJWhGHUAMWjIj5OxQ0m_2 zh)a>PfUkN>k|H&H#WqnpJ5HDpS^Is&v!f33;;ypB9;|(iaWniiyCc5{Z7nP^VWr*S zP&FAml5KDj^19GlBlYiwKrohyX?#Wx8Qu3sz4Q|9YoVJ}o3rKe za&lh{o%%*gzaU7^htGm-xk++Xe!}8^y1+9<-%VlZh;A*^y zid>e=AbYA+Fb20n|7#|U!L9kH_Ps`W_)xJo5o?q}EMFs7#e^~2V-6`t#mBbWfFWJ3ze%)BK8za zea+lKdQRb!W;W!`QEeO^zhc5;EzpNiH#AQN`OtJ;++s-UOx&Q1VVSIEmfdk;vfF5w zwqxvC7UO)3j%#JgR8-NHQ|mhIg{yjR-W!mUGR>oge9h~hAm-b76D&0?XG<);VW6s9Am~@k*Ntw{w_X}Hy894a{`Bm%*ueDCaw%PxRkj$Qxd0;Z(^(#8XkG3ay<>& z{T&T5$y|pUVhmfyn?o+IeVjNt)gCfi5hizAis?u1S%m48bswi3)YYwR87>vTJ6YQH)1>?}QGWPp3bxJS_5-c&qN%)37ZekT*4%T>+a-#CU z?vR7Lez7c_P>&KJKCi^Sc><8)01Q~9ti|%&hC2AxQgPZoch7MqbW>$)} z(Rn&bBd7Qoe)eo!YU<`(l#!>dh8oTr*^xJHF`9%C?Fzd#uLuJLOW- zT8L77=YUYj?-(vf68yl67x)$|{VS!tB}yUoQkk(Jc{|_)`UQ6M3omY)#wg@jbcV|W zfp<)>Tj?0Pmmr|<=>seRsY~ceCoo;SjcGcL`FxT?3}Pr}@XolrYWciMGtvE6A9S1v z8&(Gtl*MjzlJZ+ZBqEmMdOt&*O=gjB2Stj>$atc{gP9_DZH~WSz6g7`*c#VU1P%DW zi|=AHKBw&@4(Eh9uSm7l2W$G*#{+9e;n$iu$Z(luqCd*c^!6$d+1Ood)(wu}O09H* zR3(@Zb}Dv2K|a)vChCk*VLY+m@+@}*Pm8S4UZUY?jgL`MGt^Tp^B@fn3_r_3@fTA| z0(ZO!y1uwjGqBZ}R~@UG4cou396k~E_+ZF0#wa6?4%kchtY9tY8e;@F^QiudCyU!^ z(kAX?SZbr4Lu1K#2*_jssyTW_Hi8QtHy(75AMoS?L<_d^BAQgJ#kZxmBrxXa#aQkV zeB5$-r5_bifz3lxg{{wF%JQ45ang;awpi+RoBoOAu}wq0f=ErCRsUJVSQY@II$qrn zrEpiwGr|dU$+o4#5*xHb*Tndb!^-!2@5sxgehU6VLzt)u!R#u)&?y+e-w_F0eC@@FtPM1utr^%F=vz=%F}=p7TpUlllWk|p!Sf7lBf1FOmF(UuZ%n7T zhjW>WQf$S8uUL&QeNKt>K$n=ZoU%|<3)q%QSFn^)UkWp*kJiB=!)GHBqpusU?~f5~ zGw>my*8D8n-;vca_7M4r?^r{6V(gEFttGXN%;Gkt;P;IR$2g;ln126yS>`dn3D#kA z1`^?#_;+(QT z7UmuC~UQRDa13Nt8`VP}f@snd(0y1uhzUcm4W29+KiwqyUc{Flyg7<~_LHToR@qX`Ej z!>hk-KLDTLK1|R=^hQg(rRaVtcXVau+5N^j`IDUp=r}ckU4MnADe{GVL!418Xi)QC zpe8gvniY>}?uZl4GXN$Hcr)Goxqd^T@3s$4pFT}}>{x0fw?3zE;=s#i^rC4G9}+{L zrNpWbsR49Ecx>#{P&+cX>A7CNg`=TigFPTh;bZs*28A6*3=uicubJNTqijmAYzC@g z#a!O({3@xI3q|98-2$^wmw}%@@%VCH<*(QyM~|+&JHR{f<>eKNzOo{~h(`n0kimk- z6ns(T=RC-&-c5#g<_frdMdlCrHjUbK9~z>3^X5%^{*5c~!uB$vjz8c2oQdQy%w!OE zz0>{1)X(2Pj0M6<9RbTu4WM0&fUIfi7lOoqf=*BaQU#!O27l!dAXa)s@_vC4V=ZB` z)dP;SH5Z+f%7*r3TpxB2lRF%a<=R)l&UnEF#BeB6*E-$&YbPB)H9% z+GZP++W7?qb$yE9iinAs97;h3-ISgO@1qFqDGJ+IDTv>~b{P+PG z3}f*6zGWw5898DL2$F%-pcG$xARiOfNkwY$+|Bo7=oAU{J0}A{aQOaor(#>0V$+fY z27}4edHsX6(f--YK=lB#E# z+pL1uJN3b10g4&=|CIbJ>XmpwDll61c^IXI`n_1YyC1b4M5;z&%v0v7_{tT1AA?^u z=(jGB158If4c$dmD9aZ1_U%OqT7Fqkr`dKzp}z2GYfi1~SA~Uz3eUz*W@%*Lj!7zS z{;F$#doRV0W_RE<3im_9A00S^9N!w3#M!sXC0|pxKZTB8OdH4cdaIg6=6hRaSzIWq zLgDR?q5jWO)6@0kk6vVFA_=x%*D%_h2RmS{^_Uz86h*^O zHMzDhY|;HDl~X%6n~3z#orNFuJ+E*18o4h>M{sJ&hw@vr13dCp#qHNmRR@Pc7Impu zLF>%TxoYD9&&`~qq$K`Z4F^q4!Fqjry}q$^j7xsHGlu@b(IY9Xl0sp^r%#Km93#SR zy6;vlzdx#`7TTU21BSKs;9pGQE8!6pLz(dt>6U)H2pH$sqU*@_Ai^JP zr`d@}oZW=}~Jo0zUwv|D+*klC<@t?o{AO4j?R)D248r z9DhZGwdT^2wW;C&Qq9mU2gdU2{V5jBEVCU&B3oYaCyW+-dOc;De$RFWa&xjr2uzwF zH2a$RCp3F|?ar(4=sWvrPhK<;rJ&MbXY$NcW1SohY7uIDc?AV|(4dEqDuBU5L&T%! zI)&Xu00h2|dy}T*pTmq@2cK7SnPB18jT?~sk9EY93`EijMLPb>k0+Np_n+8LP35QX zkd0pgBrJS@vNGAKND)f8Z|xrcC}6?DZD=7xi5;}vDC}NJ*5oR1p`&^Nqws(<)wBsh z4MI{97OD13Q+e6OXKFzVW;FK&RUpe&mf)Bf;Dylh(R>>*L8cZrpO)+5?M{8d*>^z! zh+1ZNH$UbRHKvEZz~;lwcCk7E+0{6qI0dOjJdF{*Z~kn0+t0?Ld&x5_QYJ63#DDqL zJ?Akh*Xk+P&NMdO7&+RTfe?oln77;=Uzw7t^MxqM*~lteqf&(KDBQZ6iR@TyI!;cex1_yVnSw~gxpjM0fmQxoD2**Cxh$4T>$>YNUCgL_fsO3- zfI{mySWsK4T9Kv8oFYAErAU3`CUI#GE4JP^j0M0qy^^uSPk9Ve!l^TE%Vl8HCnK~` zP{PKnIT(_ovF7u6#6-1c{>75)$`I^7-I1k`=ymrO1(3V6V$p#S$Ve;1n6FgFHXG=j zz%qSPElAT1mM|rs=JZa2!HRi8hS_n)be_fZi!J%TukwKLokuZGguUt>2|q|Q5y6!elM@I=&B2v5bdNZGxD{YHXH;7WcMn8H>9w#Evx-uf_KAd z1^LZo5aN=N+k7+Ti?=fjgnKc_z`d0Y91&$SYM;P3L7wPyK*Zr(d3qGDjiXwkcpL|# zRKC2j(9t~Ly^Jbhf4D2(O!}LkSz{z8<5~2Tlm$cB(s1NRGnZJpGTMz^z;?oeWB6KF zbr2q{%Cb_cn2-voT2(3Ar}MVY&A=-v7d*VGTMkEf#W2Z1|4FV?+g z34F+65TQ>~*__0rKW~b9dUIK@*KV)lX(S;s7{nU&teip!80jD5*VByAtu3<6&2pkqNe9A zvZ!CCWKyYEOIq&G#(ghU(?K_q*NF>PMLZ2$S2WRClOnj}t(nQQ2wz;X=>>#m@AdxV_qdy)nR=Hrp2PpyR*vw`64O|Z7M!my3LTp24Bz2O4}-DILe zp=^v6v*7?;ACMvN?!9;tJZGU}PIqvIr^jTNGIS(3E>)q0tEeqq!SoaCR^rD`$T&{b zUJtu<@qn!hHPu_gr7^QB1%O=@Tc^?(nQ)Q~DnL|Tp+ogMDyP!fM=7}JeO3=hv%zw9 zruip}qp7O{Q9B#jOOCDLYOZBqbXW?Mrng)$QyQq*S1-!s_@$m6%VCf3NE3KBQgfXV z-^^a_)Q4_F{{)$CZuc^Iadz z%zBhah@%=Aqn@2S@8*f(2#9yn&^fxJRhzkmmOztY=U!rKEKi?KSzl`5m0TK=bNYU2 zrWa;}>ZHW(_ka(csO~`kBJG;hsA^eN)iD>+!JF`%h`LiCSjjQwr59llxWklVWDSpe zyTI3C_5eU5%QgaMqo+s4LL1kYQimJSy?B6xSnK_JSq=`h;RZFUZ2r3^+b$!@E@LMq z%jcT=iC?~irq$k>H?7%6~?r) zXDHF534mJZn{FH5kP4K;%jaad>bfX5S-F}LNSFGUq9x`qA{`ZB%2S!Ct4=H410MAv z1-->*sa!okOb10_2KUp8MEA8dMG1*!KuA72DW$5IU?pSH8pHeub!EiqX^JhTjewk? z$XDV5-a^SyiZQXWMDHd@K&J;2|E;ZnFE|72yH+>VVP=0Bp{81kp0}M$jY@fR03c3$ zh*ca~t(^L0`wwdIrs-^)W}X3%~y-z&7jYI+4S z8yl7dZX4r?U}x77j&U{jwr7|n(j!&%li|O9o_8}Ls0|<_tp56sg))_)y^u+B&U0H6 zAnYle^i+Qz0GOqi?>ZJZ$km~{o~e6bl<64|g;cuAu_?oH{kHmn+01Gs;KW$=DP#cT z0>a6=EkdvveyK;KvkUXck94p8&-Duem4?SzTUxc4+Og!vnD4|6VYqZod3LiNzM`T+K__1_zyoU-);C~C#Lw{-N+^!z*-4H*`=IcIyRc*>dP@|P zC9QWny0XB6VBgt^w8U)YRRJ(J4_X*PwbwUs1+DVHcl@Gw`ZT228is(q49`6~6rxMk z3or^9b6}8a9~1guJBzXkP5r44pAxX#Ww|Y{Zf)J*ZWg5uxF%@i{tWudbTH&(f|H+r7xsCs=EVeigXb?hI)q&AD+Uc z`lEo?&_njje;MB3Sb;FdIk3F77BhP4VhLXT29X3z^AX%!eNKtpw&5Waz`g~$>s2_v z_S{)FUzzyI+}bO>M~BBsc$Dp|f}K zTbl#LM^AmbeLFhjD_$i;NF@f@Ss{PHnZslnhoYKB-_XD7LTOcC@l}DuF1p(%0&;-k z6FZ5(59$?LWdJbP@?3c7O^Je{qGsJ=iiptABv7PM3kdqLiyKaLwKkhF3VaHf=5EO3 zaISr&3VJ#eLdD0Hh>c~hmWYR5PkycAk&(v(U4G9%3T-=BLx9Rofy&maa(D4=@?|Lk zkQ`d@)tAw8!u#QT_y<``JvnB}d8zZ|gYF=L`IL4IFs6Jj0~ZO1*+60PbO2@dYWSSR zg(!j8_6rQ`czvs}5E>7%fQ&DUS^o=ZKG7QV?%u_e5N0%#WDswD)erGk;n|wL&xu0* zDwbV>yR!%}=o?ZjI#Pfu(KdP=f95T4=vNgY&z4T;xVo05@_Ro}$mcPYwyAp0{5O7{a1I+&=xbYi$8^GGhaOsaJRp z3f@bKdAM3fiM!v0a*jV%i;{+cP`_w13+jUyB9+uBh&V9BN@$;D3jh> z`7b8@b@P0Md_+o+q+osBB>(1x=OA4=Mp*wRo(`u(%uc%NM8`ZPC8p)6{Z#+tF!M6# z3H0MdojSiJU*qj|&Uc#Y4^2@`mcb0Yd(=~49+Z@v%o-zpb*d{b5B8R{j0`8>s!L-n z>HsA1yDs0c@#3&8*o(=NO1!-6F`a(L46=ffK7JNd&&#bgq804~r&{&mx1v!d@Ivwr zZ>dYb>qi-Ta>&UmDQVf+EwawFaxj%n0$9}VGEQH)yR(Txp(xr12F0Q|`h2F!OP}=9 zbxmN8%kHO2?&?~!%f2jE00ylf+=IhxnLi3w? zUtjMdX7^D`O<`3?0i8`@sGxQ8&HGG}dpfe}WK>5+Fz63byr$!P&k4z>si|H(ft)MW zeMS${%J3(u_mR{o(~wt+Oj8!>awDplt8b8pG(C(Bfb+Ki*4u`SwWQ|gw=f)<>50)U zEUh^lwbaC$_O_v3uCEL)?E>^+?pRZlg5)lmqk-3`3H%fl)$cW6by~C4J3X{3>FL5R|3HUk& zhv7inV+u$aBs@@kCh9Cr+VEmE*#fikYDcAZIj#c}Its?{oNk>)Xo}6>Am7NJoQ;$aTV^Lqe>0rKJ&qJpz>DF9sz#18ML5wpS!Hq4ev$j3&lrm0$rAvq`IqPonKuWs z%BmpGX9h?$H3JE8KGVO#`W9AQ4h|UMqyI>v(Zw}j=uxy>Lu6)nL*_B0gF`Se{vr;+ zYU(Zf9^=tnzCM;O*AuK7&i@JRnW$LK$XD-h0%HQu7WzQb&O9lmPh`nY>$1-}Z=MCo zW4vbWok2k#3cw6ZqE+a?{OU=>1m@`3fI+W0zX! zl!pSu&<9596PU4zAV!;hKws$odM62;toK(q$_By(reN@P8fE;?*nY-I3Gaqu;SH>nV+b@dJpn{KrtU=GC9wV>m-Rug%!?ui)PEwdvGTn~fE1 z%m=Crh$Y;dAdDb>@Gn^^3UUmRa%zT3?0hrm0a5$!2>;Q5+sI)^^X!Em)}hg%2^GDy z(K1MxxAp_ne-w|Bnq8mkwNVJ9QxJ9hru66}7#~PY|8U(*(IBn?{4l116cy(VnuFC+ zjBDN%0*qAOOo+0%s0$*9Bc%jHU_hvVmDVdPw6hq~l2rr%tZ$48zPr3DqcuONg4akhKV|f|sEmfyBS9_I?xr5AmXI&YX z#|#+?w9i=p09)pUq$z_A`t1d1Pvgl$AXd@?jI`j|AB@zevAaNRcSo(#b?huP^6kiB zG<|M~ZwfxL21y(h@~^=Dz{{A&KU{_z;~pgfJ6i7tdzI-MC{c97ithlsFV)Y^B!y)+ zHgetdC~z5q`VH|_*~!Ubb&}kf-qiF&L9Z@6K8gVM3;L6>H{T(I#bBGjkPxKkoS*DA zPc7-cu=yC0@!n$SzcYR#ef^3q*){U?wtzdkgG7K`;2(HpsSVcGUeUHK4T>zjDN*bE z0Z2pS!y$u0cG9H}*lR_GRLq+~-G@}4bMiW?u!PYR3Xtl2YA%fbv^&!al?|pl5_lFK zd8-3dwbMX^cG=`ID(VNuY^W@+~-jV2m-XaWNnE3D3G2&q2u!SLx3+>1+NB(LxY z1mG_OK3A^?gomC6Fde+aaKm}lR3@-?44&UA-h4w9=}2%1*w?o7R6Kc)f8xV`z@(^% zl5_VQB&BiuE|8TWwx*y#x_2*N!@0ZT7ybpNhF(-~0ma&)9lJ}d@sNxpZ1)aUR z#HR=VcaTN%2^AFaNKM8I04`--A>|P0urm2pQOfToSe`z9M!e796V;ynivk%jZc7_m z)CZFT`e!WDum8Xw%s~4-s{b#5bdc9hW--^uT9A#{odibKZfFRIAvxgoZ7NO)Se`-U z+7UK7<(+>k-S;8nJQDrSClf?Kb}|T70bz-0A+KJ$EOKAo;+x9GYPB`n8!qO$0N$!? zNBjN9k+tM`+XAnz?;hb^-vSG?kA%2}F1AA`6FK+1)h6^CVk3 zB(@>{5p)tUNAJO;m>YYcEVW|2-pAixc z`u!V;NFnLDzW&pSZ)Iv@8d841yO7n*rNkCjD)<1}Ab0t^4y^>&?$D5pTkLlLHJ5 zc}kIG%bmu36wSMvfRUz-+y)>uF`IAhyptXhP$5ny<(D)M}{WQnVP@Fi;ZFH7i_$CM| zDBzJ^FjIv$9-L_FO%I9@O^m^acI=Anyj}MewT#rgPKngs8I&q8vjNqD9F-M}k=W_e zmt!Rdwn8Os5*!-o&8GaUVF@-U{~`Sm6J?EXhq+2K@)y-}2fQA(aW9yn+5HntZ3_P`8Y8m3^J-O65?PA{ zA#)^mOT!y%39MM>`v0~|Zi!yKf~bI6H91A(e#NQ_K}{Ew%l*-w`hY>ec0GJ33xps@ z$P4c!m$GoN)-NRraWxsZY8IsNyB3a1G9qhXz=~yR3LhW@WEhz*J4D~iWj)#y7vFB) z`s?THWEflA*BbjV*`c9?O)+FJ$nO{*vrE+3^V2JcGPs!hACz9fojG9)&QefLu(29- z;llS_mpzEI;L96bzEv-Ggp8PIUv$yE0<&&dxKO}lqUih970|`{nwO zrrF}PdqjwxHlBpW1)KmoI}WjzPFH%+ay<<$LtU-zF~DPP60=+DxND3t+RO40cCC%X zSgDBLMQRw){e<9Uu*z>&vUVoeoQ#zaGL7F=Yb^u` zAY~oo`#0tM$e1c7njK%kFwq}?POV~yognqkTiH4{`qPvIHiHmk zF_0B^+$5JqHx)S{=zGAgQyux7XDE;s0RrLV;qx~40&47}<$_7WIoDR&4d zb6RzfN65~;d^vV-k~j&_Cg`Oy@N46x8~SiQfU>m`CC@h7iy-h8dhZ zG&cfMwOB(0{1sZ`%L3J-!3O*`{RZ{^G}RM2fwTe|Fs9Z6RpPCiJjPW9$2c{MDs%mbMYCm&xMbBwptvEyeHV{aP;x@}s6Wd}w4cbjsvKz^G9uzeb(Z=CE2 zrCzYB%S`olQB!$Zd$WPi=i{gyDh=`T^ZT^qs9$nd^T9p}>LYdw#@0|Ret%kHg(zm0+v7TL!{NRB2V0qaIxFz93UX64VrZS z2D%b-RLxpqGeG*~(l5x&d}U~8DC#7yYSQ~ht3>P-e`nC|mIbX-K%@#~-n61xnW}Y; zml3jqO6lE+5RSk~y~FT?AsHjTNr_so{>p07$K!1h9=BaItW`)@Q#`q;m1{BPyZNgf*Q zU!zcE|KIq}dYSR+Y#$1ufe>pKv_!DSQ2g#0gLv#Tt6nJR>M|cw&~c(V6i1YR z6yKOgj=f;&cj3YX^q>T8oqQ>wK8LXM({QYS^VQ!bniK=+dZe6Ol?lV@#b>+iMUn&pUyKM-T&5 zDE$2x3ii1;tY!ce?jp9*+Q!^ahzk^OcnD;9TR>mK<@L~L_U(sD5V1^0Oaiq%e*%J6C?V)#cDZv0VC2wzi;_>>bqCsico*>Sd~jv z0Ak`1T26>adH<*IdJCLCG=fC%_7l+WE>C5l%>`!)E5ih;C8K!Y^&l-k|7KT82VMnS z3e*dDp>k?O>Sf_w?v9)$StII$_}4rNsSm!dw=gL2gs z1u4aP19X^h?-D1^=WNm&EW9jxe`Y)1!_1ptzMh_(Q0>K-(BUVMA?k1Guhm{}he7VU zvq%HE=H!+O+;zD(Q3|Lsl z@n5Y-(-#6}Qf6@<-`?M6&R%z&IEDEwU^OAydwWDMQ>g(CrMl248~Rhhi9cun!tlxO zpsyLU#;*ovLm&hJRn@nFWofh5-s!21FumgNwCj+W^rIC%AbhcO<{qAj!N1XSXuho&k+~<;x5Blf#+MjJNlFC~ZENBg+V!~dl?#GM35|=}ZcL~u@ z5n5IDMTO(Ms_QVtWDNndLJr@GwTGk(!H`|W@WCcx+Z))U|CYr$lj8?Xf*Isl&MJDv z&CrnFmxd$tQzUo328`S*c zDWf2G*>rBXMzNKy@X149Hkpi94f9RBPae9KYeO2%MT#K7>A_Seuv2BQv2G5{y^A_R zO;k~eR+9qj*XP)4y)hD|Oulu#A&@Q|P82b8$i5zPVRk=EDOC-W>?b(CyZ#=E_^s2u zMOCGEl5f`g$tx(d-S09?hr>Ny0Wm>bLWP2D_eYZcFSVbq>EX>aycgrs9%dW!7Tp1B4i#2E z2o@F=K&xFnKVCjprB5&7paO1j1&#~41K#?08;tT=(65=6m|zZdb|!Bhh5QQ8nI5;m zr96eha<25M!pv*|Q3?3v%}2|_K~R4|54uokV3odl?QRZm_22!k_(54&8hqHiE}+98 zJLWTkBQwS;Tg(4lcG-V`3d)a+mf=vMERd3GSM-5ep#8-qB3>4>(;k2Oq-8kEK~vjh zd}-|OBFl+*gaCL-KXz_zMbN;10fjKnNm2K;dDap)P|%`eT={HSQ``bHobd8L+BKh~ z9|vU_z=x;ZYJ7&--PsOJ85y~~4cjy{1sHyybS%IT>%Fyo`RiwQ^2Ed)IW^#SG^;&5 z>fgU-zi>ewCU;_!;Gm#mZ_aN*6EjS2pjMe z-(NT;+(n(1l9Im#0Z)db_2gJwi^f?xRIsl@gj(S0LEGp8CN6Dxw^e{A-tE~G zo9`%`TzKN+0Z<5{yJ~=4SB>_ZRIIqZ$o)@Hcn0VYJnL!tfeKvCWQVpj+oc&ReyLFH-Ixl(%Rwp3tidE`-Y~(&dslOO87};mZ!MzbJk!6I6&16Ri! z!T)r+cq1)U13rY&GdMI1G~Rm+&-+tz{pZRhIA*0YH==6elVj*0-pxpkP7^v z2nZE5xDq(C=(qUf4F0qOgo^*j5th9=;n2aoxc18vztGuA?N|vz5vZ1p#UDL{NU6xl zhk9=TN; zF2@6~1>^{DomYIV()aW1T}8E?UsOOK46g!(9E|Cu3@hCXCmT$|L77F@-ydw649DS8 zga!)ZS)BIGA~s2OPKBpy>fr9I5ctN7XH|y%dFJ37g zSea0!w(SaS-5b~sa4^K!^$+13@Yr5#7VIBP1p=7gC(!IaNgdpD5l*RV2sn`pmRJk*DtAoU+0~9BzX=$!J=b%B~bCnrEL=H0s-y$U$xqu$a z2GvFwIEVs0rHkC*|D`(X<$HLGZAn&MB%o$+gcn)^d*+GQY%?lTYCm8 z4^B}sa%+JJmV$_9e%=<22DtyI3`KA>UtT8Zvu_ord4?7}fm_!rwM)Ig$Jcl4sL^{$ z!hr_-*itvCcJc#$E^b*GCTxwz_)X-n$hq;h_ihM1|o8 zp-+#_JG_ECE z%b+qLKxaz(^8j#jUFpz1Mo;h2U4V8ipAEno_=F~$cwu#IL7zJKG(>yqpliw0&dbfu zKPRVVVDKERfIG&e#m>W{6x#jG7Vrl+)51P?`U(pNBmwAYdtIpaeld7)NtGYU$*#u< zD#CFO<>!hxg@tt`x0l0MI3O$qjn99d)4%~o=m~Z}FqCIgy{)CHsv3L#)?+C3<9Gb2 zqi>8Jw`dYyH+oYvqUq_gXPvOh>^zK>h9vT>@cu2@gnLye} z#@#jFn>TM(gfpNsBaXKzh*6yc6a0yu&-sG{4k+B1Hr_=~IhoAN%2k7kyyaR_#*yhN z$=$62I0Qs9?fMB+tp(?mbgT||%?-V;P0kN$QHP_8eCzIg)czY$fJHlM5a0}nZ8;Eu zy@r~zd)=GyVlHS?RAFLef*5jOV$qXY;CLAvh;TA2^C~<2jipUEf8GikPK`2BgOe3` zwuJfrPdiut4RscWdpXr+PdTTpbQ0696iv+OMM~o2?5qg0c8M4yr58mrk{NHarxMjN zQBtXq?RAZpB*sfpw2eZu3L}|`1``vKMhvmfE$yj2XZJ7IpPXT4-0!`g`}y4ae4gi7 zQG0@Q@FA>SRuKMPriZHt#vfM3cj|Ltso05r&#oi83%9++|j zgJ1oR8^)YV1S3|fq(b7PqptT10rv~b??^CWxMZeTgmg0dZ{wM>jrfe|0&_tQklpjE zST>PE{jQhollE!e?kpK>PazaKVh&@tU;%l5sMg&{{Kf*-#SMu8WrwEf#BbZWwE*GU z4bk2v!8<(=6!90RBX!~d)5>Peo!d#mP`qr)vEM9L`rZGPWygkiwBgCpgY{>YZ)XBn zgH32#BbEoB8yOxPEb7q>y48}{3JToQXg_QfHu2;EV8~$Hn`S}hMzl*(dlFm8C#HNe z`6?c<7|Drdksqvda^r9cRPyki>b|ij)0XJ=tl^E^QsqAj43>ee!j;D>o$j)xo{BL6 zehgq*$4oiH(Eb{4bcE2pqK13LmOamalwoOKmgNwK83MB7&b0RInmIGuavrp>3yDEW z;pw(!fEX=B-9;*`kmqfc$wHfGts~zy-)N%P$-n$_!a?s0!XGff8K@t)YhDdSbL$QL)}D zQeD?6D;7=Hw|sfzn$d;N^st`HPYAbiE zx;ER#+A-D2Yg8jGUBPZ$p9#4f*tQbs8QYmvm``<|#9D~&|9gNOIY$iI9Z3)}jv_9s zHNmzv)6@K;!O`m`<5jcM!rkG@Q&wJ1TP;{_Vr*cbxbg0U%eA5Jc$m8NkD;2TY2aQl zyxlCY$(>|h|EkkzVszO{e4Xdhxw~7q|9$bDbP5E*9TmsF#RfT}>F-KW?!hYitkb6m z%;mlCB&8zJqxwk^Vo6C!IEYKyxfJ9Y*v@DZlUH_0{|AFtIj?ZYZgFj7f^~G{Zquu7 z+1@1d2>-FjkfJ4#@l7nVKi;UPN+{+Q@W-;)|LPhF=+^=AH)VbIi?uWZt%sFyL$Zjz z>Rr;T^XO+>1jg*Sh#_zReBH-XJqW(qY&2hgW6l=(<;$17(5G-mCSaHNpa{*<*Y_Tq zl~(+6u?5(O3At%IEPJsuoRIvZF2Qnxs$7dk5by+c zYb=|lSf;6^fk?baq?7WM15H0xPl zv7<_w?FQj?Ce8T5*m2|u8eD3yO*$atcYd!?8^TK2j<`dWM!hG(_pYb8wly_1iB;Nz zvrRl+;)`zYaBHMQ4XjHs52}a!d~2^zYhH9@1kjQ+jao%rxbO!YY3v;x>p&cLL4@v+ z-vWKX<@$i@por9BoDnvXE{wVc&zdfpVNS1M(FH!KVyHREZ z)Md*a&ze2EJ>4TOJpdB#ESzr|25ly+fAmgRS<=?b^|N&*sYQ?nAj}P`OM0Yxah|LS zsWy`qp!=)DalVZ%QWPrm&waRjHJfFO{Tw^LeEwTTu>vg`)_q~%A#$qd_ zR-#ZSF;kNxRw$IPJPNhM_Mc_&Us7>5eBndS|ER71X`f5}f#?0)P$$m&`(E+!zv6L0 z9_QwV_3**$)zH@1ttNlj-`^LzUsKcjzhBVs@pIQ~IX_H=A6emRVuwYc#LpvtgkBlG z^gy8^x0xO}cqS;F&$*T!6R=P_H1Xv8t_wUbMe`rouj4=F-#!wrWS&fyceQ+U>;A@5 zU(W7!RdvWZW7V6zVOgN)bG6%rZqjwTF3|>K2Q8KMa74ESqh&<-%my&|Ncj6#9dHo(nK_;eZ>XMS8 z;^l?;xl7^fva79?*GOlHjybv3d1eVxT9=>p6DurU9E+ikG#F;mQ&u61Fl7tvo}H*( zXo{F0k;`|gIIAB#`h9+GrXDTdlxv-+nD0m#e%%RAlM+6|*PWa0FHpfDc5h^VOxy&u zV#SKSA1|Wz2lNs83#!|G>fO6{Z~AwX+!=TbtaIMk{8Z$snopmc`^-h)D!Pd5yOrnO z-&)aH6;_d~X4f&5F1&no&z~=eJMD{m^a}8LrCtmTr?9c66yAQY&B)OkX!$Rbu->uJ zpO#*Kf4$pj`{pTk#3Fs4g6Y>}b(UU%TSLa&YvfVln7+#uH(TnDJsR$+dnv0QoX)tW z=l3U05r5#?a5|%-ql5N!mIaSC#&pljIg?}ciPW7PxydrK5oAXahV!gJXW-fsQz zmW$lV@UA4AT=#|yslAuJ=@s0*b*l%qnbFsj*KmzLGgxiYOV3JDwIM3DlVQ~fY{Tyo zJI|E8G8ERJwoya8Mtgqbgl#rimTB2qNij`=Y40%iI>fZ_B~M08_1b~fi_hSilTubsojtdmquA4?d8`}R)` zb?$VjnsuREs!#2`r=0N}mbB0+)}P`M{saExgwIVY%~ZL|R^*<1c4Mb?uD{BHN!ik^ zTenU+(svnT7DqR1=Xa1yS(Bj}F-M~bxz<@PjD@)d3yz*GC;ON`S>*>bB7W~MN;vt{ zNQe{I9>(2bh^_6+EGY3}Shz&YmCWl|bp0@vYfAH}%DE#Zo$FE^{xwxQ&-q%HgoK8p zZP?8CiQjq4QK+|@yXwpnmCe=dTbSW;5pKq_E?jpO7?3FV5R(K%(wc0Ep zeQTNUlAuQLzQ(GhOW$$@@5U)q$3r^Pa`-Iq(N61xjhe|D)#8rD6g7NFwGN(e%JDsq zuJF$pZ9FX*_Qd2)!$L#12q9P7tNE2**Vp?GA7=N0rT=o74flAzl(9orZee2R`^nyD zO#%x;VoXkZ$FWF4 z##J~M`)GQ%h0ZhXzZIm0xNL9cR!%5MD=c{UiGGOV+Tp@Q&c!M>_+#sQpr0(S&F?*_yUZX#M+k7z@dDzEqrH2es zoW<)@3rw)wR?6Lf$RWuo*KQ0}Zab0c%Kf#Nj_8&4C4>~`;beZxsn)U|G>-4hiT53a zwQQZLvLHc*GLZ}(^E)h}ZX8?Q7W2ZGe}z?Q#USDa8dY-a_V)fdh>rgEDf%i$Z6`zN zEFW_l7jJh)LCV%`)O>Vs@tKlb{LEZn_f5x@i$7>xScqAkXGh{)Av)4$r!Y&(-I~>U zV|00rrB{ePiV@-H8BQQ>+4aQ-ZhGx9IAHMY$+mz##kpBh7)_gD;zRB7o-+s|NT~80 zza-EfY#z`Mwc04iuU0j$?W{^X>6+DL1+j90RB7XfEnI`YkzT9FA8RYB;=^WmJf}d@ zh6C&OohIC+u3qv2NykPf@%y{(M>XeXU96>r^HW?1QF7zW6+?7$s~9O9hHH^yt*X15 zGu(LE>gF=jY8=C9Kx2i*YQY_ET&i82V4QiL&t0xZy zPkW#x*L7?sx?HVkuMIT_bpO*Cn|0tCPkYU#f3I)a>7&1SZpOSWnMtYB42<==9Wt|!JxNykf0ced@8m3Q2E$dwB-8E)OyRzQj+j8lNf0h z9Sp^+gDeS)o%`*-V1Gk0X&}g^&X}J%733mCd(t1hdv3_o8RY@aJo%;6% zIRNb+r{i6O)ot4mBRA+>KfuZAx$I--xL;r?Ws0SkBn;)Kl(w1YkN09i#iPC3w7osK z7cM#teyZl)QOr8LTxAcPhCk3N#i2SXYsd&HFUMj_gR5xce7nx7`EiHdp<4b(sKLa( zGZa7E_{&U!hEwGjrgA#BBEx8qB6e(>OqSk}$u4dLG3HZi#8 zzu;M8LARn<%-)|_E7tQI$9b5i=i4extDQ;Ns>yxLC5-o3M+LhjS`<<3Kf$5DIsc_S zDQNP%$A@Jh`TaylXT#%F`XOejHmww^EW+l!Y^UUiz9w_~@-sV~*{qr0^yi2Pz7^T5 zbKJgipKo{1DMrQB(N|RGG#&bj*jCD95O|sm%1%lvZkaf)y5Y%BG083ZskKC_^@6R} z_&m~JF0r$PA|_3+z#|VgeE$r!96!@3o)h3T@z3L~-j`12Nq$*g`;Ghly%&YIM>lYr zy9Ih?pn~3Cz@l7X67;Zy*(pAcn`wSHs+9sEE=HH&MJyzlTw8n_=OlIp$@MVE1uU65 ztLL}0yqYOgY17RHj?#wLtjn7wD4V`~M2)^;_jfA>(wyXBl! z>j>hDA%Ncdla3w?>gNOv9VM6KyD>C+uPGhxhN<-(nBl=J9M?=BRtElI6z)0mmpIq|tNq zGI+Gg_U^&X6)T7hotd=>r^!TC1|xc$pXCx!f=${6y^tVR)~`u zjYCHVrDrJivkJp$(PxEPozp_&$RLc#LKAZTE4#kx&-iX}R zS4tmye$lu$#f9fsbDw@|MO!oP$6N;yI=|1`iJ&idTXBkG1N`7UYK4Uw zfxO3fZ-zx5n@NL!dFk5h)JuF9(Rf-Al2E1t%V)kMX5|r!NQMhM+3UmfirtqUoXNV6 zXH_|Qvd)?vFD98(3*OJ3>mQGJ%mt%U(Byv7UwVr^Hd`ya5?`0W(JtfGxw{+Aoe3Vd zLr0AYwpIQn+W&L?=T~P>w7z*qa|y(2c_ffaLdFuR7TRb@JC*7m|K<EIB+XDJ8aXUI8c2tGoO2flEKCeNisHge=eTqLk zeFX90xPwQJnreHRIF!zYUFkfQb6VMlDqT>y)w%$~#qr|o$rizpzpKS$c`M`pJW|;f zHi2)k3D=*%2y9Q1BZXmBGe8Fdss!SbErCQiN?&s_{`Ie?(*CC59x^>Is=Sy=X z^Nbn$&QBQCw)+zNfVgmh*}2LqTbPu1GH=dJk0n-3eR3{1vcjWT4esAypc*z?h_B0Z zNkuax_9dvA9eem_qnfYqs`$cxFV*uadv+uF=gX{(>ID4|9*<8p{}2@tvQX-smG8iJ zY|0(bIR5O=z`&PCA>x{YpK5ENpox1|Ir-ebP929~XfV94P8Z{4_MI%}%Ax~%;-syD zI&R@(9{5-IHRXG`$6pngjq5HuQn|suzGrHw@5ViJh~V*)pjuE&@?x0j2e;^DHn6U& zaa9wT&E}JZ7w8p(!6C1EZ%Yy;+R4~!BjpOWnMF({xgA#>86j|P6ZhG0-Jc(i zaEq+XBBzAUHPJZ!iCUO+vUWuPHt=v{gdk{{7(bK?tT{9{jrr$kRz!rZ2*>Zw-G0u# za5l|gG;Z|o2i(9PQ}Xu1B14vf`}cnAuBX@QnnS^(?lK_tLERuz+Qhc>HR4e@UN%05 zSGq)SIAVpfLs7+7p5PC63!Eu#b>%`8nCxh0Y=oS&_SgEKZ;l)(Yl}gAs@Q{0FvV#Y zubybT-i_O!x?SC=Z`rCP?tkPVle)=*fR;Q z>ePBd+tQ3vPEXZtD-}N0eMn$+$M`H|&ef>2{`mA4##z}XI1Bc&p(ml911D3`_S!xN zUgq8Mm+c+8d{rD&uPY@>8;rXJ89nZpTZ2Z;#}>+1Kj);OCRO_4r!tj4$?Xv-t;O-{ z*710I8_GO-DOhvSjV9ZS&FqR=xC+?C?R??amC)Ug;@4eFB3&}|sXWgua>G)kr$$)J z27|C~Vq9^=u0zK`9Fo+G@MxXNR*a z4NI^hqQNW=(Wgj4GwHN1NeCW&gB=>O8tEnme)NN?>4!>m`=D%1YiT`giSy#V32RoX zHN85|#SQq2M5naHyvzXD{06&xby@ak**>(Wtsr1neaSg()sR8&`GM0>J03i!Kk+KD z3ugU{EHj&Qt-ly_Lp;Ru`VlN5a#EoCq%1f{TNqdJANydaWfJ( zY7}FqhA9fS&!5NP=!&}PGG&_tR&#K?E5$0yEJF8zvZ9H)gIPr&i`$&X^>*L+rGBnu zv95}dk=~c6nOS@}Ui$Jsj;jRDFK>F>fA5}wvQGA~p~st$Z1x9R7b#@*;uvn=@A=;O znXL87yels)tfE$%R-etfpXTXYT}54ePQV8JbK`InVfzWfbzvukghvI{$;-rw7%NxA zXP-zkODQGwH=A-QSm0dsYjbRec%683MeT>m(R--H9bEd(($G{c;tJO}_=}ia@t-f9 z!Ma86WD~KcIy_gv58!5X9fX05_z3UscQO~ah%0nmd zJ4zuqj{!#&A7^yM<@mI5`h`JDcib)1HBQ>*zgeLcL?)iV2gVszWS-w+WUo$QS5 zkJhX@S)ZysRx1~=wD&7)`7S>AqBj5(LA@Df!7BZ?-ego1MpWayzZ*#%XrWEbS@kz# zyw1tD>@Zr=)0CHDurP}~f!}W}CZ%=q(4iX^`27-p(>JQyU;g??#gjqA4^#{qh{+i^ zW`>PFnV%i8)UTbh06R*z&SNVM?G4@ z52x#KzKM_59`t;QTJbb=YPfuEoYh2hLWhn1Fy0?L`btXMlQ6<(6AsQu&yQGID zo=s-?_9goZCV=r(=>)pRT(TD*n9u;o#5!kd)2p>X`4AGH?9G}U<5z6|aL2SMucDT^ zMCIgL?JP!am+bHTFOG`p#_h5pzU6*+qV1IocMWJR{17GnlHTOHWrIBT_s4td6Et{o zmFf_fMgxDQsImfGK_Z{X&^YOWui zWRw+R&*W{VPUrUT{gQArc*?D}u(nb==YwgVAu5x3M^f^k@tcGhc&geUhuSE zzM&(7~`@O-9VVybU}L9y{>-X!Hgdnf&Ro{+H~d!T>sZIS#pYKWTin zivHh7)njUw1|IFqIKqD_c0RpE%+RsCk)M5Z=z4J^THdsWO(8wpqW8SD#49gosH2q{ zLca9v@v|6dUC+i>r^Cy>~|IZV-R$2Ff*ebqM zFLfg0fSr=rqwh}--n1IB1cpx|>CLWQ>I5MTlaMg!6YDJB><6^}qvvSARN#vlsy4A*Hgmdk}cH3*x2 z%*6l&*~waEo_zk$R70S-GU4&wh7HGzjei0iWVC;`A3@9aDL(qHFIC#@YYUUy=I1bF zU*w*0_H`Vl@&~Ph{)<}@T`PlBuMtBtS#~5qatG$i8p{gt=+G81mDvS-?RhmH~*Qfv=9uW z%y5^LtD*yM>ztq%c+#448e`5{Bv``qb(+WQ6vtYtOMC|J3uz8AUqA8x6^Zw5G%@p3 zUZ@!GV-20n_bd=1C?Ppn{&auimlHbTe_m#?p~eZ@SWwk>>6&e+PvGhL_3Qb(H|MRW z!2=TpnWNwK(re|+p@QKq@euG^pBrT(99N2yu|HpignZl?g43G=?}P3|KIyyU6}?iuk0Gv8Rf!s>TwdRahCdaMu zSP1tS6eHLO-ouBfKkIX>lvMUl4_>UmndaM5E_~eM`?T@RGdw!zyO_#@Xc7sJHs`m3 z1w1=MPf$8==k|kG!47R&u>y1K*oQmBeUs#ZD!-WD@Y@qRfLX`k`1U!*;%M#&#rQR` zdY;YjZ1z80-w(u!5FL$}T%3a9b~Av+*>{$w+8PL;`*`rR5g>sn-TrkBKBkGvEXrSB z@}&WGgj_%5N&&B8Aouwn#LSKEtXa3UiO5%H5%Ht_1vsLWk_UUAj~A}Ld*F`k*QCU3 zizJPTJU@pf(E$wsqI^M9M%#15HM+BUtyL9GN7puc$x!1|J=bJ-jk8 zc(lJA{;zfc`HF}8^#_73p6jOn;bSKc_O)vN?X-8VoG*t^hQrit3=CA-{e!k~$2s@N z2X_U#vSaO9V#8MB)o8SLDl40juq`UBD_%JkKi9L3;l4!PL$K>kdRO1m*uFQlu0AJ^ z1&_up`dNtFx_v=!SXPp6DT2o=_nj`*Iu|M37dZL~AMy7fBqnrV-=lucZ&97>3SBGbz(=v(9vFQW)?662z)TH&TXOaSy?*X>@92S_BNQ1 z+cDhn!4!k}7Mjo#fmfOQ1c2%tRpH;;UVEv2xKtkl#BHF!v6$}v%dv**5*z-|)YWZ& zWoHWSlSX#cByl!8aQO=Xxx!|IOe9y$(44vVRUO~`8eua33nI$jZ_k;M(PhgYH4b5| z-t_r_;2Be~yvIQF6F=_@+uwUUw6dSmLT+##Bv!CroZeX7u7AJ8I}M-j%#kozXe8o` zFWe@3M_&JSAK7Yxo*l`odI4bkfPa0PbFgkku$kxgw=|i3zgDIJ%3zh`soK7OPgt>@ zy(;0a@5}-Dy#hb<00gh){*Zh=%q3JoZ1V*qyXchLIG%TZ@gmEa%1dwHmd-d8z8?z- zy7o=VwbfT$jr0(J?$hEPQ zGGIH5zgwQA?!a?Q+6i=Ahyw}giim}gWRXRl-sERS&>_#p&or6+ikygQvT1MZfvo{O z=eXedsZpz$cBnQ4OhrC5KmG{V0e}xA1%-iAujpBPUU`7k!~l-QO>7G~oNN1oHa`wc zF>1vnl%&8lmo_~+Y?Zj3{`NXFtT5*?m3*8%+7757)a|vR(Qy_|;{bc?=8+>UOmRd1 zn=6GJ4BKttxaCXKvYBjyPew?D)*5f@*Q4Hwco^N-N`!%^lx}M~ol6`!r z^tL8S@bdq;|7u6hF|i4gpN8O6(`w*UjGmTLeT`ob3b*-y?^_x)Yy0<_nmyTrX)R9) zU;FQ4ZIF;uK6=wl{nmwt-VuRzGJc)rrV_O0|CoR<&UCY@-SNr=@vj1ctj(!tZuHL}lS+LDG@`{F)ug zhmyp~@qbz)*RT*d(IG$w|9Xd`4WN1mI^zENxoS4|uo>5!3|Ry@6RoF-{-ml1b5$6`y>~FTc{lW}kU}3-3g+Lb?D# zCU%H9{dzt*2V@@oz67q+)zoI!WGD{Tva2`IlIM7R2|ZoUs`)i;9BAFmdwE+T9|;~* zqh+YmYG#~R{Q}lJ=sc5-9(;6XbqO}SI%IBHup%yDiTnEKVoB};;f~kjkCukzjkiJR7R&YLP2_@=W zDZVcLL;mKq=iMbWM#nd8iY&f|7X+UL=y*okC*8;9@ergHid$7!hJi!$e-1Bxz( z*o$xSZBb}t@f@R8Te-p;x<;=b-UsfbgHaRPa+$kseb#bWUy?D3pov(GQG$QpqqO)R zWrfmx?qe7e%ydxKR~&|=L*APVFZTQg!oqAVjnCp{7i8{t1Dli)Tm>bJ-R1&ncU6V~ zM{JDb+MrR7SM93za+g$3$Oxg)+`e>_&Zs*d@P*hjPp;}Cy)5+ouaAazi=w3co zASBOUyk6>nt(4XdUzbmE5oI?N6yC9d137No`PrXi7{uo)0t4&2-H#kIt4pb<&_FG+ zyL)mpoCr%qc1 z_L!sX$_>6JdJ)Qrz0c;2twzN+2{oDhEp{jmImD};wD8Fl9q*%;%RMT^lwhs%=Ba7P zN_(uPhoItrZ>eT<=N)-3e2opUT7RO2@*mU{^Wafy2?+}X@>Euq}LEsauG*pSuAcqs|Wf}%5JZFE%1WsPuuSwWIo65WQhLVNjs!kzq|N3jk-t5!`Z0ltFHey4^At>fbH)&lWQE_)mDO}{wl6XBWYV35jXoLupE<8G-)ApJ#AWsqfSHm| zvpordgEO4+fD%CF8>&1}MwgmP@@4}1&I~^RuG=(`R|3F@Dt&FZg&Av*aO`0*)~=|m zVoS~axvCS4t+gg)~U)1G4vqqKyWSdh(rrr{=B%bB_Wcv(tw7x~y!UH@j>%lN~ZheF>1) zla{XM+5Gy6q6hFr<$bw%jZY5>*Qe|4Wnvkx?OuFMQdQl*-yYC+`JaeoD5FpC%pt4l z0Bo=BtInMPR|1S8;e@`qpsgiXgVo&<-uFGYC3=b~tKDcQ*~6 z2VgnR<7ak+j4CLH?29iy6E~PNQnt)E{##W9<$CttDpSBxUv(I>&$#yKggKeluBq)K zJGu7F-W>*!-(r?1MqaFa=mEN_X+F7KD|qyB*zgx+pnRO)Vw;Ihd1gsFRTTg57QFJT z^8_SgV#>XfD3oI)v@$8!tk5YQ&pq=R0Ai_~_30{3;S~Jbd`OOu3wpUc_61N&KJ8VA zt_cK9d7!DuF-_bqg~nZZi@k8+!kOaBKd=uo5nU8<1LZ92hoq#tJF)D+YKyv+VH z)1#-+HgBG)UMad%Lb_OMw)awt9_opthzW`sJVxhcMi6sOzsx;&bD5cm$#<2`w&8p0 zl^?0!+g-#Yxi9nwpis1^cFh=BJ}$bI+(2s&ND1-(T2~Gzl2yuN_BC;~IXgrAF`WCO zE-F9r?ElPz8HLS&+RCJ;VcWpM0?8VP7Asz`5NfmKmDem{simCFj$+TQ{J_qfmxN_v zJoa^AE7GO50ir*@Z{^0ip-yPew8FYiCpT6_juPxhm->LM(eZ92fG%$Tm*YR4AC*|Y z9!neATYG;FD?I8tI>2#QyLPR+j8{``l6{G1fRK$C%A}yVASG2NDSWDH@Zw^XJmCYK zF6g^}2En^scAvD2`dFXa;M1-VXkD5Cn{Bv|?!R5_o7kSvKk;w9gnHirMGNiZ*QAFs z2BG_sh}%1#qV&YwWiwZfinjrU`O2*>S!MU`q1VSo2dfvHfB*iCnDDoW&HBMwta2fv z?YdscGYa!9g;*C*0`_*NWrYl^s5~}5OAPExcHnVxwp%>%Y>aCqi>KE?_gv+*eG>;i z^-`7SJIHlY){xgX`G04}28f?N#YjuY$t~CrVW(P2?y=nsnG1|U;C8(b?*ck2VNLfJxAgtmx6|1tul0AUXTE-b{;AAO)?mK@O$H7BW4= zrQrkqd|nGor$bG&Bx8V6p?&ewDsbXch@be6CmL_^@6?ayrq5@QD$*Pb;;@y>()IV>a5KJKL`fB9N?Oi{+r?k zCo>JgmSji+DSkA1V-CEWV15asu{t6`V#i4%m+&dKqOfhqMy%^tdHeqcK|aMK?mSye zs-EkX{eX0Xw;eO7OQtNj)gc^dV-WFF9lz#`dxpvb&|EEFirPa7=y$Zdlfubq3pNMh z3q8?aFtAodQ@BYAMmQU`MR^+=zpsfof2eNY?nu?3Y@sRkU@%z?S$(J3yDcv1n_ zYnum(%8DPgUl@+Yktdd+G?7$jaz1*aX_9ItYe#tBqsB@~F;qaGIDzIjmkTPOCNn_= zM9NOYLD#W-wN2eqh7U<<`? z!T)ysmqc8tu{gY2Qf$-f-up_LVOvqYRf1b9gp3EV%mRllfXrisSkfihBeO?uNUAnw zVKc3ZO{$ac!Z>*;P`#h2EDO^>IsSvxq*e&fI`{TV>0z}O4ktV`~BHYv_wO{^Ki;k(eH#SstP#>&+! ziF{-Pom8WhA;YFQ2Zhmrf1ftKe!?O$=vmQ?rTKXD)%Rj52mC(LmKg04)SvJF7mOEx z4$IVC2g?WcqbshF()2sr{wkmdTF9a`-ro=bPLV|kopQ`PYWod&{{=5I-L zG+L%6b@)DBIPwwF9(^maFU&r8_G*sDY2X3fqQfk@zS3I?+nNVkt*6@;cx-E%YpQ1z zb$@Q#-|DatWwjD1`nVnwnBH+#R*JKqi<|*(TFoN;O#{EtwD0@7#d1idh;$;&Y;J}{ znV-u7@UR6%P(+qS`v4ScmKmMlk{K4t+28xam~S8oJ;G%}R{zWb#wX$*J-+Oo8R4Ak zgzjbdag*C3TtNQyHKE2?6lJvzxwq`(9I$fu{lNUwpS;@9%O~>}tDCiJak$@y5Ud0c z8M1`g(fjFPvv;J~7i}imyb6R+?~whDTyCCX294kqE3RWX6(Rkk9jA|_>eRDB45tXA zT`4%C$y%W3*&qGCaP;j85AAelX3ap?33&s*4sy!>kH{nrH&9FhjS8*;n&+H&MCu|G zc@C+k-@dSM2|(M~m3m_jFQEeJm)?%xX-vC8&NXarqY8Sa@jqDMG_IAOcyivc;HRnGJuX(tzmboJf?emfgC9T{_KRoAwAG|J=A* zCLvWTewmox?azx~gA0v#fo`OgX`M%Cqw>=ml&MqMXP(`_Q~5e7YTuC639c<}&c{~R zhfV%w)<%gVl$}Afkr3n2K-QW~hR(rU8FV1i4J4NX5s3-<6)fk#_e-Hc7NU4@S@Q7eJ>R6Qx!ss#)e7E9o2ulHK7yg3n)sat52;b7zjHClT9?Kk7oPricA1VR48es2`}QT-nN-R zbL(71eYemVoNg1_eDKrvXV7E8VHU!5cl-r6W079=k4%y;)UY*d^+SqkG$OyVvZ^sVCPc9g>LE~ww6!h78xt}kO0f*b&gnD99*#CYUX9u;0U=MGBYmJnVr#PS za>vskD!`5ZFwSbu=d*zW`t;iD|h5NS<)TZIB1CcSD^_^z0PJ=-r!LX!Q9TTHdL6C^g40f4;i8b7^Dq zEgMm;mjvJRZdS{=V&_q}5$#ZtJKE~i*34(y3DQH8jrr;9Acv8m+AO!aN;e^S5)$(W zrY|i_WA(eIcj0&dJe4h4xWI*a^dy(Mx>X>J%?aai^JfjyKCwBX~) zVMTa#MV&7s?W(XZ+;T*|rRG)_?4^|g;+Cu|bH3w`=b%f+JGq|8>BvqEVJ_Q*LQ;vz zHjwC=z=Xyx_LoGWa_6suA@w1H0g1Kh_wHHMC4mHw43I~Q%>zecFH^p0P?knogygoxF;#n0oo<9!Vk0q1BX3Wf!%lM%rck&1-vKk5Z z^ang9=Rgdouz9s|6~?7%fZ_8)qa{eB_VE5vdI_j6!Q5A)d^Hd^O!ezZ!sJpUZ!TXX ze{5n*3F_vN=Uiae#@yA@xDYPhRpCMu{8&cxr z_2-H!NV~m~wL;6nEdI;5|eT*SpJ`Tf>xMyHgKqe6gx9# z1*(uz?{O&*GJNvY+`26*GrR~3El|T#`tBAGwme2J``i{s{hCE$q6W}!QijgC)~Zv> zhJ=?BF4Yst_JvT1PS8%`fs+9q`@9y-D#~gkahbJWQ&P_6`$07!AeDb5po7l@{}u^( z<>Eg|Icj7TYT(gvq_@?J&&xhRKP*n!`gyn`+NwTvU_#xY9IXS2XRZ)RV+$DVuY|g! zIS)GuZ%M+q9>ks}=wpezjNE8}E8 zf5FFoO-{-_F;Y68XldpW?!mpk5!{h+;yAf-wut;L3KjXt7=He*nohGmz42vif8oA= z*tH0Uu{_6;Oh5H!bV?htDST0tyA}Md7aQw7B}e054;Es2g8Lz?98|+ z^t4}EFqsBnj=~{UAl#mfd$nU?$0n5gn42kI)Fj$D=(k81Kv>VVN(!dz_cDGP)dA3~ zHDu~hm`&;}?%m{>XdmGrRdOQP~^;wF?;GmnWe2Hcwv9tMpZ3K!C$a6zI{ zbQ<~U&l7Z_({+;SSrZtjD5#R!2mS#CqqG zp6rzvk6mF8dYi^EhD$;GHe=7}(cW$;MBgMapTAsQxaG}3Pflxwnt|(G5Y_`ExsJWP zl2WmQ%-N1Nkl8oW>IGvWFn?s^$HjeE4iM#`Tnc0+$Q{F6@laWm{AAEl)fF&NbRcgs zjsR0dVATapFkN+^V7;_--mP1=%)_ps)|>&84%AV!d0LE7pPR41=iB{c5&XVzuGs-u z>lBCw;Uyg!vT_l}hlEfb5+ZkvVK6adoOcQ)=nizjgkm0>f>k|mz!B6n&E55Brzckm z$#(&k40Au6@W1G7^57Mdfx?2B!9cOU$<6s3{NpwA{ADPQjS)vfw19Nrvni)xHs z6&JUOS}imBN!sV~yBi|GvqQCGHIgo`fEu0dj5WOBUxM=WKvbfaOq9%9nMtZyg5s1r zV0-4(Id@%*f8e3lwQnItXEqFS3$S%SpjcCb1DrzmCHAFPo*bfH*}ouOm!fF{b7B&k zH|MNazg;oSfpjSbp)h-TF2g8h?*&b1lm-RrUi*?tzJ)e4%4O<`uD)P7^+S||@gjlZ zGxE{<1}{k%9lOJ+2+RXs_5R(v-(fQvDqz2XsLBZ*>$5L%HC^ebfO4!1=qD`7qQ{A6 z-2Df5xa^`wnuEB);pZSLzAl4`3ojHsi$Z2VZ!Qz_@A<9(n{mA6zC^{Z60=7;o&mv8 zbGj5HRB)4h{kAZ0iHw_xhC}sbapqAYRBQFxblr9nCTTe>a6k=I8KX4xb)e@#Mhwpp zF_(mU2qFKX!wci(G`xX2)QOOxB{a6|6Gce^3uc!JmSR`CTFxNd%qH>b-+#{T0dXT( zUEogOXw8$=PQ^%RYj{#Y%(z2GN9Wwy_v-qncN-*?^~WFgf7sUvwGYtL*jTv~OcfMV z54WFSZ=@Kw{BjJfgv6+NV~Gdb7N&SE6t9bHe2Z)wD8l06;+`TjF+64JUU!S48squ^6e|x7UTat7JifnXl;a>+SW_{QJ9NZnw^{ItrYlJy^wd%Z%5c!n_PBuy^w>P;d1B(|P_pwm)cBFGDV@@x;#R zp-*r7A3@Kj-LthZya7u4xou?5#(g%gACJPE3&`RZw!8r9I_;A)nKsIRFLr2%B47m?)KGCIOxf=0xC*k^YzA@Z84jEHz%+qaKCN5B|YQEK(bXL8$PLtDl z_WI-;@t>V`bP3(_=f_)7YgQq1hCM<;k;fH{S2HSpXGL8Aynb7f2Y7v8=+*P1eBN-^ zz*V*A2JZC1PY<3=q9O$vl|4`BS)4c{O^`oKcZi~G2zOOCRX#9-90|fzbjt0hTSD@O zq3Vo0{_51WFLhO$p!~`^OMfVXD}|+GrBqTQY9C^j>_;KAv{2-#?umA%-{)X*Dc7a} z=@2dx$$$=F$e5oUQpwbI3M!e;h=f~S_}W5P?{t5bdu9pg*)#fUI~ea|F+u1%Jw}2- zMvxlNn`~IjvHq;jpWmYWgiw2ApA|} z*&yAS55oc|-WIHT?4f^Rz54FmbYbx_??@rkUj!}FcWM5t{qP~{+6a5VDtV$k5TsqN z?Xm*flVD1L*9fyyo4-ZWzNd_f>!2XoD9A%oBr)w%Kp3(~$5 zsei)T%L?{xXBA9`kPV$nwb2S6Yq4~?^d9HrZTeZ+y#>~K*) z#U0zA)-%0(vDH<6qb5N$rJwT|yp5K@Zx%txD@2}}b77*#OIGiKyIhb_FmE?HaJsmEHxp(C zm!NzVkhdu^g%FgW>7JdkDsUdxGst9l_bnm@*jz6~UbG#M56^EI?*xdS9%VO5b1^)b|1RV&EdMRCf87TD; zvrRd9`BgvZ?%g*ex$qvCa{??|`~3cfLJ|Pwo6DG#xHXLEJ${7Zpu#g{Z^B^2AY}y6v2q_(unC5;Uueu}7oL2CMp4ZmEL^B|}!bHf$oitZd10(A6V>e#7^&LPWl7 z4pT1s5qh@7%UrYa7k>^0c%d8u%2PnlMsC=1<+0afDUZdyvw&yj>c&Csf3aLir@8B) z42cA%pdcfw=`Ueg>8TM^GLQu@Q7U!!CH{?-%H}psZtgIfa{s}o4q?r|Hy_yrKA4}H z)-?n@k*bpl2%*`~(PRUrt`vyT4c&EsO{1mvuG|Q~9^uXY{%TYKoshNagl%H`VEDq! z;5bFwD>zea{_h9+9WmTMd4DD6Hl<&qw`!)63d(u`n|((HP1qkmU^t^^CNm>o1_c7Y zcJQe0-u-H|4^2;`mCLR+Pt&m?V@#{NM@$djfjV5RtHGy01ZDIFu{xAXxQcb|pJ1(o zA{ezrCOGLJPKA=586%zbKXbX$%-Z2D?CsjU4MD6@$$z)amB&%#U}ITXJL5nh1bVeG z(1z4=%XoXxGi@QCv_!PelS9(-5 z$3_!zz`6nEMR%WSCuoR#&ESA1#-$<`pfVfg&P8wnaJZ36pg;$ysAkm;j$bsMRZ@4t z%rLAK`KB!FL!^Sc1&kI$A&8EsBG;A{T?z=3y}gpj%reh#(8%ynePcsD}8@-@9xDM8G7NXK;fAFZdD#1WEs4EFDa{KYf z3`lrVJQ=0fknyr*!;?X1R&_Pmql@9J+V%N77g}p|@Vu@&|08R`;YgG3Z-ad%C`E@{ z$pw0_B9wx-0`QofPT)hCz099uH#TTufIks%+VVebOTLQrIaKcJWn_k0L7f$X-+I(h zn3AH>`Qn)A;$aMop7}xa7J+JedsbyiMd1@=5;+`hM7mzvM4AOpZh5`*gzL$bi#947`~vciw| z7~0iVNTJ1P&G)(&o06&1OChv`j4v=-`3IooPJnk)+8GocUiZ;h3%N6#y0rM%gdqJHu%ZGh8% zXDe6_6D-+;eBmPJ!2^RZT2N<~;DJqzks5Yfv-ML>OD&)FB+;{5U&)+g2*1oxM)Jn*spro=nqh7&;`Fg|}{ zgG84%1NL{%R%F4n=SSfTi|a3=7pC(XpWP(4#Yj5)o~Hj>dtLZA{Bb=D7Dhxk6%i_K z(kXi#cuP=wbdf79D1MkXPrK$OvQk`1v7mW|n9h%X1-_@8S&RiF)|g|}ONTa;saJCu zS_f8((ARXlUl&_^!ui0^EmbKsaKWt~&Cic?MAL7GAXD;?_mT|eZNFx=(;$9qMz#i& z@s`l(1TLvZHtpY93>n~^82o;)wUSAb&>YBr1ub_H1gJO+bOqpd|LboBnb0G^qy0WY zQx>Lomn@R9CW)0BL?L&n`$TUnA+?8Y6Wer-3kBEL4HJ)3x@zUJ;NS-Ba$yuIYROWg zR1)Yb_$45etCg#q)5oQePXj zf=C^=@A~%SG|Y`5$3zvw5Q`;OZoj`te0)5j2lDIwmQxQl$*! zY@egYj`dXorwDi*BHpyhFA5ykJ_yhqU_Vu^ehv%HB@6Esqfb(s+erb5IrEra{jb3(77E6(2>{k#pq09<9{N3PG3k1I9*+qfbex8R4nudi zK@Sp%DRBAO<7?(OilBZ~{w=VGoei%Z=P_Tv2Yo;Zssnn5vE@$1X~ulCNw zpX$8*|7z}PQd*T!St>0eW)eb{R)iKQWu2mAI|#?_v`njtNGhbo5`{z#PN)#Fl_hZ; zOXy_ZcaGodeVF^6nfv?w{r-X9JRb9ydDK}x=ks|l*ZaC&ujdt>E1XDBM#2H<9v$tW zF8ji~TO(rxq$$W#tCen4+x|7h;VH&eBrJ^2kOi5MxM6?DLBPq16&@;k3}%|l>6Q;G zu{A?_j7lwAPxlFg`WCtX7YRuQHT6Pszon7~I}~L)9{YriYV#Q`T}QJn^DSGv_;IQu zd(+mfTYpzkzqLwp5a!CsWEF)s^Oy5%xgVksG16ze@XD#$?Ja@&?MBD$_1AjnzPmsj zd>)UyDdYq^DXzkMT~3+&cfw1iMBE_h83b{~3f|xE1x+N?I}SaF0hJ8xo$ zSvBJ)f}5&-{S*7T03@_7HLKUHpIv-wl{nx=PF?8_r3FD9z`W8*JYA&N_O&7F%HbBP zEeqh}Z9Z|iaD$Z8ewSLes3%XKzBbQaQt|9iR+$?-%9XhN1O}G4;`7?IYl$dIXq&xg zzIu|FB|eK-fAJ1vi6XRm**v<;??t+koVb+kSH`xJ9F$;~sn~mET$gOtXaAz0yJFj? z;yt@HP|iD5P<@<7?R)nBGXX}sp+WykpZd!+bZa^+#ef zL_9@b+u7A!@`K*JU1_C%k_Wx~RRn!*6FdTpM2F#*^zY7?A_TqliEd|#8vPZkPmj(o zShej@OIcLdK--P;Kh25Rd5O0g57RUkIlxFDxQ+`cQOjSR$l6M&*;WyKSRH2P6^c%0 zti~PAJyPOk)KbSd>NWfr5vO_nH{sw-y4l52hU-z!!Q$yMCsOmT^i7^pPzsTZ?bObH%Zh}fF;(G1qMiVbW3FD$lSU)eMCxVI4n9EFUuZoBY8%#!Al9Gf-$4FP!df(H% zZUrjiFOyDO3}tjGT9%60StADXnoOr-Z~s|^e5{eLFlFx*s6OaN>_~`Hwzu_5QE^VL zR?&l0lcl&zgV0X{H%9;&)6T_Fw8BR1FGNShW=y_*GxQMUWvY~9;lMq9xXywkZ$G5f7SG^0SC4GW zqJt-&V8=#UnWp-MUr>Bv_^B6Vq!;`y505aY0qrsp9M|pBNIad&kUXy!z2>zwyCWUz zSU1`Jzr4Fh*$a7H+#-d>?78x!s^@WY`Q{qqr3EczQVa{<(DVta<#2bCX;;m0gJkWr zZ@eCxp?EVwu;HIX31#@av_DZAZ^sp&<5%SfL>N*M9RFe}bShD{5Gi)f9+_f`7b5{fYOPH=>kPABAtZBedpz|q)VRHJz;p^x&r1_7AMb0FYUIrafXhDm<= zLtmA2uneuf6c!69Z<4ib!nrRzg{B%9T?6 zJ%>BxCQLbhCEv2Dl$8*D@Ev4lJmFsaqH613x+Nk|FYRy9%;Z#CLmWA%nCb|TLASk< z63X0^kkEp=MFTgR3S0(zU?76#s|`Aec!XD_b6^9x2)w1sm4;lApV*4dXI-Quadv|q ziaylIV(M(58#!EpUzbs^Ml2d)rrJLzr**v(lh&S`xc;T|yf7)I9!|iKd2*~;x4&KMGqRc!O3P3dEC^u1j`l05V*ExrVPQDiEz zLvt3UeQ?5BrEusRPs=ARnT6sKZI;VXQL%p%9CkwdKMCE4S@YVBYTv|tMugKOGtj2> z`Lk~7*+F-#U1nH@O7C;iE;aqVG{|rblJ7uaRhil$XPN!8U02oPi@}kU)SeIFQDI}A zQ?*azT)H}xu@HU0vL3z`veUp#now1SA#W(_mz&FM8)(}gYk87=Yn8(;-bpw5It3x6 zwCGlgrh*Nd4Vsc023{ds#gq4S*nvkw9h!b-$PK%U+q35rnj*>csXy@^)dYOU@F_%S zjGe@)R`JH$YYZdbPXUWxi$=qH%5YfF8uR`$L9hP}2kLJr&AvvJg2lYgY~-x|RuFJP zKokm>q1Sw~rAuhK|5yzooRZ7L0I?OKf?cM}ds^H;@8jJf*y>PE!AYs~@t^ryEU74+ zpe%-RVyKeyKC3U<)tPlTD)EeydHcataQtt=59DnzB!7IS#9eLnRSlQ34Tr=8Be5GS zB()W`HW#wKu@j)56AOvdibVK}|do|1po zH6^4f0UyF;L$(iO-*$B#d-MI{cp$QD&%Rdrco%nolKj4|@y-iSs}WK0BPHs1N-)Er z>8cm!r-d56%BR^V<6z)A?v!+?73ULLvF$M7=Rwg|iSOJ76}845ZpNiU_|&M2?}td8 zf9H33XBHHP#{J$?YSG&i&{^$Yniw^uEG9>ex2hO{(M^SVeK|hBF9x%b|2%O53M0)~ z^Qx=jGyETVSCe52GeY2|fuV#D1!L9xd%wX4Nz$%7lgOWj`(yFTl=M}V+H825M6NkI zu3qVC{WshJ37fR<{k_c*sNLS(+|h@(`9r`F?myNQG&=a2n}f>jWKjoICvU^{#vG$t zZ5BK~ou?*Y910JjLoVUVmMgomWY zdn}{IHUURQYf)ccdPz%JE&xMZKo87?He-Wa39s|M*79rW30Pu3qQD@v8WO0EXNT3V zUvtRC@d~0A?jb(fNI_|=>;SfsTHecC74NLGS$tfvE&@{?Dbzyjui>K^LSCaOv5u3L zg}=ye{NxWlTn`Dl{scJa8#NUK^|y6YW6ocoTYk8a*k%6*m!P-aNqQE}%x@#GEf~#|P8XZ#)v7N{FpJAwEBD1D z9;Q!2c_jf6Y4Eo?m=+dol|dLv6B*{3l~GFs^Ud@zWy#{LRXXb} zX7af>Z{QRVqSA4hzmsKc(Ur1mg+2LHms?fQd4=E^nAlE4@3NoRpMUV=Si?7n`Tjf%6#OF z^3=q1RMEKC=D$dQecIfq-F2{0n~)dRuK!^2lwu68c6z$$bj-z7@oI1exK3{f;?vWm z@$Q#c2PVlnhuT@N-TH}9qPDu`ORq$hs-@PWqoyGX3BO?nrLpcTO3bM0r9V=X#Bp+i zkIx$S8KkB~^&-&5?!8&_C`0Y;=78A?k}8qnH8Ucx77=ehVXt!R2!Jr^$fwo`Y!cfH zqTj;4-aG+v>ECjf)r-LK+tbK;t5WdnP-#snRqBASCC5{o&>ftudRZi4E!S;QJGb-bV1Q z{QU^@p^6-VplBdYyQr{zB`Vekmv~A8B%{4%SXSU5DQolf*FeeRy2~VvmX}6om3aJx z`z1W6p>rfyo}nHIE)BTMajEO=5eph0b`6#r1SN=Ztwp|W)wU`2inS=c^TGDM8z=(l zWoArp@HH(nW@&7>zI<~G$;*iA?tZQxe-s%$e8+6Q70<2*v27RecV%|X=72PiL8;b* z6%;1NZa2vFC61=V+Wj<@R%Ylfll*0yVP*y zpGl?WFL80nT7vaHOe#>)txE25ce$`19OiLao;^-i5NIqx&y5h$n*G@ zXWY?l1|ZVlb-U4jN2u}#yR+vitrTpbHp4zb;-7Gf?M;h!6St`}=F9hgrlj^oYZbiY zDwOqO^zt*Z5&-sY3Wz&17tAMkQjq3#;EztKe#)JKARWK2cD*3=*MvzHrC((;-8j+#go*hXVa`Bgd05j&S)mfH%~ zE#O)~VwMf>FI6X)T@j1Mq$c_|XseI|*0W`$5kaNDSjCIuhvh-mgm>`C)vG_p0*e>0 z;lR;rOV(|Lq^f+h(R>>${oe`Pd`ffF-?jnbwUrvb+-W-FA7Orlg@O~X|$lo!H~Cd`7c*XHUPznv|1)-#kR3VUxW=P zU39)t1NDQ&df92eO5t1Q3s=E22yFZgjf{IsJ3d-G|4;9rnBcW*aR(YfMCh6%kg)GX z9kSlT?*`mlt3s{DUG=XLx42HroP{XVH-Dtm=1$g$Awp~6SB*E`?ylxXS*6(YAZyNo z?R3ZoNi;!8s!VN|&5b6i5~G;Hm0U%*n=VX$k9I-WMkF)gOR=0Weire*=SdLG7F>k$ zzlk6&`ltR41fkJFl^Hl{fao&@tfKHdFsB@!+D!CC#7Cn zn%Ol-ka9E9se$5+556iny(}S1ls4Wb)%0;)QJ8ddp{0dIk$uy8l5;M~HDiMI5DS_5$HM4`c(r zeIq4Ap5i?-E^Iw9=}q@3zxNwxo`pr@8;^Y|_8y7o?=J&lJlpoEN6s>;oMN?Y3HkIs z^%)oR=vINulkglybJa~_A>gDJ)uK*3*PG)8%C8c7LI77FVJ`_SD*RZ;`tXF@ocXWl z4_!g)wf(Fr&Kk1zbvi`f-5h~4b02DN0Gu*Clo1-8@*$!63MD!FNKZHUkWU4yC4at| zC0 zG2a|d8#&FT1A0!p-g?F_{iw!O_(Js3l0uC^67E05_{;)};ii(uE%m#SajK^VZR7*6 z`0@zgDeVBFef?d-e;_>%9m1l;OeIw3#fx>p*DZ~R>!~St13Ms(7#I#a^cl&(kkDYdV9O|4}#chP8=l;$DIkAO{lBU-5XaXLx})+hJt$0wrT)PpLmd zm3y!LhZs`p{v4_iNNacqut;MWH?q)+3kjWQ*#O2Aq3C)P0YXwm9I}D%KVfLvw;Vht zT7i?XiVrEn^|w$8Okj#dP^3G}-brPXL=q1Hwi}&INa%EU#<#M)eV|gyE~CYk^-!6$ zJms!#1mV{4HSW5cGUF;<{PP05ShuZG3yoC{M@=;&aI8*pU^W_Xq0XEMYVwYsEW3XP zcTG!3X`h1wMESUxxGh12FaF&WK;Fh)rvU+xiCa#nHJlpyFU7>ZRz-!~?R+C+A8D>h zLr{&ZDv($uJD0?@hKrVpx|mPNnvowEX(3lE8{q;har} z*0fZ1r4`sFz}AyazFCS77kp=0RbqNTr7X&~zk@z#uucnDUH3NDFLH&lw+*L}AT*J_ zk#GW-<9s!INA9*3j*9}P#$92=d3zsd=kG-ks45L~zA0m}_%uP&u1>2cKEg#kj(KHx z#@P#Q$!4v_-B+_egaq$=!8QMk?HOIkj!4`@I!W_2LHL$+g)#TXKpPY50#7*9WJY(U zh>90xb%3p&3doSgRS1_UO2!2bWaB2Yp=E?Q+*XIYd=*}v^ex9Y(e3IL+5@QcREFOc zHy5x0o;{VA2&|mW{gY$`AvAV-&2hGBZPazTo%F=$bIi3W<^lu%;I*$Pq2x@$ql>)m z%3fgk_mOE&a3(eeF?J8fft3`;gP1Rb|#K6VAH4ZsLp{CRidSn8|Z54{pzb zKk@0d<#|OvkI-QNkI-UKjL5e#H>!090hL&yGB0Fg#6thrvGkSx!73(PkU9$lX*lPz zE^7|7rq;0wTaqV*xa2w&?zdvJ(*%_b2=kECDgF%7YqS>@OvmUucOl9NSL*aT8tV`I zDcMWZ0Fu9uY2*=0kF?ipWf_ikDos-lnMEIa3uWRKCGbmb+-xFw1hRg-o^a&rB@s#k zOPq@K1RzJ%sVE_eCJQ)dSD2N)=6D^+@V!&`2k}s4B1vMmBp2@t$p;w$U>5mlH}zXW zq(q!L?()3IlcLP49wiCILVt^HzCV>ZU_}H`m&VDSP8Gbn}m7DW$d^P z9&dT-+it`er`$u!jL^_uS9jNt%{d%du={W2>r$e>BOlBV-E5%YG5D3u@@BwNMIUnQ z-a+hm`dl?FRmz1TzUDpCgRGE%SLVM3pnUBVv97BT6^e!1>#r{^m`3pIx?P)1k=xl~^N@Ih}&Y*SMYhv#!(Z>`~ zK zWw9;f+giXbnO%{YiDm}W&b-AWF-&?D;840y)rP(Qp_8>RF~X+nqe+Zx6uC8xPkHhm zd8&wS`p7!a&g zEa}Bb<*plBRCxV}IU`zgseB#DmQL1R>|88S0MO{&+%Q^o)n&eP+r~Do_8Y2N?zrta zbS_*501{HK_1+k3+I4x<4QwWv51|$#6E4#XHkG~`!FMSN?s~+ONwz8OH;HZ97jtMA zP&ApoMGw}c_gwb+8}$$=cqy*17W=@6Hdp2A795F%=Nt%_VnSDa99PO={g?kj&5;=yh!OOQy%5n1M?c~jG+yXKf-F8YYP#3$Q z;YO=Cmyl2oFzA8SxPnaQj#hVd-sMgV6TV;Sc^Bh-^hs}yD z*+`B6#e8@ODT0(2zPVN!LoKt;oK}1zzI_r=tC@fPmF-}|={`CDJ8+Agh*k`}r zxh_B_sgJxjxVCHV1K}2719!cq=O3v1qislymtKl>!f3GX!E0_qRIvAEd(Gorr4vJ1 zBt;5^v7*yUM+XN=5xlQt{qS3b?-mowR^~l26i zn0>`Hd_Ze)rM%2*WZp@KK?%}3g*t2RC6aMUe1yIM~ZYwSCGD^IhrZwbnT zFIm^#{xMKDOoi<%h)aCCZpRG&8*nCVTEBiTPSTZH(&+6Ox24ipJj?f3y=M~dv>8S&q6k49;RM1sj3>d zR{fS($r$6;szvq_QpPp>{mBfKLxW9LHigmrV= zf!+A+;JJUnsDBKJxgYVy&zDOXJ%fZt1C20#JVw%mZ`HPghokmae2FzY(mJWc2>y%A z2bA?AkJM(syt)5Rna_j+d3t*6zOe~TdB)Wwf_;L#&%-}HRD2u0!G8aYHJWo43hP0W zG{70_YQryzF?bzg0tOQ#QR_7Hx#17OSbPq8jVet4cc$3S`yXzPp7GP{XXu{z5n2L* z(nn&irY!Y;?VlOqX7t0NuTZ8WkKGsA*H-va)94M@^j44&qDx=~8bEFcD~WpcY-7p) zpr?El##!g|Ti5|TJw5+ARpgnWUfW+aucV%^)rklaj@To>XdQt|^Yxj}J24S@omA@N z#oJD*h-BF5e!<1PfT#7jQ%flxsy-~QdJL*9Y_#)_z2vq2Pjs->=^-* ztvKAuXmCge%wGbjVTr0fvX()A*NO*>&YXU)P8Y>|eyZM<%%qgzkMF3`IHDZ!Es^sk zY>IzT^?oD8Xn&QNv4D?HC%>RbZ?LN_9?sHemrWcNpE;-%hH8$-wCwDG~ps_l22TZ&T`4fqRKBWgb38Gh0&E!h9;3g?Wvx1;ct9KJd5kc)}Ml9>HpeQBhS2~*@HcE`1X^GNsdRR z*d$iM{>p@<8}<(u+!1e#2S)6zqbUs6_$y?>Cl!-VWX+~wMWn%ZDNZfApOfP$x9jr! z9LrW!Uj%fEEt0^8<40 zV1d1pQlrhr@5QTz*Ny1M8U$X@zo$S9-W((vA3EEVMe1;{0*x?}ESGj0;i=ZgL~eJ8NkV#)q-JkSy1yMgYkh1QU$6XWsBhlbFm9f>s< zh&MyoLLbmQh#I>hemGmq7U-)AX&4$KEVYrUf(Cz3%!04;i=W-W-4rg(6F7>im{dH9 zo*87EiFkIO$5e2OmXaw;yH~;vJ85PX=;voYH|}R{AA-MYS@HbOv3tD&dZoVijcmxU zyRi1JG$(5dCN`er8!yd*}Z$Cew>jND*yP+0ZW3e_a*&hpIC;QO7@AGzP@ga;2&ne&@U#To%&nGho$>Jp1q&M1W*sV}@Pf`KRz2dpPbiD$(-@JbO z%a<;Fikht=m%B56k|TyHvNQb<16>y)lM#O3b0Bm#gV^BEJ?4>2!ydu(O7A?EBHO z4|>zGIAaDGYXq|wwLg^CbwQmH8^&{SNg};f850l?7^-Z`58cpmq4<)#z-X0N6duzi z-SDgEyn9wNZlOS@E)CZ*T>%;4r2awes*S}8v1!ekZ}AW5YQq_GJ0wVOnggfL!${u%~EpQC_CnhiO=H$x|i20c#D) z722@1^M1{JH~ls8wXRPAhIHC*`gW*}Hd)!Gi7o0g!6je_h4oeA593kdL&IF7pmBGZ z06Y@^#-TBoeB{7X?bbD3BlU@Z9P!B;3COV#ACiu7;#_e=KcX~nwI8+I9gAci7B`Nq-zL@Hr@4S^2ZfD?AynQkmo}xEsuU0pUD}0VKLv#%*?q=M_yb`j@$SL+yK(d zWFy8U4DF4Xu0}I4`u@0bo#uRzWp^vL-6C* z1T$v@^jNsGNMjVNkmRE^|BPDjcRT)!THuV<>@@Ag?IbO;oxbQ*<53!8w7|V>8hV`; zbE9#mY8bZPmdIcV?2t$Jp-;zSKhxWkkD&G0bT5^tZ(*Cf+R#miI3#4P%747L+$@7& zOhOZzoOuBSoKJpqOI(3LA!#_UkMTmQ4^NrBOk?ITSA9Mot(i!5H;!2M7}5F9Kc zqd-W6D$@MsHx<_D=T9hJ>Uezbyo41#M3)^ Rm&qS%?%BUPZs+e8{tqH$p7a0! literal 0 HcmV?d00001 diff --git a/tests/subtests/baseline/test_hdiff_imatch.png b/tests/subtests/baseline/test_hdiff_imatch.png new file mode 100644 index 0000000000000000000000000000000000000000..c1a2a3effb2e724ff1a3d92c9dd8beae74dcca2e GIT binary patch literal 24420 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 literal 0 HcmV?d00001 diff --git a/tests/subtests/baseline/test_hdiff_imatch_savefig.png b/tests/subtests/baseline/test_hdiff_imatch_savefig.png new file mode 100644 index 0000000000000000000000000000000000000000..c791a4b17e02a8ca2ae9350059b942e94b6e427a GIT binary patch literal 17381 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 U>YfVzmm4tjZmV4d#xAk{59-RzB>(^b literal 0 HcmV?d00001 diff --git a/tests/subtests/baseline/test_hdiff_imatch_style.png b/tests/subtests/baseline/test_hdiff_imatch_style.png new file mode 100644 index 0000000000000000000000000000000000000000..49f4adeecb3ec5b8a963fed52a56f2f82d90b4a7 GIT binary patch literal 26038 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 literal 0 HcmV?d00001 diff --git a/tests/subtests/baseline/test_hdiff_imatch_tolerance.png b/tests/subtests/baseline/test_hdiff_imatch_tolerance.png new file mode 100644 index 0000000000000000000000000000000000000000..516801e02a2bf25617120f6790cb9a0050c78739 GIT binary patch literal 32950 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 Rm&qS%?%BUPZs+e8{tqH$p7a0! literal 0 HcmV?d00001 diff --git a/tests/subtests/baseline/test_hmatch_idiff.png b/tests/subtests/baseline/test_hmatch_idiff.png new file mode 100644 index 0000000000000000000000000000000000000000..028c9be456224d9653a34b8c2a819f4bce9e4782 GIT binary patch literal 20709 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�> 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 literal 0 HcmV?d00001 diff --git a/tests/subtests/baseline/test_hmissing_imatch.png b/tests/subtests/baseline/test_hmissing_imatch.png new file mode 100644 index 0000000000000000000000000000000000000000..03483cbc06b708c0c48a7303a6e70286fb3e7075 GIT binary patch literal 23405 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 Date: Sat, 5 Feb 2022 19:19:36 +0000 Subject: [PATCH 11/22] Add mpl33 to mpl35 baselines --- tests/subtests/hashes/mpl33_ft261.json | 13 +++++++++++++ tests/subtests/hashes/mpl34_ft261.json | 13 +++++++++++++ tests/subtests/hashes/mpl35_ft261.json | 2 +- tests/subtests/result_hashes/mpl33_ft261.json | 16 ++++++++++++++++ tests/subtests/result_hashes/mpl34_ft261.json | 16 ++++++++++++++++ tests/subtests/result_hashes/mpl35_ft261.json | 2 +- tox.ini | 2 ++ 7 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 tests/subtests/hashes/mpl33_ft261.json create mode 100644 tests/subtests/hashes/mpl34_ft261.json create mode 100644 tests/subtests/result_hashes/mpl33_ft261.json create mode 100644 tests/subtests/result_hashes/mpl34_ft261.json diff --git a/tests/subtests/hashes/mpl33_ft261.json b/tests/subtests/hashes/mpl33_ft261.json new file mode 100644 index 00000000..e285cd6f --- /dev/null +++ b/tests/subtests/hashes/mpl33_ft261.json @@ -0,0 +1,13 @@ +{ + "subtests.subtest.test_hmatch_imatch": "42c391b37022e2c4edb53f5fd988e94f421905b40cea1544e62ffb3c049292a8", + "subtests.subtest.test_hmatch_idiff": "c14ba098dbda0988e35be5724ffb15b8e666253a4b37dec6a21203607c17473d", + "subtests.subtest.test_hmatch_imissing": "6c07931bac1a926c88bea5d07c40c8c1ce30648712e3fc963028193863e3ae65", + "subtests.subtest.test_hdiff_imatch": "d1ff383721a0c395c856302be7de8a8138a2693651425dc181ede262860aef7b", + "subtests.subtest.test_hdiff_idiff": "d1fff55ace5ef7e45dcd9913b54e0d9970028cae59666e937ccb3586d0f76e9a", + "subtests.subtest.test_hdiff_imissing": "d1ff35845c5887c034230e02aa4b60e053c779c693867e4803e1d72dde9240f7", + "subtests.subtest.test_hdiff_imatch_tolerance": "d1ff6912989a4b47ea910b04edfa58cf5d756d60825ea52ad59dcde8e03d4d8b", + "subtests.subtest.test_hdiff_idiff_tolerance": "d1ff6912989a4b47ea910b04edfa58cf5d756d60825ea52ad59dcde8e03d4d8b", + "subtests.subtest.test_hdiff_imatch_savefig": "d1ff5dc3f9e8acda06b0097ee893819be62ca9adbbcca7d2300602f079a93b92", + "subtests.subtest.test_hdiff_imatch_style": "d1ff7692747ec72d3c8669cdb3d66468426b83ecf49a214cd918b8f5a0752a1f", + "subtests.subtest.test_hdiff_imatch_removetext": "d1ff0d60d794a7cdfec884463c4fe14612ab1fe7fda4bc7fa702c8f1615e1539" +} diff --git a/tests/subtests/hashes/mpl34_ft261.json b/tests/subtests/hashes/mpl34_ft261.json new file mode 100644 index 00000000..a8012e51 --- /dev/null +++ b/tests/subtests/hashes/mpl34_ft261.json @@ -0,0 +1,13 @@ +{ + "subtests.subtest.test_hmatch_imatch": "573f4c1482192b7b15bbe4f2bd370ae3b5ab40c9afa441543b87e15a71e9f672", + "subtests.subtest.test_hmatch_idiff": "8b5c00365e6f784d5c8947091f09a92fd7d222e790431f297b4848f173e64857", + "subtests.subtest.test_hmatch_imissing": "fd069e642e3b154c24077a4996b545e1c4dbffdbed34ea5ad34c7b36873af68f", + "subtests.subtest.test_hdiff_imatch": "d1ffdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1", + "subtests.subtest.test_hdiff_idiff": "d1ff61bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43", + "subtests.subtest.test_hdiff_imissing": "d1ff63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e", + "subtests.subtest.test_hdiff_imatch_tolerance": "d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96", + "subtests.subtest.test_hdiff_idiff_tolerance": "d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96", + "subtests.subtest.test_hdiff_imatch_savefig": "d1ffc2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568", + "subtests.subtest.test_hdiff_imatch_style": "d1ffd1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7", + "subtests.subtest.test_hdiff_imatch_removetext": "d1fff83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e" +} diff --git a/tests/subtests/hashes/mpl35_ft261.json b/tests/subtests/hashes/mpl35_ft261.json index 46118a85..2ae41a7e 100644 --- a/tests/subtests/hashes/mpl35_ft261.json +++ b/tests/subtests/hashes/mpl35_ft261.json @@ -10,4 +10,4 @@ "subtests.subtest.test_hdiff_imatch_savefig": "d1ff803a4b4026d8c6dc0ab950228793ea255cd9b6c629c39db9e6315a9af6bc", "subtests.subtest.test_hdiff_imatch_style": "d1ffde36c2bad7dca131e4cbbfe229f882b5beec62750fb7da29314fd6a1ff13", "subtests.subtest.test_hdiff_imatch_removetext": "d1ff6cf613c6836c1b1202abaae69cf65bc2232a8e31ab1040454bedc8e31e7a" -} \ No newline at end of file +} diff --git a/tests/subtests/result_hashes/mpl33_ft261.json b/tests/subtests/result_hashes/mpl33_ft261.json new file mode 100644 index 00000000..b5bf8b7a --- /dev/null +++ b/tests/subtests/result_hashes/mpl33_ft261.json @@ -0,0 +1,16 @@ +{ + "subtests.subtest.test_hmatch_imatch": "42c391b37022e2c4edb53f5fd988e94f421905b40cea1544e62ffb3c049292a8", + "subtests.subtest.test_hmatch_idiff": "c14ba098dbda0988e35be5724ffb15b8e666253a4b37dec6a21203607c17473d", + "subtests.subtest.test_hmatch_imissing": "6c07931bac1a926c88bea5d07c40c8c1ce30648712e3fc963028193863e3ae65", + "subtests.subtest.test_hdiff_imatch": "b48a383721a0c395c856302be7de8a8138a2693651425dc181ede262860aef7b", + "subtests.subtest.test_hdiff_idiff": "44edf55ace5ef7e45dcd9913b54e0d9970028cae59666e937ccb3586d0f76e9a", + "subtests.subtest.test_hdiff_imissing": "042235845c5887c034230e02aa4b60e053c779c693867e4803e1d72dde9240f7", + "subtests.subtest.test_hmissing_imatch": "d87e9aa72d2cd0ccd9959ec7af2dc00ab86e2fe8f06a6c54a017cde62f7e5c1f", + "subtests.subtest.test_hmissing_idiff": "00b074ef61b5061b8ec50dbe32b3214ffd21c9489ae212671558f338ad792e5f", + "subtests.subtest.test_hmissing_imissing": "3e5c4a4386e32133083c36ca0c95f5e38be904ed238e49ab1e06edd35603abfc", + "subtests.subtest.test_hdiff_imatch_tolerance": "04eb6912989a4b47ea910b04edfa58cf5d756d60825ea52ad59dcde8e03d4d8b", + "subtests.subtest.test_hdiff_idiff_tolerance": "04eb6912989a4b47ea910b04edfa58cf5d756d60825ea52ad59dcde8e03d4d8b", + "subtests.subtest.test_hdiff_imatch_savefig": "c47d5dc3f9e8acda06b0097ee893819be62ca9adbbcca7d2300602f079a93b92", + "subtests.subtest.test_hdiff_imatch_style": "8fa57692747ec72d3c8669cdb3d66468426b83ecf49a214cd918b8f5a0752a1f", + "subtests.subtest.test_hdiff_imatch_removetext": "f9fc0d60d794a7cdfec884463c4fe14612ab1fe7fda4bc7fa702c8f1615e1539" +} diff --git a/tests/subtests/result_hashes/mpl34_ft261.json b/tests/subtests/result_hashes/mpl34_ft261.json new file mode 100644 index 00000000..b30b198a --- /dev/null +++ b/tests/subtests/result_hashes/mpl34_ft261.json @@ -0,0 +1,16 @@ +{ + "subtests.subtest.test_hmatch_imatch": "573f4c1482192b7b15bbe4f2bd370ae3b5ab40c9afa441543b87e15a71e9f672", + "subtests.subtest.test_hmatch_idiff": "8b5c00365e6f784d5c8947091f09a92fd7d222e790431f297b4848f173e64857", + "subtests.subtest.test_hmatch_imissing": "fd069e642e3b154c24077a4996b545e1c4dbffdbed34ea5ad34c7b36873af68f", + "subtests.subtest.test_hdiff_imatch": "6e2fdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1", + "subtests.subtest.test_hdiff_idiff": "443361bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43", + "subtests.subtest.test_hdiff_imissing": "301e63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e", + "subtests.subtest.test_hmissing_imatch": "eabd8a2e22afd88682990bfb8e4a0700a942fe68e5114e8da4ab6bd93c47b824", + "subtests.subtest.test_hmissing_idiff": "e69570c4a70b2cc88ddee0f0a82312cae4f394b7f62e5760245feda1364c03ab", + "subtests.subtest.test_hmissing_imissing": "5c8a9c7412e4e098f6f2683ee247c08bd50805a93df4d4b6d8fccf3579b4c56b", + "subtests.subtest.test_hdiff_imatch_tolerance": "aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96", + "subtests.subtest.test_hdiff_idiff_tolerance": "aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96", + "subtests.subtest.test_hdiff_imatch_savefig": "5dc1c2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568", + "subtests.subtest.test_hdiff_imatch_style": "185ed1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7", + "subtests.subtest.test_hdiff_imatch_removetext": "be5af83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e" +} diff --git a/tests/subtests/result_hashes/mpl35_ft261.json b/tests/subtests/result_hashes/mpl35_ft261.json index d7272f5a..edc8605a 100644 --- a/tests/subtests/result_hashes/mpl35_ft261.json +++ b/tests/subtests/result_hashes/mpl35_ft261.json @@ -13,4 +13,4 @@ "subtests.subtest.test_hdiff_imatch_savefig": "8864803a4b4026d8c6dc0ab950228793ea255cd9b6c629c39db9e6315a9af6bc", "subtests.subtest.test_hdiff_imatch_style": "e3c8de36c2bad7dca131e4cbbfe229f882b5beec62750fb7da29314fd6a1ff13", "subtests.subtest.test_hdiff_imatch_removetext": "e4c06cf613c6836c1b1202abaae69cf65bc2232a8e31ab1040454bedc8e31e7a" -} \ No newline at end of file +} diff --git a/tox.ini b/tox.ini index 8c8e617c..6c242ccd 100644 --- a/tox.ini +++ b/tox.ini @@ -25,6 +25,8 @@ deps = mpl31: matplotlib==3.1.* mpl32: matplotlib==3.2.* mpl33: matplotlib==3.3.* + mpl34: matplotlib==3.4.* + mpl35: matplotlib==3.5.* mpldev: git+https://github.com/matplotlib/matplotlib.git#egg=matplotlib extras = test From 2d035e1a9b828955208959e71fed63c2d64703ad Mon Sep 17 00:00:00 2001 From: Conor MacBride Date: Sat, 5 Feb 2022 21:22:28 +0000 Subject: [PATCH 12/22] Add draft baseline summaries --- tests/subtests/summaries/test_default.json | 156 ++++++++++++++++++ tests/subtests/summaries/test_hash.json | 156 ++++++++++++++++++ .../summaries/test_html_hashes_only.json | 156 ++++++++++++++++++ .../summaries/test_html_images_only.json | 156 ++++++++++++++++++ tests/subtests/summaries/test_hybrid.json | 156 ++++++++++++++++++ .../summaries/test_results_always.json | 156 ++++++++++++++++++ 6 files changed, 936 insertions(+) create mode 100644 tests/subtests/summaries/test_default.json create mode 100644 tests/subtests/summaries/test_hash.json create mode 100644 tests/subtests/summaries/test_html_hashes_only.json create mode 100644 tests/subtests/summaries/test_html_images_only.json create mode 100644 tests/subtests/summaries/test_hybrid.json create mode 100644 tests/subtests/summaries/test_results_always.json diff --git a/tests/subtests/summaries/test_default.json b/tests/subtests/summaries/test_default.json new file mode 100644 index 00000000..afe67989 --- /dev/null +++ b/tests/subtests/summaries/test_default.json @@ -0,0 +1,156 @@ +{ + "subtests.subtest.test_hmatch_imatch": { + "status": "passed", + "status_msg": "Image comparison passed.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": 2, + "result_image": null, + "baseline_hash": null, + "result_hash": null + }, + "subtests.subtest.test_hmatch_idiff": { + "status": "failed", + "status_msg": "Error: Image files did not match.\n RMS Value: 11.100353848213828\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hmatch_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hmatch_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hmatch_idiff/result-failed-diff.png\n Tolerance: \n 2", + "baseline_image": "subtests.subtest.test_hmatch_idiff/baseline.png", + "diff_image": "subtests.subtest.test_hmatch_idiff/result-failed-diff.png", + "rms": 11.100353848213828, + "tolerance": 2, + "result_image": "subtests.subtest.test_hmatch_idiff/result.png", + "baseline_hash": null, + "result_hash": null + }, + "subtests.subtest.test_hmatch_imissing": { + "status": "failed", + "status_msg": "Image file not found for comparison test in: \n\t/Users/conor/Documents/pytest-mpl/tests/subtests/baseline\n(This is expected for new tests.)\nGenerated Image: \n\t/private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hmatch_imissing/result.png", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hmatch_imissing/result.png", + "baseline_hash": null, + "result_hash": null + }, + "subtests.subtest.test_hdiff_imatch": { + "status": "passed", + "status_msg": "Image comparison passed.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": 2, + "result_image": null, + "baseline_hash": null, + "result_hash": null + }, + "subtests.subtest.test_hdiff_idiff": { + "status": "failed", + "status_msg": "Error: Image files did not match.\n RMS Value: 11.182677079602481\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hdiff_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hdiff_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hdiff_idiff/result-failed-diff.png\n Tolerance: \n 2", + "baseline_image": "subtests.subtest.test_hdiff_idiff/baseline.png", + "diff_image": "subtests.subtest.test_hdiff_idiff/result-failed-diff.png", + "rms": 11.182677079602481, + "tolerance": 2, + "result_image": "subtests.subtest.test_hdiff_idiff/result.png", + "baseline_hash": null, + "result_hash": null + }, + "subtests.subtest.test_hdiff_imissing": { + "status": "failed", + "status_msg": "Image file not found for comparison test in: \n\t/Users/conor/Documents/pytest-mpl/tests/subtests/baseline\n(This is expected for new tests.)\nGenerated Image: \n\t/private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hdiff_imissing/result.png", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hdiff_imissing/result.png", + "baseline_hash": null, + "result_hash": null + }, + "subtests.subtest.test_hmissing_imatch": { + "status": "passed", + "status_msg": "Image comparison passed.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": 2, + "result_image": null, + "baseline_hash": null, + "result_hash": null + }, + "subtests.subtest.test_hmissing_idiff": { + "status": "failed", + "status_msg": "Error: Image files did not match.\n RMS Value: 12.12938597648977\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hmissing_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hmissing_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hmissing_idiff/result-failed-diff.png\n Tolerance: \n 2", + "baseline_image": "subtests.subtest.test_hmissing_idiff/baseline.png", + "diff_image": "subtests.subtest.test_hmissing_idiff/result-failed-diff.png", + "rms": 12.12938597648977, + "tolerance": 2, + "result_image": "subtests.subtest.test_hmissing_idiff/result.png", + "baseline_hash": null, + "result_hash": null + }, + "subtests.subtest.test_hmissing_imissing": { + "status": "failed", + "status_msg": "Image file not found for comparison test in: \n\t/Users/conor/Documents/pytest-mpl/tests/subtests/baseline\n(This is expected for new tests.)\nGenerated Image: \n\t/private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hmissing_imissing/result.png", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hmissing_imissing/result.png", + "baseline_hash": null, + "result_hash": null + }, + "subtests.subtest.test_hdiff_imatch_tolerance": { + "status": "passed", + "status_msg": "Image comparison passed.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": 200, + "result_image": null, + "baseline_hash": null, + "result_hash": null + }, + "subtests.subtest.test_hdiff_idiff_tolerance": { + "status": "failed", + "status_msg": "Error: Image files did not match.\n RMS Value: 29.260332173249314\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hdiff_idiff_tolerance/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hdiff_idiff_tolerance/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hdiff_idiff_tolerance/result-failed-diff.png\n Tolerance: \n 3", + "baseline_image": "subtests.subtest.test_hdiff_idiff_tolerance/baseline.png", + "diff_image": "subtests.subtest.test_hdiff_idiff_tolerance/result-failed-diff.png", + "rms": 29.260332173249314, + "tolerance": 3, + "result_image": "subtests.subtest.test_hdiff_idiff_tolerance/result.png", + "baseline_hash": null, + "result_hash": null + }, + "subtests.subtest.test_hdiff_imatch_savefig": { + "status": "passed", + "status_msg": "Image comparison passed.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": 2, + "result_image": null, + "baseline_hash": null, + "result_hash": null + }, + "subtests.subtest.test_hdiff_imatch_style": { + "status": "passed", + "status_msg": "Image comparison passed.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": 2, + "result_image": null, + "baseline_hash": null, + "result_hash": null + }, + "subtests.subtest.test_hdiff_imatch_removetext": { + "status": "passed", + "status_msg": "Image comparison passed.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": 2, + "result_image": null, + "baseline_hash": null, + "result_hash": null + } +} \ No newline at end of file diff --git a/tests/subtests/summaries/test_hash.json b/tests/subtests/summaries/test_hash.json new file mode 100644 index 00000000..34677d75 --- /dev/null +++ b/tests/subtests/summaries/test_hash.json @@ -0,0 +1,156 @@ +{ + "subtests.subtest.test_hmatch_imatch": { + "status": "passed", + "status_msg": "Test hash matches baseline hash.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": null, + "baseline_hash": "573f4c1482192b7b15bbe4f2bd370ae3b5ab40c9afa441543b87e15a71e9f672", + "result_hash": "573f4c1482192b7b15bbe4f2bd370ae3b5ab40c9afa441543b87e15a71e9f672" + }, + "subtests.subtest.test_hmatch_idiff": { + "status": "passed", + "status_msg": "Test hash matches baseline hash.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": null, + "baseline_hash": "8b5c00365e6f784d5c8947091f09a92fd7d222e790431f297b4848f173e64857", + "result_hash": "8b5c00365e6f784d5c8947091f09a92fd7d222e790431f297b4848f173e64857" + }, + "subtests.subtest.test_hmatch_imissing": { + "status": "passed", + "status_msg": "Test hash matches baseline hash.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": null, + "baseline_hash": "fd069e642e3b154c24077a4996b545e1c4dbffdbed34ea5ad34c7b36873af68f", + "result_hash": "fd069e642e3b154c24077a4996b545e1c4dbffdbed34ea5ad34c7b36873af68f" + }, + "subtests.subtest.test_hdiff_imatch": { + "status": "failed", + "status_msg": "Test hash does not match baseline hash.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hdiff_imatch/result.png", + "baseline_hash": "d1ffdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1", + "result_hash": "6e2fdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1" + }, + "subtests.subtest.test_hdiff_idiff": { + "status": "failed", + "status_msg": "Test hash does not match baseline hash.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hdiff_idiff/result.png", + "baseline_hash": "d1ff61bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43", + "result_hash": "443361bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43" + }, + "subtests.subtest.test_hdiff_imissing": { + "status": "failed", + "status_msg": "Test hash does not match baseline hash.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hdiff_imissing/result.png", + "baseline_hash": "d1ff63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e", + "result_hash": "301e63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e" + }, + "subtests.subtest.test_hmissing_imatch": { + "status": "failed", + "status_msg": "Hash for test 'subtests.subtest.test_hmissing_imatch' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is eabd8a2e22afd88682990bfb8e4a0700a942fe68e5114e8da4ab6bd93c47b824.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hmissing_imatch/result.png", + "baseline_hash": null, + "result_hash": "eabd8a2e22afd88682990bfb8e4a0700a942fe68e5114e8da4ab6bd93c47b824" + }, + "subtests.subtest.test_hmissing_idiff": { + "status": "failed", + "status_msg": "Hash for test 'subtests.subtest.test_hmissing_idiff' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is e69570c4a70b2cc88ddee0f0a82312cae4f394b7f62e5760245feda1364c03ab.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hmissing_idiff/result.png", + "baseline_hash": null, + "result_hash": "e69570c4a70b2cc88ddee0f0a82312cae4f394b7f62e5760245feda1364c03ab" + }, + "subtests.subtest.test_hmissing_imissing": { + "status": "failed", + "status_msg": "Hash for test 'subtests.subtest.test_hmissing_imissing' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is 5c8a9c7412e4e098f6f2683ee247c08bd50805a93df4d4b6d8fccf3579b4c56b.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hmissing_imissing/result.png", + "baseline_hash": null, + "result_hash": "5c8a9c7412e4e098f6f2683ee247c08bd50805a93df4d4b6d8fccf3579b4c56b" + }, + "subtests.subtest.test_hdiff_imatch_tolerance": { + "status": "failed", + "status_msg": "Test hash does not match baseline hash.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hdiff_imatch_tolerance/result.png", + "baseline_hash": "d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96", + "result_hash": "aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96" + }, + "subtests.subtest.test_hdiff_idiff_tolerance": { + "status": "failed", + "status_msg": "Test hash does not match baseline hash.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hdiff_idiff_tolerance/result.png", + "baseline_hash": "d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96", + "result_hash": "aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96" + }, + "subtests.subtest.test_hdiff_imatch_savefig": { + "status": "failed", + "status_msg": "Test hash does not match baseline hash.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hdiff_imatch_savefig/result.png", + "baseline_hash": "d1ffc2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568", + "result_hash": "5dc1c2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568" + }, + "subtests.subtest.test_hdiff_imatch_style": { + "status": "failed", + "status_msg": "Test hash does not match baseline hash.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hdiff_imatch_style/result.png", + "baseline_hash": "d1ffd1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7", + "result_hash": "185ed1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7" + }, + "subtests.subtest.test_hdiff_imatch_removetext": { + "status": "failed", + "status_msg": "Test hash does not match baseline hash.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hdiff_imatch_removetext/result.png", + "baseline_hash": "d1fff83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e", + "result_hash": "be5af83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e" + } +} \ No newline at end of file diff --git a/tests/subtests/summaries/test_html_hashes_only.json b/tests/subtests/summaries/test_html_hashes_only.json new file mode 100644 index 00000000..b2b53d2f --- /dev/null +++ b/tests/subtests/summaries/test_html_hashes_only.json @@ -0,0 +1,156 @@ +{ + "subtests.subtest.test_hmatch_imatch": { + "status": "passed", + "status_msg": "Test hash matches baseline hash.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hmatch_imatch/result.png", + "baseline_hash": "573f4c1482192b7b15bbe4f2bd370ae3b5ab40c9afa441543b87e15a71e9f672", + "result_hash": "573f4c1482192b7b15bbe4f2bd370ae3b5ab40c9afa441543b87e15a71e9f672" + }, + "subtests.subtest.test_hmatch_idiff": { + "status": "passed", + "status_msg": "Test hash matches baseline hash.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hmatch_idiff/result.png", + "baseline_hash": "8b5c00365e6f784d5c8947091f09a92fd7d222e790431f297b4848f173e64857", + "result_hash": "8b5c00365e6f784d5c8947091f09a92fd7d222e790431f297b4848f173e64857" + }, + "subtests.subtest.test_hmatch_imissing": { + "status": "passed", + "status_msg": "Test hash matches baseline hash.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hmatch_imissing/result.png", + "baseline_hash": "fd069e642e3b154c24077a4996b545e1c4dbffdbed34ea5ad34c7b36873af68f", + "result_hash": "fd069e642e3b154c24077a4996b545e1c4dbffdbed34ea5ad34c7b36873af68f" + }, + "subtests.subtest.test_hdiff_imatch": { + "status": "failed", + "status_msg": "Test hash does not match baseline hash.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hdiff_imatch/result.png", + "baseline_hash": "d1ffdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1", + "result_hash": "6e2fdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1" + }, + "subtests.subtest.test_hdiff_idiff": { + "status": "failed", + "status_msg": "Test hash does not match baseline hash.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hdiff_idiff/result.png", + "baseline_hash": "d1ff61bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43", + "result_hash": "443361bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43" + }, + "subtests.subtest.test_hdiff_imissing": { + "status": "failed", + "status_msg": "Test hash does not match baseline hash.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hdiff_imissing/result.png", + "baseline_hash": "d1ff63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e", + "result_hash": "301e63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e" + }, + "subtests.subtest.test_hmissing_imatch": { + "status": "failed", + "status_msg": "Hash for test 'subtests.subtest.test_hmissing_imatch' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is eabd8a2e22afd88682990bfb8e4a0700a942fe68e5114e8da4ab6bd93c47b824.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hmissing_imatch/result.png", + "baseline_hash": null, + "result_hash": "eabd8a2e22afd88682990bfb8e4a0700a942fe68e5114e8da4ab6bd93c47b824" + }, + "subtests.subtest.test_hmissing_idiff": { + "status": "failed", + "status_msg": "Hash for test 'subtests.subtest.test_hmissing_idiff' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is e69570c4a70b2cc88ddee0f0a82312cae4f394b7f62e5760245feda1364c03ab.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hmissing_idiff/result.png", + "baseline_hash": null, + "result_hash": "e69570c4a70b2cc88ddee0f0a82312cae4f394b7f62e5760245feda1364c03ab" + }, + "subtests.subtest.test_hmissing_imissing": { + "status": "failed", + "status_msg": "Hash for test 'subtests.subtest.test_hmissing_imissing' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is 5c8a9c7412e4e098f6f2683ee247c08bd50805a93df4d4b6d8fccf3579b4c56b.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hmissing_imissing/result.png", + "baseline_hash": null, + "result_hash": "5c8a9c7412e4e098f6f2683ee247c08bd50805a93df4d4b6d8fccf3579b4c56b" + }, + "subtests.subtest.test_hdiff_imatch_tolerance": { + "status": "failed", + "status_msg": "Test hash does not match baseline hash.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hdiff_imatch_tolerance/result.png", + "baseline_hash": "d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96", + "result_hash": "aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96" + }, + "subtests.subtest.test_hdiff_idiff_tolerance": { + "status": "failed", + "status_msg": "Test hash does not match baseline hash.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hdiff_idiff_tolerance/result.png", + "baseline_hash": "d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96", + "result_hash": "aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96" + }, + "subtests.subtest.test_hdiff_imatch_savefig": { + "status": "failed", + "status_msg": "Test hash does not match baseline hash.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hdiff_imatch_savefig/result.png", + "baseline_hash": "d1ffc2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568", + "result_hash": "5dc1c2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568" + }, + "subtests.subtest.test_hdiff_imatch_style": { + "status": "failed", + "status_msg": "Test hash does not match baseline hash.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hdiff_imatch_style/result.png", + "baseline_hash": "d1ffd1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7", + "result_hash": "185ed1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7" + }, + "subtests.subtest.test_hdiff_imatch_removetext": { + "status": "failed", + "status_msg": "Test hash does not match baseline hash.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hdiff_imatch_removetext/result.png", + "baseline_hash": "d1fff83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e", + "result_hash": "be5af83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e" + } +} \ No newline at end of file diff --git a/tests/subtests/summaries/test_html_images_only.json b/tests/subtests/summaries/test_html_images_only.json new file mode 100644 index 00000000..47298e01 --- /dev/null +++ b/tests/subtests/summaries/test_html_images_only.json @@ -0,0 +1,156 @@ +{ + "subtests.subtest.test_hmatch_imatch": { + "status": "passed", + "status_msg": "Image comparison passed.", + "baseline_image": "subtests.subtest.test_hmatch_imatch/baseline.png", + "diff_image": null, + "rms": null, + "tolerance": 2, + "result_image": "subtests.subtest.test_hmatch_imatch/result.png", + "baseline_hash": null, + "result_hash": null + }, + "subtests.subtest.test_hmatch_idiff": { + "status": "failed", + "status_msg": "Error: Image files did not match.\n RMS Value: 11.100353848213828\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hmatch_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hmatch_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hmatch_idiff/result-failed-diff.png\n Tolerance: \n 2", + "baseline_image": "subtests.subtest.test_hmatch_idiff/baseline.png", + "diff_image": "subtests.subtest.test_hmatch_idiff/result-failed-diff.png", + "rms": 11.100353848213828, + "tolerance": 2, + "result_image": "subtests.subtest.test_hmatch_idiff/result.png", + "baseline_hash": null, + "result_hash": null + }, + "subtests.subtest.test_hmatch_imissing": { + "status": "failed", + "status_msg": "Image file not found for comparison test in: \n\t/Users/conor/Documents/pytest-mpl/tests/subtests/baseline\n(This is expected for new tests.)\nGenerated Image: \n\t/private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hmatch_imissing/result.png", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hmatch_imissing/result.png", + "baseline_hash": null, + "result_hash": null + }, + "subtests.subtest.test_hdiff_imatch": { + "status": "passed", + "status_msg": "Image comparison passed.", + "baseline_image": "subtests.subtest.test_hdiff_imatch/baseline.png", + "diff_image": null, + "rms": null, + "tolerance": 2, + "result_image": "subtests.subtest.test_hdiff_imatch/result.png", + "baseline_hash": null, + "result_hash": null + }, + "subtests.subtest.test_hdiff_idiff": { + "status": "failed", + "status_msg": "Error: Image files did not match.\n RMS Value: 11.182677079602481\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hdiff_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hdiff_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hdiff_idiff/result-failed-diff.png\n Tolerance: \n 2", + "baseline_image": "subtests.subtest.test_hdiff_idiff/baseline.png", + "diff_image": "subtests.subtest.test_hdiff_idiff/result-failed-diff.png", + "rms": 11.182677079602481, + "tolerance": 2, + "result_image": "subtests.subtest.test_hdiff_idiff/result.png", + "baseline_hash": null, + "result_hash": null + }, + "subtests.subtest.test_hdiff_imissing": { + "status": "failed", + "status_msg": "Image file not found for comparison test in: \n\t/Users/conor/Documents/pytest-mpl/tests/subtests/baseline\n(This is expected for new tests.)\nGenerated Image: \n\t/private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hdiff_imissing/result.png", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hdiff_imissing/result.png", + "baseline_hash": null, + "result_hash": null + }, + "subtests.subtest.test_hmissing_imatch": { + "status": "passed", + "status_msg": "Image comparison passed.", + "baseline_image": "subtests.subtest.test_hmissing_imatch/baseline.png", + "diff_image": null, + "rms": null, + "tolerance": 2, + "result_image": "subtests.subtest.test_hmissing_imatch/result.png", + "baseline_hash": null, + "result_hash": null + }, + "subtests.subtest.test_hmissing_idiff": { + "status": "failed", + "status_msg": "Error: Image files did not match.\n RMS Value: 12.12938597648977\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hmissing_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hmissing_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hmissing_idiff/result-failed-diff.png\n Tolerance: \n 2", + "baseline_image": "subtests.subtest.test_hmissing_idiff/baseline.png", + "diff_image": "subtests.subtest.test_hmissing_idiff/result-failed-diff.png", + "rms": 12.12938597648977, + "tolerance": 2, + "result_image": "subtests.subtest.test_hmissing_idiff/result.png", + "baseline_hash": null, + "result_hash": null + }, + "subtests.subtest.test_hmissing_imissing": { + "status": "failed", + "status_msg": "Image file not found for comparison test in: \n\t/Users/conor/Documents/pytest-mpl/tests/subtests/baseline\n(This is expected for new tests.)\nGenerated Image: \n\t/private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hmissing_imissing/result.png", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hmissing_imissing/result.png", + "baseline_hash": null, + "result_hash": null + }, + "subtests.subtest.test_hdiff_imatch_tolerance": { + "status": "passed", + "status_msg": "Image comparison passed.", + "baseline_image": "subtests.subtest.test_hdiff_imatch_tolerance/baseline.png", + "diff_image": null, + "rms": null, + "tolerance": 200, + "result_image": "subtests.subtest.test_hdiff_imatch_tolerance/result.png", + "baseline_hash": null, + "result_hash": null + }, + "subtests.subtest.test_hdiff_idiff_tolerance": { + "status": "failed", + "status_msg": "Error: Image files did not match.\n RMS Value: 29.260332173249314\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hdiff_idiff_tolerance/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hdiff_idiff_tolerance/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hdiff_idiff_tolerance/result-failed-diff.png\n Tolerance: \n 3", + "baseline_image": "subtests.subtest.test_hdiff_idiff_tolerance/baseline.png", + "diff_image": "subtests.subtest.test_hdiff_idiff_tolerance/result-failed-diff.png", + "rms": 29.260332173249314, + "tolerance": 3, + "result_image": "subtests.subtest.test_hdiff_idiff_tolerance/result.png", + "baseline_hash": null, + "result_hash": null + }, + "subtests.subtest.test_hdiff_imatch_savefig": { + "status": "passed", + "status_msg": "Image comparison passed.", + "baseline_image": "subtests.subtest.test_hdiff_imatch_savefig/baseline.png", + "diff_image": null, + "rms": null, + "tolerance": 2, + "result_image": "subtests.subtest.test_hdiff_imatch_savefig/result.png", + "baseline_hash": null, + "result_hash": null + }, + "subtests.subtest.test_hdiff_imatch_style": { + "status": "passed", + "status_msg": "Image comparison passed.", + "baseline_image": "subtests.subtest.test_hdiff_imatch_style/baseline.png", + "diff_image": null, + "rms": null, + "tolerance": 2, + "result_image": "subtests.subtest.test_hdiff_imatch_style/result.png", + "baseline_hash": null, + "result_hash": null + }, + "subtests.subtest.test_hdiff_imatch_removetext": { + "status": "passed", + "status_msg": "Image comparison passed.", + "baseline_image": "subtests.subtest.test_hdiff_imatch_removetext/baseline.png", + "diff_image": null, + "rms": null, + "tolerance": 2, + "result_image": "subtests.subtest.test_hdiff_imatch_removetext/result.png", + "baseline_hash": null, + "result_hash": null + } +} \ No newline at end of file diff --git a/tests/subtests/summaries/test_hybrid.json b/tests/subtests/summaries/test_hybrid.json new file mode 100644 index 00000000..f69efea3 --- /dev/null +++ b/tests/subtests/summaries/test_hybrid.json @@ -0,0 +1,156 @@ +{ + "subtests.subtest.test_hmatch_imatch": { + "status": "passed", + "status_msg": "Test hash matches baseline hash.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": null, + "baseline_hash": "573f4c1482192b7b15bbe4f2bd370ae3b5ab40c9afa441543b87e15a71e9f672", + "result_hash": "573f4c1482192b7b15bbe4f2bd370ae3b5ab40c9afa441543b87e15a71e9f672" + }, + "subtests.subtest.test_hmatch_idiff": { + "status": "passed", + "status_msg": "Test hash matches baseline hash.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": null, + "baseline_hash": "8b5c00365e6f784d5c8947091f09a92fd7d222e790431f297b4848f173e64857", + "result_hash": "8b5c00365e6f784d5c8947091f09a92fd7d222e790431f297b4848f173e64857" + }, + "subtests.subtest.test_hmatch_imissing": { + "status": "passed", + "status_msg": "Test hash matches baseline hash.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": null, + "baseline_hash": "fd069e642e3b154c24077a4996b545e1c4dbffdbed34ea5ad34c7b36873af68f", + "result_hash": "fd069e642e3b154c24077a4996b545e1c4dbffdbed34ea5ad34c7b36873af68f" + }, + "subtests.subtest.test_hdiff_imatch": { + "status": "failed", + "status_msg": "Hash 6e2fdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 doesn't match hash d1ffdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch.\n\nHowever, the comparison to the baseline image succeeded.", + "baseline_image": "subtests.subtest.test_hdiff_imatch/baseline.png", + "diff_image": null, + "rms": null, + "tolerance": 2, + "result_image": "subtests.subtest.test_hdiff_imatch/result.png", + "baseline_hash": "d1ffdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1", + "result_hash": "6e2fdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1" + }, + "subtests.subtest.test_hdiff_idiff": { + "status": "failed", + "status_msg": "Hash 443361bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 doesn't match hash d1ff61bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_idiff.\nError: Image files did not match.\n RMS Value: 11.182677079602481\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_hybrid0/results/subtests.subtest.test_hdiff_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_hybrid0/results/subtests.subtest.test_hdiff_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_hybrid0/results/subtests.subtest.test_hdiff_idiff/result-failed-diff.png\n Tolerance: \n 2", + "baseline_image": "subtests.subtest.test_hdiff_idiff/baseline.png", + "diff_image": "subtests.subtest.test_hdiff_idiff/result-failed-diff.png", + "rms": 11.182677079602481, + "tolerance": 2, + "result_image": "subtests.subtest.test_hdiff_idiff/result.png", + "baseline_hash": "d1ff61bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43", + "result_hash": "443361bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43" + }, + "subtests.subtest.test_hdiff_imissing": { + "status": "failed", + "status_msg": "Hash 301e63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e doesn't match hash d1ff63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imissing.\nUnable to find baseline image for .", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hdiff_imissing/result.png", + "baseline_hash": "d1ff63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e", + "result_hash": "301e63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e" + }, + "subtests.subtest.test_hmissing_imatch": { + "status": "failed", + "status_msg": "Hash for test 'subtests.subtest.test_hmissing_imatch' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is eabd8a2e22afd88682990bfb8e4a0700a942fe68e5114e8da4ab6bd93c47b824.\n\nHowever, the comparison to the baseline image succeeded.", + "baseline_image": "subtests.subtest.test_hmissing_imatch/baseline.png", + "diff_image": null, + "rms": null, + "tolerance": 2, + "result_image": "subtests.subtest.test_hmissing_imatch/result.png", + "baseline_hash": null, + "result_hash": "eabd8a2e22afd88682990bfb8e4a0700a942fe68e5114e8da4ab6bd93c47b824" + }, + "subtests.subtest.test_hmissing_idiff": { + "status": "failed", + "status_msg": "Hash for test 'subtests.subtest.test_hmissing_idiff' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is e69570c4a70b2cc88ddee0f0a82312cae4f394b7f62e5760245feda1364c03ab.\nError: Image files did not match.\n RMS Value: 11.182677079602481\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_hybrid0/results/subtests.subtest.test_hdiff_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_hybrid0/results/subtests.subtest.test_hdiff_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_hybrid0/results/subtests.subtest.test_hdiff_idiff/result-failed-diff.png\n Tolerance: \n 2", + "baseline_image": "subtests.subtest.test_hmissing_idiff/baseline.png", + "diff_image": "subtests.subtest.test_hmissing_idiff/result-failed-diff.png", + "rms": 99999, + "tolerance": 2, + "result_image": "subtests.subtest.test_hmissing_idiff/result.png", + "baseline_hash": null, + "result_hash": "e69570c4a70b2cc88ddee0f0a82312cae4f394b7f62e5760245feda1364c03ab" + }, + "subtests.subtest.test_hmissing_imissing": { + "status": "failed", + "status_msg": "Hash for test 'subtests.subtest.test_hmissing_imissing' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is 5c8a9c7412e4e098f6f2683ee247c08bd50805a93df4d4b6d8fccf3579b4c56b.\nUnable to find baseline image for .", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hmissing_imissing/result.png", + "baseline_hash": null, + "result_hash": "5c8a9c7412e4e098f6f2683ee247c08bd50805a93df4d4b6d8fccf3579b4c56b" + }, + "subtests.subtest.test_hdiff_imatch_tolerance": { + "status": "failed", + "status_msg": "Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_tolerance.\n\nHowever, the comparison to the baseline image succeeded.", + "baseline_image": "subtests.subtest.test_hdiff_imatch_tolerance/baseline.png", + "diff_image": null, + "rms": null, + "tolerance": 200, + "result_image": "subtests.subtest.test_hdiff_imatch_tolerance/result.png", + "baseline_hash": "d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96", + "result_hash": "aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96" + }, + "subtests.subtest.test_hdiff_idiff_tolerance": { + "status": "failed", + "status_msg": "Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_idiff_tolerance.\nError: Image files did not match.\n RMS Value: 29.260332173249314\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_hybrid0/results/subtests.subtest.test_hdiff_idiff_tolerance/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_hybrid0/results/subtests.subtest.test_hdiff_idiff_tolerance/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_hybrid0/results/subtests.subtest.test_hdiff_idiff_tolerance/result-failed-diff.png\n Tolerance: \n 3", + "baseline_image": "subtests.subtest.test_hdiff_idiff_tolerance/baseline.png", + "diff_image": "subtests.subtest.test_hdiff_idiff_tolerance/result-failed-diff.png", + "rms": 29.260332173249314, + "tolerance": 3, + "result_image": "subtests.subtest.test_hdiff_idiff_tolerance/result.png", + "baseline_hash": "d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96", + "result_hash": "aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96" + }, + "subtests.subtest.test_hdiff_imatch_savefig": { + "status": "failed", + "status_msg": "Hash 5dc1c2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 doesn't match hash d1ffc2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_savefig.\n\nHowever, the comparison to the baseline image succeeded.", + "baseline_image": "subtests.subtest.test_hdiff_imatch_savefig/baseline.png", + "diff_image": null, + "rms": null, + "tolerance": 2, + "result_image": "subtests.subtest.test_hdiff_imatch_savefig/result.png", + "baseline_hash": "d1ffc2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568", + "result_hash": "5dc1c2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568" + }, + "subtests.subtest.test_hdiff_imatch_style": { + "status": "failed", + "status_msg": "Hash 185ed1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 doesn't match hash d1ffd1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_style.\n\nHowever, the comparison to the baseline image succeeded.", + "baseline_image": "subtests.subtest.test_hdiff_imatch_style/baseline.png", + "diff_image": null, + "rms": null, + "tolerance": 2, + "result_image": "subtests.subtest.test_hdiff_imatch_style/result.png", + "baseline_hash": "d1ffd1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7", + "result_hash": "185ed1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7" + }, + "subtests.subtest.test_hdiff_imatch_removetext": { + "status": "failed", + "status_msg": "Hash be5af83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e doesn't match hash d1fff83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_removetext.\n\nHowever, the comparison to the baseline image succeeded.", + "baseline_image": "subtests.subtest.test_hdiff_imatch_removetext/baseline.png", + "diff_image": null, + "rms": null, + "tolerance": 2, + "result_image": "subtests.subtest.test_hdiff_imatch_removetext/result.png", + "baseline_hash": "d1fff83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e", + "result_hash": "be5af83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e" + } +} \ No newline at end of file diff --git a/tests/subtests/summaries/test_results_always.json b/tests/subtests/summaries/test_results_always.json new file mode 100644 index 00000000..d1f8116d --- /dev/null +++ b/tests/subtests/summaries/test_results_always.json @@ -0,0 +1,156 @@ +{ + "subtests.subtest.test_hmatch_imatch": { + "status": "passed", + "status_msg": "Test hash matches baseline hash.", + "baseline_image": "subtests.subtest.test_hmatch_imatch/baseline.png", + "diff_image": null, + "rms": null, + "tolerance": 2, + "result_image": "subtests.subtest.test_hmatch_imatch/result.png", + "baseline_hash": "573f4c1482192b7b15bbe4f2bd370ae3b5ab40c9afa441543b87e15a71e9f672", + "result_hash": "573f4c1482192b7b15bbe4f2bd370ae3b5ab40c9afa441543b87e15a71e9f672" + }, + "subtests.subtest.test_hmatch_idiff": { + "status": "passed", + "status_msg": "Test hash matches baseline hash.", + "baseline_image": "subtests.subtest.test_hmatch_idiff/baseline.png", + "diff_image": "subtests.subtest.test_hmatch_idiff/result-failed-diff.png", + "rms": 11.100353848213828, + "tolerance": 2, + "result_image": "subtests.subtest.test_hmatch_idiff/result.png", + "baseline_hash": "8b5c00365e6f784d5c8947091f09a92fd7d222e790431f297b4848f173e64857", + "result_hash": "8b5c00365e6f784d5c8947091f09a92fd7d222e790431f297b4848f173e64857" + }, + "subtests.subtest.test_hmatch_imissing": { + "status": "passed", + "status_msg": "Test hash matches baseline hash.", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hmatch_imissing/result.png", + "baseline_hash": "fd069e642e3b154c24077a4996b545e1c4dbffdbed34ea5ad34c7b36873af68f", + "result_hash": "fd069e642e3b154c24077a4996b545e1c4dbffdbed34ea5ad34c7b36873af68f" + }, + "subtests.subtest.test_hdiff_imatch": { + "status": "failed", + "status_msg": "Hash 6e2fdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 doesn't match hash d1ffdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch.\n\nHowever, the comparison to the baseline image succeeded.", + "baseline_image": "subtests.subtest.test_hdiff_imatch/baseline.png", + "diff_image": null, + "rms": null, + "tolerance": 2, + "result_image": "subtests.subtest.test_hdiff_imatch/result.png", + "baseline_hash": "d1ffdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1", + "result_hash": "6e2fdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1" + }, + "subtests.subtest.test_hdiff_idiff": { + "status": "failed", + "status_msg": "Hash 443361bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 doesn't match hash d1ff61bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_idiff.\nError: Image files did not match.\n RMS Value: 11.182677079602481\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_basic_html0/results/subtests.subtest.test_hdiff_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_basic_html0/results/subtests.subtest.test_hdiff_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_basic_html0/results/subtests.subtest.test_hdiff_idiff/result-failed-diff.png\n Tolerance: \n 2", + "baseline_image": "subtests.subtest.test_hdiff_idiff/baseline.png", + "diff_image": "subtests.subtest.test_hdiff_idiff/result-failed-diff.png", + "rms": 11.182677079602481, + "tolerance": 2, + "result_image": "subtests.subtest.test_hdiff_idiff/result.png", + "baseline_hash": "d1ff61bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43", + "result_hash": "443361bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43" + }, + "subtests.subtest.test_hdiff_imissing": { + "status": "failed", + "status_msg": "Hash 301e63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e doesn't match hash d1ff63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imissing.\nUnable to find baseline image for .", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hdiff_imissing/result.png", + "baseline_hash": "d1ff63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e", + "result_hash": "301e63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e" + }, + "subtests.subtest.test_hmissing_imatch": { + "status": "failed", + "status_msg": "Hash for test 'subtests.subtest.test_hmissing_imatch' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is eabd8a2e22afd88682990bfb8e4a0700a942fe68e5114e8da4ab6bd93c47b824.\n\nHowever, the comparison to the baseline image succeeded.", + "baseline_image": "subtests.subtest.test_hmissing_imatch/baseline.png", + "diff_image": null, + "rms": null, + "tolerance": 2, + "result_image": "subtests.subtest.test_hmissing_imatch/result.png", + "baseline_hash": null, + "result_hash": "eabd8a2e22afd88682990bfb8e4a0700a942fe68e5114e8da4ab6bd93c47b824" + }, + "subtests.subtest.test_hmissing_idiff": { + "status": "failed", + "status_msg": "Hash for test 'subtests.subtest.test_hmissing_idiff' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is e69570c4a70b2cc88ddee0f0a82312cae4f394b7f62e5760245feda1364c03ab.\nError: Image files did not match.\n RMS Value: 11.182677079602481\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_hybrid0/results/subtests.subtest.test_hdiff_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_hybrid0/results/subtests.subtest.test_hdiff_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_hybrid0/results/subtests.subtest.test_hdiff_idiff/result-failed-diff.png\n Tolerance: \n 2", + "baseline_image": "subtests.subtest.test_hmissing_idiff/baseline.png", + "diff_image": "subtests.subtest.test_hmissing_idiff/result-failed-diff.png", + "rms": 99999, + "tolerance": 2, + "result_image": "subtests.subtest.test_hmissing_idiff/result.png", + "baseline_hash": null, + "result_hash": "e69570c4a70b2cc88ddee0f0a82312cae4f394b7f62e5760245feda1364c03ab" + }, + "subtests.subtest.test_hmissing_imissing": { + "status": "failed", + "status_msg": "Hash for test 'subtests.subtest.test_hmissing_imissing' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is 5c8a9c7412e4e098f6f2683ee247c08bd50805a93df4d4b6d8fccf3579b4c56b.\nUnable to find baseline image for .", + "baseline_image": null, + "diff_image": null, + "rms": null, + "tolerance": null, + "result_image": "subtests.subtest.test_hmissing_imissing/result.png", + "baseline_hash": null, + "result_hash": "5c8a9c7412e4e098f6f2683ee247c08bd50805a93df4d4b6d8fccf3579b4c56b" + }, + "subtests.subtest.test_hdiff_imatch_tolerance": { + "status": "failed", + "status_msg": "Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_tolerance.\n\nHowever, the comparison to the baseline image succeeded.", + "baseline_image": "subtests.subtest.test_hdiff_imatch_tolerance/baseline.png", + "diff_image": null, + "rms": null, + "tolerance": 200, + "result_image": "subtests.subtest.test_hdiff_imatch_tolerance/result.png", + "baseline_hash": "d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96", + "result_hash": "aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96" + }, + "subtests.subtest.test_hdiff_idiff_tolerance": { + "status": "failed", + "status_msg": "Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_idiff_tolerance.\nError: Image files did not match.\n RMS Value: 29.260332173249314\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_basic_html0/results/subtests.subtest.test_hdiff_idiff_tolerance/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_basic_html0/results/subtests.subtest.test_hdiff_idiff_tolerance/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_basic_html0/results/subtests.subtest.test_hdiff_idiff_tolerance/result-failed-diff.png\n Tolerance: \n 3", + "baseline_image": "subtests.subtest.test_hdiff_idiff_tolerance/baseline.png", + "diff_image": "subtests.subtest.test_hdiff_idiff_tolerance/result-failed-diff.png", + "rms": 29.260332173249314, + "tolerance": 3, + "result_image": "subtests.subtest.test_hdiff_idiff_tolerance/result.png", + "baseline_hash": "d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96", + "result_hash": "aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96" + }, + "subtests.subtest.test_hdiff_imatch_savefig": { + "status": "failed", + "status_msg": "Hash 5dc1c2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 doesn't match hash d1ffc2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_savefig.\n\nHowever, the comparison to the baseline image succeeded.", + "baseline_image": "subtests.subtest.test_hdiff_imatch_savefig/baseline.png", + "diff_image": null, + "rms": null, + "tolerance": 2, + "result_image": "subtests.subtest.test_hdiff_imatch_savefig/result.png", + "baseline_hash": "d1ffc2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568", + "result_hash": "5dc1c2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568" + }, + "subtests.subtest.test_hdiff_imatch_style": { + "status": "failed", + "status_msg": "Hash 185ed1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 doesn't match hash d1ffd1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_style.\n\nHowever, the comparison to the baseline image succeeded.", + "baseline_image": "subtests.subtest.test_hdiff_imatch_style/baseline.png", + "diff_image": null, + "rms": null, + "tolerance": 2, + "result_image": "subtests.subtest.test_hdiff_imatch_style/result.png", + "baseline_hash": "d1ffd1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7", + "result_hash": "185ed1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7" + }, + "subtests.subtest.test_hdiff_imatch_removetext": { + "status": "failed", + "status_msg": "Hash be5af83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e doesn't match hash d1fff83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_removetext.\n\nHowever, the comparison to the baseline image succeeded.", + "baseline_image": "subtests.subtest.test_hdiff_imatch_removetext/baseline.png", + "diff_image": null, + "rms": null, + "tolerance": 2, + "result_image": "subtests.subtest.test_hdiff_imatch_removetext/result.png", + "baseline_hash": "d1fff83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e", + "result_hash": "be5af83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e" + } +} \ No newline at end of file From ab4bd470436eed761841bd10416ad46b66234f5e Mon Sep 17 00:00:00 2001 From: Conor MacBride Date: Sat, 5 Feb 2022 21:47:08 +0000 Subject: [PATCH 13/22] Document `--mpl-baseline-relative` --- README.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index a81b8322..ea76006b 100644 --- a/README.rst +++ b/README.rst @@ -188,8 +188,11 @@ running tests by running ``pytest`` with:: pytest --mpl --mpl-baseline-path=baseline_images -This directory will be interpreted as being relative to where the tests -are run. In addition, if both this option and the ``baseline_dir`` +This directory will be interpreted as being relative to where pytest +is run. However, if the ``--mpl-baseline-relative`` option is also +included, this directory will be interpreted as being relative to +the current test directory. +In addition, if both this option and the ``baseline_dir`` option in the ``mpl_image_compare`` decorator are used, the one in the decorator takes precedence. From b0f62ba02a814176cd24dddb4c9b5d8d5d939cbe Mon Sep 17 00:00:00 2001 From: Conor MacBride Date: Sat, 5 Feb 2022 22:58:32 +0000 Subject: [PATCH 14/22] Clarify behaviour In hybrid mode only hashes can affect result. Clarify what `--mpl-results-always` does and when it's used. --- README.rst | 45 +++++++++++++++++++++++++++----------------- pytest_mpl/plugin.py | 4 ++-- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/README.rst b/README.rst index ea76006b..ebdee082 100644 --- a/README.rst +++ b/README.rst @@ -75,8 +75,8 @@ directly in the right directory. With a Hash Library ^^^^^^^^^^^^^^^^^^^ -Instead of comparing to baseline images, you can instead compare against a json -library of sha256 hashes. This has the advantage of not having to check baseline +Instead of comparing to baseline images, you can instead compare against a JSON +library of SHA-256 hashes. This has the advantage of not having to check baseline images into the repository with the tests, or download them from a remote source. @@ -91,8 +91,11 @@ Hybrid Mode: Hashes and Images ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ It is possible to configure both hashes and baseline images. In this scenario -the hashes will be compared first, with the baseline images only used if the hash -comparison fails. +only the hash comparison can determine the test result. If the hash comparison +fails, the test will fail, however a comparison to the baseline image will be +carried out so the actual difference can be seen. If the hash comparison passes, +the comparison to the baseline image is skipped (unless **results always** is +configured). This is especially useful if the baseline images are external to the repository containing the tests, and are accessed via HTTP. In this situation, if the hashes @@ -104,7 +107,7 @@ without having to modify the external images. Running Tests ^^^^^^^^^^^^^ -Once tests are written with either baseline images or a hash library to compare +Once tests are written with baseline images, a hash library, or both to compare against, the tests can be run with:: pytest --mpl @@ -118,12 +121,15 @@ Generating a Test Summary ^^^^^^^^^^^^^^^^^^^^^^^^^ By specifying the ``--mpl-generate-summary=html`` CLI argument, a HTML summary -page will be generated showing the result, log entry and RMS of each test, -and the hashes if configured. The baseline, diff and result image for each -failing test will be shown. If **Results always** is configured -(see section below), images for passing tests will also be shown. -If no baseline images are configured, just the result images will -be displayed. +page will be generated showing the test result, log entry and generated result +image. When in the (default) image comparison mode, the baseline image, diff +image and RMS (if any), and tolerance of each test will also be shown. +When in the hash comparison mode, the baseline hash and result hash will +also be shown. When in hybrid mode, all of these are included. + +When generating a HTML summary, the ``--mpl-results-always`` option is +automatically applied (see section below). Therefore images for passing +tests will also be shown. +---------------+---------------+---------------+ | |html all| | |html filter| | |html result| | @@ -199,20 +205,25 @@ decorator takes precedence. Results always ^^^^^^^^^^^^^^ -By default, result images are only generated for tests that fail. +By default, result images are only saved for tests that fail. Passing ``--mpl-results-always`` to pytest will force result images -to be generated for all tests, even for tests that pass. -If a baseline image exists, a diff image will also be generated. -All of these images will be shown in the summary (if requested). +to be saved for all tests, even for tests that pass. + +When in **hybrid mode**, even if a test passes hash comparison, +a comparison to the baseline image will also be carried out, +with the baseline image and diff image (if image comparison fails) +saved for all tests. This secondary comparison will not affect +the success status of the test. -This option is useful for always *comparing* the result images again +This option is useful for always *comparing* the result images against the baseline images, while only *assessing* the tests against the hash library. If you only update your baseline images after merging a PR, this option means that the generated summary will always show how the PR affects the baseline images, with the success status of each test (based on the hash library) also shown in the generated -summary. +summary. This option is applied automatically when generating +a HTML summary. Base style ^^^^^^^^^^ diff --git a/pytest_mpl/plugin.py b/pytest_mpl/plugin.py index 6efa6a42..19aec150 100644 --- a/pytest_mpl/plugin.py +++ b/pytest_mpl/plugin.py @@ -140,7 +140,7 @@ def pytest_addoption(parser): group.addoption('--mpl-results-path', help=results_path_help, action='store') parser.addini('mpl-results-path', help=results_path_help) - results_always_help = ("Always generate result images, not just for failed tests. " + results_always_help = ("Always compare to baseline images and save result images, even for passing tests. " "This option is automatically applied when generating a HTML summary.") group.addoption('--mpl-results-always', action='store_true', help=results_always_help) @@ -272,7 +272,7 @@ def __init__(self, if len(unsupported_formats) > 0: raise ValueError(f"The mpl summary type(s) '{sorted(unsupported_formats)}' " "are not supported.") - # Ignore `results_always` and always save result images for HTML output + # When generating HTML always apply `results_always` if generate_summary & {'html', 'basic-html'}: results_always = True self.generate_summary = generate_summary From de57cabcc028e664f07834e219d0be93e8e702f6 Mon Sep 17 00:00:00 2001 From: Conor MacBride Date: Sun, 6 Feb 2022 17:45:42 +0000 Subject: [PATCH 15/22] Update summaries for new hash and hybrid logs --- tests/subtests/summaries/test_hash.json | 16 +++++----- .../summaries/test_html_hashes_only.json | 16 +++++----- tests/subtests/summaries/test_hybrid.json | 24 +++++++-------- .../summaries/test_results_always.json | 30 +++++++++---------- 4 files changed, 43 insertions(+), 43 deletions(-) diff --git a/tests/subtests/summaries/test_hash.json b/tests/subtests/summaries/test_hash.json index 34677d75..f96572a4 100644 --- a/tests/subtests/summaries/test_hash.json +++ b/tests/subtests/summaries/test_hash.json @@ -34,7 +34,7 @@ }, "subtests.subtest.test_hdiff_imatch": { "status": "failed", - "status_msg": "Test hash does not match baseline hash.", + "status_msg": "Hash 6e2fdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 doesn't match hash d1ffdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch.", "baseline_image": null, "diff_image": null, "rms": null, @@ -45,7 +45,7 @@ }, "subtests.subtest.test_hdiff_idiff": { "status": "failed", - "status_msg": "Test hash does not match baseline hash.", + "status_msg": "Hash 443361bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 doesn't match hash d1ff61bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_idiff.", "baseline_image": null, "diff_image": null, "rms": null, @@ -56,7 +56,7 @@ }, "subtests.subtest.test_hdiff_imissing": { "status": "failed", - "status_msg": "Test hash does not match baseline hash.", + "status_msg": "Hash 301e63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e doesn't match hash d1ff63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imissing.", "baseline_image": null, "diff_image": null, "rms": null, @@ -100,7 +100,7 @@ }, "subtests.subtest.test_hdiff_imatch_tolerance": { "status": "failed", - "status_msg": "Test hash does not match baseline hash.", + "status_msg": "Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_tolerance.", "baseline_image": null, "diff_image": null, "rms": null, @@ -111,7 +111,7 @@ }, "subtests.subtest.test_hdiff_idiff_tolerance": { "status": "failed", - "status_msg": "Test hash does not match baseline hash.", + "status_msg": "Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_idiff_tolerance.", "baseline_image": null, "diff_image": null, "rms": null, @@ -122,7 +122,7 @@ }, "subtests.subtest.test_hdiff_imatch_savefig": { "status": "failed", - "status_msg": "Test hash does not match baseline hash.", + "status_msg": "Hash 5dc1c2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 doesn't match hash d1ffc2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_savefig.", "baseline_image": null, "diff_image": null, "rms": null, @@ -133,7 +133,7 @@ }, "subtests.subtest.test_hdiff_imatch_style": { "status": "failed", - "status_msg": "Test hash does not match baseline hash.", + "status_msg": "Hash 185ed1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 doesn't match hash d1ffd1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_style.", "baseline_image": null, "diff_image": null, "rms": null, @@ -144,7 +144,7 @@ }, "subtests.subtest.test_hdiff_imatch_removetext": { "status": "failed", - "status_msg": "Test hash does not match baseline hash.", + "status_msg": "Hash be5af83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e doesn't match hash d1fff83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_removetext.", "baseline_image": null, "diff_image": null, "rms": null, diff --git a/tests/subtests/summaries/test_html_hashes_only.json b/tests/subtests/summaries/test_html_hashes_only.json index b2b53d2f..bdb1ba01 100644 --- a/tests/subtests/summaries/test_html_hashes_only.json +++ b/tests/subtests/summaries/test_html_hashes_only.json @@ -34,7 +34,7 @@ }, "subtests.subtest.test_hdiff_imatch": { "status": "failed", - "status_msg": "Test hash does not match baseline hash.", + "status_msg": "Hash 6e2fdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 doesn't match hash d1ffdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch.", "baseline_image": null, "diff_image": null, "rms": null, @@ -45,7 +45,7 @@ }, "subtests.subtest.test_hdiff_idiff": { "status": "failed", - "status_msg": "Test hash does not match baseline hash.", + "status_msg": "Hash 443361bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 doesn't match hash d1ff61bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_idiff.", "baseline_image": null, "diff_image": null, "rms": null, @@ -56,7 +56,7 @@ }, "subtests.subtest.test_hdiff_imissing": { "status": "failed", - "status_msg": "Test hash does not match baseline hash.", + "status_msg": "Hash 301e63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e doesn't match hash d1ff63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imissing.", "baseline_image": null, "diff_image": null, "rms": null, @@ -100,7 +100,7 @@ }, "subtests.subtest.test_hdiff_imatch_tolerance": { "status": "failed", - "status_msg": "Test hash does not match baseline hash.", + "status_msg": "Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_tolerance.", "baseline_image": null, "diff_image": null, "rms": null, @@ -111,7 +111,7 @@ }, "subtests.subtest.test_hdiff_idiff_tolerance": { "status": "failed", - "status_msg": "Test hash does not match baseline hash.", + "status_msg": "Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_idiff_tolerance.", "baseline_image": null, "diff_image": null, "rms": null, @@ -122,7 +122,7 @@ }, "subtests.subtest.test_hdiff_imatch_savefig": { "status": "failed", - "status_msg": "Test hash does not match baseline hash.", + "status_msg": "Hash 5dc1c2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 doesn't match hash d1ffc2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_savefig.", "baseline_image": null, "diff_image": null, "rms": null, @@ -133,7 +133,7 @@ }, "subtests.subtest.test_hdiff_imatch_style": { "status": "failed", - "status_msg": "Test hash does not match baseline hash.", + "status_msg": "Hash 185ed1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 doesn't match hash d1ffd1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_style.", "baseline_image": null, "diff_image": null, "rms": null, @@ -144,7 +144,7 @@ }, "subtests.subtest.test_hdiff_imatch_removetext": { "status": "failed", - "status_msg": "Test hash does not match baseline hash.", + "status_msg": "Hash be5af83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e doesn't match hash d1fff83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_removetext.", "baseline_image": null, "diff_image": null, "rms": null, diff --git a/tests/subtests/summaries/test_hybrid.json b/tests/subtests/summaries/test_hybrid.json index f69efea3..7362dbea 100644 --- a/tests/subtests/summaries/test_hybrid.json +++ b/tests/subtests/summaries/test_hybrid.json @@ -34,7 +34,7 @@ }, "subtests.subtest.test_hdiff_imatch": { "status": "failed", - "status_msg": "Hash 6e2fdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 doesn't match hash d1ffdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch.\n\nHowever, the comparison to the baseline image succeeded.", + "status_msg": "Hash 6e2fdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 doesn't match hash d1ffdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded.", "baseline_image": "subtests.subtest.test_hdiff_imatch/baseline.png", "diff_image": null, "rms": null, @@ -45,7 +45,7 @@ }, "subtests.subtest.test_hdiff_idiff": { "status": "failed", - "status_msg": "Hash 443361bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 doesn't match hash d1ff61bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_idiff.\nError: Image files did not match.\n RMS Value: 11.182677079602481\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_hybrid0/results/subtests.subtest.test_hdiff_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_hybrid0/results/subtests.subtest.test_hdiff_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_hybrid0/results/subtests.subtest.test_hdiff_idiff/result-failed-diff.png\n Tolerance: \n 2", + "status_msg": "Hash 443361bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 doesn't match hash d1ff61bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_idiff.\n\nImage comparison test\n---------------------\nError: Image files did not match.\n RMS Value: 11.182677079602481\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_hybrid0/results/subtests.subtest.test_hdiff_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_hybrid0/results/subtests.subtest.test_hdiff_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_hybrid0/results/subtests.subtest.test_hdiff_idiff/result-failed-diff.png\n Tolerance: \n 2", "baseline_image": "subtests.subtest.test_hdiff_idiff/baseline.png", "diff_image": "subtests.subtest.test_hdiff_idiff/result-failed-diff.png", "rms": 11.182677079602481, @@ -56,7 +56,7 @@ }, "subtests.subtest.test_hdiff_imissing": { "status": "failed", - "status_msg": "Hash 301e63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e doesn't match hash d1ff63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imissing.\nUnable to find baseline image for .", + "status_msg": "Hash 301e63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e doesn't match hash d1ff63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imissing.\n\nImage comparison test\n---------------------\nImage file not found for comparison test in: \n\t/Users/conor/Documents/pytest-mpl/tests/subtests/baseline\n(This is expected for new tests.)\nGenerated Image: \n\t/private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_hybrid0/results/subtests.subtest.test_hdiff_imissing/result.png", "baseline_image": null, "diff_image": null, "rms": null, @@ -67,7 +67,7 @@ }, "subtests.subtest.test_hmissing_imatch": { "status": "failed", - "status_msg": "Hash for test 'subtests.subtest.test_hmissing_imatch' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is eabd8a2e22afd88682990bfb8e4a0700a942fe68e5114e8da4ab6bd93c47b824.\n\nHowever, the comparison to the baseline image succeeded.", + "status_msg": "Hash for test 'subtests.subtest.test_hmissing_imatch' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is eabd8a2e22afd88682990bfb8e4a0700a942fe68e5114e8da4ab6bd93c47b824.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded.", "baseline_image": "subtests.subtest.test_hmissing_imatch/baseline.png", "diff_image": null, "rms": null, @@ -78,10 +78,10 @@ }, "subtests.subtest.test_hmissing_idiff": { "status": "failed", - "status_msg": "Hash for test 'subtests.subtest.test_hmissing_idiff' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is e69570c4a70b2cc88ddee0f0a82312cae4f394b7f62e5760245feda1364c03ab.\nError: Image files did not match.\n RMS Value: 11.182677079602481\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_hybrid0/results/subtests.subtest.test_hdiff_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_hybrid0/results/subtests.subtest.test_hdiff_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_hybrid0/results/subtests.subtest.test_hdiff_idiff/result-failed-diff.png\n Tolerance: \n 2", + "status_msg": "Hash for test 'subtests.subtest.test_hmissing_idiff' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is e69570c4a70b2cc88ddee0f0a82312cae4f394b7f62e5760245feda1364c03ab.\n\nImage comparison test\n---------------------\nError: Image files did not match.\n RMS Value: 12.12938597648977\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_hybrid0/results/subtests.subtest.test_hmissing_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_hybrid0/results/subtests.subtest.test_hmissing_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_hybrid0/results/subtests.subtest.test_hmissing_idiff/result-failed-diff.png\n Tolerance: \n 2", "baseline_image": "subtests.subtest.test_hmissing_idiff/baseline.png", "diff_image": "subtests.subtest.test_hmissing_idiff/result-failed-diff.png", - "rms": 99999, + "rms": 12.12938597648977, "tolerance": 2, "result_image": "subtests.subtest.test_hmissing_idiff/result.png", "baseline_hash": null, @@ -89,7 +89,7 @@ }, "subtests.subtest.test_hmissing_imissing": { "status": "failed", - "status_msg": "Hash for test 'subtests.subtest.test_hmissing_imissing' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is 5c8a9c7412e4e098f6f2683ee247c08bd50805a93df4d4b6d8fccf3579b4c56b.\nUnable to find baseline image for .", + "status_msg": "Hash for test 'subtests.subtest.test_hmissing_imissing' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is 5c8a9c7412e4e098f6f2683ee247c08bd50805a93df4d4b6d8fccf3579b4c56b.\n\nImage comparison test\n---------------------\nImage file not found for comparison test in: \n\t/Users/conor/Documents/pytest-mpl/tests/subtests/baseline\n(This is expected for new tests.)\nGenerated Image: \n\t/private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_hybrid0/results/subtests.subtest.test_hmissing_imissing/result.png", "baseline_image": null, "diff_image": null, "rms": null, @@ -100,7 +100,7 @@ }, "subtests.subtest.test_hdiff_imatch_tolerance": { "status": "failed", - "status_msg": "Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_tolerance.\n\nHowever, the comparison to the baseline image succeeded.", + "status_msg": "Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_tolerance.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded.", "baseline_image": "subtests.subtest.test_hdiff_imatch_tolerance/baseline.png", "diff_image": null, "rms": null, @@ -111,7 +111,7 @@ }, "subtests.subtest.test_hdiff_idiff_tolerance": { "status": "failed", - "status_msg": "Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_idiff_tolerance.\nError: Image files did not match.\n RMS Value: 29.260332173249314\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_hybrid0/results/subtests.subtest.test_hdiff_idiff_tolerance/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_hybrid0/results/subtests.subtest.test_hdiff_idiff_tolerance/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_hybrid0/results/subtests.subtest.test_hdiff_idiff_tolerance/result-failed-diff.png\n Tolerance: \n 3", + "status_msg": "Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_idiff_tolerance.\n\nImage comparison test\n---------------------\nError: Image files did not match.\n RMS Value: 29.260332173249314\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_hybrid0/results/subtests.subtest.test_hdiff_idiff_tolerance/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_hybrid0/results/subtests.subtest.test_hdiff_idiff_tolerance/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_hybrid0/results/subtests.subtest.test_hdiff_idiff_tolerance/result-failed-diff.png\n Tolerance: \n 3", "baseline_image": "subtests.subtest.test_hdiff_idiff_tolerance/baseline.png", "diff_image": "subtests.subtest.test_hdiff_idiff_tolerance/result-failed-diff.png", "rms": 29.260332173249314, @@ -122,7 +122,7 @@ }, "subtests.subtest.test_hdiff_imatch_savefig": { "status": "failed", - "status_msg": "Hash 5dc1c2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 doesn't match hash d1ffc2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_savefig.\n\nHowever, the comparison to the baseline image succeeded.", + "status_msg": "Hash 5dc1c2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 doesn't match hash d1ffc2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_savefig.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded.", "baseline_image": "subtests.subtest.test_hdiff_imatch_savefig/baseline.png", "diff_image": null, "rms": null, @@ -133,7 +133,7 @@ }, "subtests.subtest.test_hdiff_imatch_style": { "status": "failed", - "status_msg": "Hash 185ed1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 doesn't match hash d1ffd1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_style.\n\nHowever, the comparison to the baseline image succeeded.", + "status_msg": "Hash 185ed1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 doesn't match hash d1ffd1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_style.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded.", "baseline_image": "subtests.subtest.test_hdiff_imatch_style/baseline.png", "diff_image": null, "rms": null, @@ -144,7 +144,7 @@ }, "subtests.subtest.test_hdiff_imatch_removetext": { "status": "failed", - "status_msg": "Hash be5af83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e doesn't match hash d1fff83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_removetext.\n\nHowever, the comparison to the baseline image succeeded.", + "status_msg": "Hash be5af83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e doesn't match hash d1fff83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_removetext.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded.", "baseline_image": "subtests.subtest.test_hdiff_imatch_removetext/baseline.png", "diff_image": null, "rms": null, diff --git a/tests/subtests/summaries/test_results_always.json b/tests/subtests/summaries/test_results_always.json index d1f8116d..023e2ebd 100644 --- a/tests/subtests/summaries/test_results_always.json +++ b/tests/subtests/summaries/test_results_always.json @@ -1,7 +1,7 @@ { "subtests.subtest.test_hmatch_imatch": { "status": "passed", - "status_msg": "Test hash matches baseline hash.", + "status_msg": "Test hash matches baseline hash.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded.", "baseline_image": "subtests.subtest.test_hmatch_imatch/baseline.png", "diff_image": null, "rms": null, @@ -12,7 +12,7 @@ }, "subtests.subtest.test_hmatch_idiff": { "status": "passed", - "status_msg": "Test hash matches baseline hash.", + "status_msg": "Test hash matches baseline hash.\n\nImage comparison test\n---------------------\nError: Image files did not match.\n RMS Value: 11.100353848213828\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hmatch_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hmatch_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hmatch_idiff/result-failed-diff.png\n Tolerance: \n 2", "baseline_image": "subtests.subtest.test_hmatch_idiff/baseline.png", "diff_image": "subtests.subtest.test_hmatch_idiff/result-failed-diff.png", "rms": 11.100353848213828, @@ -23,7 +23,7 @@ }, "subtests.subtest.test_hmatch_imissing": { "status": "passed", - "status_msg": "Test hash matches baseline hash.", + "status_msg": "Test hash matches baseline hash.\n\nImage comparison test\n---------------------\nImage file not found for comparison test in: \n\t/Users/conor/Documents/pytest-mpl/tests/subtests/baseline\n(This is expected for new tests.)\nGenerated Image: \n\t/private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hmatch_imissing/result.png", "baseline_image": null, "diff_image": null, "rms": null, @@ -34,7 +34,7 @@ }, "subtests.subtest.test_hdiff_imatch": { "status": "failed", - "status_msg": "Hash 6e2fdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 doesn't match hash d1ffdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch.\n\nHowever, the comparison to the baseline image succeeded.", + "status_msg": "Hash 6e2fdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 doesn't match hash d1ffdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded.", "baseline_image": "subtests.subtest.test_hdiff_imatch/baseline.png", "diff_image": null, "rms": null, @@ -45,7 +45,7 @@ }, "subtests.subtest.test_hdiff_idiff": { "status": "failed", - "status_msg": "Hash 443361bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 doesn't match hash d1ff61bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_idiff.\nError: Image files did not match.\n RMS Value: 11.182677079602481\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_basic_html0/results/subtests.subtest.test_hdiff_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_basic_html0/results/subtests.subtest.test_hdiff_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_basic_html0/results/subtests.subtest.test_hdiff_idiff/result-failed-diff.png\n Tolerance: \n 2", + "status_msg": "Hash 443361bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 doesn't match hash d1ff61bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_idiff.\n\nImage comparison test\n---------------------\nError: Image files did not match.\n RMS Value: 11.182677079602481\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hdiff_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hdiff_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hdiff_idiff/result-failed-diff.png\n Tolerance: \n 2", "baseline_image": "subtests.subtest.test_hdiff_idiff/baseline.png", "diff_image": "subtests.subtest.test_hdiff_idiff/result-failed-diff.png", "rms": 11.182677079602481, @@ -56,7 +56,7 @@ }, "subtests.subtest.test_hdiff_imissing": { "status": "failed", - "status_msg": "Hash 301e63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e doesn't match hash d1ff63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imissing.\nUnable to find baseline image for .", + "status_msg": "Hash 301e63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e doesn't match hash d1ff63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imissing.\n\nImage comparison test\n---------------------\nImage file not found for comparison test in: \n\t/Users/conor/Documents/pytest-mpl/tests/subtests/baseline\n(This is expected for new tests.)\nGenerated Image: \n\t/private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hdiff_imissing/result.png", "baseline_image": null, "diff_image": null, "rms": null, @@ -67,7 +67,7 @@ }, "subtests.subtest.test_hmissing_imatch": { "status": "failed", - "status_msg": "Hash for test 'subtests.subtest.test_hmissing_imatch' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is eabd8a2e22afd88682990bfb8e4a0700a942fe68e5114e8da4ab6bd93c47b824.\n\nHowever, the comparison to the baseline image succeeded.", + "status_msg": "Hash for test 'subtests.subtest.test_hmissing_imatch' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is eabd8a2e22afd88682990bfb8e4a0700a942fe68e5114e8da4ab6bd93c47b824.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded.", "baseline_image": "subtests.subtest.test_hmissing_imatch/baseline.png", "diff_image": null, "rms": null, @@ -78,10 +78,10 @@ }, "subtests.subtest.test_hmissing_idiff": { "status": "failed", - "status_msg": "Hash for test 'subtests.subtest.test_hmissing_idiff' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is e69570c4a70b2cc88ddee0f0a82312cae4f394b7f62e5760245feda1364c03ab.\nError: Image files did not match.\n RMS Value: 11.182677079602481\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_hybrid0/results/subtests.subtest.test_hdiff_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_hybrid0/results/subtests.subtest.test_hdiff_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_hybrid0/results/subtests.subtest.test_hdiff_idiff/result-failed-diff.png\n Tolerance: \n 2", + "status_msg": "Hash for test 'subtests.subtest.test_hmissing_idiff' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is e69570c4a70b2cc88ddee0f0a82312cae4f394b7f62e5760245feda1364c03ab.\n\nImage comparison test\n---------------------\nError: Image files did not match.\n RMS Value: 12.12938597648977\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hmissing_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hmissing_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hmissing_idiff/result-failed-diff.png\n Tolerance: \n 2", "baseline_image": "subtests.subtest.test_hmissing_idiff/baseline.png", "diff_image": "subtests.subtest.test_hmissing_idiff/result-failed-diff.png", - "rms": 99999, + "rms": 12.12938597648977, "tolerance": 2, "result_image": "subtests.subtest.test_hmissing_idiff/result.png", "baseline_hash": null, @@ -89,7 +89,7 @@ }, "subtests.subtest.test_hmissing_imissing": { "status": "failed", - "status_msg": "Hash for test 'subtests.subtest.test_hmissing_imissing' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is 5c8a9c7412e4e098f6f2683ee247c08bd50805a93df4d4b6d8fccf3579b4c56b.\nUnable to find baseline image for .", + "status_msg": "Hash for test 'subtests.subtest.test_hmissing_imissing' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is 5c8a9c7412e4e098f6f2683ee247c08bd50805a93df4d4b6d8fccf3579b4c56b.\n\nImage comparison test\n---------------------\nImage file not found for comparison test in: \n\t/Users/conor/Documents/pytest-mpl/tests/subtests/baseline\n(This is expected for new tests.)\nGenerated Image: \n\t/private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hmissing_imissing/result.png", "baseline_image": null, "diff_image": null, "rms": null, @@ -100,7 +100,7 @@ }, "subtests.subtest.test_hdiff_imatch_tolerance": { "status": "failed", - "status_msg": "Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_tolerance.\n\nHowever, the comparison to the baseline image succeeded.", + "status_msg": "Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_tolerance.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded.", "baseline_image": "subtests.subtest.test_hdiff_imatch_tolerance/baseline.png", "diff_image": null, "rms": null, @@ -111,7 +111,7 @@ }, "subtests.subtest.test_hdiff_idiff_tolerance": { "status": "failed", - "status_msg": "Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_idiff_tolerance.\nError: Image files did not match.\n RMS Value: 29.260332173249314\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_basic_html0/results/subtests.subtest.test_hdiff_idiff_tolerance/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_basic_html0/results/subtests.subtest.test_hdiff_idiff_tolerance/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_basic_html0/results/subtests.subtest.test_hdiff_idiff_tolerance/result-failed-diff.png\n Tolerance: \n 3", + "status_msg": "Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_idiff_tolerance.\n\nImage comparison test\n---------------------\nError: Image files did not match.\n RMS Value: 29.260332173249314\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hdiff_idiff_tolerance/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hdiff_idiff_tolerance/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hdiff_idiff_tolerance/result-failed-diff.png\n Tolerance: \n 3", "baseline_image": "subtests.subtest.test_hdiff_idiff_tolerance/baseline.png", "diff_image": "subtests.subtest.test_hdiff_idiff_tolerance/result-failed-diff.png", "rms": 29.260332173249314, @@ -122,7 +122,7 @@ }, "subtests.subtest.test_hdiff_imatch_savefig": { "status": "failed", - "status_msg": "Hash 5dc1c2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 doesn't match hash d1ffc2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_savefig.\n\nHowever, the comparison to the baseline image succeeded.", + "status_msg": "Hash 5dc1c2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 doesn't match hash d1ffc2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_savefig.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded.", "baseline_image": "subtests.subtest.test_hdiff_imatch_savefig/baseline.png", "diff_image": null, "rms": null, @@ -133,7 +133,7 @@ }, "subtests.subtest.test_hdiff_imatch_style": { "status": "failed", - "status_msg": "Hash 185ed1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 doesn't match hash d1ffd1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_style.\n\nHowever, the comparison to the baseline image succeeded.", + "status_msg": "Hash 185ed1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 doesn't match hash d1ffd1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_style.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded.", "baseline_image": "subtests.subtest.test_hdiff_imatch_style/baseline.png", "diff_image": null, "rms": null, @@ -144,7 +144,7 @@ }, "subtests.subtest.test_hdiff_imatch_removetext": { "status": "failed", - "status_msg": "Hash be5af83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e doesn't match hash d1fff83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_removetext.\n\nHowever, the comparison to the baseline image succeeded.", + "status_msg": "Hash be5af83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e doesn't match hash d1fff83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_removetext.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded.", "baseline_image": "subtests.subtest.test_hdiff_imatch_removetext/baseline.png", "diff_image": null, "rms": null, From a5272a60b23d55b0490292f71fc25dbeb8af5e90 Mon Sep 17 00:00:00 2001 From: Conor MacBride Date: Sun, 6 Feb 2022 18:08:12 +0000 Subject: [PATCH 16/22] Update tests for new status_msg --- tests/subtests/helpers.py | 8 ++++++-- tests/subtests/test_subtest.py | 13 +++++++------ tests/test_pytest_mpl.py | 4 ++-- tox.ini | 3 ++- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/tests/subtests/helpers.py b/tests/subtests/helpers.py index 6fabe30e..4c126f32 100644 --- a/tests/subtests/helpers.py +++ b/tests/subtests/helpers.py @@ -53,7 +53,7 @@ def diff_summary(baseline, result, baseline_hash_library=None, result_hash_libra # Swap the baseline and result hashes in the summary # for the corresponding hashes in each hash library - if baseline_hash_library: + if baseline_hash_library and test in baseline_hash_library: baseline_summary = replace_hash(baseline_summary, 'baseline_hash', baseline_hash_library[test]) if result_hash_library: @@ -114,7 +114,11 @@ def diff_dict_item(baseline, result, error=''): if isinstance(baseline, (bool, type(None))) and baseline is result: return - # Handle str, int and float + # Handle float + if isinstance(baseline, float) and abs(baseline - result) < 1e-4: + return + + # Handle str and int if baseline == result: return diff --git a/tests/subtests/test_subtest.py b/tests/subtests/test_subtest.py index 3e0276c4..48a2f461 100644 --- a/tests/subtests/test_subtest.py +++ b/tests/subtests/test_subtest.py @@ -6,8 +6,8 @@ import matplotlib import matplotlib.ft2font -from packaging.version import Version import pytest +from packaging.version import Version from .helpers import assert_existence, diff_summary, patch_summary @@ -20,7 +20,8 @@ HASH_LIBRARY_FLAG = rf'--mpl-hash-library={HASH_LIBRARY}' FULL_BASELINE_PATH = Path(__file__).parent / 'baseline' -BASELINE_IMAGES_FLAG = '--mpl-baseline-path=baseline' +BASELINE_IMAGES_FLAG_REL = ['--mpl-baseline-path=baseline', '--mpl-baseline-relative'] +BASELINE_IMAGES_FLAG_ABS = rf'--mpl-baseline-path={FULL_BASELINE_PATH}' TEST_FILE = Path(__file__).parent / 'subtest.py' @@ -121,19 +122,19 @@ def test_hash(tmp_path): @pytest.mark.skipif(not HASH_LIBRARY.exists(), reason="No hash library for this mpl version") def test_hybrid(tmp_path): - run_subtest('test_hybrid', tmp_path, [HASH_LIBRARY_FLAG, BASELINE_IMAGES_FLAG]) + run_subtest('test_hybrid', tmp_path, [HASH_LIBRARY_FLAG, BASELINE_IMAGES_FLAG_ABS]) @pytest.mark.skipif(not HASH_LIBRARY.exists(), reason="No hash library for this mpl version") def test_results_always(tmp_path): run_subtest('test_results_always', tmp_path, - [HASH_LIBRARY_FLAG, BASELINE_IMAGES_FLAG, '--mpl-results-always']) + [HASH_LIBRARY_FLAG, BASELINE_IMAGES_FLAG_ABS, '--mpl-results-always']) @pytest.mark.skipif(not HASH_LIBRARY.exists(), reason="No hash library for this mpl version") def test_html(tmp_path): run_subtest('test_results_always', tmp_path, - [HASH_LIBRARY_FLAG, BASELINE_IMAGES_FLAG], summaries=['html']) + [HASH_LIBRARY_FLAG, BASELINE_IMAGES_FLAG_ABS], summaries=['html']) assert (tmp_path / 'results' / 'fig_comparison.html').exists() assert (tmp_path / 'results' / 'extra.js').exists() assert (tmp_path / 'results' / 'styles.css').exists() @@ -157,5 +158,5 @@ def test_html_images_only(tmp_path): @pytest.mark.skipif(not HASH_LIBRARY.exists(), reason="No hash library for this mpl version") def test_basic_html(tmp_path): run_subtest('test_results_always', tmp_path, - [HASH_LIBRARY_FLAG, BASELINE_IMAGES_FLAG], summaries=['basic-html']) + [HASH_LIBRARY_FLAG, *BASELINE_IMAGES_FLAG_REL], summaries=['basic-html']) assert (tmp_path / 'results' / 'fig_comparison_basic.html').exists() diff --git a/tests/test_pytest_mpl.py b/tests/test_pytest_mpl.py index 62fe0091..a3f37eff 100644 --- a/tests/test_pytest_mpl.py +++ b/tests/test_pytest_mpl.py @@ -291,7 +291,7 @@ def test_hash_fails(tmpdir): # If we use --mpl, it should detect that the figure is wrong output = assert_pytest_fails_with(['--mpl', test_file], "doesn't match hash FAIL in library") # We didn't specify a baseline dir so we shouldn't attempt to find one - assert "Unable to find baseline image" not in output, output + assert "Image file not found for comparison test" not in output, output # Check that the summary path is printed and that it exists. output = assert_pytest_fails_with(['--mpl', test_file, '--mpl-generate-summary=html'], @@ -337,7 +337,7 @@ def test_hash_fail_hybrid(tmpdir): output = assert_pytest_fails_with(['--mpl', test_file, '--mpl-baseline-path=/not/a/path'], "doesn't match hash FAIL in library") - assert "Unable to find baseline image" in output, output + assert "Image file not found for comparison test" in output, output # Assert reports image comparison succeeds output = assert_pytest_fails_with(['--mpl', test_file, diff --git a/tox.ini b/tox.ini index 6c242ccd..774cc0f9 100644 --- a/tox.ini +++ b/tox.ini @@ -33,7 +33,8 @@ extras = commands = pip freeze # Make sure the tests pass with and without --mpl - pytest '{toxinidir}' {posargs} + # Use -m so pytest skips "subtests" which always apply --mpl + pytest '{toxinidir}' -m "mpl_image_compare" {posargs} pytest '{toxinidir}' --mpl --cov pytest_mpl {posargs} [testenv:codestyle] From a2fc28b0a197b5a3261d7c2825cc1c629e95e800 Mon Sep 17 00:00:00 2001 From: Conor MacBride Date: Sun, 6 Feb 2022 18:12:00 +0000 Subject: [PATCH 17/22] Record image filenames during test --- pytest_mpl/plugin.py | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/pytest_mpl/plugin.py b/pytest_mpl/plugin.py index 19aec150..de32461a 100644 --- a/pytest_mpl/plugin.py +++ b/pytest_mpl/plugin.py @@ -431,7 +431,7 @@ def compare_image_to_baseline(self, item, fig, result_dir, summary=None): test_image = (result_dir / "result.png").absolute() fig.savefig(str(test_image), **savefig_kwargs) - summary['result_image'] = '%EXISTS%' + summary['result_image'] = str(test_image.relative_to(self.results_dir)) if not os.path.exists(baseline_image_ref): summary['status'] = 'failed' @@ -447,7 +447,7 @@ def compare_image_to_baseline(self, item, fig, result_dir, summary=None): # copy to our tmpdir to be sure to keep them in case of failure baseline_image = (result_dir / "baseline.png").absolute() shutil.copyfile(baseline_image_ref, baseline_image) - summary['baseline_image'] = '%EXISTS%' + summary['baseline_image'] = str(baseline_image.relative_to(self.results_dir)) # Compare image size ourselves since the Matplotlib # exception is a bit cryptic in this case and doesn't show @@ -472,7 +472,8 @@ def compare_image_to_baseline(self, item, fig, result_dir, summary=None): else: summary['status'] = 'failed' summary['rms'] = results['rms'] - summary['diff_image'] = '%EXISTS%' + diff_image = (result_dir / 'result-failed-diff.png').absolute() + summary['diff_image'] = str(diff_image.relative_to(self.results_dir)) template = ['Error: Image files did not match.', 'RMS Value: {rms}', 'Expected: \n {expected}', @@ -521,7 +522,7 @@ def compare_image_to_hash_library(self, item, fig, result_dir, summary=None): # Save the figure for later summary (will be removed later if not needed) test_image = (result_dir / "result.png").absolute() fig.savefig(str(test_image), **savefig_kwargs) - summary['result_image'] = '%EXISTS%' + summary['result_image'] = str(test_image.relative_to(self.results_dir)) if not new_test: if test_hash == hash_library[hash_name]: @@ -572,8 +573,6 @@ def compare_image_to_hash_library(self, item, fig, result_dir, summary=None): summary['status_msg'] = error_message return error_message - summary['baseline_image'] = '%EXISTS%' - # Override the tolerance (if not explicitly set) to 0 as the hashes are not forgiving tolerance = compare.kwargs.get('tolerance', None) if not tolerance: @@ -673,7 +672,7 @@ def item_function_wrapper(*args, **kwargs): if not self.results_always: shutil.rmtree(result_dir) for image_type in ['baseline_image', 'diff_image', 'result_image']: - summary[image_type] = None # image no longer %EXISTS% + summary[image_type] = None # image no longer exists else: self._test_results[str(pathify(test_name))] = summary pytest.fail(msg, pytrace=False) @@ -704,21 +703,6 @@ def pytest_unconfigure(self, config): json.dump(self._generated_hash_library, fp, indent=2) if self.generate_summary: - # Generate a list of test directories - dir_list = [p.relative_to(self.results_dir) - for p in self.results_dir.iterdir() if p.is_dir()] - - # Resolve image paths - for directory in dir_list: - test_name = directory.parts[-1] - for image_type, filename in [ - ('baseline_image', 'baseline.png'), - ('diff_image', 'result-failed-diff.png'), - ('result_image', 'result.png'), - ]: - if self._test_results[test_name][image_type] == '%EXISTS%': - self._test_results[test_name][image_type] = str(directory / filename) - if 'json' in self.generate_summary: summary = self.generate_summary_json() print(f"A JSON report can be found at: {summary}") From 188a08d7957f090dd260495b9a5f6a5a40cde07f Mon Sep 17 00:00:00 2001 From: Conor MacBride Date: Sun, 6 Feb 2022 18:15:57 +0000 Subject: [PATCH 18/22] Fix bugs and refactor compare_image_to_hash_library --- pytest_mpl/plugin.py | 103 ++++++++++++++++--------------------------- 1 file changed, 37 insertions(+), 66 deletions(-) diff --git a/pytest_mpl/plugin.py b/pytest_mpl/plugin.py index de32461a..17998c32 100644 --- a/pytest_mpl/plugin.py +++ b/pytest_mpl/plugin.py @@ -489,9 +489,7 @@ def load_hash_library(self, library_path): return json.load(fp) def compare_image_to_hash_library(self, item, fig, result_dir, summary=None): - new_test = False hash_comparison_pass = False - baseline_image_path = None if summary is None: summary = {} @@ -506,85 +504,58 @@ def compare_image_to_hash_library(self, item, fig, result_dir, summary=None): hash_library = self.load_hash_library(hash_library_filename) hash_name = self.generate_test_name(item) + baseline_hash = hash_library.get(hash_name, None) + summary['baseline_hash'] = baseline_hash test_hash = self.generate_image_hash(item, fig) summary['result_hash'] = test_hash - if hash_name not in hash_library: - new_test = True + if baseline_hash is None: # hash-missing summary['status'] = 'failed' - error_message = (f"Hash for test '{hash_name}' not found in {hash_library_filename}. " - f"Generated hash is {test_hash}.") - summary['status_msg'] = error_message - else: - summary['baseline_hash'] = hash_library[hash_name] + summary['status_msg'] = (f"Hash for test '{hash_name}' not found in {hash_library_filename}. " + f"Generated hash is {test_hash}.") + elif test_hash == baseline_hash: # hash-match + hash_comparison_pass = True + summary['status'] = 'passed' + summary['status_msg'] = 'Test hash matches baseline hash.' + else: # hash-diff + summary['status'] = 'failed' + summary['status_msg'] = (f"Hash {test_hash} doesn't match hash " + f"{baseline_hash} in library " + f"{hash_library_filename} for test {hash_name}.") # Save the figure for later summary (will be removed later if not needed) test_image = (result_dir / "result.png").absolute() fig.savefig(str(test_image), **savefig_kwargs) summary['result_image'] = str(test_image.relative_to(self.results_dir)) - if not new_test: - if test_hash == hash_library[hash_name]: - hash_comparison_pass = True - summary['status'] = 'passed' - summary['status_msg'] = 'Test hash matches baseline hash.' - else: - error_message = (f"Hash {test_hash} doesn't match hash " - f"{hash_library[hash_name]} in library " - f"{hash_library_filename} for test {hash_name}.") - summary['status'] = 'failed' - summary['status_msg'] = 'Test hash does not match baseline hash.' - - # If the compare has only been specified with hash and not baseline - # dir, don't attempt to find a baseline image at the default path. - if not hash_comparison_pass and not self.baseline_directory_specified(item) or new_test: - return error_message + # Hybrid mode (hash and image comparison) + if self.baseline_directory_specified(item): + + # Skip image comparison if hash matches (unless `--mpl-results-always`) + if hash_comparison_pass and not self.results_always: + return - # If this is not a new test try and get the baseline image. - if not new_test: - baseline_error = None - baseline_summary = {} - # Ignore Errors here as it's possible the reference image dosen't exist yet. - try: - baseline_image_path = self.obtain_baseline_image(item, result_dir) - baseline_image = baseline_image_path - if baseline_image and not baseline_image.exists(): - baseline_image = None - # Get the baseline and generate a diff image, always so that - # --mpl-results-always can be respected. + # Run image comparison + baseline_summary = {} # summary for image comparison to merge with hash comparison summary + try: # Ignore all errors as success does not influence the overall test result baseline_comparison = self.compare_image_to_baseline(item, fig, result_dir, summary=baseline_summary) - except Exception as e: - baseline_image = None - baseline_error = e - for k in ['baseline_image', 'diff_image', 'rms', 'tolerance', 'result_image']: - summary[k] = summary[k] or baseline_summary.get(k) - - # If the hash comparison passes then return - if hash_comparison_pass: + except Exception as baseline_error: # Append to test error later + baseline_comparison = str(baseline_error) + else: # Update main summary + for k in ['baseline_image', 'diff_image', 'rms', 'tolerance', 'result_image']: + summary[k] = summary[k] or baseline_summary.get(k) + + # Append the log from image comparison + r = baseline_comparison or "The comparison to the baseline image succeeded." + summary['status_msg'] += ("\n\n" + "Image comparison test\n" + "---------------------\n") + r + + if hash_comparison_pass: # Return None to indicate test passed return - - if baseline_image is None: - error_message += f"\nUnable to find baseline image for {item}." - if baseline_error: - error_message += f"\n{baseline_error}" - summary['status'] = 'failed' - summary['status_msg'] = error_message - return error_message - - # Override the tolerance (if not explicitly set) to 0 as the hashes are not forgiving - tolerance = compare.kwargs.get('tolerance', None) - if not tolerance: - compare.kwargs['tolerance'] = 0 - - comparison_error = (baseline_comparison or - "\nHowever, the comparison to the baseline image succeeded.") - - error_message = f"{error_message}\n{comparison_error}" - summary['status'] = 'failed' - summary['status_msg'] = error_message - return error_message + return summary['status_msg'] def pytest_runtest_setup(self, item): # noqa From e44c0c16cd9add2aafac4429a8b8ac7a7d8672d8 Mon Sep 17 00:00:00 2001 From: Conor MacBride Date: Sun, 6 Feb 2022 20:04:09 +0000 Subject: [PATCH 19/22] Update wording in log message --- tests/test_pytest_mpl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_pytest_mpl.py b/tests/test_pytest_mpl.py index a3f37eff..0843fe68 100644 --- a/tests/test_pytest_mpl.py +++ b/tests/test_pytest_mpl.py @@ -343,7 +343,7 @@ def test_hash_fail_hybrid(tmpdir): output = assert_pytest_fails_with(['--mpl', test_file, rf'--mpl-baseline-path={hash_baseline_dir_abs / "succeed"}'], "doesn't match hash FAIL in library") - assert "However, the comparison to the baseline image succeeded." in output, output + assert "The comparison to the baseline image succeeded." in output, output # If we don't use --mpl option, the test should succeed code = call_pytest([test_file]) From 03d1f7612deeec264482cf2b5bc96cffaa5922ac Mon Sep 17 00:00:00 2001 From: Conor MacBride Date: Sun, 6 Feb 2022 21:43:08 +0000 Subject: [PATCH 20/22] Convert status_msg to regex --- tests/subtests/helpers.py | 74 ++++++++++++++++++- tests/subtests/summaries/test_default.json | 16 ++-- tests/subtests/summaries/test_hash.json | 24 +++--- .../summaries/test_html_hashes_only.json | 24 +++--- .../summaries/test_html_images_only.json | 16 ++-- tests/subtests/summaries/test_hybrid.json | 24 +++--- .../summaries/test_results_always.json | 28 +++---- 7 files changed, 139 insertions(+), 67 deletions(-) diff --git a/tests/subtests/helpers.py b/tests/subtests/helpers.py index 4c126f32..0c688843 100644 --- a/tests/subtests/helpers.py +++ b/tests/subtests/helpers.py @@ -2,7 +2,7 @@ import json from pathlib import Path -__all__ = ['diff_summary', 'assert_existence', 'patch_summary'] +__all__ = ['diff_summary', 'assert_existence', 'patch_summary', 'apply_regex'] class MatchError(Exception): @@ -31,10 +31,14 @@ def diff_summary(baseline, result, baseline_hash_library=None, result_hash_libra # Load "correct" baseline hashes with open(baseline_hash_library, 'r') as f: baseline_hash_library = json.load(f) + else: + baseline_hash_library = {} if result_hash_library and result_hash_library.exists(): # Load "correct" result hashes with open(result_hash_library, 'r') as f: result_hash_library = json.load(f) + else: + result_hash_library = {} # Get test names baseline_tests = set(baseline.keys()) @@ -176,3 +180,71 @@ def assert_existence(summary, items=('baseline_image', 'diff_image', 'result_ima for item in items: if test[item] is not None: assert (Path(path) / test[item]).exists() + + +def _escape_regex(msg): + if not msg.startswith('REGEX:'): + msg = msg.replace('.', r'\.').replace('(', r'\(').replace(')', r'\)') + msg = 'REGEX:' + msg + return msg + + +def _escape_path(msg, path): + pattern = (rf"({path}[A-Za-z0-9_\-\/.\\]*)" + + r"(baseline\\.png|result-failed-diff\\.png|result\\.png|\\.json)") + msg = re.sub(pattern, r".*\2", msg) + pattern = rf"({path}[A-Za-z0-9_\-\/.\\]*)" + msg = re.sub(pattern, r".*", msg) + return msg + + +def _escape_float(msg, key): + pattern = rf"({key}[0-9]+\\\.[0-9]{{1}})([0-9]+)" + msg = re.sub(pattern, r"\1[0-9]*", msg) + return msg + + +def apply_regex(file, regex_paths, regex_strs): + """Convert all `status_msg` entries in JSON summary file to regex. + + Use in your own script to assist with updating baseline summaries. + + Parameters + ---------- + file : Path + JSON summary file to convert `status_msg` to regex in. Overwritten. + regex_paths : list of str + List of path beginnings to identify paths that need to be converted to regex. + E.g. `['/home/user/']` + Does: `aaa /home/user/pytest/tmp/result\\.png bbb` -> `aaa .*result\\.png bbb` + regex_strs : list of str + List of keys to convert following floats to 1 d.p. + E.g. ['RMS Value: '] + Does: `aaa RMS Value: 12\\.432644 bbb` -> `aaa RMS Value: 12\\.4[0-9]* bbb` + """ + + with open(file, 'r') as f: + summary = json.load(f) + + for test in summary.keys(): + + msg = summary[test]['status_msg'] + + for signal in [*regex_paths, *regex_strs]: + if signal in msg: + msg = _escape_regex(msg) + if not msg.startswith('REGEX:'): + continue + + for signal in regex_paths: + if signal in msg: + msg = _escape_path(msg, path=signal) + + for signal in regex_strs: + if signal in msg: + msg = _escape_float(msg, key=signal) + + summary[test]['status_msg'] = msg + + with open(file, 'w') as f: + json.dump(summary, f, indent=2) diff --git a/tests/subtests/summaries/test_default.json b/tests/subtests/summaries/test_default.json index afe67989..a4acadc6 100644 --- a/tests/subtests/summaries/test_default.json +++ b/tests/subtests/summaries/test_default.json @@ -12,7 +12,7 @@ }, "subtests.subtest.test_hmatch_idiff": { "status": "failed", - "status_msg": "Error: Image files did not match.\n RMS Value: 11.100353848213828\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hmatch_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hmatch_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hmatch_idiff/result-failed-diff.png\n Tolerance: \n 2", + "status_msg": "REGEX:Error: Image files did not match\\.\n RMS Value: 11\\.1[0-9]*\n Expected: \n .*baseline\\.png\n Actual: \n .*result\\.png\n Difference:\n .*result-failed-diff\\.png\n Tolerance: \n 2", "baseline_image": "subtests.subtest.test_hmatch_idiff/baseline.png", "diff_image": "subtests.subtest.test_hmatch_idiff/result-failed-diff.png", "rms": 11.100353848213828, @@ -23,7 +23,7 @@ }, "subtests.subtest.test_hmatch_imissing": { "status": "failed", - "status_msg": "Image file not found for comparison test in: \n\t/Users/conor/Documents/pytest-mpl/tests/subtests/baseline\n(This is expected for new tests.)\nGenerated Image: \n\t/private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hmatch_imissing/result.png", + "status_msg": "REGEX:Image file not found for comparison test in: \n\t.*\n\\(This is expected for new tests\\.\\)\nGenerated Image: \n\t.*result\\.png", "baseline_image": null, "diff_image": null, "rms": null, @@ -45,7 +45,7 @@ }, "subtests.subtest.test_hdiff_idiff": { "status": "failed", - "status_msg": "Error: Image files did not match.\n RMS Value: 11.182677079602481\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hdiff_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hdiff_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hdiff_idiff/result-failed-diff.png\n Tolerance: \n 2", + "status_msg": "REGEX:Error: Image files did not match\\.\n RMS Value: 11\\.1[0-9]*\n Expected: \n .*baseline\\.png\n Actual: \n .*result\\.png\n Difference:\n .*result-failed-diff\\.png\n Tolerance: \n 2", "baseline_image": "subtests.subtest.test_hdiff_idiff/baseline.png", "diff_image": "subtests.subtest.test_hdiff_idiff/result-failed-diff.png", "rms": 11.182677079602481, @@ -56,7 +56,7 @@ }, "subtests.subtest.test_hdiff_imissing": { "status": "failed", - "status_msg": "Image file not found for comparison test in: \n\t/Users/conor/Documents/pytest-mpl/tests/subtests/baseline\n(This is expected for new tests.)\nGenerated Image: \n\t/private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hdiff_imissing/result.png", + "status_msg": "REGEX:Image file not found for comparison test in: \n\t.*\n\\(This is expected for new tests\\.\\)\nGenerated Image: \n\t.*result\\.png", "baseline_image": null, "diff_image": null, "rms": null, @@ -78,7 +78,7 @@ }, "subtests.subtest.test_hmissing_idiff": { "status": "failed", - "status_msg": "Error: Image files did not match.\n RMS Value: 12.12938597648977\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hmissing_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hmissing_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hmissing_idiff/result-failed-diff.png\n Tolerance: \n 2", + "status_msg": "REGEX:Error: Image files did not match\\.\n RMS Value: 12\\.1[0-9]*\n Expected: \n .*baseline\\.png\n Actual: \n .*result\\.png\n Difference:\n .*result-failed-diff\\.png\n Tolerance: \n 2", "baseline_image": "subtests.subtest.test_hmissing_idiff/baseline.png", "diff_image": "subtests.subtest.test_hmissing_idiff/result-failed-diff.png", "rms": 12.12938597648977, @@ -89,7 +89,7 @@ }, "subtests.subtest.test_hmissing_imissing": { "status": "failed", - "status_msg": "Image file not found for comparison test in: \n\t/Users/conor/Documents/pytest-mpl/tests/subtests/baseline\n(This is expected for new tests.)\nGenerated Image: \n\t/private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hmissing_imissing/result.png", + "status_msg": "REGEX:Image file not found for comparison test in: \n\t.*\n\\(This is expected for new tests\\.\\)\nGenerated Image: \n\t.*result\\.png", "baseline_image": null, "diff_image": null, "rms": null, @@ -111,7 +111,7 @@ }, "subtests.subtest.test_hdiff_idiff_tolerance": { "status": "failed", - "status_msg": "Error: Image files did not match.\n RMS Value: 29.260332173249314\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hdiff_idiff_tolerance/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hdiff_idiff_tolerance/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_default0/results/subtests.subtest.test_hdiff_idiff_tolerance/result-failed-diff.png\n Tolerance: \n 3", + "status_msg": "REGEX:Error: Image files did not match\\.\n RMS Value: 29\\.2[0-9]*\n Expected: \n .*baseline\\.png\n Actual: \n .*result\\.png\n Difference:\n .*result-failed-diff\\.png\n Tolerance: \n 3", "baseline_image": "subtests.subtest.test_hdiff_idiff_tolerance/baseline.png", "diff_image": "subtests.subtest.test_hdiff_idiff_tolerance/result-failed-diff.png", "rms": 29.260332173249314, @@ -153,4 +153,4 @@ "baseline_hash": null, "result_hash": null } -} \ No newline at end of file +} diff --git a/tests/subtests/summaries/test_hash.json b/tests/subtests/summaries/test_hash.json index f96572a4..bec04c5a 100644 --- a/tests/subtests/summaries/test_hash.json +++ b/tests/subtests/summaries/test_hash.json @@ -34,7 +34,7 @@ }, "subtests.subtest.test_hdiff_imatch": { "status": "failed", - "status_msg": "Hash 6e2fdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 doesn't match hash d1ffdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch.", + "status_msg": "REGEX:Hash 6e2fdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 doesn't match hash d1ffdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 in library .*\\.json for test subtests\\.subtest\\.test_hdiff_imatch\\.", "baseline_image": null, "diff_image": null, "rms": null, @@ -45,7 +45,7 @@ }, "subtests.subtest.test_hdiff_idiff": { "status": "failed", - "status_msg": "Hash 443361bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 doesn't match hash d1ff61bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_idiff.", + "status_msg": "REGEX:Hash 443361bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 doesn't match hash d1ff61bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 in library .*\\.json for test subtests\\.subtest\\.test_hdiff_idiff\\.", "baseline_image": null, "diff_image": null, "rms": null, @@ -56,7 +56,7 @@ }, "subtests.subtest.test_hdiff_imissing": { "status": "failed", - "status_msg": "Hash 301e63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e doesn't match hash d1ff63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imissing.", + "status_msg": "REGEX:Hash 301e63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e doesn't match hash d1ff63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e in library .*\\.json for test subtests\\.subtest\\.test_hdiff_imissing\\.", "baseline_image": null, "diff_image": null, "rms": null, @@ -67,7 +67,7 @@ }, "subtests.subtest.test_hmissing_imatch": { "status": "failed", - "status_msg": "Hash for test 'subtests.subtest.test_hmissing_imatch' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is eabd8a2e22afd88682990bfb8e4a0700a942fe68e5114e8da4ab6bd93c47b824.", + "status_msg": "REGEX:Hash for test 'subtests\\.subtest\\.test_hmissing_imatch' not found in .*\\.json\\. Generated hash is eabd8a2e22afd88682990bfb8e4a0700a942fe68e5114e8da4ab6bd93c47b824\\.", "baseline_image": null, "diff_image": null, "rms": null, @@ -78,7 +78,7 @@ }, "subtests.subtest.test_hmissing_idiff": { "status": "failed", - "status_msg": "Hash for test 'subtests.subtest.test_hmissing_idiff' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is e69570c4a70b2cc88ddee0f0a82312cae4f394b7f62e5760245feda1364c03ab.", + "status_msg": "REGEX:Hash for test 'subtests\\.subtest\\.test_hmissing_idiff' not found in .*\\.json\\. Generated hash is e69570c4a70b2cc88ddee0f0a82312cae4f394b7f62e5760245feda1364c03ab\\.", "baseline_image": null, "diff_image": null, "rms": null, @@ -89,7 +89,7 @@ }, "subtests.subtest.test_hmissing_imissing": { "status": "failed", - "status_msg": "Hash for test 'subtests.subtest.test_hmissing_imissing' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is 5c8a9c7412e4e098f6f2683ee247c08bd50805a93df4d4b6d8fccf3579b4c56b.", + "status_msg": "REGEX:Hash for test 'subtests\\.subtest\\.test_hmissing_imissing' not found in .*\\.json\\. Generated hash is 5c8a9c7412e4e098f6f2683ee247c08bd50805a93df4d4b6d8fccf3579b4c56b\\.", "baseline_image": null, "diff_image": null, "rms": null, @@ -100,7 +100,7 @@ }, "subtests.subtest.test_hdiff_imatch_tolerance": { "status": "failed", - "status_msg": "Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_tolerance.", + "status_msg": "REGEX:Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library .*\\.json for test subtests\\.subtest\\.test_hdiff_imatch_tolerance\\.", "baseline_image": null, "diff_image": null, "rms": null, @@ -111,7 +111,7 @@ }, "subtests.subtest.test_hdiff_idiff_tolerance": { "status": "failed", - "status_msg": "Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_idiff_tolerance.", + "status_msg": "REGEX:Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library .*\\.json for test subtests\\.subtest\\.test_hdiff_idiff_tolerance\\.", "baseline_image": null, "diff_image": null, "rms": null, @@ -122,7 +122,7 @@ }, "subtests.subtest.test_hdiff_imatch_savefig": { "status": "failed", - "status_msg": "Hash 5dc1c2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 doesn't match hash d1ffc2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_savefig.", + "status_msg": "REGEX:Hash 5dc1c2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 doesn't match hash d1ffc2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 in library .*\\.json for test subtests\\.subtest\\.test_hdiff_imatch_savefig\\.", "baseline_image": null, "diff_image": null, "rms": null, @@ -133,7 +133,7 @@ }, "subtests.subtest.test_hdiff_imatch_style": { "status": "failed", - "status_msg": "Hash 185ed1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 doesn't match hash d1ffd1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_style.", + "status_msg": "REGEX:Hash 185ed1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 doesn't match hash d1ffd1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 in library .*\\.json for test subtests\\.subtest\\.test_hdiff_imatch_style\\.", "baseline_image": null, "diff_image": null, "rms": null, @@ -144,7 +144,7 @@ }, "subtests.subtest.test_hdiff_imatch_removetext": { "status": "failed", - "status_msg": "Hash be5af83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e doesn't match hash d1fff83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_removetext.", + "status_msg": "REGEX:Hash be5af83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e doesn't match hash d1fff83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e in library .*\\.json for test subtests\\.subtest\\.test_hdiff_imatch_removetext\\.", "baseline_image": null, "diff_image": null, "rms": null, @@ -153,4 +153,4 @@ "baseline_hash": "d1fff83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e", "result_hash": "be5af83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e" } -} \ No newline at end of file +} diff --git a/tests/subtests/summaries/test_html_hashes_only.json b/tests/subtests/summaries/test_html_hashes_only.json index bdb1ba01..448ce9c2 100644 --- a/tests/subtests/summaries/test_html_hashes_only.json +++ b/tests/subtests/summaries/test_html_hashes_only.json @@ -34,7 +34,7 @@ }, "subtests.subtest.test_hdiff_imatch": { "status": "failed", - "status_msg": "Hash 6e2fdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 doesn't match hash d1ffdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch.", + "status_msg": "REGEX:Hash 6e2fdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 doesn't match hash d1ffdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 in library .*\\.json for test subtests\\.subtest\\.test_hdiff_imatch\\.", "baseline_image": null, "diff_image": null, "rms": null, @@ -45,7 +45,7 @@ }, "subtests.subtest.test_hdiff_idiff": { "status": "failed", - "status_msg": "Hash 443361bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 doesn't match hash d1ff61bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_idiff.", + "status_msg": "REGEX:Hash 443361bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 doesn't match hash d1ff61bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 in library .*\\.json for test subtests\\.subtest\\.test_hdiff_idiff\\.", "baseline_image": null, "diff_image": null, "rms": null, @@ -56,7 +56,7 @@ }, "subtests.subtest.test_hdiff_imissing": { "status": "failed", - "status_msg": "Hash 301e63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e doesn't match hash d1ff63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imissing.", + "status_msg": "REGEX:Hash 301e63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e doesn't match hash d1ff63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e in library .*\\.json for test subtests\\.subtest\\.test_hdiff_imissing\\.", "baseline_image": null, "diff_image": null, "rms": null, @@ -67,7 +67,7 @@ }, "subtests.subtest.test_hmissing_imatch": { "status": "failed", - "status_msg": "Hash for test 'subtests.subtest.test_hmissing_imatch' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is eabd8a2e22afd88682990bfb8e4a0700a942fe68e5114e8da4ab6bd93c47b824.", + "status_msg": "REGEX:Hash for test 'subtests\\.subtest\\.test_hmissing_imatch' not found in .*\\.json\\. Generated hash is eabd8a2e22afd88682990bfb8e4a0700a942fe68e5114e8da4ab6bd93c47b824\\.", "baseline_image": null, "diff_image": null, "rms": null, @@ -78,7 +78,7 @@ }, "subtests.subtest.test_hmissing_idiff": { "status": "failed", - "status_msg": "Hash for test 'subtests.subtest.test_hmissing_idiff' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is e69570c4a70b2cc88ddee0f0a82312cae4f394b7f62e5760245feda1364c03ab.", + "status_msg": "REGEX:Hash for test 'subtests\\.subtest\\.test_hmissing_idiff' not found in .*\\.json\\. Generated hash is e69570c4a70b2cc88ddee0f0a82312cae4f394b7f62e5760245feda1364c03ab\\.", "baseline_image": null, "diff_image": null, "rms": null, @@ -89,7 +89,7 @@ }, "subtests.subtest.test_hmissing_imissing": { "status": "failed", - "status_msg": "Hash for test 'subtests.subtest.test_hmissing_imissing' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is 5c8a9c7412e4e098f6f2683ee247c08bd50805a93df4d4b6d8fccf3579b4c56b.", + "status_msg": "REGEX:Hash for test 'subtests\\.subtest\\.test_hmissing_imissing' not found in .*\\.json\\. Generated hash is 5c8a9c7412e4e098f6f2683ee247c08bd50805a93df4d4b6d8fccf3579b4c56b\\.", "baseline_image": null, "diff_image": null, "rms": null, @@ -100,7 +100,7 @@ }, "subtests.subtest.test_hdiff_imatch_tolerance": { "status": "failed", - "status_msg": "Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_tolerance.", + "status_msg": "REGEX:Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library .*\\.json for test subtests\\.subtest\\.test_hdiff_imatch_tolerance\\.", "baseline_image": null, "diff_image": null, "rms": null, @@ -111,7 +111,7 @@ }, "subtests.subtest.test_hdiff_idiff_tolerance": { "status": "failed", - "status_msg": "Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_idiff_tolerance.", + "status_msg": "REGEX:Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library .*\\.json for test subtests\\.subtest\\.test_hdiff_idiff_tolerance\\.", "baseline_image": null, "diff_image": null, "rms": null, @@ -122,7 +122,7 @@ }, "subtests.subtest.test_hdiff_imatch_savefig": { "status": "failed", - "status_msg": "Hash 5dc1c2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 doesn't match hash d1ffc2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_savefig.", + "status_msg": "REGEX:Hash 5dc1c2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 doesn't match hash d1ffc2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 in library .*\\.json for test subtests\\.subtest\\.test_hdiff_imatch_savefig\\.", "baseline_image": null, "diff_image": null, "rms": null, @@ -133,7 +133,7 @@ }, "subtests.subtest.test_hdiff_imatch_style": { "status": "failed", - "status_msg": "Hash 185ed1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 doesn't match hash d1ffd1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_style.", + "status_msg": "REGEX:Hash 185ed1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 doesn't match hash d1ffd1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 in library .*\\.json for test subtests\\.subtest\\.test_hdiff_imatch_style\\.", "baseline_image": null, "diff_image": null, "rms": null, @@ -144,7 +144,7 @@ }, "subtests.subtest.test_hdiff_imatch_removetext": { "status": "failed", - "status_msg": "Hash be5af83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e doesn't match hash d1fff83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_removetext.", + "status_msg": "REGEX:Hash be5af83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e doesn't match hash d1fff83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e in library .*\\.json for test subtests\\.subtest\\.test_hdiff_imatch_removetext\\.", "baseline_image": null, "diff_image": null, "rms": null, @@ -153,4 +153,4 @@ "baseline_hash": "d1fff83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e", "result_hash": "be5af83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e" } -} \ No newline at end of file +} diff --git a/tests/subtests/summaries/test_html_images_only.json b/tests/subtests/summaries/test_html_images_only.json index 47298e01..c8a5058c 100644 --- a/tests/subtests/summaries/test_html_images_only.json +++ b/tests/subtests/summaries/test_html_images_only.json @@ -12,7 +12,7 @@ }, "subtests.subtest.test_hmatch_idiff": { "status": "failed", - "status_msg": "Error: Image files did not match.\n RMS Value: 11.100353848213828\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hmatch_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hmatch_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hmatch_idiff/result-failed-diff.png\n Tolerance: \n 2", + "status_msg": "REGEX:Error: Image files did not match\\.\n RMS Value: 11\\.1[0-9]*\n Expected: \n .*baseline\\.png\n Actual: \n .*result\\.png\n Difference:\n .*result-failed-diff\\.png\n Tolerance: \n 2", "baseline_image": "subtests.subtest.test_hmatch_idiff/baseline.png", "diff_image": "subtests.subtest.test_hmatch_idiff/result-failed-diff.png", "rms": 11.100353848213828, @@ -23,7 +23,7 @@ }, "subtests.subtest.test_hmatch_imissing": { "status": "failed", - "status_msg": "Image file not found for comparison test in: \n\t/Users/conor/Documents/pytest-mpl/tests/subtests/baseline\n(This is expected for new tests.)\nGenerated Image: \n\t/private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hmatch_imissing/result.png", + "status_msg": "REGEX:Image file not found for comparison test in: \n\t.*\n\\(This is expected for new tests\\.\\)\nGenerated Image: \n\t.*result\\.png", "baseline_image": null, "diff_image": null, "rms": null, @@ -45,7 +45,7 @@ }, "subtests.subtest.test_hdiff_idiff": { "status": "failed", - "status_msg": "Error: Image files did not match.\n RMS Value: 11.182677079602481\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hdiff_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hdiff_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hdiff_idiff/result-failed-diff.png\n Tolerance: \n 2", + "status_msg": "REGEX:Error: Image files did not match\\.\n RMS Value: 11\\.1[0-9]*\n Expected: \n .*baseline\\.png\n Actual: \n .*result\\.png\n Difference:\n .*result-failed-diff\\.png\n Tolerance: \n 2", "baseline_image": "subtests.subtest.test_hdiff_idiff/baseline.png", "diff_image": "subtests.subtest.test_hdiff_idiff/result-failed-diff.png", "rms": 11.182677079602481, @@ -56,7 +56,7 @@ }, "subtests.subtest.test_hdiff_imissing": { "status": "failed", - "status_msg": "Image file not found for comparison test in: \n\t/Users/conor/Documents/pytest-mpl/tests/subtests/baseline\n(This is expected for new tests.)\nGenerated Image: \n\t/private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hdiff_imissing/result.png", + "status_msg": "REGEX:Image file not found for comparison test in: \n\t.*\n\\(This is expected for new tests\\.\\)\nGenerated Image: \n\t.*result\\.png", "baseline_image": null, "diff_image": null, "rms": null, @@ -78,7 +78,7 @@ }, "subtests.subtest.test_hmissing_idiff": { "status": "failed", - "status_msg": "Error: Image files did not match.\n RMS Value: 12.12938597648977\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hmissing_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hmissing_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hmissing_idiff/result-failed-diff.png\n Tolerance: \n 2", + "status_msg": "REGEX:Error: Image files did not match\\.\n RMS Value: 12\\.1[0-9]*\n Expected: \n .*baseline\\.png\n Actual: \n .*result\\.png\n Difference:\n .*result-failed-diff\\.png\n Tolerance: \n 2", "baseline_image": "subtests.subtest.test_hmissing_idiff/baseline.png", "diff_image": "subtests.subtest.test_hmissing_idiff/result-failed-diff.png", "rms": 12.12938597648977, @@ -89,7 +89,7 @@ }, "subtests.subtest.test_hmissing_imissing": { "status": "failed", - "status_msg": "Image file not found for comparison test in: \n\t/Users/conor/Documents/pytest-mpl/tests/subtests/baseline\n(This is expected for new tests.)\nGenerated Image: \n\t/private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hmissing_imissing/result.png", + "status_msg": "REGEX:Image file not found for comparison test in: \n\t.*\n\\(This is expected for new tests\\.\\)\nGenerated Image: \n\t.*result\\.png", "baseline_image": null, "diff_image": null, "rms": null, @@ -111,7 +111,7 @@ }, "subtests.subtest.test_hdiff_idiff_tolerance": { "status": "failed", - "status_msg": "Error: Image files did not match.\n RMS Value: 29.260332173249314\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hdiff_idiff_tolerance/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hdiff_idiff_tolerance/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-124/test_html_images_only0/results/subtests.subtest.test_hdiff_idiff_tolerance/result-failed-diff.png\n Tolerance: \n 3", + "status_msg": "REGEX:Error: Image files did not match\\.\n RMS Value: 29\\.2[0-9]*\n Expected: \n .*baseline\\.png\n Actual: \n .*result\\.png\n Difference:\n .*result-failed-diff\\.png\n Tolerance: \n 3", "baseline_image": "subtests.subtest.test_hdiff_idiff_tolerance/baseline.png", "diff_image": "subtests.subtest.test_hdiff_idiff_tolerance/result-failed-diff.png", "rms": 29.260332173249314, @@ -153,4 +153,4 @@ "baseline_hash": null, "result_hash": null } -} \ No newline at end of file +} diff --git a/tests/subtests/summaries/test_hybrid.json b/tests/subtests/summaries/test_hybrid.json index 7362dbea..2f2930b0 100644 --- a/tests/subtests/summaries/test_hybrid.json +++ b/tests/subtests/summaries/test_hybrid.json @@ -34,7 +34,7 @@ }, "subtests.subtest.test_hdiff_imatch": { "status": "failed", - "status_msg": "Hash 6e2fdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 doesn't match hash d1ffdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded.", + "status_msg": "REGEX:Hash 6e2fdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 doesn't match hash d1ffdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 in library .*\\.json for test subtests\\.subtest\\.test_hdiff_imatch\\.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded\\.", "baseline_image": "subtests.subtest.test_hdiff_imatch/baseline.png", "diff_image": null, "rms": null, @@ -45,7 +45,7 @@ }, "subtests.subtest.test_hdiff_idiff": { "status": "failed", - "status_msg": "Hash 443361bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 doesn't match hash d1ff61bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_idiff.\n\nImage comparison test\n---------------------\nError: Image files did not match.\n RMS Value: 11.182677079602481\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_hybrid0/results/subtests.subtest.test_hdiff_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_hybrid0/results/subtests.subtest.test_hdiff_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_hybrid0/results/subtests.subtest.test_hdiff_idiff/result-failed-diff.png\n Tolerance: \n 2", + "status_msg": "REGEX:Hash 443361bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 doesn't match hash d1ff61bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 in library .*\\.json for test subtests\\.subtest\\.test_hdiff_idiff\\.\n\nImage comparison test\n---------------------\nError: Image files did not match\\.\n RMS Value: 11\\.1[0-9]*\n Expected: \n .*baseline\\.png\n Actual: \n .*result\\.png\n Difference:\n .*result-failed-diff\\.png\n Tolerance: \n 2", "baseline_image": "subtests.subtest.test_hdiff_idiff/baseline.png", "diff_image": "subtests.subtest.test_hdiff_idiff/result-failed-diff.png", "rms": 11.182677079602481, @@ -56,7 +56,7 @@ }, "subtests.subtest.test_hdiff_imissing": { "status": "failed", - "status_msg": "Hash 301e63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e doesn't match hash d1ff63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imissing.\n\nImage comparison test\n---------------------\nImage file not found for comparison test in: \n\t/Users/conor/Documents/pytest-mpl/tests/subtests/baseline\n(This is expected for new tests.)\nGenerated Image: \n\t/private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_hybrid0/results/subtests.subtest.test_hdiff_imissing/result.png", + "status_msg": "REGEX:Hash 301e63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e doesn't match hash d1ff63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e in library .*\\.json for test subtests\\.subtest\\.test_hdiff_imissing\\.\n\nImage comparison test\n---------------------\nImage file not found for comparison test in: \n\t.*\n\\(This is expected for new tests\\.\\)\nGenerated Image: \n\t.*result\\.png", "baseline_image": null, "diff_image": null, "rms": null, @@ -67,7 +67,7 @@ }, "subtests.subtest.test_hmissing_imatch": { "status": "failed", - "status_msg": "Hash for test 'subtests.subtest.test_hmissing_imatch' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is eabd8a2e22afd88682990bfb8e4a0700a942fe68e5114e8da4ab6bd93c47b824.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded.", + "status_msg": "REGEX:Hash for test 'subtests\\.subtest\\.test_hmissing_imatch' not found in .*\\.json\\. Generated hash is eabd8a2e22afd88682990bfb8e4a0700a942fe68e5114e8da4ab6bd93c47b824\\.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded\\.", "baseline_image": "subtests.subtest.test_hmissing_imatch/baseline.png", "diff_image": null, "rms": null, @@ -78,7 +78,7 @@ }, "subtests.subtest.test_hmissing_idiff": { "status": "failed", - "status_msg": "Hash for test 'subtests.subtest.test_hmissing_idiff' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is e69570c4a70b2cc88ddee0f0a82312cae4f394b7f62e5760245feda1364c03ab.\n\nImage comparison test\n---------------------\nError: Image files did not match.\n RMS Value: 12.12938597648977\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_hybrid0/results/subtests.subtest.test_hmissing_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_hybrid0/results/subtests.subtest.test_hmissing_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_hybrid0/results/subtests.subtest.test_hmissing_idiff/result-failed-diff.png\n Tolerance: \n 2", + "status_msg": "REGEX:Hash for test 'subtests\\.subtest\\.test_hmissing_idiff' not found in .*\\.json\\. Generated hash is e69570c4a70b2cc88ddee0f0a82312cae4f394b7f62e5760245feda1364c03ab\\.\n\nImage comparison test\n---------------------\nError: Image files did not match\\.\n RMS Value: 12\\.1[0-9]*\n Expected: \n .*baseline\\.png\n Actual: \n .*result\\.png\n Difference:\n .*result-failed-diff\\.png\n Tolerance: \n 2", "baseline_image": "subtests.subtest.test_hmissing_idiff/baseline.png", "diff_image": "subtests.subtest.test_hmissing_idiff/result-failed-diff.png", "rms": 12.12938597648977, @@ -89,7 +89,7 @@ }, "subtests.subtest.test_hmissing_imissing": { "status": "failed", - "status_msg": "Hash for test 'subtests.subtest.test_hmissing_imissing' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is 5c8a9c7412e4e098f6f2683ee247c08bd50805a93df4d4b6d8fccf3579b4c56b.\n\nImage comparison test\n---------------------\nImage file not found for comparison test in: \n\t/Users/conor/Documents/pytest-mpl/tests/subtests/baseline\n(This is expected for new tests.)\nGenerated Image: \n\t/private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_hybrid0/results/subtests.subtest.test_hmissing_imissing/result.png", + "status_msg": "REGEX:Hash for test 'subtests\\.subtest\\.test_hmissing_imissing' not found in .*\\.json\\. Generated hash is 5c8a9c7412e4e098f6f2683ee247c08bd50805a93df4d4b6d8fccf3579b4c56b\\.\n\nImage comparison test\n---------------------\nImage file not found for comparison test in: \n\t.*\n\\(This is expected for new tests\\.\\)\nGenerated Image: \n\t.*result\\.png", "baseline_image": null, "diff_image": null, "rms": null, @@ -100,7 +100,7 @@ }, "subtests.subtest.test_hdiff_imatch_tolerance": { "status": "failed", - "status_msg": "Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_tolerance.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded.", + "status_msg": "REGEX:Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library .*\\.json for test subtests\\.subtest\\.test_hdiff_imatch_tolerance\\.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded\\.", "baseline_image": "subtests.subtest.test_hdiff_imatch_tolerance/baseline.png", "diff_image": null, "rms": null, @@ -111,7 +111,7 @@ }, "subtests.subtest.test_hdiff_idiff_tolerance": { "status": "failed", - "status_msg": "Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_idiff_tolerance.\n\nImage comparison test\n---------------------\nError: Image files did not match.\n RMS Value: 29.260332173249314\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_hybrid0/results/subtests.subtest.test_hdiff_idiff_tolerance/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_hybrid0/results/subtests.subtest.test_hdiff_idiff_tolerance/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_hybrid0/results/subtests.subtest.test_hdiff_idiff_tolerance/result-failed-diff.png\n Tolerance: \n 3", + "status_msg": "REGEX:Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library .*\\.json for test subtests\\.subtest\\.test_hdiff_idiff_tolerance\\.\n\nImage comparison test\n---------------------\nError: Image files did not match\\.\n RMS Value: 29\\.2[0-9]*\n Expected: \n .*baseline\\.png\n Actual: \n .*result\\.png\n Difference:\n .*result-failed-diff\\.png\n Tolerance: \n 3", "baseline_image": "subtests.subtest.test_hdiff_idiff_tolerance/baseline.png", "diff_image": "subtests.subtest.test_hdiff_idiff_tolerance/result-failed-diff.png", "rms": 29.260332173249314, @@ -122,7 +122,7 @@ }, "subtests.subtest.test_hdiff_imatch_savefig": { "status": "failed", - "status_msg": "Hash 5dc1c2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 doesn't match hash d1ffc2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_savefig.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded.", + "status_msg": "REGEX:Hash 5dc1c2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 doesn't match hash d1ffc2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 in library .*\\.json for test subtests\\.subtest\\.test_hdiff_imatch_savefig\\.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded\\.", "baseline_image": "subtests.subtest.test_hdiff_imatch_savefig/baseline.png", "diff_image": null, "rms": null, @@ -133,7 +133,7 @@ }, "subtests.subtest.test_hdiff_imatch_style": { "status": "failed", - "status_msg": "Hash 185ed1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 doesn't match hash d1ffd1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_style.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded.", + "status_msg": "REGEX:Hash 185ed1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 doesn't match hash d1ffd1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 in library .*\\.json for test subtests\\.subtest\\.test_hdiff_imatch_style\\.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded\\.", "baseline_image": "subtests.subtest.test_hdiff_imatch_style/baseline.png", "diff_image": null, "rms": null, @@ -144,7 +144,7 @@ }, "subtests.subtest.test_hdiff_imatch_removetext": { "status": "failed", - "status_msg": "Hash be5af83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e doesn't match hash d1fff83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_removetext.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded.", + "status_msg": "REGEX:Hash be5af83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e doesn't match hash d1fff83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e in library .*\\.json for test subtests\\.subtest\\.test_hdiff_imatch_removetext\\.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded\\.", "baseline_image": "subtests.subtest.test_hdiff_imatch_removetext/baseline.png", "diff_image": null, "rms": null, @@ -153,4 +153,4 @@ "baseline_hash": "d1fff83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e", "result_hash": "be5af83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e" } -} \ No newline at end of file +} diff --git a/tests/subtests/summaries/test_results_always.json b/tests/subtests/summaries/test_results_always.json index 023e2ebd..c42fee29 100644 --- a/tests/subtests/summaries/test_results_always.json +++ b/tests/subtests/summaries/test_results_always.json @@ -12,7 +12,7 @@ }, "subtests.subtest.test_hmatch_idiff": { "status": "passed", - "status_msg": "Test hash matches baseline hash.\n\nImage comparison test\n---------------------\nError: Image files did not match.\n RMS Value: 11.100353848213828\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hmatch_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hmatch_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hmatch_idiff/result-failed-diff.png\n Tolerance: \n 2", + "status_msg": "REGEX:Test hash matches baseline hash\\.\n\nImage comparison test\n---------------------\nError: Image files did not match\\.\n RMS Value: 11\\.1[0-9]*\n Expected: \n .*baseline\\.png\n Actual: \n .*result\\.png\n Difference:\n .*result-failed-diff\\.png\n Tolerance: \n 2", "baseline_image": "subtests.subtest.test_hmatch_idiff/baseline.png", "diff_image": "subtests.subtest.test_hmatch_idiff/result-failed-diff.png", "rms": 11.100353848213828, @@ -23,7 +23,7 @@ }, "subtests.subtest.test_hmatch_imissing": { "status": "passed", - "status_msg": "Test hash matches baseline hash.\n\nImage comparison test\n---------------------\nImage file not found for comparison test in: \n\t/Users/conor/Documents/pytest-mpl/tests/subtests/baseline\n(This is expected for new tests.)\nGenerated Image: \n\t/private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hmatch_imissing/result.png", + "status_msg": "REGEX:Test hash matches baseline hash\\.\n\nImage comparison test\n---------------------\nImage file not found for comparison test in: \n\t.*\n\\(This is expected for new tests\\.\\)\nGenerated Image: \n\t.*result\\.png", "baseline_image": null, "diff_image": null, "rms": null, @@ -34,7 +34,7 @@ }, "subtests.subtest.test_hdiff_imatch": { "status": "failed", - "status_msg": "Hash 6e2fdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 doesn't match hash d1ffdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded.", + "status_msg": "REGEX:Hash 6e2fdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 doesn't match hash d1ffdde5a6682dc6abba7121f5df702c3664b1ce09593534fc0d7c3514eb07e1 in library .*\\.json for test subtests\\.subtest\\.test_hdiff_imatch\\.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded\\.", "baseline_image": "subtests.subtest.test_hdiff_imatch/baseline.png", "diff_image": null, "rms": null, @@ -45,7 +45,7 @@ }, "subtests.subtest.test_hdiff_idiff": { "status": "failed", - "status_msg": "Hash 443361bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 doesn't match hash d1ff61bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_idiff.\n\nImage comparison test\n---------------------\nError: Image files did not match.\n RMS Value: 11.182677079602481\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hdiff_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hdiff_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hdiff_idiff/result-failed-diff.png\n Tolerance: \n 2", + "status_msg": "REGEX:Hash 443361bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 doesn't match hash d1ff61bdd0efd1cdd343eabf73af6f20439d4834ab5503a574ac7ec28e0c2b43 in library .*\\.json for test subtests\\.subtest\\.test_hdiff_idiff\\.\n\nImage comparison test\n---------------------\nError: Image files did not match\\.\n RMS Value: 11\\.1[0-9]*\n Expected: \n .*baseline\\.png\n Actual: \n .*result\\.png\n Difference:\n .*result-failed-diff\\.png\n Tolerance: \n 2", "baseline_image": "subtests.subtest.test_hdiff_idiff/baseline.png", "diff_image": "subtests.subtest.test_hdiff_idiff/result-failed-diff.png", "rms": 11.182677079602481, @@ -56,7 +56,7 @@ }, "subtests.subtest.test_hdiff_imissing": { "status": "failed", - "status_msg": "Hash 301e63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e doesn't match hash d1ff63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imissing.\n\nImage comparison test\n---------------------\nImage file not found for comparison test in: \n\t/Users/conor/Documents/pytest-mpl/tests/subtests/baseline\n(This is expected for new tests.)\nGenerated Image: \n\t/private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hdiff_imissing/result.png", + "status_msg": "REGEX:Hash 301e63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e doesn't match hash d1ff63d656d7a586cc4e498bc32b970f8cb7c7c47bbd2fec33b931219fc0690e in library .*\\.json for test subtests\\.subtest\\.test_hdiff_imissing\\.\n\nImage comparison test\n---------------------\nImage file not found for comparison test in: \n\t.*\n\\(This is expected for new tests\\.\\)\nGenerated Image: \n\t.*result\\.png", "baseline_image": null, "diff_image": null, "rms": null, @@ -67,7 +67,7 @@ }, "subtests.subtest.test_hmissing_imatch": { "status": "failed", - "status_msg": "Hash for test 'subtests.subtest.test_hmissing_imatch' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is eabd8a2e22afd88682990bfb8e4a0700a942fe68e5114e8da4ab6bd93c47b824.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded.", + "status_msg": "REGEX:Hash for test 'subtests\\.subtest\\.test_hmissing_imatch' not found in .*\\.json\\. Generated hash is eabd8a2e22afd88682990bfb8e4a0700a942fe68e5114e8da4ab6bd93c47b824\\.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded\\.", "baseline_image": "subtests.subtest.test_hmissing_imatch/baseline.png", "diff_image": null, "rms": null, @@ -78,7 +78,7 @@ }, "subtests.subtest.test_hmissing_idiff": { "status": "failed", - "status_msg": "Hash for test 'subtests.subtest.test_hmissing_idiff' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is e69570c4a70b2cc88ddee0f0a82312cae4f394b7f62e5760245feda1364c03ab.\n\nImage comparison test\n---------------------\nError: Image files did not match.\n RMS Value: 12.12938597648977\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hmissing_idiff/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hmissing_idiff/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hmissing_idiff/result-failed-diff.png\n Tolerance: \n 2", + "status_msg": "REGEX:Hash for test 'subtests\\.subtest\\.test_hmissing_idiff' not found in .*\\.json\\. Generated hash is e69570c4a70b2cc88ddee0f0a82312cae4f394b7f62e5760245feda1364c03ab\\.\n\nImage comparison test\n---------------------\nError: Image files did not match\\.\n RMS Value: 12\\.1[0-9]*\n Expected: \n .*baseline\\.png\n Actual: \n .*result\\.png\n Difference:\n .*result-failed-diff\\.png\n Tolerance: \n 2", "baseline_image": "subtests.subtest.test_hmissing_idiff/baseline.png", "diff_image": "subtests.subtest.test_hmissing_idiff/result-failed-diff.png", "rms": 12.12938597648977, @@ -89,7 +89,7 @@ }, "subtests.subtest.test_hmissing_imissing": { "status": "failed", - "status_msg": "Hash for test 'subtests.subtest.test_hmissing_imissing' not found in /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json. Generated hash is 5c8a9c7412e4e098f6f2683ee247c08bd50805a93df4d4b6d8fccf3579b4c56b.\n\nImage comparison test\n---------------------\nImage file not found for comparison test in: \n\t/Users/conor/Documents/pytest-mpl/tests/subtests/baseline\n(This is expected for new tests.)\nGenerated Image: \n\t/private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hmissing_imissing/result.png", + "status_msg": "REGEX:Hash for test 'subtests\\.subtest\\.test_hmissing_imissing' not found in .*\\.json\\. Generated hash is 5c8a9c7412e4e098f6f2683ee247c08bd50805a93df4d4b6d8fccf3579b4c56b\\.\n\nImage comparison test\n---------------------\nImage file not found for comparison test in: \n\t.*\n\\(This is expected for new tests\\.\\)\nGenerated Image: \n\t.*result\\.png", "baseline_image": null, "diff_image": null, "rms": null, @@ -100,7 +100,7 @@ }, "subtests.subtest.test_hdiff_imatch_tolerance": { "status": "failed", - "status_msg": "Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_tolerance.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded.", + "status_msg": "REGEX:Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library .*\\.json for test subtests\\.subtest\\.test_hdiff_imatch_tolerance\\.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded\\.", "baseline_image": "subtests.subtest.test_hdiff_imatch_tolerance/baseline.png", "diff_image": null, "rms": null, @@ -111,7 +111,7 @@ }, "subtests.subtest.test_hdiff_idiff_tolerance": { "status": "failed", - "status_msg": "Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_idiff_tolerance.\n\nImage comparison test\n---------------------\nError: Image files did not match.\n RMS Value: 29.260332173249314\n Expected: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hdiff_idiff_tolerance/baseline.png\n Actual: \n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hdiff_idiff_tolerance/result.png\n Difference:\n /private/var/folders/28/h4xnr2f56qbcdlrntr3z54f80000gp/T/pytest-of-conor/pytest-127/test_basic_html0/results/subtests.subtest.test_hdiff_idiff_tolerance/result-failed-diff.png\n Tolerance: \n 3", + "status_msg": "REGEX:Hash aaf4e85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 doesn't match hash d1ffe85fda98298347c274adae98ca7728f9bb2444ca8a49295145b0727b8c96 in library .*\\.json for test subtests\\.subtest\\.test_hdiff_idiff_tolerance\\.\n\nImage comparison test\n---------------------\nError: Image files did not match\\.\n RMS Value: 29\\.2[0-9]*\n Expected: \n .*baseline\\.png\n Actual: \n .*result\\.png\n Difference:\n .*result-failed-diff\\.png\n Tolerance: \n 3", "baseline_image": "subtests.subtest.test_hdiff_idiff_tolerance/baseline.png", "diff_image": "subtests.subtest.test_hdiff_idiff_tolerance/result-failed-diff.png", "rms": 29.260332173249314, @@ -122,7 +122,7 @@ }, "subtests.subtest.test_hdiff_imatch_savefig": { "status": "failed", - "status_msg": "Hash 5dc1c2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 doesn't match hash d1ffc2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_savefig.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded.", + "status_msg": "REGEX:Hash 5dc1c2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 doesn't match hash d1ffc2c68c2d34c03a89ab394e3c11349b76594d0c8837374daef299ac227568 in library .*\\.json for test subtests\\.subtest\\.test_hdiff_imatch_savefig\\.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded\\.", "baseline_image": "subtests.subtest.test_hdiff_imatch_savefig/baseline.png", "diff_image": null, "rms": null, @@ -133,7 +133,7 @@ }, "subtests.subtest.test_hdiff_imatch_style": { "status": "failed", - "status_msg": "Hash 185ed1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 doesn't match hash d1ffd1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_style.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded.", + "status_msg": "REGEX:Hash 185ed1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 doesn't match hash d1ffd1b702c7bbd810370b12e46ecea4b9c9eb87b743397f1d4a50177e7ba7f7 in library .*\\.json for test subtests\\.subtest\\.test_hdiff_imatch_style\\.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded\\.", "baseline_image": "subtests.subtest.test_hdiff_imatch_style/baseline.png", "diff_image": null, "rms": null, @@ -144,7 +144,7 @@ }, "subtests.subtest.test_hdiff_imatch_removetext": { "status": "failed", - "status_msg": "Hash be5af83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e doesn't match hash d1fff83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e in library /Users/conor/Documents/pytest-mpl/tests/subtests/hashes/mpl34_ft261.json for test subtests.subtest.test_hdiff_imatch_removetext.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded.", + "status_msg": "REGEX:Hash be5af83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e doesn't match hash d1fff83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e in library .*\\.json for test subtests\\.subtest\\.test_hdiff_imatch_removetext\\.\n\nImage comparison test\n---------------------\nThe comparison to the baseline image succeeded\\.", "baseline_image": "subtests.subtest.test_hdiff_imatch_removetext/baseline.png", "diff_image": null, "rms": null, @@ -153,4 +153,4 @@ "baseline_hash": "d1fff83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e", "result_hash": "be5af83a43cb89f5e13923f532fe5c9bedbf7d13585533efef2f4051c4968b5e" } -} \ No newline at end of file +} From 1cb3d4e3be89c27f73d74342a47379bc8b717f13 Mon Sep 17 00:00:00 2001 From: Conor MacBride Date: Sun, 6 Feb 2022 21:45:08 +0000 Subject: [PATCH 21/22] Improved support for hash comparison only HTML summary --- pytest_mpl/summary/templates/filter.html | 4 ++++ .../summary/templates/result_images.html | 18 ++++++++++++------ pytest_mpl/summary/templates/styles.css | 2 +- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/pytest_mpl/summary/templates/filter.html b/pytest_mpl/summary/templates/filter.html index 036ee80b..3cd8432f 100644 --- a/pytest_mpl/summary/templates/filter.html +++ b/pytest_mpl/summary/templates/filter.html @@ -16,7 +16,9 @@
Sort tests by...
{{ sort_option('status-sort', 'status', 'desc', default=true) }} {{ sort_option('collected-sort', 'collected', 'asc') }} {{ sort_option('test-name', 'name') }} + {% if results.warn_missing['baseline_image'] -%} {{ sort_option('rms-sort', 'RMS', 'desc') }} + {%- endif %}
Show tests which have...
@@ -32,11 +34,13 @@
Show tests which have...
{{ filter_option('overall-failed', 'failed') }} {{ filter_option('overall-skipped', 'skipped') }} + {% if results.warn_missing['baseline_image'] -%}
{{ filter_option('image-match', 'matching images') }} {{ filter_option('image-diff', 'differing images') }} {{ filter_option('image-missing', 'no baseline image') }}
+ {%- endif %} {% if results.warn_missing['baseline_hash'] -%}
{{ filter_option('hash-match', 'matching hashes') }} diff --git a/pytest_mpl/summary/templates/result_images.html b/pytest_mpl/summary/templates/result_images.html index 5560b3ae..822bbd46 100644 --- a/pytest_mpl/summary/templates/result_images.html +++ b/pytest_mpl/summary/templates/result_images.html @@ -6,6 +6,7 @@
{{ r.module }}
{{ r.name }}
+ {% if results.warn_missing['baseline_image'] -%}
{% macro image_card(file, name) -%}
@@ -21,6 +22,7 @@
{{ r.name }}
{{ image_card(r.diff_image, 'Diff') }} {{ image_card(r.result_image, 'Result') }}
+ {%- endif %}
@@ -31,19 +33,23 @@
{{ r.name }}
+ {% macro pre_data(id, value, name) -%} + {% filter indent(width=8) -%} +
+ +
{{ value }}
+
+ {%- endfilter %} + {%- endmacro -%} + {% if results.warn_missing['baseline_image'] -%}
{{ r.image_status | image_status_msg }}
- {% macro pre_data(id, value, name) -%} -
- -
{{ value }}
-
- {%- endmacro -%} {{ pre_data('rms', r.rms_str, 'RMS') }} {{ pre_data('tolerance', r.tolerance, 'Tolerance') }}
+ {%- endif %} {% if results.warn_missing['baseline_hash'] -%}
{{ r.hash_status | hash_status_msg }}
diff --git a/pytest_mpl/summary/templates/styles.css b/pytest_mpl/summary/templates/styles.css index 7a07169e..3effb1df 100644 --- a/pytest_mpl/summary/templates/styles.css +++ b/pytest_mpl/summary/templates/styles.css @@ -33,7 +33,7 @@ body.no-hash-test .mpl-hash { display: none; } pre { - white-space: pre-wrap; + white-space: pre-line; } div.result div.status-badge button img { vertical-align: sub; From 33338ba2f8513f4fe62552d4e4232eb9197297ca Mon Sep 17 00:00:00 2001 From: Conor MacBride Date: Mon, 7 Feb 2022 15:26:56 +0000 Subject: [PATCH 22/22] Always record image paths as POSIX --- pytest_mpl/plugin.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pytest_mpl/plugin.py b/pytest_mpl/plugin.py index 17998c32..d147e57c 100644 --- a/pytest_mpl/plugin.py +++ b/pytest_mpl/plugin.py @@ -431,7 +431,7 @@ def compare_image_to_baseline(self, item, fig, result_dir, summary=None): test_image = (result_dir / "result.png").absolute() fig.savefig(str(test_image), **savefig_kwargs) - summary['result_image'] = str(test_image.relative_to(self.results_dir)) + summary['result_image'] = test_image.relative_to(self.results_dir).as_posix() if not os.path.exists(baseline_image_ref): summary['status'] = 'failed' @@ -447,7 +447,7 @@ def compare_image_to_baseline(self, item, fig, result_dir, summary=None): # copy to our tmpdir to be sure to keep them in case of failure baseline_image = (result_dir / "baseline.png").absolute() shutil.copyfile(baseline_image_ref, baseline_image) - summary['baseline_image'] = str(baseline_image.relative_to(self.results_dir)) + summary['baseline_image'] = baseline_image.relative_to(self.results_dir).as_posix() # Compare image size ourselves since the Matplotlib # exception is a bit cryptic in this case and doesn't show @@ -473,7 +473,7 @@ def compare_image_to_baseline(self, item, fig, result_dir, summary=None): summary['status'] = 'failed' summary['rms'] = results['rms'] diff_image = (result_dir / 'result-failed-diff.png').absolute() - summary['diff_image'] = str(diff_image.relative_to(self.results_dir)) + summary['diff_image'] = diff_image.relative_to(self.results_dir).as_posix() template = ['Error: Image files did not match.', 'RMS Value: {rms}', 'Expected: \n {expected}', @@ -527,7 +527,7 @@ def compare_image_to_hash_library(self, item, fig, result_dir, summary=None): # Save the figure for later summary (will be removed later if not needed) test_image = (result_dir / "result.png").absolute() fig.savefig(str(test_image), **savefig_kwargs) - summary['result_image'] = str(test_image.relative_to(self.results_dir)) + summary['result_image'] = test_image.relative_to(self.results_dir).as_posix() # Hybrid mode (hash and image comparison) if self.baseline_directory_specified(item):