Add per-monitor workspace back-and-forth
This commit is contained in:
1 parent
cb6fe73e44
commit
17b8482433
12 files changed
+405
-20
No files matched your search
@@ -0,0 +1,98 @@
|
||||
"""Workspace history against a temporary nested compositor.
|
||||
|
||||
FERESE_TEST_WORKSPACE_HISTORY=1 python3 scripts/tests/test_workspace_history_isolated.py
|
||||
Requires target/debug/ferese, feresectl, dbus-daemon, and a Wayland host.
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import signal
|
||||
import subprocess
|
||||
import tempfile
|
||||
import time
|
||||
import unittest
|
||||
|
||||
|
||||
@unittest.skipUnless(os.environ.get("FERESE_TEST_WORKSPACE_HISTORY") == "1", "nested workspace test is opt-in")
|
||||
class WorkspaceHistoryTest(unittest.TestCase):
|
||||
def test_empty_workspace_history_and_explicit_ipc_selection(self):
|
||||
repo = Path(__file__).resolve().parents[2]
|
||||
with tempfile.TemporaryDirectory(prefix="ferese-workspace-test-") as temporary:
|
||||
root = Path(temporary)
|
||||
runtime = root / "runtime"
|
||||
runtime.mkdir(mode=0o700)
|
||||
config = root / "config/ferese"
|
||||
config.mkdir(parents=True)
|
||||
source = config / "config.kdl"
|
||||
source.write_text("animations { enabled #false; }\n")
|
||||
display = Path(os.environ["WAYLAND_DISPLAY"])
|
||||
if not display.is_absolute():
|
||||
display = Path(os.environ["XDG_RUNTIME_DIR"]) / display
|
||||
env = dict(os.environ, WAYLAND_DISPLAY=str(display), XDG_RUNTIME_DIR=str(runtime),
|
||||
XDG_CONFIG_HOME=str(root / "config"))
|
||||
env.pop("WAYLAND_SOCKET", None)
|
||||
env.pop("FERESE_SHELL_CONTROL_SOCKET", None)
|
||||
children = []
|
||||
|
||||
def launch(command, **kwargs):
|
||||
child = subprocess.Popen(command, env=env, start_new_session=True, **kwargs)
|
||||
children.append(child)
|
||||
return child
|
||||
|
||||
def command(*args):
|
||||
return json.loads(subprocess.check_output(
|
||||
[str(repo / "target/debug/feresectl"), *args], env=env, timeout=5
|
||||
))
|
||||
|
||||
def active():
|
||||
return next(workspace["name"] for workspace in command("get-workspaces") if workspace["active"])
|
||||
|
||||
with (root / "compositor.log").open("w") as log:
|
||||
try:
|
||||
bus = launch(["dbus-daemon", "--session", "--nofork", "--print-address=1"],
|
||||
stdout=subprocess.PIPE, text=True)
|
||||
env["DBUS_SESSION_BUS_ADDRESS"] = bus.stdout.readline().strip()
|
||||
compositor = launch([str(repo / "target/debug/ferese"), "--backend", "nested"],
|
||||
stdout=log, stderr=log)
|
||||
deadline = time.monotonic() + 10
|
||||
while not (runtime / "ferese/control.sock").is_socket():
|
||||
if compositor.poll() is not None or time.monotonic() >= deadline:
|
||||
self.fail((root / "compositor.log").read_text())
|
||||
time.sleep(.025)
|
||||
self.assertEqual(active(), "1")
|
||||
command("workspace-back-and-forth")
|
||||
self.assertEqual(active(), "1", "no history is a no-op")
|
||||
command("workspace", "2")
|
||||
self.assertEqual(active(), "2")
|
||||
self.assertEqual({w["name"] for w in command("get-workspaces")}, {"1", "2"})
|
||||
for expected in ["1", "2", "1", "2"]:
|
||||
command("workspace-back-and-forth")
|
||||
self.assertEqual(active(), expected)
|
||||
command("workspace", "3")
|
||||
self.assertEqual({w["name"] for w in command("get-workspaces")}, {"2", "3"},
|
||||
"old empty history is pruned")
|
||||
replacement = source.with_suffix(".new")
|
||||
replacement.write_text("animations { enabled #false; }\nworkspaces { auto-back-and-forth #true; }\n")
|
||||
replacement.replace(source)
|
||||
command("reload-config")
|
||||
command("workspace", "3")
|
||||
self.assertEqual(active(), "3", "literal IPC selection must not auto-toggle")
|
||||
command("workspace-back-and-forth")
|
||||
self.assertEqual(active(), "2", "reload must preserve history")
|
||||
command("workspace-back-and-forth")
|
||||
self.assertEqual(active(), "3")
|
||||
finally:
|
||||
for child in reversed(children):
|
||||
if child.poll() is None:
|
||||
os.killpg(child.pid, signal.SIGTERM)
|
||||
try:
|
||||
child.wait(timeout=3)
|
||||
except subprocess.TimeoutExpired:
|
||||
os.killpg(child.pid, signal.SIGKILL)
|
||||
child.wait(timeout=3)
|
||||
if child.stdout is not None:
|
||||
child.stdout.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user