aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xdotfiles/common/.local/bin/tmux-util36
-rwxr-xr-xtests/tmux-util/fake-tmux21
-rw-r--r--tests/tmux-util/test_tmux_util.py103
3 files changed, 159 insertions, 1 deletions
diff --git a/dotfiles/common/.local/bin/tmux-util b/dotfiles/common/.local/bin/tmux-util
index 2fa861d..4a7b596 100755
--- a/dotfiles/common/.local/bin/tmux-util
+++ b/dotfiles/common/.local/bin/tmux-util
@@ -36,6 +36,37 @@ EOF
}
# -----------------------------------------------------------------------------
+# go
+# -----------------------------------------------------------------------------
+
+cmd_go() {
+ local name="${1:-}"
+ if [ -z "$name" ]; then
+ echo "tmux-util go: missing session name" >&2
+ echo "Usage: tmux-util go <name>" >&2
+ return 2
+ fi
+
+ # `=name` forces exact match in `tmux has-session` (otherwise tmux does
+ # prefix matching, which would let `go foo` attach to a session named
+ # `foobar`).
+ if tmux has-session -t "=$name" 2>/dev/null; then
+ if [ -n "${TMUX:-}" ]; then
+ tmux switch-client -t "$name"
+ else
+ tmux attach-session -t "$name"
+ fi
+ else
+ if [ -n "${TMUX:-}" ]; then
+ tmux new-session -d -s "$name" -c "$PWD"
+ tmux switch-client -t "$name"
+ else
+ tmux new-session -s "$name" -c "$PWD"
+ fi
+ fi
+}
+
+# -----------------------------------------------------------------------------
# ls
# -----------------------------------------------------------------------------
@@ -144,7 +175,10 @@ main() {
ls)
cmd_ls "$@"
;;
- go|pick|find|rename)
+ go)
+ cmd_go "$@"
+ ;;
+ pick|find|rename)
echo "tmux-util: subcommand '$sub' is not implemented yet" >&2
return 2
;;
diff --git a/tests/tmux-util/fake-tmux b/tests/tmux-util/fake-tmux
index ba2d5cc..163ea24 100755
--- a/tests/tmux-util/fake-tmux
+++ b/tests/tmux-util/fake-tmux
@@ -97,6 +97,8 @@ case "$cmd" in
*) shift ;;
esac
done
+ # tmux accepts a `=name` form to force exact match; strip the prefix.
+ session="${session#=}"
while IFS=' ' read -r name attached pids _rest; do
if [ "$name" = "$session" ]; then
exit 0
@@ -104,6 +106,25 @@ case "$cmd" in
done < "$STATE"
exit 1
;;
+ new-session)
+ detached=0
+ name=""
+ cwd=""
+ while [ "$#" -gt 0 ]; do
+ case "$1" in
+ -s) shift; name="$1"; shift ;;
+ -c) shift; cwd="$1"; shift ;;
+ -d) detached=1; shift ;;
+ *) shift ;;
+ esac
+ done
+ attached=1
+ [ "$detached" -eq 1 ] && attached=0
+ printf '%s %s - 0 1 %s\n' "$name" "$attached" "${cwd:-/tmp}" >> "$STATE"
+ ;;
+ attach-session|switch-client)
+ # No state mutation needed — the call log already records intent.
+ ;;
kill-session)
session=""
while [ "$#" -gt 0 ]; do
diff --git a/tests/tmux-util/test_tmux_util.py b/tests/tmux-util/test_tmux_util.py
index 283c3c7..24cc335 100644
--- a/tests/tmux-util/test_tmux_util.py
+++ b/tests/tmux-util/test_tmux_util.py
@@ -374,5 +374,108 @@ class TestLsBoundary(TmuxUtilHarness):
self.assertNotIn(cwd_under_home, result.stdout)
+# -----------------------------------------------------------------------------
+# go — Normal cases
+# -----------------------------------------------------------------------------
+
+class TestGoNormal(TmuxUtilHarness):
+
+ def test_go_existing_session_outside_tmux_attaches(self):
+ self.set_sessions([
+ ("work", 0, [201], 900, 1, "/tmp/work"),
+ ])
+ # Outside tmux: TMUX env should be unset.
+ env = {k: v for k, v in os.environ.items() if k != "TMUX"}
+ result = subprocess.run(
+ [SCRIPT, "go", "work"],
+ env={**env, "PATH": self.bin_dir + os.pathsep + env.get("PATH", ""),
+ "FAKE_TMUX_DIR": self.tmp},
+ capture_output=True, text=True, timeout=10,
+ )
+ self.assertEqual(result.returncode, 0, msg=result.stderr)
+ calls = self.tmux_calls()
+ # has-session should be the first existence check (exact-match form)
+ self.assertTrue(any("has-session" in c and "work" in c for c in calls),
+ f"expected has-session probe in {calls!r}")
+ self.assertTrue(any("attach-session -t work" in c for c in calls),
+ f"expected attach-session -t work in {calls!r}")
+ # Must NOT create a new session
+ self.assertFalse(any("new-session" in c for c in calls),
+ f"unexpected new-session in {calls!r}")
+
+ def test_go_existing_session_inside_tmux_switches_client(self):
+ self.set_sessions([
+ ("work", 0, [201], 900, 1, "/tmp/work"),
+ ])
+ result = self.run_script("go", "work", env_extra={"TMUX": "/tmp/fake-tmux-socket,1234,0"})
+ self.assertEqual(result.returncode, 0, msg=result.stderr)
+ calls = self.tmux_calls()
+ self.assertTrue(any("switch-client -t work" in c for c in calls),
+ f"expected switch-client in {calls!r}")
+ # attach-session shouldn't be called inside tmux
+ self.assertFalse(any("attach-session" in c for c in calls),
+ f"unexpected attach-session in {calls!r}")
+
+ def test_go_missing_session_outside_tmux_creates(self):
+ self.set_sessions([])
+ env = {k: v for k, v in os.environ.items() if k != "TMUX"}
+ result = subprocess.run(
+ [SCRIPT, "go", "fresh"],
+ env={**env, "PATH": self.bin_dir + os.pathsep + env.get("PATH", ""),
+ "FAKE_TMUX_DIR": self.tmp},
+ cwd="/tmp",
+ capture_output=True, text=True, timeout=10,
+ )
+ self.assertEqual(result.returncode, 0, msg=result.stderr)
+ calls = self.tmux_calls()
+ # new-session with -s and -c pointing at the subprocess's cwd
+ self.assertTrue(
+ any("new-session" in c and "-s fresh" in c and "-c /tmp" in c
+ for c in calls),
+ f"expected new-session -s fresh -c /tmp in {calls!r}",
+ )
+
+ def test_go_missing_session_inside_tmux_creates_detached_then_switches(self):
+ self.set_sessions([])
+ env = os.environ.copy()
+ env.update({
+ "PATH": self.bin_dir + os.pathsep + env.get("PATH", ""),
+ "FAKE_TMUX_DIR": self.tmp,
+ "TMUX": "/tmp/fake-tmux-socket,1234,0",
+ })
+ result = subprocess.run(
+ [SCRIPT, "go", "fresh"],
+ env=env,
+ cwd="/tmp",
+ capture_output=True, text=True, timeout=10,
+ )
+ self.assertEqual(result.returncode, 0, msg=result.stderr)
+ calls = self.tmux_calls()
+ self.assertTrue(
+ any("new-session" in c and "-d" in c and "-s fresh" in c and "-c /tmp" in c
+ for c in calls),
+ f"expected detached new-session in {calls!r}",
+ )
+ self.assertTrue(any("switch-client -t fresh" in c for c in calls),
+ f"expected switch-client after create in {calls!r}")
+
+
+# -----------------------------------------------------------------------------
+# go — Error / Boundary cases
+# -----------------------------------------------------------------------------
+
+class TestGoErrors(TmuxUtilHarness):
+
+ def test_go_with_no_name_exits_nonzero(self):
+ result = self.run_script("go")
+ self.assertNotEqual(result.returncode, 0)
+ self.assertIn("missing session name", result.stderr)
+
+ def test_go_with_empty_string_name_exits_nonzero(self):
+ result = self.run_script("go", "")
+ self.assertNotEqual(result.returncode, 0)
+ self.assertIn("missing session name", result.stderr)
+
+
if __name__ == "__main__":
unittest.main()