22Source: https://github.com/BoboTiG/python-mss.
33"""
44
5+ from __future__ import annotations
6+
7+ import ctypes .util
58import platform
6- from collections .abc import Generator
79from ctypes import CFUNCTYPE , POINTER , _Pointer , c_int
8- from typing import Any
10+ from typing import TYPE_CHECKING , Any
911from unittest .mock import Mock , NonCallableMock , patch
1012
1113import pytest
1214
1315import mss
1416import mss .linux
17+ import mss .linux .xcb
1518import mss .linux .xlib
1619from mss .base import MSSBase
1720from mss .exception import ScreenShotError
1821
22+ if TYPE_CHECKING :
23+ from collections .abc import Generator
24+
1925pyvirtualdisplay = pytest .importorskip ("pyvirtualdisplay" )
2026
2127PYPY = platform .python_implementation () == "PyPy"
@@ -33,6 +39,26 @@ def spy_and_patch(monkeypatch: pytest.MonkeyPatch, obj: Any, name: str) -> Mock:
3339 return spy
3440
3541
42+ @pytest .fixture (autouse = True )
43+ def without_libraries (monkeypatch : pytest .MonkeyPatch , request : pytest .FixtureRequest ) -> Generator [None ]:
44+ marker = request .node .get_closest_marker ("without_libraries" )
45+ if marker is None :
46+ yield None
47+ return
48+ skip_find = frozenset (marker .args )
49+ old_find_library = ctypes .util .find_library
50+
51+ def new_find_library (name : str , * args : list , ** kwargs : dict [str , Any ]) -> str | None :
52+ if name in skip_find :
53+ return None
54+ return old_find_library (name , * args , ** kwargs )
55+
56+ # We use a context here so other fixtures or the test itself can use .undo.
57+ with monkeypatch .context () as mp :
58+ mp .setattr (ctypes .util , "find_library" , new_find_library )
59+ yield None
60+
61+
3662@pytest .fixture
3763def display () -> Generator :
3864 with pyvirtualdisplay .Display (size = (WIDTH , HEIGHT ), color_depth = DEPTH ) as vdisplay :
@@ -84,15 +110,18 @@ def test_arg_display(display: str, backend: str, monkeypatch: pytest.MonkeyPatch
84110 pass
85111
86112 # No `DISPLAY` in envars
87- monkeypatch .delenv ("DISPLAY" )
88- with pytest .raises (ScreenShotError ), mss .mss (backend = backend ):
89- pass
113+ # The monkeypatch implementation of delenv seems to interact badly with some other uses of setenv, so we use a
114+ # monkeypatch context to isolate it a bit.
115+ with monkeypatch .context () as mp :
116+ mp .delenv ("DISPLAY" )
117+ with pytest .raises (ScreenShotError ), mss .mss (backend = backend ):
118+ pass
90119
91120
92121def test_xerror_without_details () -> None :
93122 # Opening an invalid display with the Xlib backend will create an XError instance, but since there was no
94123 # XErrorEvent, then the details won't be filled in. Generate one.
95- with pytest .raises (ScreenShotError ) as excinfo , mss .mss (display = ":INVALID" , backend = "xlib" ):
124+ with pytest .raises (ScreenShotError ) as excinfo , mss .mss (display = ":INVALID" ):
96125 pass
97126
98127 exc = excinfo .value
@@ -102,15 +131,17 @@ def test_xerror_without_details() -> None:
102131 str (exc )
103132
104133
134+ @pytest .mark .without_libraries ("xcb" )
105135@patch ("mss.linux.xlib._X11" , new = None )
106- def test_no_xlib_library () -> None :
107- with pytest .raises (ScreenShotError ), mss .mss (backend = "xlib" ):
136+ def test_no_xlib_library (backend : str ) -> None :
137+ with pytest .raises (ScreenShotError ), mss .mss (backend = backend ):
108138 pass
109139
110140
141+ @pytest .mark .without_libraries ("xcb-randr" )
111142@patch ("mss.linux.xlib._XRANDR" , new = None )
112- def test_no_xrandr_extension () -> None :
113- with pytest .raises (ScreenShotError ), mss .mss (backend = "xlib" ):
143+ def test_no_xrandr_extension (backend : str ) -> None :
144+ with pytest .raises (ScreenShotError ), mss .mss (backend = backend ):
114145 pass
115146
116147
@@ -132,9 +163,6 @@ def test_unsupported_depth(backend: str) -> None:
132163def test_region_out_of_monitor_bounds (display : str , backend : str ) -> None :
133164 monitor = {"left" : - 30 , "top" : 0 , "width" : WIDTH , "height" : HEIGHT }
134165
135- if backend == "xlib" :
136- assert not mss .linux .xlib ._ERROR
137-
138166 with mss .mss (display = display , backend = backend , with_cursor = True ) as sct :
139167 # At one point, I had accidentally been reporting the resource ID as a CData object instead of the contained
140168 # int. This is to make sure I don't repeat that mistake. That said, change this error regex if needed to keep
@@ -164,9 +192,6 @@ def test_region_out_of_monitor_bounds(display: str, backend: str) -> None:
164192 if backend == "xlib" :
165193 assert not mss .linux .xlib ._ERROR
166194
167- if backend == "xlib" :
168- assert not mss .linux .xlib ._ERROR
169-
170195
171196def test__is_extension_enabled_unknown_name (display : str ) -> None :
172197 with mss .mss (display = display , backend = "xlib" ) as sct :
0 commit comments