175 lines
4.7 KiB
Python
175 lines
4.7 KiB
Python
"""
|
|
Helper functions for testing.
|
|
"""
|
|
from pathlib import Path
|
|
from tempfile import TemporaryDirectory
|
|
import locale
|
|
import logging
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
|
|
import matplotlib as mpl
|
|
from matplotlib import _api
|
|
|
|
_log = logging.getLogger(__name__)
|
|
|
|
|
|
def set_font_settings_for_testing():
|
|
mpl.rcParams['font.family'] = 'DejaVu Sans'
|
|
mpl.rcParams['text.hinting'] = 'none'
|
|
mpl.rcParams['text.hinting_factor'] = 8
|
|
|
|
|
|
def set_reproducibility_for_testing():
|
|
mpl.rcParams['svg.hashsalt'] = 'matplotlib'
|
|
|
|
|
|
def setup():
|
|
# The baseline images are created in this locale, so we should use
|
|
# it during all of the tests.
|
|
|
|
try:
|
|
locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
|
|
except locale.Error:
|
|
try:
|
|
locale.setlocale(locale.LC_ALL, 'English_United States.1252')
|
|
except locale.Error:
|
|
_log.warning(
|
|
"Could not set locale to English/United States. "
|
|
"Some date-related tests may fail.")
|
|
|
|
mpl.use('Agg')
|
|
|
|
with _api.suppress_matplotlib_deprecation_warning():
|
|
mpl.rcdefaults() # Start with all defaults
|
|
|
|
# These settings *must* be hardcoded for running the comparison tests and
|
|
# are not necessarily the default values as specified in rcsetup.py.
|
|
set_font_settings_for_testing()
|
|
set_reproducibility_for_testing()
|
|
|
|
|
|
def subprocess_run_for_testing(command, env=None, timeout=None, stdout=None,
|
|
stderr=None, check=False, text=True,
|
|
capture_output=False):
|
|
"""
|
|
Create and run a subprocess.
|
|
|
|
Thin wrapper around `subprocess.run`, intended for testing. Will
|
|
mark fork() failures on Cygwin as expected failures: not a
|
|
success, but not indicating a problem with the code either.
|
|
|
|
Parameters
|
|
----------
|
|
args : list of str
|
|
env : dict[str, str]
|
|
timeout : float
|
|
stdout, stderr
|
|
check : bool
|
|
text : bool
|
|
Also called ``universal_newlines`` in subprocess. I chose this
|
|
name since the main effect is returning bytes (`False`) vs. str
|
|
(`True`), though it also tries to normalize newlines across
|
|
platforms.
|
|
capture_output : bool
|
|
Set stdout and stderr to subprocess.PIPE
|
|
|
|
Returns
|
|
-------
|
|
proc : subprocess.Popen
|
|
|
|
See Also
|
|
--------
|
|
subprocess.run
|
|
|
|
Raises
|
|
------
|
|
pytest.xfail
|
|
If platform is Cygwin and subprocess reports a fork() failure.
|
|
"""
|
|
if capture_output:
|
|
stdout = stderr = subprocess.PIPE
|
|
try:
|
|
proc = subprocess.run(
|
|
command, env=env,
|
|
timeout=timeout, check=check,
|
|
stdout=stdout, stderr=stderr,
|
|
text=text
|
|
)
|
|
except BlockingIOError:
|
|
if sys.platform == "cygwin":
|
|
# Might want to make this more specific
|
|
import pytest
|
|
pytest.xfail("Fork failure")
|
|
raise
|
|
return proc
|
|
|
|
|
|
def subprocess_run_helper(func, *args, timeout, extra_env=None):
|
|
"""
|
|
Run a function in a sub-process.
|
|
|
|
Parameters
|
|
----------
|
|
func : function
|
|
The function to be run. It must be in a module that is importable.
|
|
*args : str
|
|
Any additional command line arguments to be passed in
|
|
the first argument to ``subprocess.run``.
|
|
extra_env : dict[str, str]
|
|
Any additional environment variables to be set for the subprocess.
|
|
"""
|
|
target = func.__name__
|
|
module = func.__module__
|
|
proc = subprocess_run_for_testing(
|
|
[
|
|
sys.executable,
|
|
"-c",
|
|
f"from {module} import {target}; {target}()",
|
|
*args
|
|
],
|
|
env={**os.environ, "SOURCE_DATE_EPOCH": "0", **(extra_env or {})},
|
|
timeout=timeout, check=True,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
text=True
|
|
)
|
|
return proc
|
|
|
|
|
|
def _check_for_pgf(texsystem):
|
|
"""
|
|
Check if a given TeX system + pgf is available
|
|
|
|
Parameters
|
|
----------
|
|
texsystem : str
|
|
The executable name to check
|
|
"""
|
|
with TemporaryDirectory() as tmpdir:
|
|
tex_path = Path(tmpdir, "test.tex")
|
|
tex_path.write_text(r"""
|
|
\documentclass{article}
|
|
\usepackage{pgf}
|
|
\begin{document}
|
|
\typeout{pgfversion=\pgfversion}
|
|
\makeatletter
|
|
\@@end
|
|
""", encoding="utf-8")
|
|
try:
|
|
subprocess.check_call(
|
|
[texsystem, "-halt-on-error", str(tex_path)], cwd=tmpdir,
|
|
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
except (OSError, subprocess.CalledProcessError):
|
|
return False
|
|
return True
|
|
|
|
|
|
def _has_tex_package(package):
|
|
try:
|
|
mpl.dviread.find_tex_file(f"{package}.sty")
|
|
return True
|
|
except FileNotFoundError:
|
|
return False
|