Files
hermes-agent/tests/tools/test_bot_desktop_thumbnail.py
teknium1 c1d996a84d fix(bot-screen): serialise the XAUTHORITY swap around thumbnail grabs
thumbnail_data_url swaps the process-wide os.environ["XAUTHORITY"] around
ImageGrab.grab; a multiplexed gateway thumbnails several profiles from worker
threads at once, so one grab could run with another profile's cookie (Xlib
auth failure or the wrong screen) and the restore left the wrong value
behind. A module lock serialises swap+grab+restore.

(cherry picked from commit be789c85fdbae869a31d63f30a3434f03d350a50)
2026-09-12 18:58:39 -07:00

42 lines
1.4 KiB
Python

"""``thumbnail_data_url`` swaps the process-wide XAUTHORITY around the grab; two profiles grabbed on
worker threads at once must each see their own cookie file, not the other's."""
from __future__ import annotations
import os
import threading
from PIL import Image, ImageGrab
from tools.bot_desktop import runtime, thumbnail
def test_concurrent_grabs_each_see_their_own_xauthority(monkeypatch):
envs = {"a": {"DISPLAY": ":91", "XAUTHORITY": "/tmp/xauth-a"},
"b": {"DISPLAY": ":92", "XAUTHORITY": "/tmp/xauth-b"}}
local = threading.local()
monkeypatch.setattr(runtime, "published_env", lambda: envs[local.profile])
monkeypatch.setattr(runtime, "_launcher_pid", lambda: 4242)
monkeypatch.delenv("XAUTHORITY", raising=False)
seen = {}
start = threading.Barrier(2)
def fake_grab(xdisplay=None):
seen[xdisplay] = os.environ.get("XAUTHORITY")
threading.Event().wait(0.05) # hold the env long enough for the other thread to collide
return Image.new("RGB", (8, 8))
monkeypatch.setattr(ImageGrab, "grab", fake_grab)
def worker(profile):
local.profile = profile
start.wait()
thumbnail.thumbnail_data_url()
threads = [threading.Thread(target=worker, args=(p,)) for p in envs]
for t in threads:
t.start()
for t in threads:
t.join(5)
assert seen == {":91": "/tmp/xauth-a", ":92": "/tmp/xauth-b"}
assert "XAUTHORITY" not in os.environ