Skip to content

Tests

Tests #1481

Workflow file for this run

---
name: Tests
concurrency:
group: ${{ github.workflow }}-${{ github.event.number }}-${{ github.event.ref }}
cancel-in-progress: true
on:
push:
branches-ignore:
- auto-backport-of-pr-[0-9]+
- v[0-9]+.[0-9]+.[0-9x]+-doc
- dependabot/**
pull_request:
branches-ignore:
- v[0-9]+.[0-9]+.[0-9x]+-doc
paths-ignore:
# Skip running tests if changes are only in documentation directories
- 'doc/**'
- 'galleries/**'
schedule:
# 5:47 UTC on Saturdays
- cron: "47 5 * * 6"
workflow_dispatch:
workflow: "*"
env:
NO_AT_BRIDGE: 1 # Necessary for GTK3 interactive test.
OPENBLAS_NUM_THREADS: 1
PYTHONFAULTHANDLER: 1
jobs:
test:
if: >-
github.event_name == 'workflow_dispatch' ||
(
github.repository == 'matplotlib/matplotlib' &&
!contains(github.event.head_commit.message, '[ci skip]') &&
!contains(github.event.head_commit.message, '[skip ci]') &&
!contains(github.event.head_commit.message, '[skip github]') &&
!contains(github.event.head_commit.message, '[ci doc]')
)
permissions:
contents: read
name: "Python ${{ matrix.python-version }} on ${{ matrix.os }} ${{ matrix.name-suffix }}"
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- name-suffix: "(Minimum Versions)"
os: ubuntu-20.04
python-version: '3.10'
extra-requirements: '-c requirements/testing/minver.txt'
delete-font-cache: true
# Oldest versions with Py3.10 wheels.
pyqt5-ver: '==5.15.5 sip==6.3.0'
pyqt6-ver: '==6.2.0 PyQt6-Qt6==6.2.0'
pyside2-ver: '==5.15.2.1'
pyside6-ver: '==6.2.0'
- os: ubuntu-20.04
python-version: '3.10'
# One CI run tests ipython/matplotlib-inline before backend mapping moved to mpl
extra-requirements:
-r requirements/testing/extra.txt
"ipython==7.29.0"
"ipykernel==5.5.6"
"matplotlib-inline<0.1.7"
CFLAGS: "-fno-lto" # Ensure that disabling LTO works.
# https://github.com/matplotlib/matplotlib/pull/26052#issuecomment-1574595954
# https://www.riverbankcomputing.com/pipermail/pyqt/2023-November/045606.html
pyqt6-ver: '!=6.5.1,!=6.6.0,!=6.7.1'
# https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346
pyside6-ver: '!=6.5.1'
- os: ubuntu-22.04
python-version: '3.11'
# https://www.riverbankcomputing.com/pipermail/pyqt/2023-November/045606.html
pyqt6-ver: '!=6.6.0'
# https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346
pyside6-ver: '!=6.5.1'
extra-requirements: '-r requirements/testing/extra.txt'
- os: ubuntu-22.04
python-version: '3.12'
# https://www.riverbankcomputing.com/pipermail/pyqt/2023-November/045606.html
pyqt6-ver: '!=6.6.0'
# https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346
pyside6-ver: '!=6.5.1'
- os: ubuntu-22.04
python-version: '3.13'
# https://www.riverbankcomputing.com/pipermail/pyqt/2023-November/045606.html
pyqt6-ver: '!=6.6.0'
# https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346
pyside6-ver: '!=6.5.1'
- name-suffix: "Free-threaded"
os: ubuntu-22.04
python-version: '3.13t'
# https://www.riverbankcomputing.com/pipermail/pyqt/2023-November/045606.html
pyqt6-ver: '!=6.6.0'
# https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346
pyside6-ver: '!=6.5.1'
- os: macos-12 # This runner is on Intel chips.
python-version: '3.10'
# https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346
pyside6-ver: '!=6.5.1'
- os: macos-14 # This runner is on M1 (arm64) chips.
python-version: '3.12'
# https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346
pyside6-ver: '!=6.5.1'
- os: macos-14 # This runner is on M1 (arm64) chips.
python-version: '3.13'
# https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346
pyside6-ver: '!=6.5.1'
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
if: matrix.python-version != '3.13t'
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Set up Python ${{ matrix.python-version }}
uses: deadsnakes/action@6c8b9b82fe0b4344f4b98f2775fcc395df45e494 # v3.1.0
if: matrix.python-version == '3.13t'
with:
python-version: '3.13'
nogil: true
- name: Install OS dependencies
run: |
case "${{ runner.os }}" in
Linux)
echo 'Acquire::Retries "3";' | sudo tee /etc/apt/apt.conf.d/80-retries
sudo apt-get update -yy
sudo apt-get install -yy --no-install-recommends \
ccache \
cm-super \
dvipng \
ffmpeg \
fonts-freefont-otf \
fonts-noto-cjk \
fonts-wqy-zenhei \
gdb \
gir1.2-gtk-3.0 \
graphviz \
inkscape \
language-pack-de \
lcov \
libcairo2 \
libcairo2-dev \
libffi-dev \
libgeos-dev \
libgirepository1.0-dev \
libsdl2-2.0-0 \
libxkbcommon-x11-0 \
libxcb-cursor0 \
libxcb-icccm4 \
libxcb-image0 \
libxcb-keysyms1 \
libxcb-randr0 \
libxcb-render-util0 \
libxcb-xinerama0 \
lmodern \
ninja-build \
pkg-config \
qtbase5-dev \
texlive-fonts-recommended \
texlive-latex-base \
texlive-latex-extra \
texlive-latex-recommended \
texlive-luatex \
texlive-pictures \
texlive-xetex
if [[ "${{ matrix.python-version }}" = '3.13t' ]]; then
# TODO: Remove this once setup-python supports nogil distributions.
sudo apt-get install -yy --no-install-recommends \
python3.13-tk-nogil
fi
if [[ "${{ matrix.os }}" = ubuntu-20.04 ]]; then
sudo apt-get install -yy --no-install-recommends libopengl0
else # ubuntu-22.04
sudo apt-get install -yy --no-install-recommends \
gir1.2-gtk-4.0 libnotify4
fi
;;
macOS)
brew update
export HOMEBREW_NO_INSTALL_UPGRADE=1 HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1
brew install ccache ghostscript ninja
# The macOS 12 images have an older Python, and this causes homebrew to generate conflicts.
# We'll just skip GTK for now, to not pull in Python updates.
if [[ "${{ matrix.os }}" = macos-14 ]]; then
brew install gobject-introspection gtk4
fi
brew install --cask font-noto-sans-cjk inkscape
;;
esac
- name: Cache pip
uses: actions/cache@v4
if: startsWith(runner.os, 'Linux')
with:
path: ~/.cache/pip
key: ${{ matrix.os }}-py${{ matrix.python-version }}-pip-${{ hashFiles('requirements/*/*.txt') }}
restore-keys: |
${{ matrix.os }}-py${{ matrix.python-version }}-pip-
- name: Cache pip
uses: actions/cache@v4
if: startsWith(runner.os, 'macOS')
with:
path: ~/Library/Caches/pip
key: ${{ matrix.os }}-py${{ matrix.python-version }}-pip-${{ hashFiles('requirements/*/*.txt') }}
restore-keys: |
${{ matrix.os }}-py${{ matrix.python-version }}-pip-
- name: Cache ccache
uses: actions/cache@v4
with:
path: |
~/.ccache
key: ${{ matrix.os }}-py${{ matrix.python-version }}-ccache-${{ hashFiles('src/*') }}
restore-keys: |
${{ matrix.os }}-py${{ matrix.python-version }}-ccache-
- name: Cache Matplotlib
uses: actions/cache@v4
with:
path: |
~/.cache/matplotlib
!~/.cache/matplotlib/tex.cache
!~/.cache/matplotlib/test_cache
key: 4-${{ runner.os }}-py${{ matrix.python-version }}-mpl-${{ github.ref }}-${{ github.sha }}
restore-keys: |
4-${{ runner.os }}-py${{ matrix.python-version }}-mpl-${{ github.ref }}-
4-${{ runner.os }}-py${{ matrix.python-version }}-mpl-
- name: Install the nightly dependencies
if: matrix.python-version == '3.13t'
run: |
python -m pip install pytz tzdata python-dateutil # Must be installed for Pandas.
python -m pip install \
--pre \
--index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple \
--upgrade --only-binary=:all: numpy pandas pillow contourpy
- name: Install Python dependencies
run: |
# Upgrade pip and setuptools and wheel to get as clean an install as
# possible.
python -m pip install --upgrade pip setuptools wheel
# Install pre-release versions during our weekly upcoming dependency tests.
if [[ "${{ github.event_name }}" == 'schedule'
&& "${{ matrix.name-suffix }}" != '(Minimum Versions)' ]]; then
PRE="--pre"
fi
# Install dependencies from PyPI.
# Preinstall build requirements to enable no-build-isolation builds.
python -m pip install --upgrade $PRE \
'contourpy>=1.0.1' cycler fonttools kiwisolver importlib_resources \
numpy packaging pillow 'pyparsing!=3.1.0' python-dateutil setuptools-scm \
'meson-python>=0.13.1' 'pybind11>=2.6' \
-r requirements/testing/all.txt \
${{ matrix.extra-requirements }}
# Install optional dependencies from PyPI.
# Sphinx is needed to run sphinxext tests
python -m pip install --upgrade sphinx!=6.1.2
if [[ "${{ matrix.python-version }}" != '3.13t' ]]; then
# GUI toolkits are pip-installable only for some versions of Python
# so don't fail if we can't install them. Make it easier to check
# whether the install was successful by trying to import the toolkit
# (sometimes, the install appears to be successful but shared
# libraries cannot be loaded at runtime, so an actual import is a
# better check).
python -m pip install --upgrade pycairo 'cairocffi>=0.8' PyGObject &&
(
python -c 'import gi; gi.require_version("Gtk", "4.0"); from gi.repository import Gtk' &&
echo 'PyGObject 4 is available' || echo 'PyGObject 4 is not available'
) && (
python -c 'import gi; gi.require_version("Gtk", "3.0"); from gi.repository import Gtk' &&
echo 'PyGObject 3 is available' || echo 'PyGObject 3 is not available'
)
python -mpip install --upgrade pyqt5${{ matrix.pyqt5-ver }} &&
python -c 'import PyQt5.QtCore' &&
echo 'PyQt5 is available' ||
echo 'PyQt5 is not available'
# Even though PySide2 wheels can be installed on Python 3.12+, they are broken and since PySide2 is
# deprecated, they are unlikely to be fixed. For the same deprecation reason, there are no wheels
# on M1 macOS, so don't bother there either.
if [[ "${{ matrix.os }}" != 'macos-14'
&& "${{ matrix.python-version }}" != '3.12' && "${{ matrix.python-version }}" != '3.13' ]]; then
python -mpip install --upgrade pyside2${{ matrix.pyside2-ver }} &&
python -c 'import PySide2.QtCore' &&
echo 'PySide2 is available' ||
echo 'PySide2 is not available'
fi
python -mpip install --upgrade pyqt6${{ matrix.pyqt6-ver }} &&
python -c 'import PyQt6.QtCore' &&
echo 'PyQt6 is available' ||
echo 'PyQt6 is not available'
python -mpip install --upgrade pyside6${{ matrix.pyside6-ver }} &&
python -c 'import PySide6.QtCore' &&
echo 'PySide6 is available' ||
echo 'PySide6 is not available'
python -mpip install --upgrade \
-f "https://extras.wxpython.org/wxPython4/extras/linux/gtk3/${{ matrix.os }}" \
wxPython &&
python -c 'import wx' &&
echo 'wxPython is available' ||
echo 'wxPython is not available'
fi # Skip backends on Python 3.13t.
- name: Install the nightly dependencies
# Only install the nightly dependencies during the scheduled event
if: github.event_name == 'schedule' && matrix.name-suffix != '(Minimum Versions)'
run: |
python -m pip install pytz tzdata # Must be installed for Pandas.
python -m pip install \
--index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple \
--upgrade --only-binary=:all: numpy pandas
- name: Install Matplotlib
run: |
ccache -s
git describe
# Set flag in a delayed manner to avoid issues with installing other
# packages
if [[ "${{ runner.os }}" == 'macOS' ]]; then
export CPPFLAGS='-fprofile-instr-generate=default.%m.profraw'
export CPPFLAGS="$CPPFLAGS -fcoverage-mapping"
else
export CPPFLAGS='--coverage -fprofile-abs-path'
fi
python -m pip install --no-deps --no-build-isolation --verbose \
--config-settings=setup-args="-DrcParams-backend=Agg" \
--editable .[dev]
if [[ "${{ runner.os }}" != 'macOS' ]]; then
unset CPPFLAGS
fi
- name: Clear font cache
run: |
rm -rf ~/.cache/matplotlib
if: matrix.delete-font-cache
- name: Run pytest
run: |
if [[ "${{ matrix.python-version }}" == '3.13t' ]]; then
export PYTHON_GIL=0
fi
pytest -rfEsXR -n auto \
--maxfail=50 --timeout=300 --durations=25 \
--cov-report=xml --cov=lib --log-level=DEBUG --color=yes
- name: Cleanup non-failed image files
if: failure()
run: |
function remove_files() {
local extension=$1
find ./result_images -type f -name "*-expected*.$extension" | while read file; do
if [[ $file == *"-expected_pdf"* ]]; then
base=${file%-expected_pdf.$extension}_pdf
elif [[ $file == *"-expected_eps"* ]]; then
base=${file%-expected_eps.$extension}_eps
elif [[ $file == *"-expected_svg"* ]]; then
base=${file%-expected_svg.$extension}_svg
else
base=${file%-expected.$extension}
fi
if [[ ! -e "${base}-failed-diff.$extension" ]]; then
if [[ -e "$file" ]]; then
rm "$file"
echo "Removed $file"
fi
if [[ -e "${base}.$extension" ]]; then
rm "${base}.$extension"
echo " Removed ${base}.$extension"
fi
fi
done
}
remove_files "png"; remove_files "svg"; remove_files "pdf"; remove_files "eps";
if [ "$(find ./result_images -mindepth 1 -type d)" ]; then
find ./result_images/* -type d -empty -delete
fi
- name: Filter C coverage
if: ${{ !cancelled() && github.event_name != 'schedule' }}
run: |
if [[ "${{ runner.os }}" != 'macOS' ]]; then
lcov --rc lcov_branch_coverage=1 --capture --directory . \
--output-file coverage.info
lcov --rc lcov_branch_coverage=1 --output-file coverage.info \
--extract coverage.info $PWD/src/'*' $PWD/lib/'*'
lcov --rc lcov_branch_coverage=1 --list coverage.info
find . -name '*.gc*' -delete
else
xcrun llvm-profdata merge -sparse default.*.profraw \
-o default.profdata
xcrun llvm-cov export -format="lcov" build/*/src/*.so \
-instr-profile default.profdata > info.lcov
fi
- name: Upload code coverage
if: ${{ !cancelled() && github.event_name != 'schedule' }}
uses: codecov/codecov-action@v4
with:
name: "${{ matrix.python-version }} ${{ matrix.os }} ${{ matrix.name-suffix }}"
token: ${{ secrets.CODECOV_TOKEN }}
- uses: actions/upload-artifact@v4
if: failure()
with:
name: "${{ matrix.python-version }} ${{ matrix.os }} ${{ matrix.name-suffix }} result images"
path: ./result_images
# Separate dependent job to only upload one issue from the matrix of jobs
create-issue:
if: ${{ failure() && github.event_name == 'schedule' }}
needs: [test]
permissions:
issues: write
runs-on: ubuntu-latest
name: "Create issue on failure"
steps:
- name: Create issue on failure
uses: imjohnbo/issue-bot@v3
with:
title: "[TST] Upcoming dependency test failures"
body: |
The weekly build with nightly wheels from numpy and pandas
has failed. Check the logs for any updates that need to be
made in matplotlib.
https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}
pinned: false
close-previous: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}