8000 Started implementing support for deterministic figure output by astrofrog · Pull Request #196 · matplotlib/pytest-mpl · GitHub
[go: up one dir, main page]

Skip to content

Started implementing support for deterministic figure output #196

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Apr 3, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Started implementing support for determinstic figure output
  • Loading branch information
astrofrog committed Apr 1, 2023
commit 95d4b9da508716100575b6795421884504726f9a
48 changes: 37 additions & 11 deletions pytest_mpl/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import contextlib
from pathlib import Path
from urllib.request import urlopen
from contextlib import nullcontext

import pytest

Expand Down Expand Up @@ -432,15 +433,13 @@ def generate_baseline_image(self, item, fig):
Generate reference figures.
"""
compare = get_compare(item)
savefig_kwargs = compare.kwargs.get('savefig_kwargs', {})

if not os.path.exists(self.generate_dir):
os.makedirs(self.generate_dir)

baseline_filename = self.generate_filename(item)
baseline_path = (self.generate_dir / baseline_filename).absolute()
fig.savefig(str(baseline_path), **savefig_kwargs)

self.save_figure(item, fig, baseline_path)
close_mpl_figure(fig)

return baseline_path
Expand All @@ -450,13 +449,9 @@ def generate_image_hash(self, item, fig):
For a `matplotlib.figure.Figure`, returns the SHA256 hash as a hexadecimal
string.
"""
compare = get_compare(item)
savefig_kwargs = compare.kwargs.get('savefig_kwargs', {})

ext = self._file_extension(item)

imgdata = io.BytesIO()
fig.savefig(imgdata, **savefig_kwargs)
self.save_figure(item, fig, imgdata)
out = _hash_file(imgdata)
imgdata.close()

Expand All @@ -476,13 +471,14 @@ def compare_image_to_baseline(self, item, fig, result_dir, summary=None):
compare = get_compare(item)
tolerance = compare.kwargs.get('tolerance', 2)
savefig_kwargs = compare.kwargs.get('savefig_kwargs', {})
deterministic = compare.kwargs.get('deterministic', False)

ext = self._file_extension(item)

baseline_image_ref = self.obtain_baseline_image(item, result_dir)

test_image = (result_dir / f"result.{ext}").absolute()
fig.savefig(str(test_image), **savefig_kwargs)
self.save_figure(item, fig, test_image)

if ext in ['png', 'svg']: # Use original file
summary['result_image'] = test_image.relative_to(self.results_dir).as_posix()
Expand Down Expand Up @@ -554,13 +550,43 @@ def load_hash_library(self, library_path):
with open(str(library_path)) as fp:
return json.load(fp)

def save_figure(self, item, fig, filename):
if isinstance(filename, Path):
filename = str(filename)
compare = get_compare(item)
savefig_kwargs = compare.kwargs.get('savefig_kwargs', {})
deterministic = compare.kwargs.get('deterministic', False)

if deterministic:

# Make sure we don't modify the original dictionary in case is a common
# object used by different tests
savefig_kwargs = savefig_kwargs.copy()

if 'metadata' not in savefig_kwargs:
savefig_kwargs['metadata'] = {}

ext = self._file_extension(item)

if ext == 'png':
extra_metadata = {"Software": None}
elif ext == 'pdf':
extra_metadata = {"Creator": None, "Producer": None, "CreationDate": None}
elif ext == 'eps':
extra_metadata = {"Creator": "test"}
elif ext == 'svg':
extra_metadata = {"Date": None}

savefig_kwargs['metadata'].update(extra_metadata)

fig.savefig(filename, **savefig_kwargs)

def compare_image_to_hash_library(self, item, fig, result_dir, summary=None):
hash_comparison_pass = False
if summary is None:
summary = {}

compare = get_compare(item)
savefig_kwargs = compare.kwargs.get('savefig_kwargs', {})

ext = self._file_extension(item)

Expand Down Expand Up @@ -601,7 +627,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 / f"result.{ext}").absolute()
fig.savefig(str(test_image), **savefig_kwargs)
self.save_figure(item, fig, test_image)
summary['result_image'] = test_image.relative_to(self.results_dir).as_posix()

# Hybrid mode (hash and image comparison)
Expand Down
20 changes: 2 additions & 18 deletions tests/test_pytest_mpl.py
Original file line number Diff line number Diff line change
Expand Up @@ -704,15 +704,6 @@ def test_formats(pytester, use_hash_library, passes, file_format):
else:
pytest.skip('Comparing EPS and PDF files requires ghostscript to be installed')

if file_format == 'png':
metadata = '{"Software": None}'
elif file_format == 'pdf':
metadata = '{"Creator": None, "Producer": None, "CreationDate": None}'
elif file_format == 'eps':
metadata = '{"Creator": "test"}'
elif file_format == 'svg':
metadata = '{"Date": None}'

pytester.makepyfile(
f"""
import os
Expand All @@ -721,16 +712,9 @@ def test_formats(pytester, use_hash_library, passes, file_format):
@pytest.mark.mpl_image_compare(baseline_dir=r"{baseline_dir_abs}",
{f'hash_library=r"{hash_library}",' if use_hash_library else ''}
tolerance={DEFAULT_TOLERANCE},
savefig_kwargs={{'format': '{file_format}',
'metadata': {metadata}}})
deterministic=True,
savefig_kwargs={{'format': '{file_format}'}})
def test_format_{file_format}():

# For reproducible EPS output
os.environ['SOURCE_DATE_EPOCH'] = '1680254601'

# For reproducible SVG output
plt.rcParams['svg.hashsalt'] = 'test'

fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.plot([{1 if passes else 3}, 2, 3])
Expand Down
0