| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- """
- Test output reproducibility.
- """
- import os
- import sys
- import pytest
- import matplotlib as mpl
- from matplotlib import pyplot as plt
- from matplotlib.cbook import get_sample_data
- from matplotlib.collections import PathCollection
- from matplotlib.image import BboxImage
- from matplotlib.offsetbox import AnchoredOffsetbox, AuxTransformBox
- from matplotlib.patches import Circle, PathPatch
- from matplotlib.path import Path
- from matplotlib.testing import subprocess_run_for_testing
- from matplotlib.testing._markers import needs_ghostscript, needs_usetex
- import matplotlib.testing.compare
- from matplotlib.text import TextPath
- from matplotlib.transforms import IdentityTransform
- def _save_figure(objects='mhip', fmt="pdf", usetex=False):
- mpl.use(fmt)
- mpl.rcParams.update({'svg.hashsalt': 'asdf', 'text.usetex': usetex})
- fig = plt.figure()
- if 'm' in objects:
- # use different markers...
- ax1 = fig.add_subplot(1, 6, 1)
- x = range(10)
- ax1.plot(x, [1] * 10, marker='D')
- ax1.plot(x, [2] * 10, marker='x')
- ax1.plot(x, [3] * 10, marker='^')
- ax1.plot(x, [4] * 10, marker='H')
- ax1.plot(x, [5] * 10, marker='v')
- if 'h' in objects:
- # also use different hatch patterns
- ax2 = fig.add_subplot(1, 6, 2)
- bars = (ax2.bar(range(1, 5), range(1, 5)) +
- ax2.bar(range(1, 5), [6] * 4, bottom=range(1, 5)))
- ax2.set_xticks([1.5, 2.5, 3.5, 4.5])
- patterns = ('-', '+', 'x', '\\', '*', 'o', 'O', '.')
- for bar, pattern in zip(bars, patterns):
- bar.set_hatch(pattern)
- if 'i' in objects:
- # also use different images
- A = [[1, 2, 3], [2, 3, 1], [3, 1, 2]]
- fig.add_subplot(1, 6, 3).imshow(A, interpolation='nearest')
- A = [[1, 3, 2], [1, 2, 3], [3, 1, 2]]
- fig.add_subplot(1, 6, 4).imshow(A, interpolation='bilinear')
- A = [[2, 3, 1], [1, 2, 3], [2, 1, 3]]
- fig.add_subplot(1, 6, 5).imshow(A, interpolation='bicubic')
- if 'p' in objects:
- # clipping support class, copied from demo_text_path.py gallery example
- class PathClippedImagePatch(PathPatch):
- """
- The given image is used to draw the face of the patch. Internally,
- it uses BboxImage whose clippath set to the path of the patch.
- FIXME : The result is currently dpi dependent.
- """
- def __init__(self, path, bbox_image, **kwargs):
- super().__init__(path, **kwargs)
- self.bbox_image = BboxImage(
- self.get_window_extent, norm=None, origin=None)
- self.bbox_image.set_data(bbox_image)
- def set_facecolor(self, color):
- """Simply ignore facecolor."""
- super().set_facecolor("none")
- def draw(self, renderer=None):
- # the clip path must be updated every draw. any solution? -JJ
- self.bbox_image.set_clip_path(self._path, self.get_transform())
- self.bbox_image.draw(renderer)
- super().draw(renderer)
- # add a polar projection
- px = fig.add_subplot(projection="polar")
- pimg = px.imshow([[2]])
- pimg.set_clip_path(Circle((0, 1), radius=0.3333))
- # add a text-based clipping path (origin: demo_text_path.py)
- (ax1, ax2) = fig.subplots(2)
- arr = plt.imread(get_sample_data("grace_hopper.jpg"))
- text_path = TextPath((0, 0), "!?", size=150)
- p = PathClippedImagePatch(text_path, arr, ec="k")
- offsetbox = AuxTransformBox(IdentityTransform())
- offsetbox.add_artist(p)
- ao = AnchoredOffsetbox(loc='upper left', child=offsetbox, frameon=True,
- borderpad=0.2)
- ax1.add_artist(ao)
- # add a 2x2 grid of path-clipped axes (origin: test_artist.py)
- exterior = Path.unit_rectangle().deepcopy()
- exterior.vertices *= 4
- exterior.vertices -= 2
- interior = Path.unit_circle().deepcopy()
- interior.vertices = interior.vertices[::-1]
- clip_path = Path.make_compound_path(exterior, interior)
- star = Path.unit_regular_star(6).deepcopy()
- star.vertices *= 2.6
- (row1, row2) = fig.subplots(2, 2, sharex=True, sharey=True)
- for row in (row1, row2):
- ax1, ax2 = row
- collection = PathCollection([star], lw=5, edgecolor='blue',
- facecolor='red', alpha=0.7, hatch='*')
- collection.set_clip_path(clip_path, ax1.transData)
- ax1.add_collection(collection)
- patch = PathPatch(star, lw=5, edgecolor='blue', facecolor='red',
- alpha=0.7, hatch='*')
- patch.set_clip_path(clip_path, ax2.transData)
- ax2.add_patch(patch)
- ax1.set_xlim([-3, 3])
- ax1.set_ylim([-3, 3])
- x = range(5)
- ax = fig.add_subplot(1, 6, 6)
- ax.plot(x, x)
- ax.set_title('A string $1+2+\\sigma$')
- ax.set_xlabel('A string $1+2+\\sigma$')
- ax.set_ylabel('A string $1+2+\\sigma$')
- stdout = getattr(sys.stdout, 'buffer', sys.stdout)
- fig.savefig(stdout, format=fmt)
- @pytest.mark.parametrize(
- "objects, fmt, usetex", [
- ("", "pdf", False),
- ("m", "pdf", False),
- ("h", "pdf", False),
- ("i", "pdf", False),
- ("mhip", "pdf", False),
- ("mhip", "ps", False),
- pytest.param(
- "mhip", "ps", True, marks=[needs_usetex, needs_ghostscript]),
- ("p", "svg", False),
- ("mhip", "svg", False),
- pytest.param("mhip", "svg", True, marks=needs_usetex),
- ]
- )
- def test_determinism_check(objects, fmt, usetex):
- """
- Output three times the same graphs and checks that the outputs are exactly
- the same.
- Parameters
- ----------
- objects : str
- Objects to be included in the test document: 'm' for markers, 'h' for
- hatch patterns, 'i' for images, and 'p' for paths.
- fmt : {"pdf", "ps", "svg"}
- Output format.
- """
- plots = [
- subprocess_run_for_testing(
- [sys.executable, "-R", "-c",
- f"from matplotlib.tests.test_determinism import _save_figure;"
- f"_save_figure({objects!r}, {fmt!r}, {usetex})"],
- env={**os.environ, "SOURCE_DATE_EPOCH": "946684800",
- "MPLBACKEND": "Agg"},
- text=False, capture_output=True, check=True).stdout
- for _ in range(3)
- ]
- for p in plots[1:]:
- if fmt == "ps" and usetex:
- if p != plots[0]:
- pytest.skip("failed, maybe due to ghostscript timestamps")
- else:
- assert p == plots[0]
- @pytest.mark.parametrize(
- "fmt, string", [
- ("pdf", b"/CreationDate (D:20000101000000Z)"),
- # SOURCE_DATE_EPOCH support is not tested with text.usetex,
- # because the produced timestamp comes from ghostscript:
- # %%CreationDate: D:20000101000000Z00\'00\', and this could change
- # with another ghostscript version.
- ("ps", b"%%CreationDate: Sat Jan 01 00:00:00 2000"),
- ]
- )
- def test_determinism_source_date_epoch(fmt, string):
- """
- Test SOURCE_DATE_EPOCH support. Output a document with the environment
- variable SOURCE_DATE_EPOCH set to 2000-01-01 00:00 UTC and check that the
- document contains the timestamp that corresponds to this date (given as an
- argument).
- Parameters
- ----------
- fmt : {"pdf", "ps", "svg"}
- Output format.
- string : bytes
- Timestamp string for 2000-01-01 00:00 UTC.
- """
- buf = subprocess_run_for_testing(
- [sys.executable, "-R", "-c",
- f"from matplotlib.tests.test_determinism import _save_figure; "
- f"_save_figure('', {fmt!r})"],
- env={**os.environ, "SOURCE_DATE_EPOCH": "946684800",
- "MPLBACKEND": "Agg"}, capture_output=True, text=False, check=True).stdout
- assert string in buf
|