Skip to content

Commit 9f7deb0

Browse files
committed
Support for version specific patches
1 parent 5f66e2e commit 9f7deb0

File tree

3 files changed

+92
-12
lines changed

3 files changed

+92
-12
lines changed

tests/subtests/__init__.py

Whitespace-only changes.

tests/subtests/helpers.py

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
import re
2+
import json
23
from pathlib import Path
34

4-
__all__ = ['diff_summary', 'assert_existence']
5+
__all__ = ['diff_summary', 'assert_existence', 'replace_baseline_hash', 'patch_summary']
56

67

78
class MatchError(Exception):
89
pass
910

1011

11-
def diff_summary(baseline, result):
12+
def diff_summary(baseline, result, hash_library=None):
1213
"""Diff a pytest-mpl summary dictionary."""
14+
if hash_library and hash_library.exists():
15+
# Load "correct" baseline hashes
16+
with open(hash_library, 'r') as f:
17+
hash_library = json.load(f)
18+
1319
# Get test names
1420
baseline_tests = set(baseline.keys())
1521
result_tests = set(result.keys())
@@ -23,6 +29,10 @@ def diff_summary(baseline, result):
2329
baseline_summary = baseline[test]
2430
result_summary = result[test]
2531

32+
# Swap the baseline hashes in the summary for the baseline hashes in the hash library
33+
if hash_library:
34+
baseline_summary = replace_baseline_hash(baseline_summary, hash_library[test])
35+
2636
# Get keys of recorded items
2737
baseline_keys = set(baseline_summary.keys())
2838
result_keys = set(result_summary.keys())
@@ -78,6 +88,46 @@ def diff_dict_item(baseline, result, error=''):
7888
raise MatchError(error)
7989

8090

91+
def patch_summary(summary, patch_file):
92+
"""Replace in `summary` any items defined in `patch_file`."""
93+
# By only applying patches, changes between MPL versions are more obvious.
94+
with open(patch_file, 'r') as f:
95+
patch = json.load(f)
96+
for test, test_summary in patch.items():
97+
for k, v in test_summary.items():
98+
summary[test][k] = v
99+
return summary
100+
101+
102+
def replace_baseline_hash(summary, new_baseline):
103+
"""Replace a baseline hash in a pytest-mpl summary with a different baseline.
104+
105+
Result hashes which match the existing baseline are also updated.
106+
107+
Parameters
108+
----------
109+
summary : dict
110+
A single test from a pytest-mpl summary.
111+
new_baseline : str
112+
The new baseline.
113+
"""
114+
assert isinstance(new_baseline, str)
115+
old_baseline = summary['baseline_hash']
116+
if not isinstance(old_baseline, str) or old_baseline == new_baseline:
117+
return summary # Either already correct or missing
118+
119+
# If the old result hash matches the old baseline hash, also update the result hash
120+
old_result = summary['result_hash']
121+
if isinstance(old_result, str) and old_result == old_baseline:
122+
summary['result_hash'] = new_baseline
123+
124+
# Update the baseline hash
125+
summary['baseline_hash'] = new_baseline
126+
summary['status_msg'] = summary['status_msg'].replace(old_baseline, new_baseline)
127+
128+
return summary
129+
130+
81131
def assert_existence(summary, items=('baseline_image', 'diff_image', 'result_image'), path=''):
82132
"""Assert that images included in a pytest-mpl summary exist.
83133

tests/subtests/test_subtest.py

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,36 @@
44
import subprocess
55
from pathlib import Path
66

7-
from helpers import assert_existence, diff_summary
7+
import matplotlib
8+
import matplotlib.ft2font
9+
from packaging.version import Version
10+
11+
from .helpers import assert_existence, diff_summary, patch_summary
12+
13+
# Handle Matplotlib and FreeType versions
14+
MPL_VERSION = Version(matplotlib.__version__)
15+
FTV = matplotlib.ft2font.__freetype_version__.replace('.', '')
16+
VERSION_ID = f"mpl{MPL_VERSION.major}{MPL_VERSION.minor}_ft{FTV}"
17+
HASH_LIBRARY = Path(__file__).parent / 'hashes' / (VERSION_ID + ".json")
18+
HASH_LIBRARY_FLAG = rf'--mpl-hash-library={HASH_LIBRARY}'
819

920
TEST_FILE = Path(__file__).parent / 'subtest.py'
1021

11-
# Set to True to replace existing baseline summaries with current result
12-
UPDATE_BASELINE_SUMMARIES = True
22+
# Global settings to update baselines when running pytest
23+
# Note: when updating baseline make sure you don't commit "fixes"
24+
# for tests that are expected to fail
25+
# (See also `run_subtest` argument `update_baseline` and `update_summary`.)
26+
UPDATE_BASELINE = False # baseline images and hashes
27+
UPDATE_SUMMARY = False # baseline summaries
1328

1429

15-
def run_subtest(baseline_summary, tmp_path, args, summaries=None, xfail=True):
30+
def run_subtest(baseline_summary_name, tmp_path, args, summaries=None, xfail=True,
31+
update_baseline=UPDATE_BASELINE, update_summary=UPDATE_SUMMARY):
1632
""" Run pytest (within pytest) and check JSON summary report.
1733
1834
Parameters
1935
----------
20-
baseline_summary : str
36+
baseline_summary_name : str
2137
String of the filename without extension for the baseline summary.
2238
tmp_path : pathlib.Path
2339
Path of a temporary directory to store results.
@@ -42,10 +58,19 @@ def run_subtest(baseline_summary, tmp_path, args, summaries=None, xfail=True):
4258
pytest_args = [sys.executable, '-m', 'pytest', str(TEST_FILE)]
4359
mpl_args = ['--mpl', rf'--mpl-results-path={results_path.as_posix()}',
4460
f'--mpl-generate-summary={summaries}']
61+
if update_baseline:
62+
mpl_args += ['--mpl-generate-path=baseline']
63+
if HASH_LIBRARY.exists():
64+
mpl_args += [rf'--mpl-generate-hash-library={HASH_LIBRARY}']
4565

