Modernize feresectl and add bounded IPC event streaming

This commit is contained in:
Samuel Onoja committed 2026-10-07 07:39:27 +01:00
1 parent cfdd2a148a
commit 97026a0db7
39 files changed
+1671 -254

No files matched your search

+1 -1
View File
@@ -130,7 +130,7 @@ wait_for_focus() {
local focused
for ((attempt = 0; attempt < 40; attempt += 1)); do
focused="$("$feresectl_program" get-focused-window 2>/dev/null || true)"
focused="$("$feresectl_program" -j focused-window 2>/dev/null || true)"
if [[ "$focused" != "null" && -n "$focused" ]]; then
return 0
fi
+1 -1
View File
@@ -66,7 +66,7 @@ class BlurredPopupTest(unittest.TestCase):
self.fail("Blurred popup stopped presenting frames:\n" + log_path.read_text())
time.sleep(.025)
control = repo / os.environ.get("FERESE_TEST_CTL", "target/debug/feresectl")
subprocess.run([str(control), "get-outputs"], env=env, check=True,
subprocess.run([str(control), "-j", "outputs"], env=env, check=True,
stdout=subprocess.DEVNULL, timeout=5)
phases = log_path.read_text().splitlines()
for name, count in [("parent", 1), ("open", 3), ("update", 9), ("close", 3)]:
+1 -1
View File
@@ -62,7 +62,7 @@ class FloatingSizeTest(unittest.TestCase):
self.assertTrue(sockets, "nested Wayland socket did not appear")
childenv = dict(env, WAYLAND_DISPLAY=str(sockets[0]))
time.sleep(2)
listed = subprocess.run([str(ctl), "get-outputs"], env=childenv,
listed = subprocess.run([str(ctl), "-j", "outputs"], env=childenv,
check=True, capture_output=True, timeout=10, text=True)
enabled = [o for o in json.loads(listed.stdout) if o.get("enabled")]
if not enabled:
@@ -68,7 +68,7 @@ class FloatingPlacementTest(unittest.TestCase):
return child
def command(*args):
return json.loads(subprocess.check_output([str(repo / "target/debug/feresectl"), *args], env=env, timeout=5))
return json.loads(subprocess.check_output([str(repo / "target/debug/feresectl"), "-j", *args], env=env, timeout=5))
with (root / "compositor.log").open("w") as log:
try:
+1 -1
View File
@@ -40,7 +40,7 @@ class FocusHistoryTest(unittest.TestCase):
def command(*args):
return json.loads(subprocess.check_output(
[str(repo / "target/debug/feresectl"), *args], env=env, timeout=5
[str(repo / "target/debug/feresectl"), "-j", *args], env=env, timeout=5
))
def wait_for(predicate):
@@ -116,7 +116,7 @@ def private_checks():
env["WAYLAND_DISPLAY"] = str(next(path for path in runtime.glob("wayland-*") if not path.name.endswith(".lock")))
def call(*args):
return json.loads(subprocess.check_output([str(CTL), *args], env=env, timeout=5))
return json.loads(subprocess.check_output([str(CTL), "-j", *args], env=env, timeout=5))
def inhibited(expected):
def matches():
+1 -1
View File
@@ -151,7 +151,7 @@ def private_checks():
wait(lambda: (runtime / 'ferese/control.sock').exists())
def call(*args):
return json.loads(subprocess.check_output([str(ctl), *args], env=env, timeout=5))
return json.loads(subprocess.check_output([str(ctl), "-j", *args], env=env, timeout=5))
def request(command, args):
payload = json.dumps({'version': 1, 'id': 17, 'type': 'command',
@@ -51,7 +51,7 @@ class OverviewDamageTest(unittest.TestCase):
return child
def command(*args):
return json.loads(subprocess.check_output([str(control), *args], env=env, timeout=5))
return json.loads(subprocess.check_output([str(control), "-j", *args], env=env, timeout=5))
def wait_for(predicate, seconds=12):
deadline = time.monotonic() + seconds
@@ -55,7 +55,7 @@ class OverviewPreviews(unittest.TestCase):
log_path = root / "compositor.log"
def call(*args):
return subprocess.check_output([str(ctl), *args], env=env, timeout=10)
return subprocess.check_output([str(ctl), "-j", *args], env=env, timeout=10)
def wait_for(predicate):
deadline = time.monotonic() + 10
+1 -1
View File
@@ -68,7 +68,7 @@ class Presentation(unittest.TestCase):
env["WAYLAND_DISPLAY"] = str(next(path for path in sockets if not path.name.endswith(".lock")))
def call(*args):
return subprocess.check_output([str(REPO / "target/debug/feresectl"), *args], env=env, timeout=10)
return subprocess.check_output([str(REPO / "target/debug/feresectl"), "-j", *args], env=env, timeout=10)
def windows(count):
deadline = time.monotonic() + 10
@@ -61,7 +61,7 @@ class ResizeDependencies(unittest.TestCase):
client_env = dict(env, WAYLAND_DISPLAY=str(socket))
def call(*args):
return json.loads(subprocess.check_output([str(ctl), *args], env=env, timeout=5))
return json.loads(subprocess.check_output([str(ctl), "-j", *args], env=env, timeout=5))
def action(*args):
subprocess.run([str(ctl), *args], env=env, check=True, capture_output=True, timeout=5)
+1 -1
View File
@@ -20,7 +20,7 @@ FOCUSED_GEOMETRY = '1920,0 2560x1440'
FARESECTL = '''#!/bin/bash
case "$1" in
get-outputs) cat "$TEST_ROOT/outputs.json" ;;
-j) [[ "$2" == outputs && "$#" == 2 ]] || exit 1; cat "$TEST_ROOT/outputs.json" ;;
screenshot)
printf '%s\\n' "$@" >> "$TEST_ROOT/captures"
cat "$TEST_ROOT/capture.png"
+1 -1
View File
@@ -43,7 +43,7 @@ class ThemeTest(unittest.TestCase):
return child
def command(*args):
return json.loads(subprocess.check_output([str(repo / "target/debug/feresectl"), *args], env=env, timeout=5))
return json.loads(subprocess.check_output([str(repo / "target/debug/feresectl"), "-j", *args], env=env, timeout=5))
def until(predicate, seconds=5):
deadline = time.monotonic() + seconds
@@ -100,7 +100,7 @@ class UnmappedToplevelTest(unittest.TestCase):
deadline = time.monotonic() + 3
while True:
result = subprocess.run(
[str(ctl_binary), "get-focused-window"],
[str(ctl_binary), "-j", "focused-window"],
env=env,
capture_output=True,
text=True,
@@ -131,7 +131,7 @@ class UnmappedToplevelTest(unittest.TestCase):
deadline = time.monotonic() + 3
while True:
remaining = json.loads(subprocess.check_output(
[str(ctl_binary), "get-windows"], env=env, text=True
[str(ctl_binary), "-j", "windows"], env=env, text=True
))
if not remaining:
break
@@ -142,7 +142,7 @@ class UnmappedToplevelTest(unittest.TestCase):
subprocess.run([str(ctl_binary), "focus-last-window"], env=env,
capture_output=True, check=True)
focused = json.loads(subprocess.check_output(
[str(ctl_binary), "get-focused-window"], env=env, text=True
[str(ctl_binary), "-j", "focused-window"], env=env, text=True
))
self.assertIsNone(focused, "focus history must not restore a destroyed window")
deadline = time.monotonic() + 3
@@ -69,7 +69,7 @@ class WindowCapture(unittest.TestCase):
env["WAYLAND_DISPLAY"] = str(next(path for path in sockets if not path.name.endswith(".lock")))
def call(*args):
return subprocess.check_output([str(CTL), *args], env=env, timeout=10)
return subprocess.check_output([str(CTL), "-j", *args], env=env, timeout=10)
def windows(count):
deadline = time.monotonic() + 10
@@ -41,7 +41,7 @@ class WorkspaceHistoryTest(unittest.TestCase):
def command(*args):
return json.loads(subprocess.check_output(
[str(repo / "target/debug/feresectl"), *args], env=env, timeout=5
[str(repo / "target/debug/feresectl"), "-j", *args], env=env, timeout=5
))
def active():