diff options
| author | Craig Jennings <c@cjennings.net> | 2026-06-11 05:07:42 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-06-11 05:07:42 -0500 |
| commit | 7095d622ab6e295143d1306bdb5c8ecd85cf0745 (patch) | |
| tree | 006df0449c9deded341904e00d2ff315742f0f8d /.ai/scripts/tests | |
| parent | 2ffb01c62b154bac73542da63825a8ab1a17a49c (diff) | |
| download | rulesets-7095d622ab6e295143d1306bdb5c8ecd85cf0745.tar.gz rulesets-7095d622ab6e295143d1306bdb5c8ecd85cf0745.zip | |
fix(scripts): keep screenshot --launch from crashing the compositor
An XWayland client launched by --launch could send a configure request while the script tore down the headless output. Hyprland's damage path then dereferenced the removed monitor and the compositor aborted (Hyprland 0.55.2, coredump analysis in docs/design/).
The fix has two layers. --launch now forces the Wayland backend (DISPLAY unset, GDK and Qt steered to wayland) so no XWayland surface exists to race. Teardown also polls until the launched clients actually unmap before removing the output.
X11-only apps fail to map under the default, and some emacs builds are X11-only. The new --x11 flag allows XWayland for them, protected by the unmap wait. The no-window error hints at the flag.
Diffstat (limited to '.ai/scripts/tests')
| -rw-r--r-- | .ai/scripts/tests/test_screenshot.py | 38 |
1 files changed, 38 insertions, 0 deletions
diff --git a/.ai/scripts/tests/test_screenshot.py b/.ai/scripts/tests/test_screenshot.py index 357caec..2a01eb1 100644 --- a/.ai/scripts/tests/test_screenshot.py +++ b/.ai/scripts/tests/test_screenshot.py @@ -95,3 +95,41 @@ def test_center_offset_boundary_exact_fit(): def test_center_offset_error_window_larger_than_output_clamps(): assert screenshot.center_offset(800, 600, 1000, 700) == (0, 0) + + +# ------------------------------- wayland_cmd --------------------------------- +# --launch must not let the app map via XWayland: an XWayland configure request +# can race the headless-output teardown and crash the compositor. + +def test_wayland_cmd_unsets_display_and_forces_wayland(): + wrapped = screenshot.wayland_cmd("emacs -Q") + assert wrapped.startswith("env -u DISPLAY ") + assert "GDK_BACKEND=wayland" in wrapped + assert "QT_QPA_PLATFORM=wayland" in wrapped + assert wrapped.endswith(" emacs -Q") + +def test_wayland_cmd_boundary_preserves_quoted_args(): + wrapped = screenshot.wayland_cmd("foot -e sh -c 'echo hi'") + assert wrapped.endswith(" foot -e sh -c 'echo hi'") + + +# --------------------------- clients_on_workspace ---------------------------- + +WS_CLIENTS = [ + {"address": "0x1", "workspace": {"id": 3}}, + {"address": "0x2", "workspace": {"id": 5}}, + {"address": "0x3", "workspace": {"id": 3}}, +] + +def test_clients_on_workspace_normal(): + got = screenshot.clients_on_workspace(WS_CLIENTS, 3) + assert [c["address"] for c in got] == ["0x1", "0x3"] + +def test_clients_on_workspace_boundary_empty_list(): + assert screenshot.clients_on_workspace([], 3) == [] + +def test_clients_on_workspace_boundary_missing_workspace_key(): + assert screenshot.clients_on_workspace([{"address": "0x9"}], 3) == [] + +def test_clients_on_workspace_error_no_match(): + assert screenshot.clients_on_workspace(WS_CLIENTS, 99) == [] |
