aboutsummaryrefslogtreecommitdiff
path: root/tests/layout-navigate/test_layout_navigate.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/layout-navigate/test_layout_navigate.py')
-rw-r--r--tests/layout-navigate/test_layout_navigate.py219
1 files changed, 0 insertions, 219 deletions
diff --git a/tests/layout-navigate/test_layout_navigate.py b/tests/layout-navigate/test_layout_navigate.py
deleted file mode 100644
index 41294b7..0000000
--- a/tests/layout-navigate/test_layout_navigate.py
+++ /dev/null
@@ -1,219 +0,0 @@
-"""Tests for dotfiles/hyprland/.local/bin/layout-navigate.
-
-The script is a sh wrapper around `hyprctl` that chooses the right dispatch
-command for the active layout (master/dwindle/scrolling) and for the active
-window's state (floating vs tiled, regular workspace vs special overlay).
-
-Tests invoke the real script with a faked `hyprctl` on PATH. The fake reads
-canned JSON for activewindow/getoption queries and records each dispatch
-call to a log file. Assertions compare the dispatch log to the expected
-sequence for the given scenario — we test behavior (what hyprctl calls
-the script emits), not implementation.
-"""
-
-import json
-import os
-import stat
-import subprocess
-import tempfile
-import unittest
-
-
-REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
-SCRIPT = os.path.join(REPO_ROOT, "dotfiles/hyprland/.local/bin/layout-navigate")
-FAKE_HYPRCTL = os.path.join(os.path.dirname(__file__), "fake-hyprctl")
-
-
-def make_activewindow(floating=False, workspace_name="1", workspace_id=1):
- return {
- "address": "0xabc",
- "floating": bool(floating),
- "workspace": {"id": workspace_id, "name": workspace_name},
- "class": "test",
- "title": "test",
- }
-
-
-def make_layout(name):
- return {"str": name}
-
-
-class LayoutNavigateHarness(unittest.TestCase):
- """Shared harness: run layout-navigate with fake hyprctl, read dispatch log."""
-
- def setUp(self):
- self.tmp = tempfile.mkdtemp(prefix="layout-navigate-test-")
- # Ensure the fake hyprctl is executable
- os.chmod(FAKE_HYPRCTL, os.stat(FAKE_HYPRCTL).st_mode | stat.S_IEXEC)
-
- # Create a bin dir with a symlink to fake-hyprctl named "hyprctl"
- self.bin_dir = os.path.join(self.tmp, "bin")
- os.makedirs(self.bin_dir)
- os.symlink(FAKE_HYPRCTL, os.path.join(self.bin_dir, "hyprctl"))
-
- # Initialize dispatch log
- self.dispatch_log = os.path.join(self.tmp, "dispatch.log")
- open(self.dispatch_log, "w").close()
-
- def tearDown(self):
- import shutil
- shutil.rmtree(self.tmp, ignore_errors=True)
-
- def set_state(self, activewindow, layout, next_activewindow=None):
- with open(os.path.join(self.tmp, "activewindow.json"), "w") as f:
- json.dump(activewindow, f)
- with open(os.path.join(self.tmp, "layout.json"), "w") as f:
- json.dump(layout, f)
- if next_activewindow is not None:
- with open(os.path.join(self.tmp, "activewindow.1.json"), "w") as f:
- json.dump(next_activewindow, f)
-
- def run_script(self, *args):
- # Preserve current PATH so jq, sh, etc. are reachable
- env = os.environ.copy()
- env["PATH"] = self.bin_dir + os.pathsep + env.get("PATH", "")
- env["FAKE_HYPR_DIR"] = self.tmp
- result = subprocess.run(
- [SCRIPT] + list(args),
- env=env,
- capture_output=True,
- text=True,
- timeout=10,
- )
- return result
-
- def dispatches(self):
- with open(self.dispatch_log) as f:
- return [line.rstrip("\n") for line in f if line.strip()]
-
-
-class TestTiledMasterLayout(LayoutNavigateHarness):
- """Characterization: existing behavior for master/dwindle layout on a regular workspace."""
-
- def test_layout_navigate_master_tiled_next_focus_emits_cyclenext(self):
- self.set_state(make_activewindow(), make_layout("master"))
- self.run_script("next")
- self.assertEqual(self.dispatches(), ["dispatch layoutmsg cyclenext"])
-
- def test_layout_navigate_master_tiled_prev_focus_emits_cycleprev(self):
- self.set_state(make_activewindow(), make_layout("master"))
- self.run_script("prev")
- self.assertEqual(self.dispatches(), ["dispatch layoutmsg cycleprev"])
-
- def test_layout_navigate_master_tiled_next_move_emits_swapnext(self):
- self.set_state(make_activewindow(), make_layout("master"))
- self.run_script("next", "move")
- self.assertEqual(self.dispatches(), ["dispatch layoutmsg swapnext"])
-
- def test_layout_navigate_master_tiled_prev_move_emits_swapprev(self):
- self.set_state(make_activewindow(), make_layout("master"))
- self.run_script("prev", "move")
- self.assertEqual(self.dispatches(), ["dispatch layoutmsg swapprev"])
-
-
-class TestScrollingLayout(LayoutNavigateHarness):
- """Characterization: existing behavior for scrolling layout on a regular workspace."""
-
- def test_layout_navigate_scrolling_next_focus_emits_focus_l(self):
- self.set_state(make_activewindow(), make_layout("scrolling"))
- self.run_script("next")
- self.assertEqual(self.dispatches(), ["dispatch layoutmsg focus l"])
-
- def test_layout_navigate_scrolling_next_move_emits_swapwindow_l(self):
- self.set_state(make_activewindow(), make_layout("scrolling"))
- self.run_script("next", "move")
- self.assertEqual(self.dispatches(), ["dispatch swapwindow l"])
-
-
-class TestFloatingOnRegularWorkspace(LayoutNavigateHarness):
- """Characterization: floating window on a regular workspace short-circuits to cyclenext tiled."""
-
- def test_layout_navigate_floating_regular_next_focus_emits_cyclenext_tiled(self):
- self.set_state(make_activewindow(floating=True), make_layout("master"))
- self.run_script("next")
- self.assertEqual(self.dispatches(), ["dispatch cyclenext tiled"])
-
- def test_layout_navigate_floating_regular_prev_focus_emits_cyclenext_prev_tiled(self):
- self.set_state(make_activewindow(floating=True), make_layout("master"))
- self.run_script("prev")
- self.assertEqual(self.dispatches(), ["dispatch cyclenext prev tiled"])
-
-
-class TestTiledInSpecialWorkspace(LayoutNavigateHarness):
- """New behavior: tiled window in a special workspace toggles overlay off, then cycles.
-
- The special:stash overlay (or any special workspace) hides the underlying regular
- workspace. Cycling with layoutmsg only operates within the current workspace, so
- without toggling first, $mod+J gets trapped inside the overlay. Fix: hide the
- overlay, then dispatch the normal cycle — one keypress does both.
- """
-
- def test_layout_navigate_tiled_special_next_focus_toggles_then_cyclenext(self):
- active = make_activewindow(workspace_name="special:stash", workspace_id=-92)
- # Post-toggle, focus lands on a tiled window on regular ws 1
- post = make_activewindow(workspace_name="1", workspace_id=1)
- self.set_state(active, make_layout("master"), next_activewindow=post)
- self.run_script("next")
- self.assertEqual(
- self.dispatches(),
- [
- "dispatch togglespecialworkspace stash",
- "dispatch layoutmsg cyclenext",
- ],
- )
-
- def test_layout_navigate_tiled_special_prev_focus_toggles_then_cycleprev(self):
- active = make_activewindow(workspace_name="special:stash", workspace_id=-92)
- post = make_activewindow(workspace_name="1", workspace_id=1)
- self.set_state(active, make_layout("master"), next_activewindow=post)
- self.run_script("prev")
- self.assertEqual(
- self.dispatches(),
- [
- "dispatch togglespecialworkspace stash",
- "dispatch layoutmsg cycleprev",
- ],
- )
-
- def test_layout_navigate_tiled_special_scrolling_toggles_then_focus_l(self):
- """Toggle-then-cycle must honor the active layout, not hard-code master."""
- active = make_activewindow(workspace_name="special:stash", workspace_id=-92)
- post = make_activewindow(workspace_name="1", workspace_id=1)
- self.set_state(active, make_layout("scrolling"), next_activewindow=post)
- self.run_script("next")
- self.assertEqual(
- self.dispatches(),
- [
- "dispatch togglespecialworkspace stash",
- "dispatch layoutmsg focus l",
- ],
- )
-
- def test_layout_navigate_tiled_special_next_move_does_not_toggle(self):
- """MOVE variant should NOT auto-toggle — moving a window out of a scratchpad
- is a separate UX we don't want triggered by the common navigate key."""
- active = make_activewindow(workspace_name="special:stash", workspace_id=-92)
- self.set_state(active, make_layout("master"))
- self.run_script("next", "move")
- self.assertEqual(self.dispatches(), ["dispatch layoutmsg swapnext"])
-
- def test_layout_navigate_floating_special_next_focus_toggles_first(self):
- """Floating scratchpad (e.g. special:S-term foot) should also toggle off.
- After the toggle, the re-read state determines whether to take the
- floating branch or fall through to the layout branch."""
- active = make_activewindow(floating=True, workspace_name="special:S-term", workspace_id=-98)
- # After toggling S-term off, focus lands on a tiled window on ws 1
- post = make_activewindow(floating=False, workspace_name="1", workspace_id=1)
- self.set_state(active, make_layout("master"), next_activewindow=post)
- self.run_script("next")
- self.assertEqual(
- self.dispatches(),
- [
- "dispatch togglespecialworkspace S-term",
- "dispatch layoutmsg cyclenext",
- ],
- )
-
-
-if __name__ == "__main__":
- unittest.main()