4666
# Run the test and record exit status
4767
status = subprocess.call(pytest_args + mpl_args + args)
4868

69+
# If updating baseline, don't check summaries
70+
if update_baseline:
71+
assert status == 0
72+
return
73+
4974
# Ensure exit status is as expected
5075
if xfail:
5176
assert status != 0
@@ -54,18 +79,23 @@ def run_subtest(baseline_summary, tmp_path, args, summaries=None, xfail=True):
5479

5580
# Load summaries
5681
baseline_path = Path(__file__).parent / 'summaries'
57-
baseline_file = baseline_path / (baseline_summary + '.json')
82+
baseline_file = baseline_path / (baseline_summary_name + '.json')
83+
results_file = results_path / 'results.json'
84+
if update_summary:
85+
shutil.copy(results_file, baseline_file)
5886
with open(baseline_file, 'r') as f:
5987
baseline_summary = json.load(f)
60-
results_file = results_path / 'results.json'
6188
with open(results_file, 'r') as f:
6289
result_summary = json.load(f)
6390

64-
if UPDATE_BASELINE_SUMMARIES:
65-
shutil.copy(results_file, baseline_file)
91+
# Apply version specific patches
92+
patch = baseline_path / (baseline_summary_name + f'_{VERSION_ID}.patch.json')
93+
if patch.exists():
94+
baseline_summary = patch_summary(baseline_summary, patch)
95+
# Note: version specific hashes should be handled by diff_summary instead
6696

6797
# Compare summaries
68-
diff_summary(baseline_summary, result_summary)
98+
diff_summary(baseline_summary, result_summary, hash_library=HASH_LIBRARY)
6999

70100
# Ensure reported images exist
71101
assert_existence(result_summary, path=results_path)

0 commit comments

Comments
 (0)