aboutsummaryrefslogtreecommitdiff
path: root/tests/layout-navigate
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-06-02 12:16:38 -0500
committerCraig Jennings <c@cjennings.net>2026-06-02 12:16:38 -0500
commitb10cba594db836c0747066addad48bda4d30cd02 (patch)
tree063119a623fa3f7139feda4ef302896d8f5f934c /tests/layout-navigate
parent49c2ba9c4510bf6e1acd306687473bc8ba9ad8dd (diff)
downloadarchsetup-b10cba594db836c0747066addad48bda4d30cd02.tar.gz
archsetup-b10cba594db836c0747066addad48bda4d30cd02.zip
refactor: drop in-repo dotfiles/, move stow tooling to the dotfiles repo
Since the installer clones DOTFILES_REPO into ~/.dotfiles and stows from there, the in-repo dotfiles/ tree was dead weight. Nothing reads it at install time. I removed it (831 files) now that both machines are migrated. The Makefile's stow / restow / reset / unstow / import targets and the dotfile-script unit suites moved to the dotfiles repo. They sit alongside the scripts they manage and run standalone (cd ~/.dotfiles && make ...). This Makefile keeps the VM-integration targets and the installer-helper suite (safe-rm-rf). I updated CLAUDE.md and README.md so stow operations run from ~/.dotfiles, and the dotfile-management, theme, and unit-test sections point at the standalone repo. The README was already describing the old in-repo model from before the installer switched to cloning. This brings it in line.
Diffstat (limited to 'tests/layout-navigate')
-rwxr-xr-xtests/layout-navigate/fake-hyprctl48
-rw-r--r--tests/layout-navigate/test_layout_navigate.py219
2 files changed, 0 insertions, 267 deletions
diff --git a/tests/layout-navigate/fake-hyprctl b/tests/layout-navigate/fake-hyprctl
deleted file mode 100755
index 701f397..0000000
--- a/tests/layout-navigate/fake-hyprctl
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/bin/sh
-# Fake hyprctl for testing layout-navigate.
-#
-# State files live in $FAKE_HYPR_DIR:
-# activewindow.json - first activewindow call returns this
-# activewindow.1.json - second call (after togglespecialworkspace) returns this, if present
-# layout.json - getoption general:layout returns this
-# dispatch.log - every "dispatch" invocation appended here (one line)
-# call-count - internal counter for activewindow calls
-
-: "${FAKE_HYPR_DIR:?FAKE_HYPR_DIR must be set}"
-
-cmd="$1"
-shift
-
-case "$cmd" in
- activewindow)
- # Count calls so tests can provide a post-toggle state
- count_file="$FAKE_HYPR_DIR/call-count"
- count=$(cat "$count_file" 2>/dev/null || echo 0)
- next=$((count + 1))
- echo "$next" > "$count_file"
-
- if [ "$count" -eq 0 ]; then
- cat "$FAKE_HYPR_DIR/activewindow.json"
- else
- # Try numbered file; fall back to original
- numbered="$FAKE_HYPR_DIR/activewindow.$count.json"
- if [ -f "$numbered" ]; then
- cat "$numbered"
- else
- cat "$FAKE_HYPR_DIR/activewindow.json"
- fi
- fi
- ;;
- getoption)
- cat "$FAKE_HYPR_DIR/layout.json"
- ;;
- dispatch)
- # Log the entire dispatch invocation as one line
- echo "dispatch $*" >> "$FAKE_HYPR_DIR/dispatch.log"
- echo "ok"
- ;;
- *)
- echo "fake-hyprctl: unknown command '$cmd'" >&2
- exit 1
- ;;
-esac
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()