"""Tests for screenshot.py — the pure helpers behind Wayland screenshot capture. The script is mostly thin wrappers around grim/hyprctl (I/O boundaries, verified functionally). The logic worth testing is pure: size parsing, grim geometry strings, window matching, the Hyprland exec-rule body, and floating-window centering. Those are imported and exercised directly here; the subprocess wrappers and the capture orchestration are not unit-tested. """ import sys from pathlib import Path import pytest sys.path.insert(0, str(Path(__file__).parent.parent)) import screenshot # noqa: E402 # ------------------------------- parse_size ---------------------------------- def test_parse_size_normal(): assert screenshot.parse_size("1600x1000") == (1600, 1000) def test_parse_size_boundary_zero_and_one(): assert screenshot.parse_size("0x0") == (0, 0) assert screenshot.parse_size("1x1") == (1, 1) @pytest.mark.parametrize("bad", ["", "bad", "100", "100x", "x100", "100X100", " 1x1"]) def test_parse_size_error_malformed_dies(bad): with pytest.raises(SystemExit): screenshot.parse_size(bad) # ------------------------------ geometry_str --------------------------------- def test_geometry_str_normal(): assert screenshot.geometry_str({"at": [10, 20], "size": [800, 600]}) == "10,20 800x600" def test_geometry_str_boundary_origin_and_unit(): assert screenshot.geometry_str({"at": [0, 0], "size": [1, 1]}) == "0,0 1x1" # ------------------------------ match_windows -------------------------------- CLIENTS = [ {"class": "Emacs", "title": "Emacs 30.2 : agent [.emacs.d]"}, {"class": "firefox", "title": "TrueNAS — Mozilla Firefox"}, {"class": "foot", "title": "foot"}, ] def test_match_windows_normal_by_class(): assert screenshot.match_windows(CLIENTS, "firefox") == [CLIENTS[1]] def test_match_windows_normal_by_title(): assert screenshot.match_windows(CLIENTS, "TrueNAS") == [CLIENTS[1]] def test_match_windows_case_insensitive(): assert screenshot.match_windows(CLIENTS, "EMACS") == [CLIENTS[0]] def test_match_windows_multiple(): # "o" appears in firefox and foot classes assert len(screenshot.match_windows(CLIENTS, "foo")) == 1 assert len(screenshot.match_windows(CLIENTS, "e")) >= 2 # Emacs + firefox (title/class) def test_match_windows_boundary_empty_clients(): assert screenshot.match_windows([], "anything") == [] def test_match_windows_boundary_missing_fields(): # client with no class/title keys must not raise assert screenshot.match_windows([{"class": None, "title": None}], "x") == [] def test_match_windows_error_no_match(): assert screenshot.match_windows(CLIENTS, "nonexistent-zzz") == [] # ------------------------------- launch_rule --------------------------------- def test_launch_rule_tiled_default(): assert screenshot.launch_rule(2, "tiled") == "workspace 2 silent" def test_launch_rule_monocle_adds_fullscreen(): assert screenshot.launch_rule(2, "monocle") == "workspace 2 silent;fullscreen 1" def test_launch_rule_floating_adds_float(): assert screenshot.launch_rule(7, "floating") == "workspace 7 silent;float" # ------------------------------ center_offset -------------------------------- def test_center_offset_normal(): assert screenshot.center_offset(1920, 1080, 800, 600) == (560, 240) def test_center_offset_boundary_exact_fit(): assert screenshot.center_offset(1920, 1080, 1920, 1080) == (0, 0) 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) == []