BodyBalanceEvaluation/backend/venv/Lib/site-packages/matplotlib/tests/test_determinism.py
2025-07-31 17:23:05 +08:00

219 lines
7.8 KiB
Python

"""
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