From 9bf26004e386c2484876fb6a7e3fe659f2013b55 Mon Sep 17 00:00:00 2001 From: Peter Corke Date: Sat, 3 Oct 2026 17:42:32 -0400 Subject: [PATCH 1/2] test: commit tests/conftest.py (headless defaults, no GUI popups) The file has been sitting untracked, so it applied on dev machines but never in CI. It sets MPLBACKEND=Agg, QT_QPA_PLATFORM=offscreen and MVTB_TEST_MODE=True (only if unset) and, for every test, replaces the OpenCV window functions and plt.show with no-ops so display code can be exercised without windows opening. Pass/skip results are identical with and without it (946 passed, 95 skipped, same skip list, headless OpenCV 5.0, same working tree). Co-Authored-By: Claude Sonnet 5.5 --- tests/conftest.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 tests/conftest.py diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..5ea1ff02 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,43 @@ +import os + +import pytest + +# Keep test runs headless by default. +os.environ.setdefault("MPLBACKEND", "Agg") +os.environ.setdefault("QT_QPA_PLATFORM", "offscreen") +os.environ.setdefault("MVTB_TEST_MODE", "True") + + +@pytest.fixture(autouse=True) +def _suppress_gui(monkeypatch): + """Disable GUI popups during tests. + + Many tests exercise display code paths; these patches keep behavior testable + without opening OpenCV or Matplotlib windows. + """ + + try: + import cv2 + + if hasattr(cv2, "namedWindow"): + monkeypatch.setattr(cv2, "namedWindow", lambda *args, **kwargs: None) + if hasattr(cv2, "imshow"): + monkeypatch.setattr(cv2, "imshow", lambda *args, **kwargs: None) + if hasattr(cv2, "waitKey"): + monkeypatch.setattr(cv2, "waitKey", lambda *args, **kwargs: -1) + if hasattr(cv2, "destroyWindow"): + monkeypatch.setattr(cv2, "destroyWindow", lambda *args, **kwargs: None) + if hasattr(cv2, "destroyAllWindows"): + monkeypatch.setattr(cv2, "destroyAllWindows", lambda *args, **kwargs: None) + except Exception: + pass + + try: + import matplotlib + + matplotlib.use("Agg", force=True) + import matplotlib.pyplot as plt + + monkeypatch.setattr(plt, "show", lambda *args, **kwargs: None) + except Exception: + pass From b7c63ce79c5d60aad6f224d387fdff887f911ada Mon Sep 17 00:00:00 2001 From: Peter Corke Date: Sat, 3 Oct 2026 17:59:41 -0400 Subject: [PATCH 2/2] test: keep real windows in CI; stub GUI calls only for local runs The OpenCV HighGUI calls (namedWindow/imshow/waitKey/destroyWindow) are the only coverage of the real window path on each OS and OpenCV version, and CI runs them for real today (pytest-xvfb on Linux, desktop sessions on macOS/Windows runners). Stubbing them everywhere would drop that. conftest.py now does nothing when CI is set (GitHub Actions sets CI=true) or MVTB_TEST_REAL_GUI=1; otherwise behaviour is as before. Co-Authored-By: Claude Sonnet 5.5 --- tests/conftest.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 5ea1ff02..2337b164 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,10 +2,23 @@ import pytest -# Keep test runs headless by default. -os.environ.setdefault("MPLBACKEND", "Agg") -os.environ.setdefault("QT_QPA_PLATFORM", "offscreen") -os.environ.setdefault("MVTB_TEST_MODE", "True") + +def _real_gui_wanted() -> bool: + """True when the tests should use real windows, not stubs. + + CI (GitHub Actions sets ``CI=true``) keeps real windows so that the OpenCV + HighGUI calls are exercised on each OS and OpenCV version. Locally, set + ``MVTB_TEST_REAL_GUI=1`` to do the same. + """ + ci = os.environ.get("CI", "").lower() not in ("", "0", "false") + return ci or os.environ.get("MVTB_TEST_REAL_GUI") == "1" + + +# Keep local test runs headless by default. +if not _real_gui_wanted(): + os.environ.setdefault("MPLBACKEND", "Agg") + os.environ.setdefault("QT_QPA_PLATFORM", "offscreen") + os.environ.setdefault("MVTB_TEST_MODE", "True") @pytest.fixture(autouse=True) @@ -13,8 +26,11 @@ def _suppress_gui(monkeypatch): """Disable GUI popups during tests. Many tests exercise display code paths; these patches keep behavior testable - without opening OpenCV or Matplotlib windows. + without opening OpenCV or Matplotlib windows. They are skipped when real + windows are wanted, see :func:`_real_gui_wanted`. """ + if _real_gui_wanted(): + return try: import cv2