From d3d72fc07a35429610afee2ffacd029610627312 Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Mon, 28 Apr 2025 22:25:30 +0200 Subject: [PATCH 1/5] FIX: Move all tests using subprocess to the same worker on windows This is a somewhat wild guess based on https://github.com/matplotlib/matplotlib/issues/29797#issuecomment-2833795708 > which makes me think we are somehow crossing state what launching the subprocesses. Using the `xdist_group` with `--dist=loadgroup` should put all tests of that group to the same worker according to https://pytest-xdist.readthedocs.io/en/stable/distribution.html. I've only added `--dist=loadgroup` to the windows pipelines, so tests on other systems are not affected at all. The first test is to see whether this works in the PR. - But to be sure, it think we would need to put this on master and monitor whether the timeouts disappear. --- azure-pipelines.yml | 1 + lib/matplotlib/tests/test_backend_inline.py | 1 + lib/matplotlib/tests/test_backend_macosx.py | 4 ++++ lib/matplotlib/tests/test_backend_nbagg.py | 1 + lib/matplotlib/tests/test_backend_tk.py | 1 + lib/matplotlib/tests/test_backend_webagg.py | 1 + lib/matplotlib/tests/test_backends_interactive.py | 1 + lib/matplotlib/tests/test_basic.py | 3 +++ lib/matplotlib/tests/test_determinism.py | 2 ++ lib/matplotlib/tests/test_font_manager.py | 2 ++ lib/matplotlib/tests/test_matplotlib.py | 3 +++ lib/matplotlib/tests/test_pickle.py | 2 ++ lib/matplotlib/tests/test_preprocess_data.py | 1 + lib/matplotlib/tests/test_pyplot.py | 2 ++ lib/matplotlib/tests/test_rcparams.py | 3 +++ lib/matplotlib/tests/test_sphinxext.py | 5 +++++ lib/matplotlib/tests/test_texmanager.py | 1 + lib/matplotlib/tests/test_ticker.py | 1 + 18 files changed, 35 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index cc0fbce11377..3e43de70d485 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -149,6 +149,7 @@ stages: PYTHONFAULTHANDLER=1 pytest -rfEsXR -n 2 \ --maxfail=50 --timeout=300 --durations=25 \ + --dist=loadgroup \ --junitxml=junit/test-results.xml --cov-report=xml --cov=lib if [[ $VS_VER == 2022 ]]; then diff --git a/lib/matplotlib/tests/test_backend_inline.py b/lib/matplotlib/tests/test_backend_inline.py index 6f0d67d51756..cdd73d9c9c9f 100644 --- a/lib/matplotlib/tests/test_backend_inline.py +++ b/lib/matplotlib/tests/test_backend_inline.py @@ -12,6 +12,7 @@ pytest.importorskip('matplotlib_inline') +@pytest.mark.xdist_group(name="subprocess") def test_ipynb(): nb_path = Path(__file__).parent / 'test_inline_01.ipynb' diff --git a/lib/matplotlib/tests/test_backend_macosx.py b/lib/matplotlib/tests/test_backend_macosx.py index fe4c9a6fba3c..93cad9c21a1d 100644 --- a/lib/matplotlib/tests/test_backend_macosx.py +++ b/lib/matplotlib/tests/test_backend_macosx.py @@ -25,6 +25,7 @@ def _test_cached_renderer(): @pytest.mark.backend('macosx', skip_on_importerror=True) +@pytest.mark.xdist_group(name="subprocess") def test_cached_renderer(): subprocess_run_helper(_test_cached_renderer, timeout=_test_timeout, extra_env={"MPLBACKEND": "macosx"}) @@ -55,6 +56,7 @@ def new_choose_save_file(title, directory, filename): @pytest.mark.backend('macosx', skip_on_importerror=True) +@pytest.mark.xdist_group(name="subprocess") def test_savefig_rcparam(tmp_path): subprocess_run_helper( _test_savefig_rcparam, timeout=_test_timeout, @@ -62,6 +64,7 @@ def test_savefig_rcparam(tmp_path): @pytest.mark.backend('macosx', skip_on_importerror=True) +@pytest.mark.xdist_group(name="subprocess") def test_ipython(): from matplotlib.testing import ipython_in_subprocess ipython_in_subprocess("osx", {(8, 24): "macosx", (7, 0): "MacOSX"}) @@ -81,6 +84,7 @@ def _test_save_figure_return(): @pytest.mark.backend('macosx', skip_on_importerror=True) +@pytest.mark.xdist_group(name="subprocess") def test_save_figure_return(): subprocess_run_helper(_test_save_figure_return, timeout=_test_timeout, extra_env={"MPLBACKEND": "macosx"}) diff --git a/lib/matplotlib/tests/test_backend_nbagg.py b/lib/matplotlib/tests/test_backend_nbagg.py index 23af88d95086..ea3a2ea9996c 100644 --- a/lib/matplotlib/tests/test_backend_nbagg.py +++ b/lib/matplotlib/tests/test_backend_nbagg.py @@ -13,6 +13,7 @@ # From https://blog.thedataincubator.com/2016/06/testing-jupyter-notebooks/ +@pytest.mark.xdist_group(name="subprocess") def test_ipynb(): nb_path = Path(__file__).parent / 'test_nbagg_01.ipynb' diff --git a/lib/matplotlib/tests/test_backend_tk.py b/lib/matplotlib/tests/test_backend_tk.py index 1210c8c9993e..ff48f5b9bd02 100644 --- a/lib/matplotlib/tests/test_backend_tk.py +++ b/lib/matplotlib/tests/test_backend_tk.py @@ -43,6 +43,7 @@ def _isolated_tk_test(success_count, func=None): sys.platform == 'darwin' and sys.version_info[:2] < (3, 11), reason='Tk version mismatch on Azure macOS CI' ) + @pytest.mark.xdist_group(name="subprocess") @functools.wraps(func) def test_func(): # even if the package exists, may not actually be importable this can diff --git a/lib/matplotlib/tests/test_backend_webagg.py b/lib/matplotlib/tests/test_backend_webagg.py index 1d6769494ef9..7df12240618a 100644 --- a/lib/matplotlib/tests/test_backend_webagg.py +++ b/lib/matplotlib/tests/test_backend_webagg.py @@ -7,6 +7,7 @@ @pytest.mark.parametrize("backend", ["webagg", "nbagg"]) +@pytest.mark.xdist_group(name="subprocess") def test_webagg_fallback(backend): pytest.importorskip("tornado") if backend == "nbagg": diff --git a/lib/matplotlib/tests/test_backends_interactive.py b/lib/matplotlib/tests/test_backends_interactive.py index a27783fa4be1..149ca96f8372 100644 --- a/lib/matplotlib/tests/test_backends_interactive.py +++ b/lib/matplotlib/tests/test_backends_interactive.py @@ -636,6 +636,7 @@ def _fallback_check(): matplotlib.pyplot.figure() +@pytest.mark.xdist_group(name="subprocess") def test_fallback_to_different_backend(): pytest.importorskip("IPython") # Runs the process that caused the GH issue 23770 diff --git a/lib/matplotlib/tests/test_basic.py b/lib/matplotlib/tests/test_basic.py index f6aa1e458555..5db2ede9733f 100644 --- a/lib/matplotlib/tests/test_basic.py +++ b/lib/matplotlib/tests/test_basic.py @@ -3,6 +3,8 @@ import sys import textwrap +import pytest + from matplotlib.testing import subprocess_run_for_testing @@ -28,6 +30,7 @@ def test_override_builtins(): assert overridden <= ok_to_override +@pytest.mark.xdist_group(name="subprocess") def test_lazy_imports(): source = textwrap.dedent(""" import sys diff --git a/lib/matplotlib/tests/test_determinism.py b/lib/matplotlib/tests/test_determinism.py index 2ecc40dbd3c0..4e713e24e67c 100644 --- a/lib/matplotlib/tests/test_determinism.py +++ b/lib/matplotlib/tests/test_determinism.py @@ -154,6 +154,7 @@ def draw(self, renderer=None): pytest.param("mhip", "svg", True, marks=needs_usetex), ] ) +@pytest.mark.xdist_group(name="subprocess") def test_determinism_check(objects, fmt, usetex): """ Output three times the same graphs and checks that the outputs are exactly @@ -195,6 +196,7 @@ def test_determinism_check(objects, fmt, usetex): ("ps", b"%%CreationDate: Sat Jan 01 00:00:00 2000"), ] ) +@pytest.mark.xdist_group(name="subprocess") def test_determinism_source_date_epoch(fmt, string): """ Test SOURCE_DATE_EPOCH support. Output a document with the environment diff --git a/lib/matplotlib/tests/test_font_manager.py b/lib/matplotlib/tests/test_font_manager.py index d15b892b3eea..3ae2d8bd7100 100644 --- a/lib/matplotlib/tests/test_font_manager.py +++ b/lib/matplotlib/tests/test_font_manager.py @@ -282,12 +282,14 @@ def bad_idea(n): raise RuntimeError("thread failed to join") +@pytest.mark.xdist_group(name="subprocess") def test_fontcache_thread_safe(): pytest.importorskip('threading') subprocess_run_helper(_test_threading, timeout=10) +@pytest.mark.xdist_group(name="subprocess") def test_lockfilefailure(tmp_path): # The logic here: # 1. get a temp directory from pytest diff --git a/lib/matplotlib/tests/test_matplotlib.py b/lib/matplotlib/tests/test_matplotlib.py index 37b41fafdb78..863c7c350c7f 100644 --- a/lib/matplotlib/tests/test_matplotlib.py +++ b/lib/matplotlib/tests/test_matplotlib.py @@ -22,6 +22,7 @@ def test_parse_to_version_info(version_str, version_tuple): reason="chmod() doesn't work as is on Windows") @pytest.mark.skipif(sys.platform != "win32" and os.geteuid() == 0, reason="chmod() doesn't work as root") +@pytest.mark.xdist_group(name="subprocess") def test_tmpconfigdir_warning(tmp_path): """Test that a warning is emitted if a temporary configdir must be used.""" mode = os.stat(tmp_path).st_mode @@ -36,6 +37,7 @@ def test_tmpconfigdir_warning(tmp_path): os.chmod(tmp_path, mode) +@pytest.mark.xdist_group(name="subprocess") def test_importable_with_no_home(tmp_path): subprocess_run_for_testing( [sys.executable, "-c", @@ -65,6 +67,7 @@ def parse(key): set(backend_registry.list_builtin(BackendFilter.NON_INTERACTIVE))) +@pytest.mark.xdist_group(name="subprocess") def test_importable_with__OO(): """ When using -OO or export PYTHONOPTIMIZE=2, docstrings are discarded, diff --git a/lib/matplotlib/tests/test_pickle.py b/lib/matplotlib/tests/test_pickle.py index 82fc60e186c7..e9094d30c953 100644 --- a/lib/matplotlib/tests/test_pickle.py +++ b/lib/matplotlib/tests/test_pickle.py @@ -136,6 +136,7 @@ def _pickle_load_subprocess(): @mpl.style.context("default") +@pytest.mark.xdist_group(name="subprocess") @check_figures_equal() def test_pickle_load_from_subprocess(fig_test, fig_ref, tmp_path): _generate_complete_test_figure(fig_ref) @@ -331,6 +332,7 @@ def _test_axeswidget_interactive(): sys.platform == 'darwin' and sys.version_info[:2] < (3, 11), reason='Tk version mismatch on Azure macOS CI' ) +@pytest.mark.xdist_group(name="subprocess") def test_axeswidget_interactive(): subprocess_run_helper( _test_axeswidget_interactive, diff --git a/lib/matplotlib/tests/test_preprocess_data.py b/lib/matplotlib/tests/test_preprocess_data.py index c983d78786e1..fd65d1694f5e 100644 --- a/lib/matplotlib/tests/test_preprocess_data.py +++ b/lib/matplotlib/tests/test_preprocess_data.py @@ -245,6 +245,7 @@ def funcy(ax, x, y, z, t=None): funcy.__doc__) +@pytest.mark.xdist_group(name="subprocess") def test_data_parameter_replacement(): """ Test that the docstring contains the correct *data* parameter stub diff --git a/lib/matplotlib/tests/test_pyplot.py b/lib/matplotlib/tests/test_pyplot.py index ab713707bace..db68fbfa0c01 100644 --- a/lib/matplotlib/tests/test_pyplot.py +++ b/lib/matplotlib/tests/test_pyplot.py @@ -11,6 +11,7 @@ from matplotlib import pyplot as plt +@pytest.mark.xdist_group(name="subprocess") def test_pyplot_up_to_date(tmp_path): pytest.importorskip("black") @@ -348,6 +349,7 @@ def test_set_current_axes_on_subfigure(): assert plt.gca() == ax +@pytest.mark.xdist_group(name="subprocess") def test_pylab_integration(): IPython = pytest.importorskip("IPython") mpl.testing.subprocess_run_helper( diff --git a/lib/matplotlib/tests/test_rcparams.py b/lib/matplotlib/tests/test_rcparams.py index 1bc148a83a7e..a52c52d3a4f6 100644 --- a/lib/matplotlib/tests/test_rcparams.py +++ b/lib/matplotlib/tests/test_rcparams.py @@ -529,6 +529,7 @@ def test_rcparams_reset_after_fail(): @pytest.mark.skipif(sys.platform != "linux", reason="Linux only") +@pytest.mark.xdist_group(name="subprocess") def test_backend_fallback_headless_invalid_backend(tmp_path): env = {**os.environ, "DISPLAY": "", "WAYLAND_DISPLAY": "", @@ -546,6 +547,7 @@ def test_backend_fallback_headless_invalid_backend(tmp_path): @pytest.mark.skipif(sys.platform != "linux", reason="Linux only") +@pytest.mark.xdist_group(name="subprocess") def test_backend_fallback_headless_auto_backend(tmp_path): # specify a headless mpl environment, but request a graphical (tk) backend env = {**os.environ, @@ -570,6 +572,7 @@ def test_backend_fallback_headless_auto_backend(tmp_path): @pytest.mark.skipif( sys.platform == "linux" and not _c_internal_utils.xdisplay_is_valid(), reason="headless") +@pytest.mark.xdist_group(name="subprocess") def test_backend_fallback_headful(tmp_path): if parse_version(pytest.__version__) >= parse_version('8.2.0'): pytest_kwargs = dict(exc_type=ImportError) diff --git a/lib/matplotlib/tests/test_sphinxext.py b/lib/matplotlib/tests/test_sphinxext.py index 1aaa6baca47c..816cc01a4e03 100644 --- a/lib/matplotlib/tests/test_sphinxext.py +++ b/lib/matplotlib/tests/test_sphinxext.py @@ -32,6 +32,7 @@ def build_sphinx_html(source_dir, doctree_dir, html_dir, extra_args=None): assert html_dir.is_dir() +@pytest.mark.xdist_group(name="subprocess") def test_tinypages(tmp_path): shutil.copytree(Path(__file__).parent / 'tinypages', tmp_path, dirs_exist_ok=True) @@ -124,6 +125,7 @@ def plot_directive_file(num): assert filecmp.cmp(range_6, plot_file(5)) +@pytest.mark.xdist_group(name="subprocess") def test_plot_html_show_source_link(tmp_path): parent = Path(__file__).parent shutil.copyfile(parent / 'tinypages/conf.py', tmp_path / 'conf.py') @@ -147,6 +149,7 @@ def test_plot_html_show_source_link(tmp_path): @pytest.mark.parametrize('plot_html_show_source_link', [0, 1]) +@pytest.mark.xdist_group(name="subprocess") def test_show_source_link_true(tmp_path, plot_html_show_source_link): # Test that a source link is generated if :show-source-link: is true, # whether or not plot_html_show_source_link is true. @@ -167,6 +170,7 @@ def test_show_source_link_true(tmp_path, plot_html_show_source_link): @pytest.mark.parametrize('plot_html_show_source_link', [0, 1]) +@pytest.mark.xdist_group(name="subprocess") def test_show_source_link_false(tmp_path, plot_html_show_source_link): # Test that a source link is NOT generated if :show-source-link: is false, # whether or not plot_html_show_source_link is true. @@ -186,6 +190,7 @@ def test_show_source_link_false(tmp_path, plot_html_show_source_link): assert len(list(html_dir.glob("**/index-1.py"))) == 0 +@pytest.mark.xdist_group(name="subprocess") def test_srcset_version(tmp_path): shutil.copytree(Path(__file__).parent / 'tinypages', tmp_path, dirs_exist_ok=True) diff --git a/lib/matplotlib/tests/test_texmanager.py b/lib/matplotlib/tests/test_texmanager.py index 64dcbf46456d..bf67ca52fcf2 100644 --- a/lib/matplotlib/tests/test_texmanager.py +++ b/lib/matplotlib/tests/test_texmanager.py @@ -64,6 +64,7 @@ def test_unicode_characters(): @needs_usetex +@pytest.mark.xdist_group(name="subprocess") def test_openin_any_paranoid(): completed = subprocess_run_for_testing( [sys.executable, "-c", diff --git a/lib/matplotlib/tests/test_ticker.py b/lib/matplotlib/tests/test_ticker.py index 0f54230663aa..17fc961220e3 100644 --- a/lib/matplotlib/tests/test_ticker.py +++ b/lib/matplotlib/tests/test_ticker.py @@ -1766,6 +1766,7 @@ def _impl_locale_comma(): assert x == '0,5' +@pytest.mark.xdist_group(name="subprocess") def test_locale_comma(): # On some systems/pytest versions, `pytest.skip` in an exception handler # does not skip, but is treated as an exception, so directly running this From 75ba0d53b260011d8690d999cc63ac441b5afc8b Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Tue, 29 Apr 2025 09:21:05 -0400 Subject: [PATCH 2/5] TST: merge tests that share the same baseline image --- lib/matplotlib/tests/test_patches.py | 42 ++++++++++------------------ 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/lib/matplotlib/tests/test_patches.py b/lib/matplotlib/tests/test_patches.py index 4ed9222eb95e..b0246d669eeb 100644 --- a/lib/matplotlib/tests/test_patches.py +++ b/lib/matplotlib/tests/test_patches.py @@ -825,8 +825,9 @@ def test_annulus(): ax.set_aspect('equal') +@pytest.mark.parametrize('mode', ('a', 'b')) @image_comparison(baseline_images=['annulus'], extensions=['png']) -def test_annulus_setters(): +def test_annulus_setters(mode): fig, ax = plt.subplots() cir = Annulus((0., 0.), 0.2, 0.01, fc='g') # circular annulus @@ -837,36 +838,21 @@ def test_annulus_setters(): ax.set_aspect('equal') cir.center = (0.5, 0.5) - cir.radii = 0.2 + if mode == 'a': + cir.set_semimajor(0.2) + cir.set_semiminor(0.2) + assert cir.radii == (0.2, 0.2) + elif mode == 'b': + cir.radii = 0.2 cir.width = 0.05 ell.center = (0.5, 0.5) - ell.radii = (0.5, 0.3) - ell.width = 0.1 - ell.angle = 45 - - -@image_comparison(baseline_images=['annulus'], extensions=['png']) -def test_annulus_setters2(): - - fig, ax = plt.subplots() - cir = Annulus((0., 0.), 0.2, 0.01, fc='g') # circular annulus - ell = Annulus((0., 0.), (1, 2), 0.1, 0, # elliptical - fc='m', ec='b', alpha=0.5, hatch='xxx') - ax.add_patch(cir) - ax.add_patch(ell) - ax.set_aspect('equal') - - cir.center = (0.5, 0.5) - cir.set_semimajor(0.2) - cir.set_semiminor(0.2) - assert cir.radii == (0.2, 0.2) - cir.width = 0.05 - - ell.center = (0.5, 0.5) - ell.set_semimajor(0.5) - ell.set_semiminor(0.3) - assert ell.radii == (0.5, 0.3) + if mode == 'a': + ell.set_semimajor(0.5) + ell.set_semiminor(0.3) + assert ell.radii == (0.5, 0.3) + elif mode == 'b': + ell.radii = (0.5, 0.3) ell.width = 0.1 ell.angle = 45 From dc401b37d94a8ae19de8f405e7b35da1ada272c5 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Tue, 29 Apr 2025 21:54:12 -0400 Subject: [PATCH 3/5] TST: re-factor a bit and use better parameter names --- lib/matplotlib/tests/test_patches.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/matplotlib/tests/test_patches.py b/lib/matplotlib/tests/test_patches.py index b0246d669eeb..9771db03e330 100644 --- a/lib/matplotlib/tests/test_patches.py +++ b/lib/matplotlib/tests/test_patches.py @@ -825,7 +825,7 @@ def test_annulus(): ax.set_aspect('equal') -@pytest.mark.parametrize('mode', ('a', 'b')) +@pytest.mark.parametrize('mode', ('by_semiaxis', 'by_radius')) @image_comparison(baseline_images=['annulus'], extensions=['png']) def test_annulus_setters(mode): @@ -838,22 +838,23 @@ def test_annulus_setters(mode): ax.set_aspect('equal') cir.center = (0.5, 0.5) - if mode == 'a': + ell.center = (0.5, 0.5) + + if mode == 'by_semiaxis': cir.set_semimajor(0.2) cir.set_semiminor(0.2) assert cir.radii == (0.2, 0.2) - elif mode == 'b': - cir.radii = 0.2 - cir.width = 0.05 - ell.center = (0.5, 0.5) - if mode == 'a': ell.set_semimajor(0.5) ell.set_semiminor(0.3) assert ell.radii == (0.5, 0.3) - elif mode == 'b': + elif mode == 'by_radius': + cir.radii = 0.2 ell.radii = (0.5, 0.3) + + cir.width = 0.05 ell.width = 0.1 + ell.angle = 45 From 13d1e51238225e9efdffbe5dcd35767f6ef775c2 Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Wed, 30 Apr 2025 13:05:17 +0200 Subject: [PATCH 4/5] Limit usage of xdist grouping to Python 3.11 --- azure-pipelines.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 3e43de70d485..9b325a683209 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -147,9 +147,13 @@ stages: fi echo "##vso[task.setvariable variable=VS_COVERAGE_TOOL]$TOOL" + if [[ $(python.version) == 3.11 ]]; then + # when removing this, also remove + # @pytest.mark.xdist_group(name="subprocess") from tests + DIST_LOAD_GROUP=--dist=loadgroup + fi PYTHONFAULTHANDLER=1 pytest -rfEsXR -n 2 \ - --maxfail=50 --timeout=300 --durations=25 \ - --dist=loadgroup \ + --maxfail=50 --timeout=300 --durations=25 $DIST_LOAD_GROUP \ --junitxml=junit/test-results.xml --cov-report=xml --cov=lib if [[ $VS_VER == 2022 ]]; then From 0590a05fdd7c5226a32540186335f60eed48e409 Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Thu, 1 May 2025 10:03:44 +0200 Subject: [PATCH 5/5] Add some more tests using subprocesses to the subprocess xdist_group Note: Some of them have been marked flaky - may be worth reconstructing whether that was due to timeouts. --- lib/matplotlib/tests/test_backends_interactive.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/matplotlib/tests/test_backends_interactive.py b/lib/matplotlib/tests/test_backends_interactive.py index 149ca96f8372..d9c26378d75d 100644 --- a/lib/matplotlib/tests/test_backends_interactive.py +++ b/lib/matplotlib/tests/test_backends_interactive.py @@ -238,6 +238,7 @@ def check_alt_backend(alt_backend): @pytest.mark.parametrize("env", _get_testable_interactive_backends()) @pytest.mark.parametrize("toolbar", ["toolbar2", "toolmanager"]) @pytest.mark.flaky(reruns=3) +@pytest.mark.xdist_group(name="subprocess") def test_interactive_backend(env, toolbar): if env["MPLBACKEND"] == "macosx": if toolbar == "toolmanager": @@ -330,6 +331,7 @@ def _test_thread_impl(): @pytest.mark.parametrize("env", _thread_safe_backends) @pytest.mark.flaky(reruns=3) +@pytest.mark.xdist_group(name="subprocess") def test_interactive_thread_safety(env): proc = _run_helper(_test_thread_impl, timeout=_test_timeout, extra_env=env) assert proc.stdout.count("CloseEvent") == 1 @@ -349,6 +351,7 @@ def _impl_test_lazy_auto_backend_selection(): assert isinstance(bk, str) +@pytest.mark.xdist_group(name="subprocess") def test_lazy_auto_backend_selection(): _run_helper(_impl_test_lazy_auto_backend_selection, timeout=_test_timeout) @@ -381,6 +384,7 @@ def _implcore(): assert 'PyQt5' in sys.modules or 'pyside2' in sys.modules +@pytest.mark.xdist_group(name="subprocess") def test_qt5backends_uses_qt5(): qt5_bindings = [ dep for dep in ['PyQt5', 'pyside2'] @@ -414,6 +418,7 @@ def _impl_missing(): plt.switch_backend("qt5agg") +@pytest.mark.xdist_group(name="subprocess") def test_qt_missing(): _run_helper(_impl_missing, timeout=_test_timeout) @@ -459,6 +464,7 @@ def qt5_and_qt6_pairs(): sys.platform == "linux" and not _c_internal_utils.display_is_valid(), reason="$DISPLAY and $WAYLAND_DISPLAY are unset") @pytest.mark.parametrize('host, mpl', [*qt5_and_qt6_pairs()]) +@pytest.mark.xdist_group(name="subprocess") def test_cross_Qt_imports(host, mpl): try: proc = _run_helper(_impl_test_cross_Qt_imports, host, mpl, @@ -541,6 +547,7 @@ def _lazy_headless(): @pytest.mark.skipif(sys.platform != "linux", reason="this a linux-only test") @pytest.mark.parametrize("env", _get_testable_interactive_backends()) +@pytest.mark.xdist_group(name="subprocess") def test_lazy_linux_headless(env): proc = _run_helper( _lazy_headless, @@ -618,6 +625,7 @@ def _test_number_of_draws_script(): @pytest.mark.parametrize("env", _blit_backends) # subprocesses can struggle to get the display, so rerun a few times @pytest.mark.flaky(reruns=4) +@pytest.mark.xdist_group(name="subprocess") def test_blitting_events(env): proc = _run_helper( _test_number_of_draws_script, timeout=_test_timeout, extra_env=env) @@ -681,6 +689,7 @@ def _impl_test_interactive_timers(): @pytest.mark.parametrize("env", _get_testable_interactive_backends()) +@pytest.mark.xdist_group(name="subprocess") def test_interactive_timers(env): if env["MPLBACKEND"] == "gtk3cairo" and os.getenv("CI"): pytest.skip("gtk3cairo timers do not work in remote CI")