aboutsummaryrefslogtreecommitdiff
path: root/tests/e2e.sh
diff options
context:
space:
mode:
Diffstat (limited to 'tests/e2e.sh')
-rwxr-xr-xtests/e2e.sh73
1 files changed, 72 insertions, 1 deletions
diff --git a/tests/e2e.sh b/tests/e2e.sh
index 983912c..425fdf3 100755
--- a/tests/e2e.sh
+++ b/tests/e2e.sh
@@ -29,13 +29,15 @@ PASSES=0
# Every dingger invocation below (daemon, focus, poke, menu bar) targets the
# isolated server and the test daemon's hook socket.
-export DINGGER_TMUX_SOCKET="$SOCK" DINGGER_AGENT_SOCKET="$HOOK"
+export DINGGER_TMUX_SOCKET="$SOCK" DINGGER_AGENT_SOCKET="$HOOK" DINGGER_STATE_DIR="$DIR/state"
t() { tmux -u -L "$SOCK" "$@"; }
cleanup() {
[ -n "${DAEMON_PID:-}" ] && kill "$DAEMON_PID" 2>/dev/null
+ [ -n "${DAEMON2_PID:-}" ] && kill "$DAEMON2_PID" 2>/dev/null
t kill-server 2>/dev/null
+ tmux -L "$SOCK-restore" kill-server 2>/dev/null
rm -rf "$DIR"
}
trap cleanup EXIT
@@ -345,6 +347,75 @@ else
fail "menu bar binary not found at $MENUBAR"
fi
+# --- 16. restore after the tmux server goes away ------------------------------
+# A second server and daemon. "viewer" hosts a client the way a terminal
+# does: Ghostty runs `tmux new-session -As 0`.
+echo "16. restore"
+R="$SOCK-restore"
+STATE2="$DIR/state2"
+r() { tmux -u -L "$R" "$@"; }
+renv() { env DINGGER_TMUX_SOCKET="$R" DINGGER_AGENT_SOCKET="$DIR/hook2.sock" DINGGER_STATE_DIR="$STATE2" \
+ DINGGER_TMUX_CONFIG=/dev/null "$@"; }
+W="$DIR/w"; mkdir -p "$W/one" "$W/two" "$W/three"
+start_viewer() {
+ r -f /dev/null new-session -d -s viewer -x 120 -y 40 \; set -g default-shell /bin/sh \; \
+ send-keys -t viewer "exec env -u TMUX tmux -u -L $R new-session -As 0 -c $W/one" Enter
+ for _ in $(seq 50); do r has-session -t =0 2>/dev/null && [ -n "$(r list-clients)" ] && return; sleep 0.1; done
+ echo "viewer client did not attach"
+}
+start_viewer
+P0=$(r display -p -t '=0:' '#{pane_id}')
+r rename-window -t "$P0" edit
+P1=$(r split-window -h -t "$P0" -c "$W/two" -P -F '#{pane_id}')
+r split-window -v -t "$P1" -c "$W/three"
+r resize-pane -t "$P0" -x 30
+r new-window -d -t '=0:5' -c "$W/two"
+r split-window -d -t '=0:5' -c "$W/three"
+r resize-pane -Z -t '=0:5.1'
+r select-window -t '=0:edit'
+r new-session -d -s work -n grid -c "$W/three" -x 100 -y 30
+for d in one two three; do r split-window -d -t '=work:grid' -c "$W/$d"; r select-layout -t '=work:grid' tiled; done
+r select-pane -t '=work:grid.2'
+r new-window -d -t '=work:7' -n logs -c "$W/one"
+r select-window -t '=work:7'
+r new-session -d -s keep -n saved -c "$W/one"
+r new-window -d -t '=keep:' -n saved2
+fingerprint() {
+ r list-panes -a -F '#{session_name}|#{window_index}|#{?automatic-rename,(auto),#{window_name}}|#{window_active}|#{window_zoomed_flag}|#{automatic-rename}|#{pane_index}|#{pane_active}|#{pane_left},#{pane_top},#{pane_width}x#{pane_height}|#{pane_current_path}' \
+ | grep -E '^(0|work)\|'
+}
+BEFORE=$(fingerprint)
+renv "$DINGGER" watch --interval-secs 1 --no-sounds --no-notifications >>"$DIR/daemon2.log" 2>&1 &
+DAEMON2_PID=$!
+latest_snapshot() { ls "$STATE2"/snapshots/*.json 2>/dev/null | tail -1; }
+expect "a snapshot of both sessions" "grep -c '\"name\": \"work\"' \"\$(latest_snapshot)\" 2>/dev/null" 1 80
+r kill-server
+expect "the dead server's layout becomes the restore candidate" \
+ "python3 -c 'import json,sys; c=json.load(open(sys.argv[1])); print(c[\"status\"], len(c[\"snapshot\"][\"sessions\"]))' $STATE2/restore.json 2>/dev/null" "offered 4" 80
+expect "the last snapshot is kept while tmux is down" "grep -c '\"name\": \"work\"' \"\$(latest_snapshot)\"" 1
+start_viewer
+r new-session -d -s keep -n mine "sleep 100000"
+expect "the new server has a bare 0" "r list-windows -t =0 -F '#{window_panes}' | tr '\n' ' '" "1 "
+expect "the candidate survives the new server's snapshots" \
+ "python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))[\"snapshot\"][\"server\"] != json.load(open(sys.argv[2]))[\"server\"])' $STATE2/restore.json \"\$(latest_snapshot)\"" True 80
+expect "dry run plans: replace 0, keep keep and viewer, create work" \
+ "renv \"$DINGGER\" restore --dry-run | cut -c1-14 | sort | tr '\n' ';'" "create work:;keep keep:;keep viewe;replace 0: 2 ;"
+renv "$DINGGER" restore >"$DIR/restore.out" 2>&1
+expect "restore succeeds" "grep -c FAILED $DIR/restore.out" 0
+expect "windows, panes, layouts, directories, focus, zoom are back" "fingerprint" "$BEFORE"
+expect "the terminal's client moved to the restored 0, still attached" "r list-clients -F '#{session_name}'" 0
+expect "a used session is left alone" "r list-windows -t =keep -F '#{window_name}'" mine
+expect "the candidate is marked restored" \
+ "python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))[\"status\"])' $STATE2/restore.json" restored
+r resize-window -t '=viewer:' -x 100 -y 30
+expect "restored windows follow the terminal's size again" "r display -p -t '=0:edit' '#{window_width}'" 100
+r resize-window -t '=viewer:' -x 120 -y 40
+expect "and back" "r display -p -t '=0:edit' '#{window_width}'" 120
+renv "$DINGGER" restore >"$DIR/restore2.out" 2>&1
+expect "restoring again changes nothing" "fingerprint" "$BEFORE"
+expect "restoring again plans nothing" "grep -cvE '^keep ' $DIR/restore2.out" 0
+[ "$FAILS" -gt 0 ] && { echo "--- restore output:"; cat "$DIR/restore.out"; echo "--- before:"; echo "$BEFORE"; echo "--- after:"; fingerprint; }
+
echo
echo "$PASSES passed, $FAILS failed"
[ "$FAILS" -eq 0 ] || { echo "--- daemon log tail"; tail -30 "$DIR/daemon.log"; exit 1; }